<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    honzeland

    記錄點(diǎn)滴。。。

    常用鏈接

    統(tǒng)計

    Famous Websites

    Java

    Linux

    P2P

    最新評論

    2008年2月15日 #

    Interesting books read or being read

    Oracle Performance Tuning for 10gR2, Second Edition -- http://www.amazon.com/Oracle-Performance-Tuning-10gR2-Second/dp/1555583458

    posted @ 2011-04-07 15:30 honzeland 閱讀(202) | 評論 (0)編輯 收藏

    GAE Logging

    Official document: http://code.google.com/appengine/docs/java/runtime.html#Logging  
    Log4j configuration in production env:
    http://blog.xam.de/2010/03/logging-in-google-appengine-for-java.html 
    http://www.mail-archive.com/google-appengine-java@googlegroups.com/msg06396.html

    posted @ 2010-11-11 12:52 honzeland 閱讀(269) | 評論 (0)編輯 收藏

    Read a Stress Test Report

    Load Average: 

    1. http://www.teamquest.com/resources/gunther/display/5/index.htm
    2. 
    http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages (Great)

    posted @ 2010-11-05 14:16 honzeland 閱讀(275) | 評論 (0)編輯 收藏

    GAE Mapping

    Executing Simple Joins Across Owned Relationships

    posted @ 2010-10-27 13:27 honzeland 閱讀(250) | 評論 (0)編輯 收藏

    Servlet Mappings - rules, pattern....

    http://www.rawbw.com/~davidm/tini/TiniHttpServer/docs/ServletMappings.html

    posted @ 2010-10-22 22:41 honzeland 閱讀(286) | 評論 (0)編輯 收藏

    GWT-RPC in a Nutshell - go through the internal

    GWT-RPC in a Nutshell: http://www.gdssecurity.com/l/b/2009/10/08/gwt-rpc-in-a-nutshell/

    posted @ 2010-10-22 22:40 honzeland 閱讀(223) | 評論 (0)編輯 收藏

    [zz] Tuning Your Stress Test Harness

    HTTP://WWW.THESERVERSIDE.COM/NEWS/1365219/TUNING-YOUR-STRESS-TEST-HARNESS?ASRC=SS_CLA_315053&PSRC=CLT_81

    posted @ 2010-09-11 12:27 honzeland 閱讀(243) | 評論 (0)編輯 收藏

    GWT 2 Spring 3 JPA 2 Hibernate 3.5 Tutorial – Eclipse and Maven 2 showcase

    See details at: http://www.javacodegeeks.com/2010/07/gwt-2-spring-3-jpa-2-hibernate-35.html
    Executing Simple Joins Across Owned Relationships for gae: http://gae-java-persistence.blogspot.com/2010/03/executing-simple-joins-across-owned.html

    posted @ 2010-08-20 13:01 honzeland 閱讀(417) | 評論 (0)編輯 收藏

    Java remote invocation frameworks (RPC)

    1. Remote Method Invocation (RMI)

    2. Hessian

    3. Burlap

    4. HTTP invoker

    5. EJB

    6. JAX-RPC

    7. JMX

    posted @ 2010-06-09 14:25 honzeland 閱讀(249) | 評論 (0)編輯 收藏

    Tomcat Architecture Diagram

    zz from http://marakana.com/forums/tomcat/general/106.html


    Valve and Filter:
    "Valve" is Tomcat specific notion, and they get applied at a higher level than anything in a specific webapp. Also, they work only in Tomcat.

    "Filter" is a Servlet Specification notion and should work in any compliant servlet container. They get applied at a lower level than all of Tomcat's
    Valves.

    However, consider also the division between your application and the application  server. Think whether the feature you're planning is part of your application, or is it rather a generic feature of the application server, which could have uses in other applications as well. This would be the correct criteria to decide between Valve and Filter.

    Order for filter: The order in which they are defined matters. The container will execute the filters in the order in which they are defined.

    posted @ 2010-05-10 10:39 honzeland 閱讀(1541) | 評論 (0)編輯 收藏

    Hibernate Annotations

    Use one single table "blank_fields" for both A and B. "blank_fields" has fields: 'ref_id', 'blank_field', 'type'. 'type' is used to identify which entity the record belongs to. Use 'type' + 'ref_id' to specify the collection of elements for one entity.

    @Entity
    @Table(name 
    = "table_a")
    public class A {
        
    private Set<BlankField> blankFields = new HashSet<BlankField>();
       
        @CollectionOfElements
        @Fetch(FetchMode.SUBSELECT)
        @Enumerated(EnumType.ORDINAL)
        @JoinTable(name 
    = "blank_fields", joinColumns = { @JoinColumn(name = "ref_id") })
        @Cascade(value 
    = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
        @Column(name 
    = "blank_field", nullable = false)
        @SQLInsert(sql 
    = "INSERT INTO blank_fields(ref_id, blank_field, type) VALUES(?,?,0)")
        @Where(clause 
    = "type=0")
        
    public Set<BlankField> getBlankFields() { // BlankField is an enum
            
    return blankFields;
        }

        @SuppressWarnings(
    "unused")
        
    private void setBlankFields(Set<BlankField> blankFields) {
            
    this.blankFields = blankFields;
        }
    // End B

    @Entity
    @Table(name 
    = "table_b")
    public class B {
        
    private Set<BlankField> blankFields = new HashSet<BlankField>();
       
        @CollectionOfElements
        @Fetch(FetchMode.SUBSELECT)
        @Enumerated(EnumType.ORDINAL)
        @JoinTable(name 
    = "blank_fields", joinColumns = { @JoinColumn(name = "ref_id") })
        @Cascade(value 
    = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
        @Column(name 
    = "blank_field", nullable = false)
        @SQLInsert(sql 
    = "INSERT INTO blank_fields(ref_id, blank_field, type) VALUES(?,?,1)"// used for insert
        @Where(clause = "type=1"// used for query, if not @CollectionOfElements, such as @OneToMany, use @WhereJoinTable instead
        public Set<BlankField> getBlankFields() {
            
    return blankFields;
        }

        @SuppressWarnings(
    "unused")
        
    private void setBlankFields(Set<BlankField> blankFields) {
            
    this.blankFields = blankFields;
        }
    }

    當(dāng)然還有其他的方式來實(shí)現(xiàn)上面的需求,上面采用的單表來記錄不同實(shí)體的associations(這兒是CollectionOfElements,并且返回的是Set<Enum>,不是Set<Embeddable>),然后用'type'來區(qū)分不同的實(shí)體,這樣做的好處是:數(shù)據(jù)庫冗余少,易于擴(kuò)展,對于新的實(shí)體,只需加一個type值,而不需更改數(shù)據(jù)庫表結(jié)構(gòu)。另外一種采用單表的方式是為每個實(shí)體增加新的字段,如
    "blank_fields": 'a_id', 'b_id', 'blank_field', a_id reference table_a (id), b_id reference table_b (id). 這樣在映射的時候更簡單,
    對于A,映射為
    @JoinTable(name = "blank_fields", joinColumns = { @JoinColumn(name = "a_id") })
    對于B,映射為
    @JoinTable(name = "blank_fields", joinColumns = { @JoinColumn(name = "b_id") })
    這樣作的缺點(diǎn)是:帶來了數(shù)據(jù)庫冗余,對于blank_fields來講,任一條記錄,a_id和b_id中只有一個不為null。當(dāng)多個實(shí)體共用這個表時,用上面的方法更合理,如果共用實(shí)體不多時,這種方法更方便。

    posted @ 2010-04-20 17:20 honzeland 閱讀(454) | 評論 (0)編輯 收藏

    One Hibernate Session Multiple Transactions

    The case to use One Hibernate Session Multiple Transactions:
    each transaction would NOT affect others.
    i.e., open multiple transactions on the same session, even though one transaction rolls back, other transactions can be committed. If one action fails, others should fail too, then we should use one transaction for all actions.

    Note:
    A rollback with a single Session will lead to that Session being cleared (through "Session.clear()").
    So do lazy collections still work if the session is cleared? =>Not of any objects that you loaded up until the rollback. Only for new objects loaded afterwards.
    We should load necessary objects to session for each transactional action to avoid LazyInitializationException, even if those objects are loaded before other forward transactional actions, since forward action may be rolled back and clear the session.

    BTW, Hibernate Session.merge() is different with Session.update() by:
    Item item2 = session.merge(item);
    item2 
    == item; // false, item - DETACHED, item2 - PERSIST
    session.update(item); // no return value, make item PERSIST


    posted @ 2010-03-01 11:47 honzeland 閱讀(409) | 評論 (0)編輯 收藏

    org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

    發(fā)生這種異常的case:
        @Transactional
        
    public void foo() {
            
    try{
                bar();
            } 
    catch (RuntimeException re) {
                
    // caught but not throw further
                
            }
            
        }

        @Transactional
        
    public void bar() {
            
        }
    如果foo在調(diào)用bar的時候,bar拋出RuntimeException,Spring在bar return時將Transactional標(biāo)記為Rollback only, 而foo捕獲了bar的RuntimeException,所以Spring將會commit foo的事務(wù),但是foo和bar使用的是同一事務(wù),因此在commit foo事務(wù)時,將會拋出UnexpectedRollbackException。注意:如果foo和bar在同一class中,不會出現(xiàn)這種情況,因?yàn)椋?br />
    Since this mechanism is based on proxies, only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with @Transactional!

    可以通過配置log4j來debug Spring事務(wù)獲取情況:
    To delve more into it I would turn up your log4j logging to debug and also look at what ExerciseModuleController is doing at line 91, e.g.: add a logger for org.springframework.transaction

    posted @ 2010-02-24 18:02 honzeland 閱讀(6984) | 評論 (0)編輯 收藏

    Discussion for Open Session In View Pattern for Hibernate

    From: http://www.mail-archive.com/stripes-users@lists.sourceforge.net/msg02908.html


    posted @ 2010-01-29 17:20 honzeland 閱讀(212) | 評論 (0)編輯 收藏

    Quartz scheduled executions

    這周被Quartz折騰了一番。
    我們知道,Quartz采用JobDataMap實(shí)現(xiàn)向Job實(shí)例傳送配置屬性,正如Quartz官方文檔說的那樣:

    How can I provide properties/configuration for a Job instance? The key is the JobDataMap, which is part of the JobDetail object.
    The JobDataMap can be used to hold any number of (serializable) objects which you wish to have made available to the job instance when it executes.
    JobDataMap map = context.getJobDetail().getJobDataMap();

    我們通過map向Job實(shí)例傳送多個objects,其中有一個是個bean,一個是基本類型。對于scheduled triggers,我們要求bean對于所有的序列都不變,包括其屬性,而基本類型可以在Job運(yùn)行過程中改變,并影響下一個序列。實(shí)際情況是,對于下個序列,bean的屬性被上次的修改了,而基本類型卻維持第一次put到Map里面的值。正好和我們要求的相反。

    受bean的影響,以為map里面包含的都是更新的對象,即每個序列里面的JobDetail是同一個對象,但是基本類型的結(jié)果否認(rèn)了這一點(diǎn)。回頭重新翻閱了下Quartz的文檔:

    Now, some additional notes about a job's state data (aka JobDataMap): A Job instance can be defined as "stateful" or "non-stateful". Non-stateful jobs only have their JobDataMap stored at the time they are added to the scheduler. This means that any changes made to the contents of the job data map during execution of the job will be lost, and will not seen by the job the next time it executes.

    Job有兩個子接口:StatefulJob and InterruptableJob,我們繼承的是InterruptableJob,或許Quartz應(yīng)該有個InterruptableStatefulJob。另外StatefulJob不支持并發(fā)執(zhí)行,和我們的需求不匹配,我們有自己的同步控制,Job必須可以并發(fā)運(yùn)行。

    然后查看了Quartz的相關(guān)源碼:

    // RAMJobStore.storeJob
    public void storeJob(SchedulingContext ctxt, JobDetail newJob,
                
    boolean replaceExisting) throws ObjectAlreadyExistsException {
            JobWrapper jw 
    = new JobWrapper((JobDetail)newJob.clone()); // clone a new one
            .
            jobsByFQN.put(jw.key, jw);
            
    }

    也就是說,store里面放的是初始JobDetail的克隆,在序列運(yùn)行完時,只有StatefulJob才會更新store里面的JobDetail:

    // RAMJobStore.triggeredJobComplete
    public void triggeredJobComplete(SchedulingContext ctxt, Trigger trigger,
                JobDetail jobDetail, 
    int triggerInstCode) {
        JobWrapper jw 
    = (JobWrapper) jobsByFQN.get(jobKey);
        
        
    if (jw != null) {
            JobDetail jd 
    = jw.jobDetail;
            
    if (jd.isStateful()) {
                JobDataMap newData 
    = jobDetail.getJobDataMap();
                
    if (newData != null) {
                    newData 
    = (JobDataMap)newData.clone();
                    newData.clearDirtyFlag();
                }
                jd.setJobDataMap(newData); 
    // set to new one
                
            
        }

    }



    然后,每次序列運(yùn)行時所用的JobDetail,是存放在Store里面的克隆。

    // RAMJobStore.retrieveJob
    public JobDetail retrieveJob(SchedulingContext ctxt, String jobName,
            String groupName) {
        JobWrapper jw 
    = (JobWrapper) jobsByFQN.get(JobWrapper.getJobNameKey(
            jobName, groupName));
        
    return (jw != null? (JobDetail)jw.jobDetail.clone() : null// clone a new
    }


    問題很清楚了,存放在Store里面的JobDetail是初始對象的克隆,然后每個序列所用的JobDetail, 是Store里面的克隆,只有Stateful job,Store里面的JobDetail才更新。
    最有Quartz里面使用的clone():

    // Shallow copy the jobDataMap.  Note that this means that if a user
    // modifies a value object in this map from the cloned Trigger
    // they will also be modifying this Trigger.
    if (jobDataMap != null) {
        copy.jobDataMap 
    = (JobDataMap)jobDataMap.clone();
    }


    所以對于前面所講的,修改bean的屬性,會影響所有clone的對象,因此,我們可以將基本類型封裝到一個bean里面,map里面存放的是bean,然后通過修改bean的屬性,來達(dá)到影響下一個序列的目的。

    posted @ 2010-01-21 17:38 honzeland 閱讀(409) | 評論 (0)編輯 收藏

    Web application design: the REST of the story

    From: Web application design: the REST of the story
    Key points:
    • HTTP is a very general, scalable protocol. While most people only think of HTTP as including the GET and POST methods used by typical interactive browsers, HTTP actually defines several other methods that can be used to manipulate resources in a properly designed application (PUT and DELETE, for instance). The HTTP methods provide the verbs in a web interaction.
    • Servers are completely stateless. Everything necessary to service a request is included by the client in the request.
    • All application resources are described by unique URIs. Performing a GET on a given URI returns a representation of that resource's state (typically an HTML page, but possibly something else like XML). The state of a resource is changed by performing a POST or PUT to the resource URI. Thus, URIs name the nouns in a web interaction.


    posted @ 2010-01-08 14:50 honzeland 閱讀(254) | 評論 (0)編輯 收藏

    實(shí)話實(shí)說:應(yīng)用型和研究性

    剛剛看CCTV實(shí)話實(shí)說,很有感觸,義烏技術(shù)職業(yè)學(xué)院給人眼前一亮,尤其是他們副院長的一番言論。
    技術(shù)職業(yè)學(xué)院非得要升本科,本科非要成清華,義烏職業(yè)技術(shù)學(xué)院副院長評價當(dāng)前高校的現(xiàn)狀,定位嚴(yán)重有問題,技術(shù)職業(yè)學(xué)院應(yīng)該培養(yǎng)應(yīng)用型人才,而清華就應(yīng)該培養(yǎng)研究性人才,兩種學(xué)校的定位不能一樣,培養(yǎng)方式,評判標(biāo)準(zhǔn)都應(yīng)該不同,而現(xiàn)在大多數(shù)高校的定位都一樣,這是不對的。個人非常贊同這個觀點(diǎn),其實(shí),這個觀點(diǎn)也可以應(yīng)用到我們這些剛開始工作的年輕人身上,消除浮躁,找準(zhǔn)定位,然后沿著定位踏實(shí)做事,并且應(yīng)該采取相應(yīng)的評判標(biāo)準(zhǔn),這個很重要。

    posted @ 2009-04-12 19:35 honzeland 閱讀(127) | 評論 (0)編輯 收藏

    SCEP(Simple Certificate Enrollment Protocol)

    1. RFC documents

    2. SCEP operations
    • PKIOperation:      
      • Certificate Enrollment - request: PKCSReq, response: PENDING, FAILURE, SUCCESS
      • Poll for Requester Initial Certificate - request: GetCertInitial, response: same as for PKCSReq
      • Certificate Access - request: GetCert, response: SUCCESS, FAILURE
      • CRL Access - request: GetCRL, response: raw DER encoded CRL
    • Non-PKIOperation: clear HTTP Get
      • Get Certificate Authority Certificate - GetCACert, GetNextCACert, GetCACaps
      • Get Certificate Authority Certificate Chain - GetCACertChain
    3. Request message formats for PKIOperation
    • Common fields in all PKIOperation messages:
      • senderNonce
      • transactionID
      • the SCEP message being transported(SCEP messages) -> encrypted using the public key of the recipient(Enveloped-data)
        -> signed by one of certificates(Signed-data): the requester can generate a self-signed certificate, or the requester can use
        a previously issued certificate, if the RA/CA supports the RENEWAL option.
    • SCEP messages:
      • PKCSReq: PKCS#10
      • GetCertInitial: messages for old versions of scep clients such as Sscep, AutoSscep, and Openscep, are different with draft-18
               issuerAndSubject ::= SEQUENCE {
                    issuer Name,
                    subject Name
               }
      • GetCert: an ASN.1 IssuerAndSerialNumber type, as specified in PKCS#7 Section 6.7
      • GetCRL: an ASN.1 IssuerAndSerialNumber type, as defined in PKCS#7 Section 6.7

    posted @ 2009-02-17 14:18 honzeland 閱讀(1709) | 評論 (2)編輯 收藏

    RAM percentage utilised in Linux

    --zz: http://forums13.itrc.hp.com/service/forums/questionanswer.do?admit=109447627+1230261484567+28353475&threadId=1213960

    Question:
    We are planning to calculate the percentage of physical memory utilised as below:

    System Page Size: 4Kbytes
    Memory: 5343128K (1562428K) real, 13632356K (3504760K) virtual, 66088K free Page# 1/604

    Now the formula goes as below:

    (free memory / actual active real memory) * 100
    (66088/1562428) * 100 = 4.22 %

    Please let us know if its the correct formula .

    Mainly we are interested in RAM percentage utilised

    Reply 1:
    Red Hat/Centos v 5 take spare ram and use it for a buffer cache.

    100% memory allocation is pretty meaningless because allocation is almost always near 100%. The 2.6.x kernel permits rapid re-allocation of buffer to other purposes eliminating a performance penalty that you see on an OS like HP-UX

    I'm not thrilled with your formula because it includes swap(virtual memory). If you start digging too deep into virtual memory, your system start paging processes from memory to disk and back again and slows down badly.

    The formula is however essentially correct.

    Reply 2:
    Here, a quick example from the machine under my desk:
    Mem:   3849216k total,  3648280k used,   200936k free,   210960k buffers
    Swap:  4194296k total,       64k used,  4194232k free,  2986460k cached

    If the value of 'Swap used' is up (i.e. hundreds of megabytes), then you've got an issue, but as you can see, it's only 64k here.
    Your formula for how much memory is used is something along the lines of this:

    (Used - (Buffers + Cached) / Total) * 100 = Used-by-programs%
    (Free + Buffers + Cached / Total) * 100 = Free%

    .. Roughly ..



    posted @ 2008-12-26 12:08 honzeland 閱讀(271) | 評論 (0)編輯 收藏

    GWT/Tomcat will re-call servlet.

     昨天遇到個非常奇怪的bug:更新了一下后臺的代碼,結(jié)果每次點(diǎn)擊頁面都會導(dǎo)致servlet方法調(diào)用兩次,從而頁面報錯(邏輯上不讓調(diào)兩次 ),我們的前臺采用gwt,servlet engine采用tomcat,debug的時候,斷點(diǎn)放在servlet所調(diào)用的method上,結(jié)果invoke兩次,由此斷定,前臺代碼的問題(有點(diǎn)武斷哦),然后負(fù)責(zé)前臺的同事debugging前臺的代碼,噼里啪啦半天。。。,說是前臺好像沒有調(diào)兩次(之所以用好像,是debugging時部分代碼走兩次,部分走一次),而我當(dāng)時的想法是,后臺怎么操作,也不至于讓servlet調(diào)用兩次吧,所以我個人就認(rèn)定是前臺邏輯導(dǎo)致重復(fù)rpc調(diào)用(gwt),但是這個bug在這兩天才出現(xiàn)的,從svn的歷史記錄來看,前臺代碼在這兩天基本沒什么改變,同事只好從svn上一個version接一個version的check,最后確定出兩個相鄰的versions,前一個能用,后一個出bug,這時我隱約感覺到是后臺的問題,但是還是想不明白,后臺的邏輯怎么就能讓前臺重復(fù)調(diào)用,非常不解,沒辦法,在同事的建議下,在servlet的那個method上加上一條debug信息,做了兩次試驗(yàn),一次是完整的代碼,一次是把method中調(diào)用后臺的接口注釋掉,結(jié)果從日志上看出,前一次試驗(yàn)debug信息打印了兩次,后一次試驗(yàn)debug只打印了一次,此時,確定是后臺邏輯影響了前臺的調(diào)用(此時,覺得走彎路了,為什么不早點(diǎn)做這個試驗(yàn),其實(shí)確定是前臺還是后臺的問題,只需要做這樣一個簡單的試驗(yàn)。。。)。接下來,我思考的就是到底是什么在作怪呢,對比svn上的兩個版本,只有兩處可能的改動,一處是將return改成throw exception, 一處是調(diào)用了Thread.currentThread.interrupt(),我一個感覺是后者,注掉這句后,一切OK,呵呵,慶幸沒有先嘗試前者,要不改動很大,。。。

    剛剛看了gwt的源碼,還沒找到問題的根源,我的觀點(diǎn)是,thread接收到interrupt信號時,會重復(fù)發(fā)送rpc調(diào)用,(呵呵,還沒確定)。。。

    posted @ 2008-12-04 10:26 honzeland 閱讀(1213) | 評論 (0)編輯 收藏

    感覺到了責(zé)任。。。

        最近心情不是很好,上周三,父親的一次意外,給家里本來平靜的生活帶來了很大的波瀾,我也第一次感受到來自于家庭的壓力,由此帶來的一系列問題,一直縈繞著我,責(zé)任,responsibility,是這幾天我告誡自己最多的一個詞,是啊,該到了承受家庭責(zé)任的時候了。
        父親的這次意外,揪住了全家人的心,我也更多的為兩位老人思考了,這兩天,老想起一句話:人只有經(jīng)歷的多了,才能成熟。我很喜歡類比,其實(shí)就跟我們做數(shù)學(xué)題一樣,看的多了,做的多了,考試的時候才能迎刃而解,什么東西,或許只有自己親身經(jīng)歷,才能體會其中的更多細(xì)節(jié),才能激發(fā)更多的收獲。
        祝福父親的身體早日康復(fù),bless。。。

    posted @ 2008-06-25 21:49 honzeland 閱讀(283) | 評論 (3)編輯 收藏

    命運(yùn)。。。

       最近,時不時地回想自己這一路的教育經(jīng)歷,使得我越來越相信——命運(yùn)!
       總體來說,我自認(rèn)為我這一路上走的太順利,缺少更多的經(jīng)歷,缺少一些該有的挫折!
       但是,順利歸順利,在兩次作抉擇的時候,隨機(jī)的選擇決定現(xiàn)在的方向!一次當(dāng)然是過獨(dú)木橋——高考,另一次是碩士入學(xué)時選擇導(dǎo)師!兩次選擇都很隨意,甚至于無意,尤其是第二次。第一次的隨意更多的是無知,而第二次的無意,卻源于自己的不適應(yīng)。隨意帶來了大學(xué)時代的混亂,無意卻給自己帶來了意外的收獲,人生無常,命運(yùn)有數(shù)。
       高考時,分?jǐn)?shù)超出了自己的預(yù)料,志愿填的有些草率,一方面,是因?yàn)樽约旱哪贻p和無知,另一方面是由于周圍缺少必要的指點(diǎn),填的很倉促,很隨意,非常的“高效”。正值00年高校擴(kuò)招猖獗之時,我所填報的學(xué)校就是由三個學(xué)校合并而成,并且是在高考的前兩個月宣布合并的,其中有兩個合并之前不是一本,但是合并之后,肯定都是一本了。我當(dāng)時選報了自動化這個專業(yè),當(dāng)時填的時候就因?yàn)楦咧邪嘀魅握f了一聲:“現(xiàn)在自動化是一個很好的方向。”然而,此時命運(yùn)開始現(xiàn)數(shù),其中有兩個學(xué)校都有自動化這個專業(yè),一個之前就是一本(合并后,稱之為‘校本部’,不知道這個是什么意思,或許我要去查查字典,好好揣測一下本部的含義。),另一個是三個學(xué)校中最差的一個,報道那天才知道有兩個自動化,但是由于剛合校,還沒來得及合并專業(yè),當(dāng)時就想,我該在哪個校區(qū)的自動化呢?最后隨著師長的指引,我被校車?yán)搅朔中^(qū),也就是那個最差的了,一路上,還在思索兩個自動化的分配算法,還是直到開學(xué)一個月以后,一次偶然的機(jī)會,才得知:兩個自動化是根據(jù)當(dāng)時各省分?jǐn)?shù)的交替順序分配,安徽省生源的第一名在本部,第二名在分校區(qū),第三名本部,第四名分校區(qū)。。。。只能怪自己被動的排在了一個偶數(shù)的序位上,如果用一個函數(shù)來表示這個序位的話,其自變量的個數(shù)還是蠻多的,當(dāng)年安徽省報考該校該專業(yè)的人生,你在這些人中的名次,另外還有,我還不太確定的因素,但是我能確定因素的存在。。。
       后來,進(jìn)一步得知,分校區(qū)的自動化之前沒有,我們是第一屆,當(dāng)時在合校之前就已經(jīng)確定要新增這個專業(yè),合的時候,各個學(xué)校的招生計劃都沒變,只是將三個計劃簡單的數(shù)學(xué)累加,現(xiàn)在看來,合校是多么的可笑,一個學(xué)校從任意層次可以一下成為中國最好的學(xué)校,只要清華愿意合并它,而合并后再很長一段時間,那個學(xué)校除了學(xué)生的層次提高之外,沒有任何的改變,教師還是那些教師,設(shè)施還是那些設(shè)施,思想還是那些思想,我不知道這可不可以稱之為赤裸裸的搶劫,它無視了那些默默地而踏踏實(shí)實(shí)前進(jìn)的高校,助長了一些不公正的風(fēng)氣,或許正應(yīng)了中國當(dāng)時浮躁的社會氛圍。
       就這樣在這度過了自己的三年大學(xué)時光,就在最后一個大學(xué)暑假之前,學(xué)校經(jīng)過三年的發(fā)展和磨合,決定將我們這個專業(yè)撤銷,統(tǒng)一合并到本部去,我們被迫搬回了第一天報道的地方,其實(shí)兩個自動化的方向是不一樣的,或許我們要慶幸,我們學(xué)習(xí)了兩個專業(yè),在大學(xué)的四年中,但是或許,更多的人可能會埋怨兩個方向影響了自己的學(xué)習(xí),其實(shí),我想,大多數(shù)的人根本不在于什么方向,什么專業(yè)了,一個大框架的混亂,注定了最終的結(jié)果,就像當(dāng)前的中國足球。。。
       我要說的是,其實(shí)我在大學(xué)中過得很愉快,我認(rèn)識了一批很好的同學(xué),我經(jīng)歷了到目前為止最好的一段時光,雖然期間有很多遺憾,比如沒談一次戀愛。。。我想這段時光勢必會在我的記憶集合中占據(jù)非常重要的一塊。這里,我只不過是要論述命運(yùn)有數(shù),這樣的一個過程多少還是影響了我的人生軌跡。
       下面要談?wù)撐业牡诙尉駬瘢T士時選擇導(dǎo)師。大學(xué)畢業(yè)時,我選擇了繼續(xù)就讀,一切都很順利,到了04年9月,我來到了新的學(xué)校,在合肥,這兒離家很近,因?yàn)槲沂前不杖耍?jīng)歷了大學(xué)時回家的艱辛,再加上我又是個比較戀家的人。剛?cè)胄#陀龅搅艘粋€。。。。


    明天繼續(xù)。。。。

    posted @ 2008-06-15 23:08 honzeland 閱讀(174) | 評論 (1)編輯 收藏

    Useful Links: ing...

    About Java:
    http://www.theserverside.com
    http://www.javablogs.com
    http://www.java2s.com
    Java(TM) Platform Performance: Strategies and Tactics
    A Simple Data Access Layer using Hibernate
    Discover the secrets of the Java Serialization API
    Setting up two-way (mutual) SSL with Tomcat on Java5
    Basic Tomcat Tour and Tomcat Security
    When Runtime.exec() won't
    Asynchronous processing support in Servlet 3.0
    About security:
    The Types Of Digital Certificates
    Cryptography Lecture PPT
    MD5 considered harmful today
    Cryptography Tutorials - Herong's Tutorial Notes
    Defective Sign & Encrypt in S/MIME, PKCS#7, MOSS, PEM, PGP, and XML
    Cryptography resources by Bouncycastle
    Others:
    Colors for the webColors for the web
    Test Frameworks
    Lightstreamer: a scalable and reliable Server for pushing live data to Rich Internet Applications
    工資計算器2009版

    posted @ 2008-06-03 15:09 honzeland 閱讀(286) | 評論 (0)編輯 收藏

    Vim使用技巧,長期更新中。。。

    1. 多行注釋:
     ctrl+v 進(jìn)入列模式,向下或向上移動光標(biāo),把需要注釋的行的開頭標(biāo)記起來,然后按大寫的I,再插入注釋符,比如#,再按esc,就會全部注釋了.

    posted @ 2008-05-31 17:22 honzeland 閱讀(287) | 評論 (0)編輯 收藏

    Managing HttpSession Objects

    zz: java.sys-con.com

    Java servlet technology provides developers with functionality, scalability, and portability that can't be found in other server-side languages. One feature of the Java servlet specification that's commonly used, and sometimes misused, is the HttpSession interface. This simple interface allows you to maintain a session or state for Web site visitors.

    In my previous article ("Introduction to Session Management," [JDJ, Vol. 7, issue 9]), I introduced you to session management and the HttpSession interface. In that article, we walked through using the HttpSession API to create, use, and destroy session objects for Web site visitors. The next step is to better understand how to manage the sessions and those objects in a session. This article will help you achieve this by helping you understand the following concepts:

    • Code-based session management through listeners
    • Proper design of the session and the objects it contains
    • Controlling what is in the session and why it's there
    • Session persistence
    • Memory management
    The Java APIs discussed in this article are from Sun's Java Servlet 2.3 specification.

    Listeners
    A listener is an object that's called when a specified event occurs. There are four listener interfaces that allow you to monitor changes to sessions and the objects that are in those sessions:

    • HttpSessionListener
    • HttpSessionBindingListener
    • HttpSessionAttributeListener
    • HttpSessionActivationListener
    Figure 1 provides a method summary for each of the listener interfaces. The implementing class that you write will override these methods to provide the functionality you need.

    HttpSessionListener
    The HttpSessionListener interface is used to monitor when sessions are created and destroyed on the application server. Its best practical use would be to track session use statistics for a server.

    The use of HttpSessionListener requires a configuration entry in the deployment descriptor, or web.xml file, of the application server. This entry points the server to a class that will be called when a session is created or destroyed. The entry required is simple. All you need is a listener and listener-class element in the following format. The listener-class element must be a fully qualified class name.

    <listener>
    <listener-class>package.Class</listener-class>
    </listener>

    As you can see in Figure 1, the class that implements this listener can override two methods: sessionCreated() and sessionDestroyed(). These methods will be notified when the server creates or destroys a session.

    These methods take an HttpSessionEvent object as a parameter. HttpSessionEvent is simply a class that represents notifications of changes to the Web application's sessions. HttpSessionEvent has one method, getSession(), that returns the HttpSession object that's been modified.

    HttpSessionBindingListener
    The HttpSessionBindingListener interface is implemented when an object needs to be notified if it's being bound to a session or unbound from a session.

    This interface has two methods, valueBound() and valueUnbound(), that are notified when the status of the object has changed (see Figure 1).

    These methods have an HttpSessionBindingEvent parameter that can be used to retrieve the session that the object was bound to and the name it was given in the session. In Figure 2, you can see the methods of this object that are used to get the name that's assigned to the object, the session it's bound to, and the actual object.

    HttpSessionAttributeListener
    The HttpSessionAttributeListener interface is used to monitor changes to attributes in any session on the server. This can be useful when you know the name assigned to a specific object that gets put into the session and you want to track how often it's being used.

    As with HttpSessionListener, HttpSessionAttributeListener also requires an entry in the deployment descriptor for the server. This entry tells the server which class to call when an attribute in a session has changed.

    The HttpSessionAttributeListener interface has three methods - attributeAdded(), attributeRemoved(), and attributeReplaced(). These methods, shown in Figure 1, are called by the server when attributes of a session are changed.

    HttpSessionActivationListener
    The final listener, HttpSessionActivationListener, is implemented when an object needs to know if the session that it's bound to is being activated or passivated (moved). You would come across this scenario if your session is being shared across JVMs or your server is persisting the session in a database or file system.

    This interface, displayed in Figure 1, has two methods that are overridden by the implementing class: sessionDidActivate() and sessionWillPassivate(). These methods are called when the status of the session in a JVM is changed.

    Session Persistence
    Today's J2EE-compliant servers allow for fault-tolerance and failover to provide support in the event that a server suddenly becomes unavailable because of hardware, software, or network failure. This support is usually provided by allowing two or more application servers, often called a cluster, to run together and provide backup support for each other. If one server fails, the others pick up the requests and continue on as if nothing happened. This allows your Web site visitors to keep going without interruption.

    A proxy server is usually used in front of the application servers. This server is responsible for directing each HTTP request to the appropriate server. The proxy server can be set up to ensure that the server receiving the first request from a user will continue to receive all subsequent requests from that user. This means that a session created for the user on the application server will continue to be available for that user. If the server suddenly fails, there has to be a system in place to allow the session to continue on without it.

    Session persistence allows the session contents to be saved outside the application server so that other servers can access it. Figure 3 shows the relationship between the persisted session data and the application servers that access it. In this figure, you see a client accessing a Web site's HTTP server. The HTTP server is forwarding requests for application resources to one of the application servers through the use of a proxy server. The application servers are persisting the session data in an external form.

    There are four types of session persistence:

    1. Memory persistence (one server or a cluster of two or more)
    2. File system persistence
    3. Database persistence
    4. Cookie persistence
    Every application server will handle session persistence differently and all servers may not support all types of persistence. Objects that are placed in the session must be serializable for persistence to work.

    Memory Persistence
    In most cases, a single standalone server will store sessions in memory. This allows for fast retrieval and update of the information. It also means that the session information will be lost when the server is shut down. This is usually the default configuration on most application servers. Memory persistence can be used when two or more servers need to share the session information. The application servers can be configured to share any changes made to the session so that the information is available on multiple servers. This redundancy of the session information helps the cluster preserve the session during a failure.

    File System Persistence
    File system persistence can be used to serialize any objects that are in the session. The object contents are placed in a file on the server. The location of the files created is configurable; however, the files must be accessible by all the servers in the cluster. The speed at which the file system is accessed can be a factor in the performance of your Web site. A slow disk drive, for example, would result in a delay as data is read from or written to the file.

    Database Persistence
    Database persistence can be used to provide a central data store for the session contents. Each application server in the cluster must be able to access the database. When sessions are modified, the changes are immediately persisted in the database. A data source is usually set up for JDBC persistence and the connections are pooled. This provides a quicker response. There's also the issue of database failover, which would be addressed at the database level of the system.

    Cookie Persistence
    The fourth type of session persistence, cookie persistence, is so ineffective and insecure that it doesn't deserve consideration when designing a fail-safe system. Cookie persistence, as the name implies, persists session data by storing the session information in browser cookie(s). There's a limitation on data handling because cookies store only text, not objects, and the amount of data that can be transmitted in a cookie is limited. There's also the fact that cookies transmit data back and forth between the client and the server. This prevents you (at least it should) from saving sensitive information, like a social security number. This type of persistence should be used in only the smallest of Web sites, and only if there's a good reason not to store the session in memory.

    The most common type of persistence is database persistence. It provides an efficient way of saving session data and it's usually fairly easy to set up on the application server. Memory persistence in a cluster is also easy to use, if your application server supports it. The only drawback is that sessions can sometimes hold large amounts of data. Storing the session in memory reduces the amount of memory available to the other processes on the server. File system persistence can be slow at times and the file system may not always be accessible to multiple servers.

    Watching the Session Size
    As you and your fellow employees work on a Web application, you may notice that more and more objects are being thrown into the session, often "for convenience" or "just temporarily." The session becomes a quick catch-all for any information you need to get from your servlets to your JSPs. The HttpSession interface makes sessions easy to use, which can lead to the session being overused. This is a concern because the session takes up space. In most cases that would be memory space. In other cases, it could be database or file system space. In all cases, it means more work for the server and more work for the programmers to manage what is there.

    Although the session is convenient because it's accessible from every servlet or JSP, it's not always the best place to put information. Most of the data that's retrieved for display in a Web application will only be used on one page. Instead of putting the information into the session scope, use the request scope and then forward the request from the servlet to the JSP. This causes the objects to be destroyed after the request has ended, which is after the data is displayed by the JSP. If you put the objects into the session, you would either have to remove them in your code or leave them there. Leaving objects in the session is not a good idea because you're using up valuable resources for no reason. This becomes even more of an issue when your Web site has hundreds or thousands of visitors, all of whom have a session that's loaded with objects.

    Some objects should be stored in the session. Objects that may be needed over and over again as a user moves through a Web site are those that should be put into the session. Anything that needs to exist longer than one request can be stored in the session, as long as these objects are removed as soon as they're no longer needed.

    Considerations for Managing Sessions
    When working with sessions, there are a few things to consider before designing or redesigning a Web application:

    • Are sessions needed in the application?
    • How long should the session be inactive before timing out?
    • Are all the objects in the session serializable?
    • Are the objects being bound to the session too large?
    • Do the objects that are in the session really need to be there?
    A Need for Sessions
    If you have unique users on a Web site and need to know who they are or need to get specific information to them, such as search results, then you should be using sessions. If you follow the guidelines set here, there's no reason not to use the HttpSession interface that Java provides. It's easy to use, flexible, secure, and it helps you to build a better Web site.

    There's another architecture that deals with maintaining state for a client. Instead of relying on the HttpSession interface, state for clients can be maintained within Enterprise JavaBeans (EJBs). The EJB architecture takes the business logic for an application and places it in components or beans. A session bean is a type of EJB that exists for a given client/server session and provides database access or other business logic, such as calculations. Session beans can be stateless or they can maintain the state for a client, very much like an HttpSession object.

    There is still some debate over where the state for a Web site visitor should be maintained. The best design for the application at this time is to continue using the HttpSession object for maintaining the state of the presentation layer of the Web application and to use stateful EJBs to maintain the state of the business logic and data layer. There are many other factors that should be considered with EJBs, one being the better performance of stateless beans over those that maintain state. These issues, which are outside the scope of this article, should be considered carefully when architecting an application.

    Session Timeout
    By default, on most servers the session is set to expire after 30 minutes of inactivity. The amount of time can be configured in the deployment descriptor of the Web application. The HttpSession API also provides a setMaxInactiveInterval() method that you can use to specify the timeout period for a session. The getMaxInactiveInterval() method will return this timeout value. The value given is in seconds.

    The length of time will vary depending on what your visitors are doing on your site. If they're logging in to check their account balance, a shorter session timeout period can be used because it doesn't take long for a person to read a couple of numbers. If, on the other hand, the user is logging in to read large amounts of data, you need to be sure that you provide enough time for the user to do what he or she wants without being logged out. If the user is constantly navigating through your site, the session will last indefinitely.

    Implement Serializable
    It's important to make sure that all objects placed in the session can be serialized. This may not be an issue if you know that your Web application will not run in a cluster, but it should still be done anyway. What happens if your Web site grows too big for one server and you suddenly have to move to two? If you implement Serializable in your code now, you won't have to go back and do it later.

    Keep It Simple
    You should design objects that are going to be placed into a session so that they're not too big and don't contain unnecessary information. A JavaBean that contains a customer's name, address, phone number, e-mail address, credit card numbers, and order history should not be placed into the session if you're only going to use the object to get the customer's name.

    Session Contents
    When you're working on a Web site, it's important to know which objects are in the session and why they're needed. The size of the session should be kept as small as possible. If you're building a new Web site, work out ahead of time what goes in the session, why it's there, and where it gets removed. If you're redesigning an existing site, this may be a little tougher, especially when you have hundreds of servlets and JSPs to deal with. In this case, try implementing an HttpSessionAttributeListener to get an idea of what is going into the session. With this information, you may be able to better manage your sessions.

    Conclusion
    Hopefully this article helped you to better understand the design issues involved in using the HttpSession interface. Java provides a more robust session implementation than other languages. It's because of this power and flexibility that you must take the time to properly lay out the use of the session. A well-designed session will help make a Web application better for the programmers and the users.

    References

  • Hall, M. (2002). More Servlets and JavaServer Pages. Prentice Hall PTR.
  • Java Servlet Technology: http://java.sun.com/products/servlet
  • Enterprise JavaBeans Technology: http://java.sun.com/products/ejb
  • Java BluePrints (J2EE): http://java.sun.com/blueprints/guidelines/ designing_enterprise_applications


  • 另外,還有一些收集的材料
    關(guān)于HttpSession的誤解實(shí)在是太多了,本來是一個很簡單的問題,怎會搞的如此的復(fù)雜呢?下面說說我的理解吧:
    1、HTTP協(xié)議本身是“連接-請求-應(yīng)答-關(guān)閉連接”模式的,是一種無狀態(tài)協(xié)議(HTTP只是一個傳輸協(xié)議);
    2、Cookie規(guī)范是為了給HTTP增加狀態(tài)跟蹤用的(如果要精確把握,建議仔細(xì)閱讀一下相關(guān)的RFC),但不是唯一的手段;
    3、所謂Session,指的是客戶端和服務(wù)端之間的一段交互過程的狀態(tài)信息(數(shù)據(jù));這個狀態(tài)如何界定,生命期有多長,這是應(yīng)用本身的事情;
    4、由于B/S計算模型中計算是在服務(wù)器端完成的,客戶端只有簡單的顯示邏輯,所以,Session數(shù)據(jù)對客戶端應(yīng)該是透明的不可理解的并且應(yīng)該受控于服務(wù)端;Session數(shù)據(jù)要么保存到服務(wù)端(HttpSession),要么在客戶端和服務(wù)端之間傳遞(Cookie或url rewritting或Hidden input);
    5、由于HTTP本身的無狀態(tài)性,服務(wù)端無法知道客戶端相繼發(fā)來的請求是來自一個客戶的,所以,當(dāng)使用服務(wù)端HttpSession存儲會話數(shù)據(jù)的時候客戶端的每個請求都應(yīng)該包含一個session的標(biāo)識(sid, jsessionid 等等)來告訴服務(wù)端;
    6、會話數(shù)據(jù)保存在服務(wù)端(如HttpSession)的好處是減少了HTTP請求的長度,提高了網(wǎng)絡(luò)傳輸效率;客戶端session信息存儲則相反;
    7、客戶端Session存儲只有一個辦法:cookie(url rewritting和hidden input因?yàn)闊o法做到持久化,不算,只能作為交換session id的方式,即a method of session tracking),而服務(wù)端做法大致也是一個道理:容器有個session管理器(如tomcat的 org.apache.catalina.session包里面的類),提供session的生命周期和持久化管理并提供訪問session數(shù)據(jù)的 api;
    8、使用服務(wù)端還是客戶端session存儲要看應(yīng)用的實(shí)際情況的。一般來說不要求用戶注冊登錄的公共服務(wù)系統(tǒng)(如google)采用 cookie做客戶端session存儲(如google的用戶偏好設(shè)置),而有用戶管理的系統(tǒng)則使用服務(wù)端存儲。原因很顯然:無需用戶登錄的系統(tǒng)唯一能夠標(biāo)識用戶的就是用戶的電腦,換一臺機(jī)器就不知道誰是誰了,服務(wù)端session存儲根本不管用;而有用戶管理的系統(tǒng)則可以通過用戶id來管理用戶個人數(shù)據(jù),從而提供任意復(fù)雜的個性化服務(wù);
    9、客戶端和服務(wù)端的session存儲在性能、安全性、跨站能力、編程方便性等方面都有一定的區(qū)別,而且優(yōu)劣并非絕對(譬如TheServerSide號稱不使用HttpSession,所以性能好,這很顯然:一個具有上億的訪問用戶的系統(tǒng),要在服務(wù)端數(shù)據(jù)庫中檢索出用戶的偏好信息顯然是低效的,Session管理器不管用什么數(shù)據(jù)結(jié)構(gòu)和算法都要耗費(fèi)大量內(nèi)存和CPU時間;而用cookie,則根本不用檢索和維護(hù)session數(shù)據(jù),服務(wù)器可以做成無狀態(tài)的,當(dāng)然高效);
    reply1:
    不過我們也不能在session里面放入過多的東西
    一般來說不能超過4K
    太多了
    對系統(tǒng)資源是一個很嚴(yán)重的浪費(fèi)
    reply2:
    4K已是很大的一個數(shù)字了。
    我一般喜歡寫一個類。封裝用戶登陸后的一些信息。
    然后把這個類放在session中,取得直接用類的方法取相關(guān)信息,

    posted @ 2008-05-21 15:49 honzeland 閱讀(294) | 評論 (0)編輯 收藏

    Svn revision retrieve and logging to database

    最近接到兩個很小的tickets,兩個都是為了項(xiàng)目開發(fā)時的方便:一是將logs寫入到數(shù)據(jù)庫中,以方便日志的查詢;一是在build時,在war包加入svn revision info。
    1) logging to database
    經(jīng)過調(diào)查,決定采用log4j的org.apache.log4j.jdbc.JDBCAppender,于是采用:
    # logging to db
    log4j.logger.com.example=DEBUG, DATABASE
    log4j.additivity.com.example=false
    log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
    log4j.appender.DATABASE.url=jdbc:postgresql://localhost:5432/test
    log4j.appender.DATABASE.driver=org.postgresql.Driver
    log4j.appender.DATABASE.user=pguser
    log4j.appender.DATABASE.password=post
    log4j.appender.DATABASE.sql=INSERT INTO debug_log(created, logger, priority, message) VALUES (to_timestamp('%d{ISO8601}','YYYY-MM-DD HH:MI:SS.MS'),'%c.%M:%L','%p','%m')
    log4j.appender.DB.layout=org.apache.log4j.PatternLayout
    log4j.appender.DATABASE.layout.ConversionPattern=%d{ISO8601} %p %c.%M:%L %m
    很直觀,用起來還很方便,但是不久就出現(xiàn)了問題,tomcat拋出了exception。只好把之前fixed ticket reopen,提交新的comments:Unfortunately, org.apache.log4j.jdbc.JDBCAppender that ships with the Log4j distribution is not able to process logging messages that have characters like ' (single quote) and , (comma) in it. When logging messages contains characters like single quote or comma, the program will throw an exception.
    重新google了,找到了一個plusjdbc,Looking further, I found an alternative JDBCAppender package (org.apache.log4j.jdbcplus.JDBCAppender) from http://www.dankomannhaupt.de/projects/index.html. It can solve this problem. 長嘆了一下。

    最后采用:
    log4j.appender.DATABASE=org.apache.log4j.jdbcplus.JDBCAppender
    log4j.appender.DATABASE.url=jdbc:postgresql://localhost:5432/test
    log4j.appender.DATABASE.dbclass=org.postgresql.Driver
    log4j.appender.DATABASE.username=pguser
    log4j.appender.DATABASE.password=post
    log4j.appender.DATABASE.sql=INSERT INTO debug_log(created, logger, priority, message) VALUES (to_timestamp('@LAYOUT:1@', 'YYYY-MM-DD HH:MI:SS.MS'),'@LAYOUT:3@','@LAYOUT:2@','@LAYOUT:4@')
    log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
    log4j.appender.DATABASE.layout.ConversionPattern=%d{ISO8601}#%p#%c.%M:%L#%m
    log4j.appender.DATABASE.layoutPartsDelimiter=#
    log4j.appender.DATABASE.buffer=1
    log4j.appender.DATABASE.commit=true
    log4j.appender.DATABASE.quoteReplace=true
    問題解決,但是中間有點(diǎn)小波折,在我的項(xiàng)目中,log4j.jar(>1.2.9)重復(fù)了,在$CATALINA_HOME/lib下有一份,在web工程下的WEB-INF/lib下也有一份,而plus-jdbc.jar放置在$CATALINA_HOME/lib下,結(jié)果啟動Tomcat,出現(xiàn)
    log4j:ERROR A "org.apache.log4j.jdbcplus.JDBCAppender" object is not assignable to a "org.apache.log4j.Appender" variable.
    log4j:ERROR The class "org.apache.log4j.Appender" was loaded by
    log4j:ERROR [WebappClassLoader^M
      delegate: false^M
      repositories:^M
    ----------> Parent Classloader:^M
    org.apache.catalina.loader.StandardClassLoader@1ccb029^M
    ] whereas object of type
    log4j:ERROR "org.apache.log4j.jdbcplus.JDBCAppender" was loaded by [org.apache.catalina.loader.StandardClassLoader@1ccb029].
    log4j:ERROR Could not instantiate appender named "DATABASE".
    原來是兩個JDBCAppender實(shí)例不在同一個classlaoder里面,將WEB-INF/lib下的log4j.jar刪除掉,重啟就沒問題了,按理,將$CATALINA_HOME/lib下的plus-jdbc.jar移到WEB-INF/lib下,應(yīng)該也沒問題,沒有測試。

    2)Add build revision info in war file and read it on tomcat startup
    這個經(jīng)歷比較慘痛,兩個問題,如何獲取revision? And how to read it when tomcat startup? 第二個問題倒是沒什么,采用javax.servlet.ServletContextListener就可以實(shí)現(xiàn),很簡單,走彎路的是第一個問題,google后發(fā)現(xiàn)有兩種常見的實(shí)現(xiàn):
    As I have learned, there are totally two solutions to get svn revision info.

    First, retrieve the svn revision from local file($BASE_HOME/.svn/entries). Just parsing the xml file, get the revision property and write it to a properties file.(就是該死的xml,遠(yuǎn)在烏克蘭的同事,該文件卻不是xml的,也只怪自己調(diào)研不充分,還得折騰了半天,后來發(fā)現(xiàn),最新版的svn為了performance的考慮,采用meta data來實(shí)現(xiàn)entries)

    Second, retrieve the svn revision from the remote repository. The solution always use a svn client to perform a demand with remote server to retrieve the revision info. Installing a snv client and using SvnAnt? are most commonly used at present. SvnAnt? is an ant task that provides an interface to Subversion revision control system and encapsulates the svn client. It uses javahl - a native (JNI) java interface for the subversion api if it can find the corresponding library. javahl is platform-dependent.

    Because of needing interaction with the server(服務(wù)器在國外,更新很慢), now I employ the first solution. But I found a flaw of this method when i was going off duty. Generally, we may update our project with svn before committing. This may make a mismatch with svn revision between remote server and local file. Svn revision in local file is usually updated when we update our project. But when we take a commit after update, the svn revision in the remote server will change to a new one.

    So, the case is that if we update, commit, and then build, we may get a mismatch with the newest svn revision, and build the error revision into our ROOT.war. If we update , then build ,without commit, we can get right revision info.

    下面是第一版實(shí)現(xiàn):
        <!--  retrieve the svn revision from the remote repository
        <path id="svnant.lib" >
            <fileset dir="${lib.dir}">
                <include name="svnant.jar"/>
                <include name="svnClientAdapter.jar"/>
                <include name="svnjavahl.jar"/>
            </fileset>
        </path>
       
        <taskdef name="svn" classpathref="svnant.lib" classname="org.tigris.subversion.svnant.SvnTask" />
       
        <target name="get-svn-revision">
            <svn username="*******" password="******" javahl="true">
                    <status urlProperty="https://example.com" path="." revisionProperty="svn.revision" />
            </svn>
            <echo>svn revision: ${svn.revision}</echo>
        </target>   
        -->
       
        <!--  retrieve the svn revision from local file(.svn/entries). The file may contain several  'wc-entries.entry.revision' elements.
        The property will get several values seperated by ',' when using xmlproperty task.  Then the svn revison expected will be the
        max one of these property values.
         -->
        <property name="svn.revision.file" value=".svn/entries" />
        <!-- This property is used to run xmlproperty task successfully with a low version of svn client (under 1.3.1). Don't  sure whether it really makes sense -->
        <property name="build.id" value="foo" />
        <target name="get-svn-revision">
            <xmlproperty file="${svn.revision.file}" collapseAttributes="true"/>
            <echo>svn revision: ${wc-entries.entry.revision}</echo>
        </target>

        <!--
            If the file doesn't contain any 'wc-entries.entry.revision' element, the content of the property file will be: revision = ${wc-entries.entry.revision};
            If contain a 'wc-entries.entry.revision' element, mark this value as $revision_value, then  the content will be: revision = $revision_value;
            If contain several 'wc-entries.entry.revision' elements, mark these values as $value1, $value2, ..., respectively, then the content will be: revision = $value1,$value2,..., seperated by a ',';
        -->
        <property name="svn.revision.propertyfile" value="${build.dir}/revision.properties" />
        <target name="write-svn-revision-to-file" depends="get-svn-revision">
            <delete file="${svn.revision.propertyfile}"/>
            <propertyfile file="${svn.revision.propertyfile}" comment="record svn revision">
                <entry  key="revision" value="${wc-entries.entry.revision}"/>
            </propertyfile>
        </target>

    結(jié)果write-svn-revision-to-file這個在我這倒是可以獲取本地的svn revision,但是遠(yuǎn)方的同事可急了,build老失敗,只好把這部分build注釋了,還好,到周末了,可以在家好好研究一下,很快找了一個新的工具:
    It's my fault. In my version of svn, the entries file is xml formatted. So i parse it using ant task - 'xmlproperty'. Now i have fix this problem by using 'svnkit' tools, a pure java svn toolkit. Now there are two ways to retrieve svn revision. One is from remote repository server. For this one, before building, you should set your own username and password for the remote repository server('remote.repository.username' and 'remote.repository.password' properties in build.xml,respectively). Another one is retrieving revision from local working copy. If using this one, you should set 'local.repository' property in build.xml to your own directory.
    利用svnkit,從服務(wù)器上獲取revision大概是:
                repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(urlStr));
                ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(username, password);
                repository.setAuthenticationManager(authManager);
                headRevision = repository.getLatestRevision();
    從本地working copy獲取revision:
                SVNClientManager clientManager = SVNClientManager.newInstance();
                SVNWCClient wcClient = clientManager.getWCClient();   
                SVNInfo info = wcClient.doInfo(new File(fileUrl), SVNRevision.WORKING);
                headRevision = info.getRevision().getNumber(); 

    利用ant task將獲取的revision寫入到一個配置文件中(如revision.properties),在tomcat啟動的時候加載進(jìn)來,就可以了。   

    posted @ 2008-04-28 15:43 honzeland 閱讀(2180) | 評論 (2)編輯 收藏

    Tomcat ClassLoader and load resources

    zz: http://rosonsandy.blogdriver.com/rosonsandy/871539.html

    1 - Tomcat的類載入器的結(jié)構(gòu)
    Tomcat Server在啟動的時候?qū)?gòu)造一個ClassLoader樹,以保證模塊的類庫是私有的
    Tomcat Server的ClassLoader結(jié)構(gòu)如下:
            +-----------------------------+

            |         Bootstrap           |

            |             |               |

            |          System             |

            |             |               |

            |          Common             |

            |         /      \            |

            |     Catalina  Shared        |

            |               /    \        |

            |          WebApp1  WebApp2   |

            +-----------------------------+

    其中:
    - Bootstrap - 載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar
    - System - 載入$CLASSPATH/*.class
    - Common - 載入$CATALINA_HOME/common/...,它們對TOMCAT和所有的WEB APP都可見
    - Catalina - 載入$CATALINA_HOME/server/...,它們僅對TOMCAT可見,對所有的WEB APP都不可見
    - Shared - 載入$CATALINA_HOME/shared/...,它們僅對所有WEB APP可見,對TOMCAT不可見(也不必見)
    - WebApp - 載入ContextBase?/WEB-INF/...,它們僅對該WEB APP可見

    2 - ClassLoader的工作原理

    每個運(yùn)行中的線程都有一個成員contextClassLoader,用來在運(yùn)行時動態(tài)地載入其它類
    系統(tǒng)默認(rèn)的contextClassLoader是systemClassLoader,所以一般而言java程序在執(zhí)行時可以使用JVM自帶的類、$JAVA_HOME/jre/lib/ext/中的類和$CLASSPATH/中的類
    可以使用Thread.currentThread().setContextClassLoader(...);更改當(dāng)前線程的contextClassLoader,來改變其載入類的行為

    ClassLoader被組織成樹形,一般的工作原理是:
    1) 線程需要用到某個類,于是contextClassLoader被請求來載入該類
    2) contextClassLoader請求它的父ClassLoader來完成該載入請求
    3) 如果父ClassLoader無法載入類,則contextClassLoader試圖自己來載入

    注意:WebApp?ClassLoader的工作原理和上述有少許不同:
    它先試圖自己載入類(在ContextBase?/WEB-INF/...中載入類),如果無法載入,再請求父ClassLoader完成

    由此可得:
    - 對于WEB APP線程,它的contextClassLoader是WebApp?ClassLoader
    - 對于Tomcat Server線程,它的contextClassLoader是CatalinaClassLoader

    3 類的查找

    ClassLoader類中l(wèi)oadClass方法為缺省實(shí)現(xiàn),用下面的順序查找類:
    1、調(diào)用findLoadedClass方法來檢查是否已經(jīng)被加載。如果沒有則繼續(xù)下面的步驟。
    2、如果當(dāng)前類裝載器有一個指定的委托父裝載器,則用委托父裝載器的loadClass方法加載類,也就是委托給父裝載器加載相應(yīng)的類。
    3、如果這個類裝載器的委托層級體系沒有一個類裝載器加載該類,則使用類裝載器定位類的特定實(shí)現(xiàn)機(jī)制,調(diào)用findClass方法來查找類。

    4 - 部分原代碼分析
    4.1 - org/apache/catalina/startup/Bootstrap.java
    Bootstrap中定義了三個classloader:commonLoader,catalinaLoader,sharedLoader.三者關(guān)系如下:
    //注意三個自己定置的ClassLoader的層次關(guān)系:
                // systemClassLoader (root)
                //   +--- commonLoader
                //          +--- catalinaLoader
                //          +--- sharedLoader

    Tomcat Server線程的起點(diǎn)
    構(gòu)造ClassLoader樹,通過Thread.currentThread().setContextClassLoader(catalinaLoader)設(shè)置當(dāng)前的classloader為catalinaLoader。
    載入若干類,然后轉(zhuǎn)入org.apache.catalina.startup.Catalina類中

    4.2 org.apache.catalina.loader.StandardClassLoader.java

    通過看loadClass這個方法來看tomcat是如何加載類的,順序如下:

    (0) Check our previously loaded class cache查找已經(jīng)裝載的class
            clazz = findLoadedClass(name);

    (1) If a system class, use system class loader通過系統(tǒng)classloader來裝載class
            ClassLoader loader = system;
                clazz = loader.loadClass(name);

    (2) Delegate to our parent if requested如果有代理則使用父類classloader
                ClassLoader loader = parent;
                if (loader == null)
                    loader = system;
                clazz = loader.loadClass(name);

    (3) Search local repositories 查找本地類池,比如$CATALINA_HOME/server
               clazz = findClass(name);

    (4) Delegate to parent unconditionally 默認(rèn)使用代理裝載器

    [查看代碼]

    4.3 - org/apache/catalina/startup/ClassLoaderFactory.java

    根據(jù)設(shè)置創(chuàng)建并返回StandardClassLoader的實(shí)例

    [查看代碼]

    4.4 - org/apache/catalina/loader/StandardClassLoader.java

    類載入器

    4.5 - org/apache/catalina/startup/SecurityClassLoad.java

    該類僅包含一個靜態(tài)方法,用來為catalinaLoader載入一些類

    [查看代碼]

    Appendix - 參考

    [1] http://jakarta.apache.org/tomcat/中的Tomcat 4.1.x文檔Class Loader HOW-TO

    在一個JVM中可能存在多個ClassLoader,每個ClassLoader擁有自己的NameSpace。一個ClassLoader只能擁有一個class對象類型的實(shí)例,但是不同的ClassLoader可能擁有相同的class對象實(shí)例,這時可能產(chǎn)生致命的問題。如ClassLoaderA,裝載了類A的類型實(shí)例A1,而ClassLoaderB,也裝載了類A的對象實(shí)例A2。邏輯上講A1=A2,但是由于A1和A2來自于不同的ClassLoader,它們實(shí)際上是完全不同的,如果A中定義了一個靜態(tài)變量c,則c在不同的ClassLoader中的值是不同的。

    [2] 深入Java2平臺安全

    zz: http://mail-archives.apache.org/mod_mbox/tomcat-users/200212.mbox/raw/%3c20021204192034.P86616-100000@icarus.apache.org%3e
    try {
        Properties props = new Properties();
        InputStream in = getClass().getResourceAsStream("/conf/db.properties");
        props.load(in);
        ......
        propertie1 = props.getProperty("propertie1");

    The examples already given will find properties files for you just fine whether the file is in a directory structure or inside an archive.  How do you think Java loads classes?  It works out of archives, no? here are some various was to access a properties file ( or any resource, for that matter) in whether the app is deployed as a directory or as a .war file (even inside a .jar file in WEB-INF/lib)....

    1. This will load a file in WEB-INF/classes/conf or any jar file in the classpath with a package of "conf"...
        getClass().getResourceAsStream("/conf/db.properties");
    2. This will load a file relative to the current class.  For instance, if the class is "org.mypackage.MyClass", then the file would be loaded at "org.mypackage.conf.dbproperties".  Note that this is because we didn't prepend "/" to the path.  When that is done, the file is loaded from the root of the current classloader where this loads it relative to the current class...
        getClass().getResourceAsStream("conf/db.properties");
    3. This will find db.properties anywhere in the current classloader as long as it exists in a "conf" package...
        getClass().getClassLoader().getResourceAsStream("conf/db.properties");
    4. This will find the file in a "conf" directory inside the webapp (starting from the root).  This starts looking in the same directory as contains WEB-INF.  When I say "directory", I don't mean "filesystem".  This could be in a .war file as well as in an actual directory on the filesystem...
        getServletContext().getResourceAsStream("/conf/db.properties");
    5. Of course you would probably not want just anyone seeing your db.properties file, so you'd probably want to put in inside WEB-INF of your webapp, so....
        getServletContext().getResourceAsStream("/WEB-INF/conf/db.properties");
    6. If your db.properties exists in another classloader which your app has access to, you can reach it by using:
        Thread.currentThread().getContextClassLoader().getResourceAsStream("conf/db.properties");
    that will act similar to getClass().getClassLoader(), but it can see across all available classloaders where the latter can only see within the classloader that loaded the current class.

    posted @ 2008-04-24 15:46 honzeland 閱讀(850) | 評論 (0)編輯 收藏

    Ubuntu下Vim設(shè)置高亮

    1、sudo vim ~/.exrc
    添加如下內(nèi)容:
    if &t_Co > 1
    syntax enable
    endif
    2、如果不行,再sudo apt-get install vim

    posted @ 2008-03-05 11:55 honzeland 閱讀(1862) | 評論 (1)編輯 收藏

    Ubuntu下安裝字體

    /usr/share/fonts/truetype/ 下面 的某文件夾,例如yahei

    cd /usr/share/fonts/truetype/yahei

    sudo mkfontscale && sudo mkfontdir && sudo fc-cache -fv

    posted @ 2008-03-05 10:44 honzeland 閱讀(458) | 評論 (2)編輯 收藏

    Double Checked Locking 模式

    經(jīng)典文章:
    Double-checked locking: Clever, but broken
    Warning! Threading in a multiprocessor world

    When is a singleton not a singleton?

    posted @ 2008-02-19 14:21 honzeland 閱讀(276) | 評論 (0)編輯 收藏

    HP大中華區(qū)總裁孫振耀退休感言

    一、關(guān)于工作與生活

    我有個有趣的觀察,外企公司多的是25-35歲的白領(lǐng),40歲以上的員工很少,二三十歲的外企員工是意氣風(fēng)發(fā)的,但外企公司40歲附近的經(jīng)理人是很尷尬的。我見過的40歲附近的外企經(jīng)理人大多在一直跳槽,最后大多跳到民企,比方說,唐駿。外企員工的成功很大程度上是公司的成功,并非個人的成功,西門子的確比國美大,但并不代表西門子中國經(jīng)理比國美的老板強(qiáng),甚至可以說差得很遠(yuǎn)。而進(jìn)外企的人往往并不能很早理解這一點(diǎn),把自己的成功90%歸功于自己的能力,實(shí)際上,外企公司隨便換個中國區(qū)總經(jīng)理并不會給業(yè)績帶來什么了不起的影響。好了問題來了,當(dāng)這些經(jīng)理人40多歲了,他們的薪資要求變得很高,而他們的才能其實(shí)又不是那么出眾,作為外企公司的老板,你會怎么選擇?有的是只要不高薪水的,要出位的精明強(qiáng)干精力沖沛的年輕人,有的是,為什么還要用你?
    從上面這個例子,其實(shí)可以看到我們的工作軌跡,二三十歲的時候,生活的壓力還比較小,身體還比較好,上面的父母身體還好,下面又沒有孩子,不用還房貸,也沒有孩子要上大學(xué),當(dāng)個外企小白領(lǐng)還是很光鮮的,掙得不多也夠花了。但是人終歸要結(jié)婚生子,終歸會老,到了40歲,父母老了,要看病要吃藥,要有人看護(hù),自己要還房貸,要過基本體面的生活,要養(yǎng)小孩……那個時候需要掙多少錢才夠花才重要。所以,看待工作,眼光要放遠(yuǎn)一點(diǎn),一時的誰高誰低并不能說明什么。
    從這個角度上來說,我不太贊成過于關(guān)注第一份工作的薪水,更沒有必要攀比第一份工作的薪水,這在剛剛出校園的學(xué)生中間是很常見的。正常人大概要工作 35年,這好比是一場馬拉松比賽,和真正的馬拉松比賽不同的是,這次比賽沒有職業(yè)選手,每個人都只有一次機(jī)會。要知到,有很多人甚至堅持不到終點(diǎn),大多數(shù)人最后是走到終點(diǎn)的,只有少數(shù)人是跑過終點(diǎn)的,因此在剛開始的時候,去搶領(lǐng)先的位置并沒有太大的意義。剛進(jìn)社會的時候如果進(jìn)500強(qiáng)公司,大概能拿到 3k-6k/月的工資,有些特別技術(shù)的人才可能可以到8k/月,可問題是,5年以后拿多少?估計5k-10k了不起了。起點(diǎn)雖然高,但增幅有限,而且,后面的年輕人追趕的壓力越來越大。
    我前兩天問我的一個銷售,你會的這些東西一個新人2年就都學(xué)會了,但新人所要求的薪水卻只是你的一半,到時候,你怎么辦?
    職業(yè)生涯就像一場體育比賽,有初賽、復(fù)賽、決賽。初賽的時候大家都剛剛進(jìn)社會,大多數(shù)都是實(shí)力一般的人,這時候努力一點(diǎn)認(rèn)真一點(diǎn)很快就能讓人脫穎而出,于是有的人二十多歲做了經(jīng)理,有的人遲些也終于贏得了初賽,三十多歲成了經(jīng)理。然后是復(fù)賽,能參加復(fù)賽的都是贏得初賽的,每個人都有些能耐,在聰明才智上都不成問題,這個時候再想要勝出就不那么容易了,單靠一點(diǎn)點(diǎn)努力和認(rèn)真還不夠,要有很強(qiáng)的堅忍精神,要懂得靠團(tuán)隊的力量,要懂得收服人心,要有長遠(yuǎn)的眼光……
    看上去贏得復(fù)賽并不容易,但,還不是那么難。因?yàn)檫@個世界的規(guī)律就是給人一點(diǎn)成功的同時讓人驕傲自滿,剛剛贏得初賽的人往往不知道自己贏得的僅僅是初賽,有了一點(diǎn)小小的成績大多數(shù)人都會驕傲自滿起來,認(rèn)為自己已經(jīng)懂得了全部,不需要再努力再學(xué)習(xí)了,他們會認(rèn)為之所以不能再進(jìn)一步已經(jīng)不是自己的原因了。雖然他們?nèi)匀徊缓脤Ω叮撬麄儧]有耐性,沒有容人的度量,更沒有清晰長遠(yuǎn)的目光。就像一只憤怒的斗牛,雖然猛烈,最終是會敗的,而贏得復(fù)賽的人則象斗牛士一樣,不急不躁,跟隨著自己的節(jié)拍,慢慢耗盡對手的耐心和體力。贏得了復(fù)賽以后,大約已經(jīng)是一位很了不起的職業(yè)經(jīng)理人了,當(dāng)上了中小公司的總經(jīng)理,大公司的副總經(jīng)理,主管著每年幾千萬乃至幾億的生意。
    最終的決賽來了,說實(shí)話我自己都還沒有贏得決賽,因此對于決賽的決勝因素也只能憑自己的猜測而已,這個時候的輸贏或許就像武俠小說里寫得那樣,大家都是高手,只能等待對方犯錯了,要想輕易擊敗對手是不可能的,除了使上渾身解數(shù),還需要一點(diǎn)運(yùn)氣和時間。世界的規(guī)律依然發(fā)揮著作用,贏得復(fù)賽的人已經(jīng)不只是驕傲自滿了,他們往往剛愎自用,聽不進(jìn)去別人的話,有些人的脾氣變得暴躁,心情變得浮躁,身體變得糟糕,他們最大的敵人就是他們自己,在決賽中要做的只是不被自己擊敗,等著別人被自己擊敗。這和體育比賽是一樣的,最后高手之間的比賽,就看誰失誤少誰就贏得了決賽。

      二、 根源

    你工作快樂么?你的工作好么?
    有沒有覺得干了一段時間以后工作很不開心?有沒有覺得自己入錯了行?有沒有覺得自己沒有得到應(yīng)有的待遇?有沒有覺得工作像一團(tuán)亂麻每天上班都是一種痛苦?有沒有很想換個工作?有沒有覺得其實(shí)現(xiàn)在的公司并沒有當(dāng)初想象得那么好?有沒有覺得這份工作是當(dāng)初因?yàn)樯鎵毫Χ业模瑢?shí)在不適合自己?你從工作中得到你想要得到的了么?你每天開心么?
    天涯上憤怒的人很多,你有沒有想過,你為什么不快樂?你為什么憤怒?
    其實(shí),你不快樂的根源,是因?yàn)槟悴恢酪裁矗∧悴恢酪裁矗阅悴恢廊プ非笫裁矗悴恢雷非笫裁矗阅闶裁匆驳貌坏健?br /> 我總覺得,職業(yè)生涯首先要關(guān)注的是自己,自己想要什么?大多數(shù)人大概沒想過這個問題,唯一的想法只是——我想要一份工作,我想要一份不錯的薪水,我知道所有人對于薪水的渴望,可是,你想每隔幾年重來一次找工作的過程么?你想每年都在這種對于工作和薪水的焦急不安中度過么?不想的話,就好好想清楚。飲鴆止渴,不能因?yàn)榭诳示推疵榷舅帯T绞墙辜保绞怯X得自己需要一份工作,越饑不擇食,越想不清楚,越容易失敗,你的經(jīng)歷越來越差,下一份工作的人看著你的簡歷就皺眉頭。于是你越喝越渴,越渴越喝,陷入惡性循環(huán)。最終只能哀嘆世事不公或者生不逢時,只能到天涯上來發(fā)泄一把,在失敗者的共鳴當(dāng)中尋求一點(diǎn)心理平衡罷了。大多數(shù)人都有生存壓力,我也是,有生存壓力就會有很多焦慮,積極的人會從焦慮中得到動力,而消極的人則會因?yàn)榻箲]而迷失方向。所有人都必須在壓力下做出選擇,這就是世道,你喜歡也罷不喜歡也罷。
    一般我們處理的事情分為重要的事情和緊急的事情,如果不做重要的事情就會常常去做緊急的事情。比如鍛煉身體保持健康是重要的事情,而看病則是緊急的事情。如果不鍛煉身體保持健康,就會常常為了病痛煩惱。又比如防火是重要的事情,而救火是緊急的事情,如果不注意防火,就要常常救火。找工作也是如此,想好自己究竟要什么是重要的事情,找工作是緊急的事情,如果不想好,就會常常要找工作。往往緊急的事情給人的壓力比較大,迫使人們?nèi)ペs緊做,相對來說重要的事情反而沒有那么大的壓力,大多數(shù)人做事情都是以壓力為導(dǎo)向的,壓力之下,總覺得非要先做緊急的事情,結(jié)果就是永遠(yuǎn)到處救火,永遠(yuǎn)沒有停歇的時候。(很多人的工作也像是救火隊一樣忙碌痛苦,也是因?yàn)楣ぷ髦袥]有做好重要的事情。)那些說自己活在水深火熱為了生存顧不上那么多的朋友,今天找工作困難是當(dāng)初你們沒有做重要的事情,是結(jié)果不是原因。如果今天你們還是因?yàn)榧庇谝乙环莨ぷ鞫蝗ニ伎迹敲椿蛟S將來要繼續(xù)承受痛苦找工作的結(jié)果。
    我始終覺得我要說的話題,沉重了點(diǎn),需要很多思考,遠(yuǎn)比唐笑打武警的話題來的枯燥乏味,但是,天下沒有輕松的成功,成功,要付代價。請先忘記一切的生存壓力,想想這輩子你最想要的是什么?所以,最要緊的事情,先想好自己想要什么。

    三、什么是好工作

    當(dāng)初微軟有個唐駿,很多大學(xué)里的年輕人覺得這才是他們向往的職業(yè)生涯,我在清華bbs里發(fā)的帖子被這些學(xué)子們所不屑,那個時候?qū)W生們只想出國或者去外企,不過如今看來,我還是對的,唐駿去了盛大,陳天橋創(chuàng)立的盛大,一家民營公司。一個高學(xué)歷的海歸在500強(qiáng)的公司里拿高薪水,這大約是很多年輕人的夢想,問題是,每年畢業(yè)的大學(xué)生都在做這個夢,好的職位卻只有500個。
    人都是要面子的,也是喜歡攀比的,即使在工作上也喜歡攀比,不管那是不是自己想要的。大家認(rèn)為外企公司很好,可是好在哪里呢?好吧,他們在比較好的寫字樓,這是你想要的么?他們出差住比較好的酒店,這是你想要的么?別人會羨慕一份外企公司的工作,這是你想要的么?那一切都是給別人看的,你干嗎要活得那么辛苦給別人看?另一方面,他們薪水福利一般,并沒有特別了不起,他們的晉升機(jī)會比較少,很難做到很高階的主管,他們雖然厭惡常常加班,卻不敢不加班,因?yàn)?#8220;你不干有得是人干”,大部分情況下會找個臺灣人香港人新加坡人來管你,而這些人又往往有些莫名其妙的優(yōu)越感。你想清楚了么?500強(qiáng)一定好么?找工作究竟是考慮你想要什么,還是考慮別人想看什么?
    我的大學(xué)同學(xué)們大多數(shù)都到美國了,甚至畢業(yè)這么多年了,還有人最近到國外去了。出國真的有那么好么?我的大學(xué)同學(xué)們,大多數(shù)還是在博士、博士后、訪問學(xué)者地掙扎著,至今只有一個正經(jīng)在一個美國大學(xué)里拿到個正式的教職。國內(nèi)的教授很難當(dāng)么?我有幾個表親也去了國外了,他們的父母獨(dú)自在國內(nèi),沒有人照顧,有好幾次人在家里昏倒都沒人知道,出國,真的這么光彩么?就像有人說的“很多事情就像看A片,看的人覺得很爽,做的人未必。”
    人總想找到那個最好的,可是,什么是最好的?你覺得是最好的那個,是因?yàn)槟愕拇_了解,還是因?yàn)閯e人說他是最好的?即使他對于別人是最好的,對于你也一定是最好的么?
    對于自己想要什么,自己要最清楚,別人的意見并不是那么重要。很多人總是常常被別人的意見所影響,親戚的意見,朋友的意見,同事的意見……問題是,你究竟是要過誰的一生?人的一生不是父母一生的續(xù)集,也不是兒女一生的前傳,更不是朋友一生的外篇,只有你自己對自己的一生負(fù)責(zé),別人無法也負(fù)不起這個責(zé)任。自己做的決定,至少到最后,自己沒什么可后悔。對于大多數(shù)正常智力的人來說,所做的決定沒有大的對錯,無論怎么樣的選擇,都是可以嘗試的。比如你沒有考自己上的那個學(xué)校,沒有入現(xiàn)在這個行業(yè),這輩子就過不下去了?就會很失敗?不見得。
    我想,好工作,應(yīng)該是適合你的工作,具體點(diǎn)說,應(yīng)該是能給你帶來你想要的東西的工作,你或許應(yīng)該以此來衡量你的工作究竟好不好,而不是拿公司的大小,規(guī)模,外企還是國企,是不是有名,是不是上市公司來衡量。小公司,未必不是好公司,賺錢多的工作,也未必是好工作。你還是要先弄清楚你想要什么,如果你不清楚你想要什么,你就永遠(yuǎn)也不會找到好工作,因?yàn)槟阌肋h(yuǎn)只看到你得不到的東西,你得到的,都是你不想要的。
    可能,最好的,已經(jīng)在你的身邊,只是,你還沒有學(xué)會珍惜。人們總是盯著得不到的東西,而忽視了那些已經(jīng)得到的東西。

    四、普通人

    我發(fā)現(xiàn)中國人的勵志和國外的勵志存在非常大的不同,中國的勵志比較鼓勵人立下大志愿,臥薪嘗膽,有朝一日成富成貴。而國外的勵志比較鼓勵人勇敢面對現(xiàn)實(shí)生活,面對普通人的困境,雖然結(jié)果也是成富成貴,但起點(diǎn)不一樣,相對來說,我覺得后者在操作上更現(xiàn)實(shí),而前者則需要用999個失敗者來堆砌一個成功者的故事。
    我們都是普通人,普通人的意思就是,概率這件事是很準(zhǔn)的。因此,我們不會買彩票中500萬,我們不會成為比爾蓋茨或者李嘉誠,我們不會坐飛機(jī)掉下來,我們當(dāng)中很少的人會創(chuàng)業(yè)成功,我們之中有30%的人會離婚,我們之中大部分人會活過65歲……
    所以請你在想自己要什么的時候,要得“現(xiàn)實(shí)”一點(diǎn),你說我想要做李嘉誠,抱歉,我?guī)筒簧夏恪3蔀楸葼柹w茨或者李嘉誠這種人,是靠命的,看我寫的這篇文章絕對不會讓你成為他們,即使你成為了他們,也絕對不是我這篇文章的功勞。“王侯將相寧有種乎”但真正當(dāng)皇帝的只有一個人,王侯將相,人也不多。目標(biāo)定得高些對于喜歡挑戰(zhàn)的人來說有好處,但對于大多數(shù)普通人來說,反而比較容易灰心沮喪,很容易就放棄了。
    回過頭來說,李嘉誠比你有錢大致50萬倍,他比你更快樂么?或許。有沒有比你快樂50萬倍,一定沒有。他比你最多也就快樂一兩倍,甚至有可能還不如你快樂。尋找自己想要的東西不是和別人比賽,比誰要得更多更高,比誰的目標(biāo)更遠(yuǎn)大。雖然成為李嘉誠這個目標(biāo)很宏大,但你并不見得會從這個目標(biāo)以及追求目標(biāo)的過程當(dāng)中獲得快樂,而且基本上你也做不到。你必須聽聽你內(nèi)心的聲音,尋找真正能夠使你獲得快樂的東西,那才是你想要的東西。
    你想要的東西,或者我們把它稱之為目標(biāo),目標(biāo)其實(shí)并沒有高低之分,你不需要因?yàn)樽约旱哪繕?biāo)沒有別人遠(yuǎn)大而不好意思,達(dá)到自己的目標(biāo)其實(shí)就是成功,成功有大有小,快樂卻是一樣的。我們追逐成功,其實(shí)追逐的是成功帶來的快樂,而非成功本身。職業(yè)生涯的道路上,我們常常會被攀比的心態(tài)蒙住眼睛,忘記了追求的究竟是什么,忘記了是什么能使我們更快樂。
    社會上一夜暴富的新聞很多,這些消息,總會在我們的心里面掀起很多漣漪,漣漪多了就變成驚濤駭浪,心里的驚濤駭浪除了打翻承載你目標(biāo)的小船,并不會使得你也一夜暴富。“只見賊吃肉,不見賊挨揍。”我們這些普通人既沒有當(dāng)賊的勇氣,又缺乏當(dāng)賊的狠辣絕決,雖然羨慕吃肉,卻更害怕挨揍,偶爾看到幾個沒挨揍的賊就按奈不住,或者心思活動,或者大感不公,真要叫去做賊,卻也不敢。
    我還是過普通人的日子,要普通人的快樂,至少,晚上睡得著覺。
       
    五、跳槽與積累

    首先要說明,工作是一件需要理智的事情,所以不要在工作上耍個性,天涯上或許會有人覺得你很有個性而叫好,煤氣公司電話公司不會因?yàn)橛X得你很有個性而免了你的帳單。當(dāng)你很帥地炒掉了你的老板,當(dāng)你很酷地挖苦了一番招聘的HR,賬單還是要照付,只是你賺錢的時間更少了,除了你自己,沒人受損失。
    我并不反對跳槽,但跳槽決不是解決問題的辦法,而且頻繁跳槽的后果是讓人覺得沒有忠誠度可言,而且不能安心工作。現(xiàn)在很多人從網(wǎng)上找工作,很多找工作的網(wǎng)站常常給人出些餿主意,要知道他們是盈利性企業(yè),當(dāng)然要從自身盈利的角度來考慮,大家越是頻繁跳槽頻繁找工作他們越是生意興隆,所以鼓動人們跳槽是他們的工作。所以他們會常常告訴你,你拿的薪水少了,你享受的福利待遇差了,又是“薪情快報”又是“贊嘆自由奔放的靈魂”。至于是否會因此讓你不能安心,你跳了槽是否解決問題,是否更加開心,那個,他們管不著。
    要跳槽肯定是有問題,一般來說問題發(fā)生了,躲是躲不開的,很多人跳槽是因?yàn)檫@樣或者那樣的不開心,如果這種不開心,在現(xiàn)在這個公司不能解決,那么在下一個公司多半也解決不掉。你必須相信,90%的情況下,你所在的公司并沒有那么爛,你認(rèn)為不錯的公司也沒有那么好。就像圍城里說的,“城里的人拼命想沖出來,而城外的人拼命想沖進(jìn)去。”每個公司都有每個公司的問題,沒有問題的公司是不存在的。換個環(huán)境你都不知道會碰到什么問題,與其如此,不如就在當(dāng)下把問題解決掉。很多問題當(dāng)你真的想要去解決的時候,或許并沒有那么難。有的時候你覺得問題無法解決,事實(shí)上,那只是“你覺得”。
    人生的曲線應(yīng)該是曲折向上的,偶爾會遇到低谷但大趨勢總歸是曲折向上的,而不是象脈沖波一樣每每回到起點(diǎn),我見過不少面試者,30多歲了,四五份工作經(jīng)歷,每次多則3年,少則1年,30多歲的時候回到起點(diǎn)從一個初級職位開始干起,拿基本初級的薪水,和20多歲的年輕人一起競爭,不覺得有點(diǎn)辛苦么?這種日子好過么?
    我非常不贊成在一個行業(yè)超過3年以后換行業(yè),基本上,35歲以前我們的生存資本靠打拼,35歲以生存的資本靠的就是積累,這種積累包括人際關(guān)系,經(jīng)驗(yàn),人脈,口碑……如果常常更換行業(yè),代表幾年的積累付之東流,一切從頭開始,如果換了兩次行業(yè),35歲的時候大概只有5年以下的積累,而一個沒有換過行業(yè)的人至少有了10年的積累,誰會占優(yōu)勢?工作到2-3年的時候,很多人覺得工作不順利,好像到了一個瓶頸,心情煩悶,就想辭職,乃至換一個行業(yè),覺得這樣所有一切煩惱都可以拋開,會好很多。其實(shí)這樣做只是讓你從頭開始,到了時候還是會發(fā)生和原來行業(yè)一樣的困難,熬過去就向上跨了一大步,要知道每個人都會經(jīng)歷這個過程,每個人的職業(yè)生涯中都會碰到幾個瓶頸,你熬過去了而別人沒有熬過去你就領(lǐng)先了。跑長跑的人會知道,開始的時候很輕松,但是很快會有第一次的難受,但過了這一段又能跑很長一段,接下來會碰到第二次的難受,堅持過了以后又能跑一段,如此往復(fù),難受一次比一次厲害,直到堅持不下去了。大多數(shù)人第一次就堅持不了了,一些人能堅持到第二次,第三次雖然大家都堅持不住了,可是跑到這里的人也沒幾個了,這點(diǎn)資本足夠你安穩(wěn)活這一輩子了。
    一份工作到兩三年的時候,大部分人都會變成熟手,這個時候往往會陷入不斷的重復(fù),有很多人會覺得厭倦,有些人會覺得自己已經(jīng)搞懂了一切,從而懶得去尋求進(jìn)步了。很多時候的跳槽是因?yàn)橛X得失去興趣了,覺得自己已經(jīng)完成比賽了。其實(shí)這個時候比賽才剛剛開始,工作兩三年的人,無論是客戶關(guān)系,人脈,手下,和領(lǐng)導(dǎo)的關(guān)系,在業(yè)內(nèi)的名氣……還都是遠(yuǎn)遠(yuǎn)不夠的,但稍有成績的人總是會自我感覺良好的,每個人都覺得自己跟客戶關(guān)系鐵得要命,覺得自己在業(yè)界的口碑好得很。其實(shí)可以肯定地說,一定不是,這個時候,還是要拿出前兩年的干勁來,穩(wěn)扎穩(wěn)打,積累才剛剛開始。
    你足夠了解你的客戶嗎?你知道他最大的煩惱是什么嗎?你足夠了解你的老板么?你知道他最大的煩惱是什么嗎?你足夠了解你的手下么?你知道他最大的煩惱是什么嗎?如果你不知道,你憑什么覺得自己已經(jīng)積累夠了?如果你都不了解,你怎么能讓他們幫你的忙,做你想讓他們做的事情?如果他們不做你想讓他們做的事情,你又何來的成功?

    六、等待

    這是個浮躁的人們最不喜歡的話題,本來不想說這個話題,因?yàn)闀鹛嗟臓幷摚矣譄o意和人爭論這些,但是考慮到對于職業(yè)生涯的長久規(guī)劃,這是一個躲避不了的話題,還是決定寫一寫,不愛看的請離開吧。
    并不是每次穿紅燈都會被汽車撞,并不是每個罪犯都會被抓到,并不是每個錯誤都會被懲罰,并不是每個貪官都會被槍斃,并不是你的每一份努力都會得到回報,并不是你的每一次堅持都會有人看到,并不是你每一點(diǎn)付出都能得到公正的回報,并不是你的每一個善意都能被理解……這個,就是世道。好吧,世道不夠好,可是,你有推翻世道的勇氣么?如果沒有,你有更好的解決辦法么?有很多時候,人需要一點(diǎn)耐心,一點(diǎn)信心。每個人總會輪到幾次不公平的事情,而通常,安心等待是最好的辦法。
    有很多時候我們需要等待,需要耐得住寂寞,等待屬于你的那一刻。周潤發(fā)等待過,劉德華等待過,周星馳等待過,王菲等待過,張藝謀也等待過……看到了他們?nèi)缃竦墓Τ擅偷娜耍憧稍吹疆?dāng)初他們的等待和耐心?你可曾看到金馬獎影帝在街邊擺地攤?你可曾看到德云社一群人在劇場里給一位觀眾說相聲?你可曾看到周星馳的角色甚至連一句臺詞都沒有?每一個成功者都有一段低沉苦悶的日子,我?guī)缀跄芟胂蟮贸鰜硭麄兘杈茲渤畹臉幼樱乙材芟胂蟮贸鏊麄優(yōu)榱松娑鴴暝木狡取T谒麄円簧钪袪N爛美好的日子里,他們渴望成功,但卻兩手空空,一如現(xiàn)在的你。沒有人保證他們將來一定會成功,而他們的選擇是耐住寂寞。如果當(dāng)時的他們總念叨著“成功只是屬于特權(quán)階級的”,你覺得他們今天會怎樣?
    曾經(jīng)我也不明白有些人為什么并不比我有能力卻要坐在我的頭上,年紀(jì)比我大就一定要當(dāng)我的領(lǐng)導(dǎo)么?為什么有些爛人不需要努力就能賺錢?為什么剛剛改革開放的時候的人能那么容易賺錢,而輪到我們的時候,什么事情都要正規(guī)化了?有一天我突然想,我還在上學(xué)的時候他們就在社會里掙扎奮斗了,他們在社會上奮斗積累了十幾二十年,我們新人來了,他們有的我都想要,我這不是在要公平,我這是在要搶劫。因?yàn)槲乙锰保驗(yàn)槲胰滩蛔〖拍6鄽q的男人,沒有錢,沒有事業(yè),卻有蓬勃的欲望。
    人總是會遇到挫折的,人總是會有低潮的,人總是會有不被人理解的時候的,人總是有要低聲下氣的時候,這些時候恰恰是人生最關(guān)鍵的時候,因?yàn)榇蠹叶紩龅酱煺郏蠖鄶?shù)人過不了這個門檻,你能過,你就成功了。在這樣的時刻,我們需要耐心等待,滿懷信心地去等待,相信,生活不會放棄你,機(jī)會總會來的。至少,你還年輕,你沒有坐牢,沒有生治不了的病,沒有欠還不起的債。比你不幸的人遠(yuǎn)遠(yuǎn)多過比你幸運(yùn)的人,你還怕什么?路要一步步走,雖然到達(dá)終點(diǎn)的那一步很激動人心,但大部分的腳步是平凡甚至枯燥的,但沒有這些腳步,或者耐不住這些平凡枯燥,你終歸是無法迎來最后的那些激動人心。
    逆境,是上帝幫你淘汰競爭者的地方。要知道,你不好受,別人也不好受,你堅持不下去了,別人也一樣,千萬不要告訴別人你堅持不住了,那只能讓別人獲得堅持的信心,讓競爭者看著你微笑的面孔,失去信心,退出比賽。勝利屬于那些有耐心的人。
    在最絕望的時候,我會去看電影《The Pursuit of Happyness》《Jerry Maguire》,讓自己重新鼓起勇氣,因?yàn)椋瑹o論什么時候,我們總還是有希望。當(dāng)所有的人離開的時候,我不失去希望,我不放棄。每天下班坐在車?yán)铮蚁矚g哼著《隱形的翅膀》看著窗外,我知道,我在靜靜等待,等待屬于我的那一刻。
    原貼里伊吉網(wǎng)友的話我很喜歡,抄錄在這里:
    每個人都希望,自己是獨(dú)一無二的特殊者
    含著金匙出生、投胎到好家庭、工作安排到電力局拿1w月薪這樣的小概率事件,當(dāng)然最好輪到自己
    紅軍長征兩萬五、打成右派反革命、胼手胝足犧牲尊嚴(yán)去奮斗,最好留給祖輩父輩和別人
    自然,不是每個吃過苦的人都會得到回報
    但是,任何時代,每一個既得利益者身后,都有他的祖輩父輩奮斗掙扎乃至流血付出生命的身影
    羨慕別人有個好爸爸,沒什么不可以
    問題是,你的下一代,會有一個好爸爸嗎?
    至于問到為什么不能有同樣的贏面概率?我只能問:為什么物種競爭中,人和猴子不能有同樣的贏面概率?
    物競天擇。猴子的靈魂不一定比你卑微,但你身后有幾十萬年的類人猿進(jìn)化積淀。

    七、入對行跟對人

    在中國,大概很少有人是一份職業(yè)做到底的,雖然如此,第一份工作還是有些需要注意的地方,有兩件事情格外重要,第一件是入行,第二件事情是跟人。第一份工作對人最大的影響就是入行,現(xiàn)代的職業(yè)分工已經(jīng)很細(xì),我們基本上只能在一個行業(yè)里成為專家,不可能在多個行業(yè)里成為專家。很多案例也證明即使一個人在一個行業(yè)非常成功,到另外一個行業(yè),往往完全不是那么回事情,“你想改變世界,還是想賣一輩子汽水?”是喬布斯邀請百事可樂總裁約翰·斯考利加盟蘋果時所說的話,結(jié)果這位在百事非常成功的約翰,到了蘋果表現(xiàn)平平。其實(shí)沒有哪個行業(yè)特別好,也沒有哪個行業(yè)特別差,或許有報道說哪個行業(yè)的平均薪資比較高,但是他們沒說的是,那個行業(yè)的平均壓力也比較大。看上去很美的行業(yè)一旦進(jìn)入才發(fā)現(xiàn)很多地方其實(shí)并不那么完美,只是外人看不見。
    說實(shí)話,我自己都沒有發(fā)大財,所以我的建議只是讓人快樂工作的建議,不是如何發(fā)大財?shù)慕ㄗh,我們只討論一般普通打工者的情況。我認(rèn)為選擇什么行業(yè)并沒有太大關(guān)系,看問題不能只看眼前。比如,從前年開始,國家開始整頓醫(yī)療行業(yè),很多醫(yī)藥公司開不下去,很多醫(yī)藥行業(yè)的銷售開始轉(zhuǎn)行。其實(shí)醫(yī)藥行業(yè)的不景氣是針對所有公司的,并非針對一家公司,大家的日子都不好過,這個時候跑掉是非常不劃算的,大多數(shù)正規(guī)的醫(yī)藥公司即使不做新生意撐個兩三年總是能撐的,大多數(shù)醫(yī)藥銷售靠工資撐個兩三年也是可以撐的,國家不可能永遠(yuǎn)捏著醫(yī)藥行業(yè)不放的,兩三年以后光景總歸還會好起來的,那個時候別人都跑了而你沒跑,那時的日子應(yīng)該會好過很多。有的時候覺得自己這個行業(yè)不行了,問題是,再不行的行業(yè),做得人少了也變成了好行業(yè),當(dāng)大家都覺得不好的時候,往往卻是最好的時候。大家都覺得金融行業(yè)好,金融行業(yè)門檻高不說,有多少人削尖腦袋要鉆進(jìn)去,競爭激勵,進(jìn)去以后還要時時提防,一個疏忽,就被后來的人給擠掉了,壓力巨大,又如何談得上快樂?也就未必是“好”工作了。
    太陽能這個東西至今還不能進(jìn)入實(shí)際應(yīng)用的階段,但是中國已經(jīng)有7家和太陽能有關(guān)的公司在紐交所上市了,國美蘇寧永樂其實(shí)是貿(mào)易型企業(yè),也能上市,魯泰紡織連續(xù)10年利潤增長超過50%,賣茶的一茶一座,賣衣服的海瀾之家都能上市……其實(shí)選什么行業(yè)真的不重要,關(guān)鍵是怎么做。事情都是人做出來的,關(guān)鍵是人。
    有一點(diǎn)是需要記住的,這個世界上,有史以來直到我們能夠預(yù)見得到的未來,成功的人總是少數(shù),有錢的人總是少數(shù),大多數(shù)人是一般的,普通的,不太成功的。因此,大多數(shù)人的做法和看法,往往都不是距離成功最近的做法和看法。因此大多數(shù)人說好的東西不見得好,大多數(shù)人說不好的東西不見得不好。大多數(shù)人都去炒股的時候說明跌只是時間問題,大家越是熱情高漲的時候,跌的日子越近。大多數(shù)人買房子的時候,房價不會漲,而房價漲的差不多的時候,大多數(shù)人才開始買房子。不會有這樣一件事情讓大家都變成功,發(fā)了財,歷史上不曾有過,將來也不會發(fā)生。有些東西即使一時運(yùn)氣好得到了,還是會在別的時候別的地方失去的。
    年輕人在職業(yè)生涯的剛開始,尤其要注意的是,要做對的事情,不要讓自己今后幾十年的人生總是提心吊膽,更不值得為了一份工作賠上自己的青春年華。我的公司是個不行賄的公司,以前很多人不理解,甚至自己的員工也不理解,不過如今,我們是同行中最大的企業(yè),客戶樂意和我們打交道,尤其是在國家打擊腐敗的時候,每個人都知道我們做生意不給錢的名聲,都敢于和我們做生意。而勇于給錢的公司,不是倒了,就是跑了,要不就是每天睡不好覺,人還是要看長遠(yuǎn)一點(diǎn)。很多時候,看起來最近的路,其實(shí)是最遠(yuǎn)的路,看起來最遠(yuǎn)的路,其實(shí)是最近的路。
    跟對人是說,入行后要跟個好領(lǐng)導(dǎo)好老師,剛進(jìn)社會的人做事情往往沒有經(jīng)驗(yàn),需要有人言傳身教。對于一個人的發(fā)展來說,一個好領(lǐng)導(dǎo)是非常重要的。所謂“好”的標(biāo)準(zhǔn),不是他讓你少干活多拿錢,而是以下三個。
    首先,好領(lǐng)導(dǎo)要有寬廣的心胸,如果一個領(lǐng)導(dǎo)每天都會發(fā)脾氣,那幾乎可以肯定他不是個心胸寬廣的人,能發(fā)脾氣的時候卻不發(fā)脾氣的領(lǐng)導(dǎo),多半是非常厲害的領(lǐng)導(dǎo)。中國人當(dāng)領(lǐng)導(dǎo)最大的毛病是容忍不了能力比自己強(qiáng)的人,所以常常可以看到的一個現(xiàn)象是,領(lǐng)導(dǎo)很有能力,手下一群庸才或者手下一群閑人。如果看到這樣的環(huán)境,還是不要去的好。
    其次,領(lǐng)導(dǎo)要愿意從下屬的角度來思考問題,這一點(diǎn)其實(shí)是從面試的時候就能發(fā)現(xiàn)的,如果這位領(lǐng)導(dǎo)總是從自己的角度來考慮問題,幾乎不聽你說什么,這就危險了。從下屬的角度來考慮問題并不代表同意下屬的說法,但他必須了解下屬的立場,下屬為什么要這么想,然后他才有辦法說服你,只關(guān)心自己怎么想的領(lǐng)導(dǎo)往往難以獲得下屬的信服。
    第三,領(lǐng)導(dǎo)敢于承擔(dān)責(zé)任,如果出了問題就把責(zé)任往下推,有了功勞就往自己身上攬,這樣的領(lǐng)導(dǎo)不跟也罷。選擇領(lǐng)導(dǎo),要選擇關(guān)鍵時刻能抗得住的領(lǐng)導(dǎo),能夠?yàn)橄聦俚腻e誤買單的領(lǐng)導(dǎo),因?yàn)檫@是他作為領(lǐng)導(dǎo)的責(zé)任。
    有可能,你碰不到好領(lǐng)導(dǎo),因?yàn)椋袊念I(lǐng)導(dǎo)往往是屁股決定腦袋的領(lǐng)導(dǎo),因?yàn)樗I(lǐng)導(dǎo)的位置,所以他的話就比較有道理,這是傳統(tǒng)觀念官本位的誤區(qū),可能有大量的這種無知無能的領(lǐng)導(dǎo),只是,這對于你其實(shí)是好事,如果將來有一天你要超過他,你希望他比較聰明還是比較笨?相對來說這樣的領(lǐng)導(dǎo)其實(shí)不難搞定,只是你要把自己的身段放下來而已。多認(rèn)識一些人,多和比自己強(qiáng)的人打交道,同樣能找到好的老師,不要和一群同樣郁悶的人一起控訴社會,控訴老板,這幫不上你,只會讓你更消極。和那些比你強(qiáng)的人打交道,看他們是怎么想的,怎么做的,學(xué)習(xí)他們,然后跟更強(qiáng)的人打交道。

    八、選擇

    我們每天做的最多的事情,其實(shí)是選擇,因此在談職業(yè)生涯的時候不得不提到這個話題。
    我始終認(rèn)為,在很大的范圍內(nèi),我們究竟會成為一個什么樣的人,決定權(quán)在我們自己,每天我們都在做各種各樣的選擇,我可以不去寫這篇文章,去別人的帖子拍拍磚頭,也可以寫下這些文字,幫助別人的同時也整理自己的思路,我可以多注意下格式讓別人易于閱讀,也可以寫成一堆,我可以就這樣發(fā)上來,也可以在發(fā)以前再看幾遍,你可以選擇不刮胡子就去面試,也可以選擇出門前照照鏡子……每天,每一刻我們都在做這樣那樣的決定,我們可以漫不經(jīng)心,也可以多花些心思,成千上萬的小選擇累計起來,就決定了最終我們是個什么樣的人。
    從某種意義上來說我們的未來不是別人給的,是我們自己選擇的,很多人會說我命苦啊,沒得選擇阿,如果你認(rèn)為“去微軟還是去IBM”“上清華還是上北大 ”“當(dāng)銷售副總還是當(dāng)廠長”這種才叫選擇的話,的確你沒有什么選擇,大多數(shù)人都沒有什么選擇。但每天你都可以選擇是否為客戶服務(wù)更周到一些,是否對同事更耐心一些,是否把工作做得更細(xì)致一些,是否把情況了解得更清楚一些,是否把不清楚的問題再弄清楚一些……你也可以選擇在是否在痛苦中繼續(xù)堅持,是否拋棄掉自己的那些負(fù)面的想法,是否原諒一個人的錯誤,是否相信我在這里寫下的這些話,是否不要再犯同樣的錯誤……生活每天都在給你選擇的機(jī)會,每天都在給你改變自己人生的機(jī)會,你可以選擇賴在地上撒潑打滾,也可以選擇咬牙站起來。你永遠(yuǎn)都有選擇。有些選擇不是立桿見影的,需要累積,比如農(nóng)民可以選擇自己常常去澆地,也可以選擇讓老天去澆地,誠然你今天澆水下去苗不見得今天馬上就長出來,但常常澆水,大部分苗終究會長出來的,如果你不澆,收成一定很糟糕。
    每天生活都在給你機(jī)會,他不會給你一疊現(xiàn)金也不會拱手送你個好工作,但實(shí)際上,他還是在給你機(jī)會。我的家庭是一個普通的家庭,沒有任何了不起的社會關(guān)系,我的父親在大學(xué)畢業(yè)以后就被分配到了邊疆,那個小縣城只有一條馬路,他們那一代人其實(shí)比我們更有理由抱怨,他們什么也沒得到,年輕的時候文化大革命,書都沒得讀,支援邊疆插隊落戶,等到老了,卻要給年輕人機(jī)會了。他有足夠的理由象成千上萬那樣的青年一樣坐在那里抱怨生不逢時,怨氣沖天。然而在分配到邊疆的十年之后,國家恢復(fù)招研究生,他考回了原來的學(xué)校。研究生畢業(yè),他被分配到了安徽一家小單位里,又是3年以后,國家第一屆招收博士生,他又考回了原來的學(xué)校,成為中國第一代博士,那時的他比現(xiàn)在的我年紀(jì)還大。生活并沒有放棄他,他也沒有放棄生活。10年的等待,他做了他自己的選擇,他沒有放棄,他沒有破罐子破摔,所以時機(jī)到來的時候,他改變了自己的人生。你最終會成為什么樣的人,就決定在你的每個小小的選擇之間。
    你選擇相信什么?你選擇和誰交朋友?你選擇做什么?你選擇怎么做?……我們面臨太多的選擇,而這些選擇當(dāng)中,意識形態(tài)層面的選擇又遠(yuǎn)比客觀條件的選擇來得重要得多,比如選擇做什么產(chǎn)品其實(shí)并不那么重要,而選擇怎么做才重要。選擇用什么人并不重要,而選擇怎么帶這些人才重要。大多數(shù)時候選擇客觀條件并不要緊,大多數(shù)關(guān)于客觀條件的選擇并沒有對錯之分,要緊的是選擇怎么做。一個大學(xué)生畢業(yè)了,他要去微軟也好,他要賣豬肉也好,他要創(chuàng)業(yè)也好,他要做游戲代練也好,只要不犯法,不害人,都沒有什么關(guān)系,要緊的是,選擇了以后,怎么把事情做好。
    除了這些,你還可以選擇時間和環(huán)境,比如,你可以選擇把這輩子最大的困難放在最有體力最有精力的時候,也可以走一步看一步,等到了40歲再說,只是到了40多歲,那正是一輩子最脆弱的時候,上有老下有小,如果在那個時候碰上了職業(yè)危機(jī),實(shí)在是一件很苦惱的事情。與其如此不如在20多歲30多歲的時候吃點(diǎn)苦,好讓自己脆弱的時候活得從容一些。你可以選擇在溫室里成長,也可以選擇到野外磨礪,你可以選擇在辦公室吹冷氣的工作,也可以選擇40度的酷熱下,去見你的客戶,只是,這一切最終會累積起來,引導(dǎo)你到你應(yīng)得的未來。
    我不敢說所有的事情你都有得選擇,但是絕大部分事情你有選擇,只是往往你不把這當(dāng)作一種選擇。認(rèn)真對待每一次選擇,才會有比較好的未來。

    九、選擇職業(yè)

    職業(yè)的選擇,總的來說,無非就是銷售、市場、客服、物流、行政、人事、財務(wù)、技術(shù)、管理幾個大類,有個有趣的現(xiàn)象就是,500強(qiáng)的CEO當(dāng)中最多的是銷售出身,第二多的人是財務(wù)出身,這兩者加起來大概超過95%。現(xiàn)代IT行業(yè)也有技術(shù)出身成為老板的,但實(shí)際上,后來他們還是從事了很多銷售和市場的工作,并且表現(xiàn)出色,公司才獲得了成功,完全靠技術(shù)能力成為公司老板的,幾乎沒有。這是有原因的,因?yàn)殇N售就是一門跟人打交道的學(xué)問,而管理其實(shí)也是跟人打交道的學(xué)問,這兩者之中有很多相通的東西,他們的共同目標(biāo)就是“讓別人去做某件特定的事情。”而財務(wù)則是從數(shù)字的層面了解生意的本質(zhì),從宏觀上看待生意的本質(zhì),對于一個生意是否掙錢,是否可以正常運(yùn)作有著最深刻的認(rèn)識。
    公司小的時候是銷售主導(dǎo)公司,而公司大的時候是財務(wù)主導(dǎo)公司,銷售的局限性在于只看人情不看數(shù)字,財務(wù)的局限性在于只看數(shù)字不看人情。公司初期,運(yùn)營成本低,有訂單就活得下去,跟客戶也沒有什么談判的條件,別人肯給生意做已經(jīng)謝天謝地了,這個時候訂單壓倒一切,客戶的要求壓倒一切,所以當(dāng)然要顧人情。公司大了以后,一切都要規(guī)范化,免得因?yàn)椴灰?guī)范引起一些不必要的風(fēng)險,同時運(yùn)營成本也變高,必須提高利潤率,把有限的資金放到最有產(chǎn)出的地方。對于上市公司來說,股東才不管你客戶是不是最近出國,最近是不是那個省又在搞嚴(yán)打,到了時候就要把業(yè)績拿出來,拿不出來就拋股票,這個時候就是數(shù)字壓倒一切。
    前兩天聽到有人說一句話覺得很有道理,開始的時候我們想“能做什么?”,等到公司做大了有規(guī)模了,我們想“不能做什么。”很多人在工作中覺得為什么領(lǐng)導(dǎo)這么保守,這也不行那也不行,錯過很多機(jī)會。很多時候是因?yàn)椋氵€年輕,你想的是“能做什么”,而作為公司領(lǐng)導(dǎo)要考慮的方面很多,他比較關(guān)心“不能做什么”。
    我并非鼓吹大家都去做銷售或者財務(wù),究竟選擇什么樣的職業(yè),和你究竟要選擇什么樣的人生有關(guān)系,有些人就喜歡下班按時回家,看看書聽聽音樂,那也挺好,但就不適合找個銷售的工作了,否則會是折磨自己。有些人就喜歡出風(fēng)頭,喜歡成為一群人的中心,如果選擇做財務(wù)工作,大概也干不久,因?yàn)橐话憷习宀幌矚g財務(wù)太積極,也不喜歡財務(wù)話太多。先想好自己要過怎樣的人生,再決定要找什么樣的職業(yè)。有很多的不快樂,其實(shí)是源自不滿足,而不滿足,很多時候是源自于心不定,而心不定則是因?yàn)椴磺宄烤棺约阂裁矗磺宄裁吹慕Y(jié)果就是什么都想要,結(jié)果什么都沒得到。
    我想,我們還是因?yàn)樯疃ぷ鳎皇且驗(yàn)楣ぷ鞫睿钍亲钜o的,工作只是生活中的一部分。我總是覺得生活的各方方面都是相互影響的,如果生活本身一團(tuán)亂麻,工作也不會順利。所以要有娛樂、要有社交、要鍛煉身體,要有和睦的家庭……最要緊的,要開心,我的兩個銷售找我聊天,一肚子苦水,我問他們,2年以前,你什么都沒有,工資不高,沒有客戶關(guān)系,沒有業(yè)績,處于被開的邊緣,現(xiàn)在的你比那時條件好了很多,為什么現(xiàn)在卻更加不開心了?如果你做得越好越不開心,那你為什么還要工作?首先的首先,人還是要讓自己高興起來,讓自己心態(tài)好起來,這種發(fā)自內(nèi)心的改變會讓你更有耐心,更有信心,更有氣質(zhì),更能包容……否則,看看鏡子里的你,你滿意么?
    有人會說,你說得容易,我每天加班,不加班老板就會把我炒掉,每天累得要死,哪有時間娛樂、社交、鍛煉?那是人們把目標(biāo)設(shè)定太高的緣故,如果你還在動不動就會被老板炒掉的邊緣,那么你當(dāng)然不能設(shè)立太高的目標(biāo),難道你還想每天去打高爾夫?你沒時間去健身房鍛煉身體,但是上下班的時候多走幾步可以吧,有樓梯的時候走走樓梯不走電梯可以吧?辦公的間隙扭扭脖子拉拉肩膀做做俯臥撐可以吧?誰規(guī)定鍛煉就一定要拿出每天2個小時去健身房?你沒時間社交,每月參加郊游一次可以吧,周末去參加個什么音樂班,繪畫班之類的可以吧,去嘗試認(rèn)識一些同行,和他們找機(jī)會交流交流可以吧?開始的時候總是有些難的,但邁出這一步就會向良性循環(huán)的方向發(fā)展。而每天工作得很苦悶,剩下的時間用來咀嚼苦悶,只會陷入惡性循環(huán),讓生活更加糟糕。

           雖然離開惠普僅有十五天,但感覺上惠普已經(jīng)離我很遠(yuǎn)。我的心思更多放在規(guī)劃自己第二階段的人生,這并非代表我對惠普沒有任何眷戀,主要還是想以此驅(qū)動自己往前走。萬科王石登珠穆朗瑪峰的體驗(yàn)給我很多啟發(fā),雖然在出發(fā)時攜帶大量的物資,但是登頂?shù)倪^程中,必須不斷減輕負(fù)荷,最終只有一個氧氣瓶和他登上峰頂。登山如此,漫長的人生又何嘗不是。我宣布退休后,接到同事朋友同學(xué)的祝賀。大部分人都認(rèn)為我能夠在這樣的職位上及年齡選擇退休,是一種勇氣,也是一種福氣。還有一部分人懷疑我只是借此機(jī)會換個工作,當(dāng)然還有一些人說我在HP做不下去了,趁此機(jī)會離開。

    我多年來已經(jīng)習(xí)慣別人對我的說三道四,但對于好友,我還是挺關(guān)心大家是否真正理解我的想法,這也是寫這篇文章的目的。

    由于受我父親早逝的影響,我很早就下定決心,要在有生之年實(shí)現(xiàn)自己的愿望,我不要像我父親一樣,為家庭生活忙碌一輩子,臨終前感傷,懊惱自己有很多沒有實(shí)現(xiàn)的理想。

    一本雜志的文章提到我們在生前就應(yīng)該思考自己的墓志銘,因?yàn)槟谴砟阕约簩ν昝廊松亩x,我們應(yīng)該盡可能在有生之年去實(shí)現(xiàn)它。

    我希望我的墓志銘上除了與家人及好友有關(guān)的內(nèi)容外,是這樣寫著:
    1.這個人曾經(jīng)服務(wù)于一家全球最大的IT公司(HP)25年,和她一起經(jīng)歷過數(shù)次重大的變革,看著她從以電子儀表為主要的業(yè)務(wù)變革成全球最大的IT公司。
    2.這個人曾經(jīng)在全球發(fā)展最快的國家(中國)工作16年,并擔(dān)任HP中國區(qū)總裁7年,見證及經(jīng)歷過中國改革開放的關(guān)鍵最新突破階段,與中國一起成長。
    3.這個人熱愛飛行,曾經(jīng)是一個有執(zhí)照的飛行員,累積飛行時數(shù)超過X小時,曾經(jīng)在X個機(jī)場起降過。
    4. 這個人曾經(jīng)獲得管理碩士學(xué)位,在領(lǐng)導(dǎo)管理上特別關(guān)注中國企業(yè)的組織行為及績效,并且在這個領(lǐng)域上獲得中國企業(yè)界的認(rèn)可。
    我費(fèi)時25年才總結(jié)1和2兩項(xiàng)成果,我不知還要費(fèi)時多久才能達(dá)成3和4的愿望,特別是第4個愿望需要經(jīng)歷學(xué)術(shù)的訓(xùn)練,才能將我的經(jīng)驗(yàn)總結(jié)成知識。
    否則我的經(jīng)驗(yàn)將無法有效影響及傳授他人。因此重新進(jìn)入學(xué)校學(xué)習(xí),拿一個管理學(xué)位是有必要的,更何況這是我一個非常重要的愿望。
    另一方面,我25年的時間都花在運(yùn)營(operation) 的領(lǐng)域,兢兢業(yè)業(yè)的做好職業(yè)人士的工作,它是一份好工作,特別是在HP,這份工作也幫助我建立財務(wù)的基礎(chǔ),支持家庭的發(fā)展。
    但是我不想終其一生,都陷入在運(yùn)營的領(lǐng)域,我想象企業(yè)家一樣,有機(jī)會靠一些點(diǎn)子 (ideas)賺錢,雖然風(fēng)險很高,但是值得一試,即使失敗,也不枉走一回,這也是第4個愿望其中的一部份。
    Carly Fiorina 曾經(jīng)對我說過“這個世界上有好想法的人很多,但有能力去實(shí)現(xiàn)的人很少”,2007 年5月21日在北大演講時,有人問起那些書對我影響較大,我想對我人生觀有影響的其中一本書叫“Trigger Point”,它的主要觀點(diǎn)是:人生最需要的不是規(guī)劃,而是在適當(dāng)?shù)臅r機(jī)掌握機(jī)會,采取行動。
    我這些愿望在我心中已經(jīng)醞釀一段很長的時間,開始的時候,也許一年想個一兩次,過了也就忘掉,但逐漸的,這個心中的聲音,愈來愈大,出現(xiàn)的頻率也愈來愈高,當(dāng)它幾乎每一個星期都會來與我對話時,我知道時機(jī)已經(jīng)成熟。
    但和任何人一樣,要丟掉自己現(xiàn)在所擁有的,所熟悉的環(huán)境及穩(wěn)定的收入,轉(zhuǎn)到一條自己未曾經(jīng)歷過,存在未知風(fēng)險的道路,需要絕大的勇氣,家人的支持和好友的鼓勵。有舍才有得,真是知易行難,我很高興自己終于跨出了第一步。
    我要感謝HP的EER提前退休優(yōu)惠政策,它是其中一個關(guān)鍵的Trigger Points,另一個關(guān)鍵因素是在去年五六月發(fā)生的事。
    當(dāng)時我家老大從大學(xué)畢業(yè),老二從高中畢業(yè),在他們繼續(xù)工作及求學(xué)前,這是一個黃金時段,讓我們?nèi)铱梢韵嗑垡欢屋^長的時間,我為此很早就計劃休一個長假,帶著他們到各地游玩。
    但這個計劃因?yàn)楣ぷ魃弦患匾氖虑椋∕ark Hurd 訪華)不得不取消。這個事件刺激了我必須嚴(yán)肅的去對待那心中的聲音,我會不會繼續(xù)不斷的錯失很多關(guān)鍵的機(jī)會?
    我已經(jīng)年過50,我會不會走向和我父親一樣的道路?人事部老總Charles跟我說,很多人在所有對他有利的星星都排成一列時,還是錯失時機(jī)。
    我知道原因,因?yàn)楦钌峒案淖儗θ耸嵌嗝吹睦щy,我相信大部分的人都有自己人生的理想,但我也相信很多人最終只是把這些理想當(dāng)成是
    幻想,然后不斷的為自己尋找不能實(shí)現(xiàn)的藉口,南非前總統(tǒng)曼德拉曾經(jīng)說過,“與改變世界相比,改變自己更困難”,真是一針見血。
    什么是快樂及有意義的人生?我相信每一個人的定義都不一樣,對我來說,能實(shí)現(xiàn)我墓志銘上的內(nèi)容就是我的定義。
    在中國惠普總裁的位置上固然可以吸引很多的關(guān)注及眼球,但是我太太及較親近的好友,都知道那不是我追求的,那只是為扮演好這個角色必須盡力做好的地方。
    做一個沒有名片的人士,雖然只有十多天的時間,但我發(fā)現(xiàn)我的腦袋里已經(jīng)空出很多空間及能量,讓我可以靜心的為我Chapter II的新生活做細(xì)致的調(diào)研及規(guī)劃。
    我預(yù)訂以兩年的時間來完成轉(zhuǎn)軌的準(zhǔn)備工作,并且花多點(diǎn)時間與家人共處。這兩年的時間我希望拿到飛行執(zhí)照,拿到管理有關(guān)的碩士學(xué)位,提升英文的水平,建立新的網(wǎng)絡(luò),多認(rèn)識不同行業(yè)的人,保持與大陸的聯(lián)系。希望兩年后,我可以順利回到大陸去實(shí)現(xiàn)我第四個愿望。
    毫不意外,在生活上,我發(fā)現(xiàn)很多需要調(diào)整的地方。
    二十多年來,我生活的步調(diào)及節(jié)奏,幾乎完全被公司及工作所左右,不斷涌出的deadline及任務(wù)驅(qū)動我每天的安排,一旦離開這樣的環(huán)境,第一個需要調(diào)整的就是要依靠自己的自律及意志力來驅(qū)動每天的活動,睡覺睡到自然醒的態(tài)度絕對不正確,放松自己,不給事情設(shè)定目標(biāo)及時間表,或者對錯失時間目標(biāo)無所謂,也不正確,沒有年度,季度,月及周計劃也不正確。
    擔(dān)任高層經(jīng)理多年,已經(jīng)養(yǎng)成交待事情的習(xí)慣,自己的時間主要花在思考,決策及追蹤項(xiàng)目的進(jìn)展情況,更多是依靠一個龐大的團(tuán)隊來執(zhí)行具體的事項(xiàng)及秘書來處理很多協(xié)調(diào)及繁瑣的事情。
    到美國后,很多事情需要打800號電話聯(lián)系,但這些電話很忙,常讓你在waiting line上等待很長的時間,當(dāng)我在等待時,我可以體會以前秘書工作辛苦的地方,但同時也提醒我自己,在這個階段要改變態(tài)度,培養(yǎng)更大的耐性及自己動手做的能力。
    生活的內(nèi)容也要做出很大的調(diào)整,多出時間鍛煉身體,多出時間關(guān)注家人,多出時間關(guān)注朋友,多出時間體驗(yàn)不同的休閑活動及飛行,一步步的,希望生活逐步調(diào)整到我所期望的軌道上,期待這兩年的生活既充實(shí)又充滿樂趣及意義。
    第一個快樂的體驗(yàn)就是準(zhǔn)備及參加大兒子的訂婚禮,那種全心投入,不需擔(dān)憂工作數(shù)字的感覺真好。同時我也租好了公寓,買好了家具及車子,陪家人在周末的時候到Reno 及Lake Tahoe玩了一趟,Lake Tahoe我去了多次,但這次的體驗(yàn)有所不同,我從心里欣賞到它的美麗。
    但同時我也在加緊調(diào)研的工作,為申請大學(xué)及飛行學(xué)校做準(zhǔn)備,這段時間也和在硅谷的朋友及一些風(fēng)險投資公司見面,了解不同的產(chǎn)業(yè)。
    我的人生觀是“完美的演出來自充分的準(zhǔn)備”,“勇于改變自己,適應(yīng)不斷變化的環(huán)境,機(jī)會將不斷出現(xiàn)”,“快樂及有意義的人生來自于實(shí)現(xiàn)自己心中的愿望,而非外在的掌聲”。
    我離開時,有兩位好朋友送給我兩個不同的祝語,Baron的是“多年功過化煙塵”,楊華的是“莫春者,風(fēng)乎舞雩,詠而歸”,它們分別代表了我離開惠普及走向未來的心情。
    我總結(jié)人生有三個階段,一個階段是為現(xiàn)實(shí)找一份工作,一個階段是為現(xiàn)實(shí),但可以選擇一份自己愿意投入的工作,一個階段是為理想去做一些事情。
    我珍惜我的福氣,感激HP及同事、好朋友給我的支持,鼓勵及協(xié)助,這篇文字化我心聲的文章與好友分享。

    posted @ 2008-02-19 13:46 honzeland 閱讀(273) | 評論 (0)編輯 收藏

    實(shí)現(xiàn)java UDP Server --2008農(nóng)歷新年第一貼(原創(chuàng))

    一、UDP Server
    項(xiàng)目的需要,需要利用java實(shí)現(xiàn)一個udp server,主要的功能是偵聽來自客戶端的udp請求,客戶請求可能是大并發(fā)量的,對于每個請求Server端的處理很簡單,處理每個請求的時間大約在1ms左右,但是Server端需要維護(hù)一個對立于請求的全局變量Cache,項(xiàng)目本身已經(jīng)采用Mina架構(gòu)(http://mina.apache.org/),我要開發(fā)的Server作為整個項(xiàng)目的一個模塊,由于之前沒有開發(fā)UDP Server,受TCP Server的影響,很自然的想利用多線程來實(shí)現(xiàn),對于每個客戶請求,新建一個線程來處理相應(yīng)的邏輯,在實(shí)現(xiàn)的過程中,利用Mina的Thread Model,實(shí)現(xiàn)了一個多線程的UDP Server,但是由于要維護(hù)一個全局Cache,需要在各線程之間同步,加之處理請求的時間很短,很快就發(fā)現(xiàn)在此利用多線程,并不能提高性能,于是決定采用單線程來實(shí)現(xiàn),在動手之前,還是發(fā)現(xiàn)有兩種方案來實(shí)現(xiàn)單線程UDP Server:
    1) 采用JDK的DatagramSocket和DatagramPacket來實(shí)現(xiàn)
    public class UDPServerUseJDK extends Thread{
        /**
         * Constructor
         * @param port port used to listen incoming connection
         * @throws SocketException error to consturct a DatagramSocket with this port
         */
        public MediatorServerUseJDK(int port) throws SocketException{
            this("MediatorServerThread", port);
        }
        
        /**
         * Constructor
         * @param name the thread name
         * @param port port used to listen incoming connection
         * @throws SocketException error to consturct a DatagramSocket with this port
         */
        public MediatorServerUseJDK(String name, int port) throws SocketException{
            super(name);
            socket = new DatagramSocket(port);
            System.out.println("Mediator server started on JDK model...");
            System.out.println("Socket buffer size: " + socket.getReceiveBufferSize());
        }
        
        public void run(){
            long startTime = 0;
            while(true){
                try {
                    buf = new byte[1024];
                    // receive request
                    packet = new DatagramPacket(buf, buf.length);
                    socket.receive(packet);
                    .........
                }catch (IOException e) {
                .........
                }
            }
        }

    2) 采用Mina的DatagramAcceptor來實(shí)現(xiàn),在創(chuàng)建Exector的時候,只傳遞單個線程
    public class MediatorServerUseMina {
        private DatagramAcceptor acceptor;

        public MediatorServerUseMina() {

        }

        public void startListener(InetSocketAddress address, IoHandler handler) {
            // create an acceptor with a single thread
            this.acceptor = new DatagramAcceptor(Executors.newSingleThreadExecutor());
            // configure the thread models
            DatagramAcceptorConfig acceptorConfig = acceptor.getDefaultConfig();
            acceptorConfig.setThreadModel(ThreadModel.MANUAL);
            // set the acceptor to reuse the address
            acceptorConfig.getSessionConfig().setReuseAddress(true);
            // add io filters
            DefaultIoFilterChainBuilder filterChainBuilder = acceptor.getFilterChain();
            // add CPU-bound job first,
            filterChainBuilder.addLast("codec", new ProtocolCodecFilter(new StringCodecFactory()));
            try {
                // bind
                acceptor.bind(address, handler);
                System.out.println("Mediator Server started on mina model...");
                System.out.println("Socket buffer size: " + acceptorConfig.getSessionConfig().getReceiveBufferSize());
            } catch (IOException e) {
                System.err.println("Error starting component listener on port(UDP) " + address.getPort() + ": "
                        + e.getMessage());
            }
        }
    }
    二、Performance Test
    為了測試兩個Server的性能,寫了個簡單的測試客戶端
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.net.SocketException;
    import java.net.UnknownHostException;

    /**
     * @author Herry Hong
     *
     */

    public class PerformanceTest {
        /** Number of threads to be created */
        static final int THREADS = 100;
        /** Packets to be sent per thread */
        static final int PACKETS = 500;
        /** The interval of two packets been sent for each thread */
        private static final int INTERVAL = 80;
        private static final String DATA = "5a76d93cb435fc54eba0b97156fe38f432a4e1da3a87cce8a222644466ed1317";

        private class Sender implements Runnable {
            private InetAddress address = null;
            private DatagramSocket socket = null;
            private String msg = null;
            private String name = null;
            private int packet_sent = 0;

            public Sender(String addr, String msg, String name) throws SocketException,
                    UnknownHostException {
                this.address = InetAddress.getByName(addr);
                this.socket = new DatagramSocket();
                this.msg = msg;
                this.name = name;
            }

            @Override
            public void run() {
                // send request
                byte[] buf = msg.getBytes();
                DatagramPacket packet = new DatagramPacket(buf, buf.length,
                        address, 8000);
                try {
                    for (int i = 0; i < PerformanceTest.PACKETS; i++) {
                        socket.send(packet);
                        packet_sent++;
                        Thread.sleep(INTERVAL);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //System.out.println("Thread " + name + " sends " + packet_sent + " packets.");
                //System.out.println("Thread " + name + " end!");
            }
        }

        /**
         * @param args
         */
        public static void main(String[] args) {
            if (args.length != 1) {
                System.out.println("Usage: java PerformanceTest <hostname>");
                return;
            }
            String msg;
            for (int i = 0; i < THREADS; i++) {
                if(i % 2 == 0){
                    msg = i + "_" + (i+1) + ""r"n" + (i+1) + "_" + i + ""r"n" + DATA;
                }else{
                    msg = i + "_" + (i-1) + ""r"n" + (i-1) + "_" + i + ""r"n" + DATA;
                }
                try {
                    new Thread(new PerformanceTest().new Sender(args[0], msg, "" + i)).start();
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    三、測試結(jié)果
    測試環(huán)境:
    Server:AMD Athlon(tm) 64 X2 Dual Core Processor 4000+,1G memory
    Client:AMD Athlon(tm) 64 X2 Dual Core Processor 4000+,1G memory
    在測試的過程中,當(dāng)INTERVAL設(shè)置的太小時,服務(wù)器端會出現(xiàn)丟包現(xiàn)象,INTERVAL越小,丟包越嚴(yán)重,為了提高Server的性能,特將Socket的ReceiveBufferSize設(shè)置成默認(rèn)大小的兩倍,
    對于JDK實(shí)現(xiàn):
        public MediatorServerUseJDK(String name, int port) throws SocketException{
            super(name);
            socket = new DatagramSocket(port);
            // set the receive buffer size to double default size
            socket.setReceiveBufferSize(socket.getReceiveBufferSize() * 2);
            System.out.println("Mediator server started on JDK model...");
            System.out.println("Socket buffer size: " + socket.getReceiveBufferSize());
        }
    對于Mina實(shí)現(xiàn):
            DatagramAcceptorConfig acceptorConfig = acceptor.getDefaultConfig();
            acceptorConfig.setThreadModel(ThreadModel.MANUAL);
            // set the acceptor to reuse the address
            acceptorConfig.getSessionConfig().setReuseAddress(true);
            // set the receive buffer size to double default size
            int recBufferSize = acceptorConfig.getSessionConfig().getReceiveBufferSize();
            acceptorConfig.getSessionConfig().setReceiveBufferSize(recBufferSize * 2);
    此時,相同的INTERVAL,丟包現(xiàn)象明顯減少。
    接下來,再測試不同實(shí)現(xiàn)的性能差異:
    UDP server started on JDK model...
    Socket buffer size: 110592
    INTERVAL = 100ms,沒有出現(xiàn)丟包,
    Process time: 49988
    Process time: 49982
    Process time: 49984
    Process time: 49986
    Process time: 49984
    INTERVAL = 80ms,仍然沒有丟包,不管Server是不是初次啟動
    Process time: 40006
    Process time: 40004
    Process time: 40003
    Process time: 40005
    Process time: 40013
    UDP Server started on mina model...
    Socket buffer size: 110592
    INTERVAL = 80ms,Server初次啟動時,經(jīng)常會出現(xiàn)丟包,當(dāng)?shù)谝淮危ㄖ阜?wù)器初次啟動時)沒有丟包時,隨后基本不丟包,
    Process time: 39973
    Process time: 40006
    Process time: 40007
    Process time: 40008
    Process time: 40008
    INTERVAL = 100ms,沒有出現(xiàn)丟包
    Process time: 49958
    Process time: 49985
    Process time: 49983
    Process time: 49988
    四、結(jié)論
    在該要求下,采用JDK和Mina實(shí)現(xiàn)性能相當(dāng),但是在Server初次啟動時JDK實(shí)現(xiàn)基本不會出現(xiàn)丟包,而Mina實(shí)現(xiàn)則在Server初次啟動時經(jīng)常出現(xiàn)丟包現(xiàn)象,在經(jīng)歷第一次測試后,兩種實(shí)現(xiàn)處理時間相近,請求并發(fā)量大概為每ms一個請求時,服務(wù)器不會出現(xiàn)丟包。

    posted @ 2008-02-15 16:19 honzeland 閱讀(7868) | 評論 (4)編輯 收藏

    主站蜘蛛池模板: 99re热精品视频国产免费| 精品国产免费人成网站| 无套内射无矿码免费看黄| 无码人妻丰满熟妇区免费| 亚洲不卡AV影片在线播放| 亚洲乱码卡三乱码新区| a在线观看免费视频| 免费一级毛片清高播放| 亚洲狠狠成人综合网| 男人j进女人p免费视频| 国产精品无码免费播放| 亚洲精品国产成人99久久| 亚洲AV无码专区在线电影成人| 久久国产乱子伦免费精品| 久久久久亚洲AV无码专区桃色| 亚洲国产欧洲综合997久久| 免费观看久久精彩视频| 女人18毛片水真多免费看| 中文字幕亚洲天堂| 美女黄频a美女大全免费皮| 成年女人毛片免费观看97| 亚洲精品福利网站| 久久WWW免费人成—看片| 国产一区二区三区在线免费| 亚洲深深色噜噜狠狠网站| 91精品免费观看| 亚洲综合精品一二三区在线| 国产亚洲Av综合人人澡精品| 久久99精品国产免费观看| 亚洲综合国产精品第一页| 国产亚洲精品美女2020久久| 性感美女视频免费网站午夜| 亚洲国产成人99精品激情在线| 日本高清在线免费| 亚洲成AV人片在线观看WWW| 国产99视频精品免费视频76| 成人免费男女视频网站慢动作 | 亚洲中文无码av永久| 中文字幕看片在线a免费| 最近免费中文字幕视频高清在线看 | 亚洲AV无一区二区三区久久|