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

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

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

    from:http://blog.csdn.net/ugg/article/details/41894947


    背景
    在很多互聯(lián)網(wǎng)產(chǎn)品應用中,有些場景需要加鎖處理,比如:秒殺,全局遞增ID,樓層生成等等。大部分的解決方案是基于DB實現(xiàn)的,Redis為單進程單線程模式,采用隊列模式將并發(fā)訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關(guān)系。其次Redis提供一些命令SETNX,GETSET,可以方便實現(xiàn)分布式鎖機制。

    Redis命令介紹
    使用Redis實現(xiàn)分布式鎖,有兩個重要函數(shù)需要介紹

    SETNX命令(SET if Not eXists)
    語法:
    SETNX key value
    功能:
    當且僅當 key 不存在,將 key 的值設(shè)為 value ,并返回1;若給定的 key 已經(jīng)存在,則 SETNX 不做任何動作,并返回0。

    GETSET命令
    語法:
    GETSET key value
    功能:
    將給定 key 的值設(shè)為 value ,并返回 key 的舊值 (old value),當 key 存在但不是字符串類型時,返回一個錯誤,當key不存在時,返回nil。

    GET命令
    語法:
    GET key
    功能:
    返回 key 所關(guān)聯(lián)的字符串值,如果 key 不存在那么返回特殊值 nil 。

    DEL命令
    語法:
    DEL key [KEY …]
    功能:
    刪除給定的一個或多個 key ,不存在的 key 會被忽略。

    兵貴精,不在多。分布式鎖,我們就依靠這四個命令。但在具體實現(xiàn),還有很多細節(jié),需要仔細斟酌,因為在分布式并發(fā)多進程中,任何一點出現(xiàn)差錯,都會導致死鎖,hold住所有進程。

    加鎖實現(xiàn)

    SETNX 可以直接加鎖操作,比如說對某個關(guān)鍵詞foo加鎖,客戶端可以嘗試
    SETNX foo.lock <current unix time>

    如果返回1,表示客戶端已經(jīng)獲取鎖,可以往下操作,操作完成后,通過
    DEL foo.lock

    命令來釋放鎖。
    如果返回0,說明foo已經(jīng)被其他客戶端上鎖,如果鎖是非堵塞的,可以選擇返回調(diào)用。如果是堵塞調(diào)用調(diào)用,就需要進入以下個重試循環(huán),直至成功獲得鎖或者重試超時。理想是美好的,現(xiàn)實是殘酷的。僅僅使用SETNX加鎖帶有競爭條件的,在某些特定的情況會造成死鎖錯誤。

    處理死鎖

    在上面的處理方式中,如果獲取鎖的客戶端端執(zhí)行時間過長,進程被kill掉,或者因為其他異常崩潰,導致無法釋放鎖,就會造成死鎖。所以,需要對加鎖要做時效性檢測。因此,我們在加鎖時,把當前時間戳作為value存入此鎖中,通過當前時間戳和Redis中的時間戳進行對比,如果超過一定差值,認為鎖已經(jīng)時效,防止鎖無限期的鎖下去,但是,在大并發(fā)情況,如果同時檢測鎖失效,并簡單粗暴的刪除死鎖,再通過SETNX上鎖,可能會導致競爭條件的產(chǎn)生,即多個客戶端同時獲取鎖。

    C1獲取鎖,并崩潰。C2和C3調(diào)用SETNX上鎖返回0后,獲得foo.lock的時間戳,通過比對時間戳,發(fā)現(xiàn)鎖超時。
    C2 向foo.lock發(fā)送DEL命令。
    C2 向foo.lock發(fā)送SETNX獲取鎖。
    C3 向foo.lock發(fā)送DEL命令,此時C3發(fā)送DEL時,其實DEL掉的是C2的鎖。
    C3 向foo.lock發(fā)送SETNX獲取鎖。

    此時C2和C3都獲取了鎖,產(chǎn)生競爭條件,如果在更高并發(fā)的情況,可能會有更多客戶端獲取鎖。所以,DEL鎖的操作,不能直接使用在鎖超時的情況下,幸好我們有GETSET方法,假設(shè)我們現(xiàn)在有另外一個客戶端C4,看看如何使用GETSET方式,避免這種情況產(chǎn)生。

    C1獲取鎖,并崩潰。C2和C3調(diào)用SETNX上鎖返回0后,調(diào)用GET命令獲得foo.lock的時間戳T1,通過比對時間戳,發(fā)現(xiàn)鎖超時。
    C4 向foo.lock發(fā)送GESET命令,
    GETSET foo.lock <current unix time>
    并得到foo.lock中老的時間戳T2

    如果T1=T2,說明C4獲得時間戳。
    如果T1!=T2,說明C4之前有另外一個客戶端C5通過調(diào)用GETSET方式獲取了時間戳,C4未獲得鎖。只能sleep下,進入下次循環(huán)中。

    現(xiàn)在唯一的問題是,C4設(shè)置foo.lock的新時間戳,是否會對鎖產(chǎn)生影響。其實我們可以看到C4和C5執(zhí)行的時間差值極小,并且寫入foo.lock中的都是有效時間錯,所以對鎖并沒有影響。
    為了讓這個鎖更加強壯,獲取鎖的客戶端,應該在調(diào)用關(guān)鍵業(yè)務(wù)時,再次調(diào)用GET方法獲取T1,和寫入的T0時間戳進行對比,以免鎖因其他情況被執(zhí)行DEL意外解開而不知。以上步驟和情況,很容易從其他參考資料中看到。客戶端處理和失敗的情況非常復雜,不僅僅是崩潰這么簡單,還可能是客戶端因為某些操作被阻塞了相當長時間,緊接著 DEL 命令被嘗試執(zhí)行(但這時鎖卻在另外的客戶端手上)。也可能因為處理不當,導致死鎖。還有可能因為sleep設(shè)置不合理,導致Redis在大并發(fā)下被壓垮。最為常見的問題還有

    GET返回nil時應該走那種邏輯?

    第一種走超時邏輯
    C1客戶端獲取鎖,并且處理完后,DEL掉鎖,在DEL鎖之前。C2通過SETNX向foo.lock設(shè)置時間戳T0 發(fā)現(xiàn)有客戶端獲取鎖,進入GET操作。
    C2 向foo.lock發(fā)送GET命令,獲取返回值T1(nil)。
    C2 通過T0>T1+expire對比,進入GETSET流程。
    C2 調(diào)用GETSET向foo.lock發(fā)送T0時間戳,返回foo.lock的原值T2
    C2 如果T2=T1相等,獲得鎖,如果T2!=T1,未獲得鎖。

    第二種情況走循環(huán)走setnx邏輯
    C1客戶端獲取鎖,并且處理完后,DEL掉鎖,在DEL鎖之前。C2通過SETNX向foo.lock設(shè)置時間戳T0 發(fā)現(xiàn)有客戶端獲取鎖,進入GET操作。
    C2 向foo.lock發(fā)送GET命令,獲取返回值T1(nil)。
    C2 循環(huán),進入下一次SETNX邏輯

    兩種邏輯貌似都是OK,但是從邏輯處理上來說,第一種情況存在問題。當GET返回nil表示,鎖是被刪除的,而不是超時,應該走SETNX邏輯加鎖。走第一種情況的問題是,正常的加鎖邏輯應該走SETNX,而現(xiàn)在當鎖被解除后,走的是GETST,如果判斷條件不當,就會引起死鎖,很悲催,我在做的時候就碰到了,具體怎么碰到的看下面的問題

    GETSET返回nil時應該怎么處理?

    C1和C2客戶端調(diào)用GET接口,C1返回T1,此時C3網(wǎng)絡(luò)情況更好,快速進入獲取鎖,并執(zhí)行DEL刪除鎖,C2返回T2(nil),C1和C2都進入超時處理邏輯。
    C1 向foo.lock發(fā)送GETSET命令,獲取返回值T11(nil)。
    C1 比對C1和C11發(fā)現(xiàn)兩者不同,處理邏輯認為未獲取鎖。
    C2 向foo.lock發(fā)送GETSET命令,獲取返回值T22(C1寫入的時間戳)。
    C2 比對C2和C22發(fā)現(xiàn)兩者不同,處理邏輯認為未獲取鎖。

    此時C1和C2都認為未獲取鎖,其實C1是已經(jīng)獲取鎖了,但是他的處理邏輯沒有考慮GETSET返回nil的情況,只是單純的用GET和GETSET值就行對比,至于為什么會出現(xiàn)這種情況?一種是多客戶端時,每個客戶端連接Redis的后,發(fā)出的命令并不是連續(xù)的,導致從單客戶端看到的好像連續(xù)的命令,到Redis server后,這兩條命令之間可能已經(jīng)插入大量的其他客戶端發(fā)出的命令,比如DEL,SETNX等。第二種情況,多客戶端之間時間不同步,或者不是嚴格意義的同步。

    時間戳的問題

    我們看到foo.lock的value值為時間戳,所以要在多客戶端情況下,保證鎖有效,一定要同步各服務(wù)器的時間,如果各服務(wù)器間,時間有差異。時間不一致的客戶端,在判斷鎖超時,就會出現(xiàn)偏差,從而產(chǎn)生競爭條件。
    鎖的超時與否,嚴格依賴時間戳,時間戳本身也是有精度限制,假如我們的時間精度為秒,從加鎖到執(zhí)行操作再到解鎖,一般操作肯定都能在一秒內(nèi)完成。這樣的話,我們上面的CASE,就很容易出現(xiàn)。所以,最好把時間精度提升到毫秒級。這樣的話,可以保證毫秒級別的鎖是安全的。

    分布式鎖的問題

    1:必要的超時機制:獲取鎖的客戶端一旦崩潰,一定要有過期機制,否則其他客戶端都降無法獲取鎖,造成死鎖問題。
    2:分布式鎖,多客戶端的時間戳不能保證嚴格意義的一致性,所以在某些特定因素下,有可能存在鎖串的情況。要適度的機制,可以承受小概率的事件產(chǎn)生。
    3:只對關(guān)鍵處理節(jié)點加鎖,良好的習慣是,把相關(guān)的資源準備好,比如連接數(shù)據(jù)庫后,調(diào)用加鎖機制獲取鎖,直接進行操作,然后釋放,盡量減少持有鎖的時間。
    4:在持有鎖期間要不要CHECK鎖,如果需要嚴格依賴鎖的狀態(tài),最好在關(guān)鍵步驟中做鎖的CHECK檢查機制,但是根據(jù)我們的測試發(fā)現(xiàn),在大并發(fā)時,每一次CHECK鎖操作,都要消耗掉幾個毫秒,而我們的整個持鎖處理邏輯才不到10毫秒,玩客沒有選擇做鎖的檢查。
    5:sleep學問,為了減少對Redis的壓力,獲取鎖嘗試時,循環(huán)之間一定要做sleep操作。但是sleep時間是多少是門學問。需要根據(jù)自己的Redis的QPS,加上持鎖處理時間等進行合理計算。
    6:至于為什么不使用Redis的muti,expire,watch等機制,可以查一參考資料,找下原因。

    鎖測試數(shù)據(jù)

    未使用sleep
    第一種,鎖重試時未做sleep。單次請求,加鎖,執(zhí)行,解鎖時間 


    可以看到加鎖和解鎖時間都很快,當我們使用

    ab -n1000 -c100 'http://sandbox6.wanke.etao.com/test/test_sequence.php?tbpm=t'
    AB 并發(fā)100累計1000次請求,對這個方法進行壓測時。 


    我們會發(fā)現(xiàn),獲取鎖的時間變成,同時持有鎖后,執(zhí)行時間也變成,而delete鎖的時間,將近10ms時間,為什么會這樣?
    1:持有鎖后,我們的執(zhí)行邏輯中包含了再次調(diào)用Redis操作,在大并發(fā)情況下,Redis執(zhí)行明顯變慢。
    2:鎖的刪除時間變長,從之前的0.2ms,變成9.8ms,性能下降近50倍。
    在這種情況下,我們壓測的QPS為49,最終發(fā)現(xiàn)QPS和壓測總量有關(guān),當我們并發(fā)100總共100次請求時,QPS得到110多。當我們使用sleep時

    使用Sleep時

    單次執(zhí)行請求時

    我們看到,和不使用sleep機制時,性能相當。當時用相同的壓測條件進行壓縮時 

    獲取鎖的時間明顯變長,而鎖的釋放時間明顯變短,僅是不采用sleep機制的一半。當然執(zhí)行時間變成就是因為,我們在執(zhí)行過程中,重新創(chuàng)建數(shù)據(jù)庫連接,導致時間變長的。同時我們可以對比下Redis的命令執(zhí)行壓力情況 

    上圖中細高部分是為未采用sleep機制的時的壓測圖,矮胖部分為采用sleep機制的壓測圖,通上圖看到壓力減少50%左右,當然,sleep這種方式還有個缺點QPS下降明顯,在我們的壓測條件下,僅為35,并且有部分請求出現(xiàn)超時情況。不過綜合各種情況后,我們還是決定采用sleep機制,主要是為了防止在大并發(fā)情況下把Redis壓垮,很不行,我們之前碰到過,所以肯定會采用sleep機制。

    參考資料

    http://www.worlduc.com/FileSystem/18/2518/590664/9f63555e6079482f831c8ab1dcb8c19c.pdf
    http://redis.io/commands/setnx
    http://m.tkk7.com/caojianhua/archive/2013/01/28/394847.html

    版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。

    posted @ 2015-08-31 15:22 小馬歌 閱讀(286) | 評論 (0)編輯 收藏
     

    先描述一下問題,多個服務(wù)器實現(xiàn)的負載均衡,每個服務(wù)器存儲在自己的硬盤里。但是現(xiàn)在需要對日志做統(tǒng)一的分析,在多個服務(wù)器上統(tǒng)計就麻煩了。思路是把日志統(tǒng)一到一臺日志服務(wù)器上,再統(tǒng)一做統(tǒng)計分析。怎么統(tǒng)一到一臺服務(wù)器上,說實話沒有特別好的思路,最后嘗試了log4j的SocketAppender。查了不少網(wǎng)絡(luò)資源,都說的有些不明了,還是得親自嘗試之后才見分曉。

    1、客戶端的配置

    客戶端的配置比較簡單,只需要告訴log4j需要監(jiān)聽哪個遠程服務(wù)器的哪個端口即可。直接在log4j.properties里直接配置就好。

    1. <span style="font-size:12px;">log4j.appender.logs=org.apache.log4j.DailyRollingFileAppender  
    2. log4j.appender.logs.File = /data/logs/request/logs.log  
    3. log4j.appender.logs.layout = org.apache.log4j.PatternLayout  
    4. log4j.appender.logs.layout.ConversionPattern=%d [%t] - %m%n  
    5. log4j.appender.logs.DatePattern='.'yyyy-MM-dd'.log'  
    6.   
    7. log4j.appender.socket=org.apache.log4j.net.SocketAppender  
    8. log4j.appender.socket.RemoteHost=172.16.2.152  
    9. log4j.appender.socket.Port=4560  
    10. log4j.appender.socket.LocationInfo=true  
    11. #下面這兩句感覺沒用  
    12. log4j.appender.socket.layout=org.apache.log4j.PatternLayout  
    13. log4j.appender.socket.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%t%m%n  
    14.   
    15. #將日志寫入本地和遠程日志服務(wù)器  
    16. log4j.logger.com.test.core.filter =DEBUG,socket,logs</span>  
     

    2、日志服務(wù)器的配置

    日志服務(wù)器需要單獨啟動一個java進程,接收客戶端給自己發(fā)送的socket請求。Log4j提供了org.apache.log4j.net.SocketServer類,直接運行其main函數(shù)就行了(當然也可以自己寫啦)。

    java -cp /log4jsocket/serverConfig/log4j-1.2.16.jarorg.apache.log4j.net.SocketServer 4560 /log4jsocket/log4jserver.properties /log4jsocket/clientConfig

    /log4jsocket/serverConfig/log4j-1.2.16.jar是log4j jar包存放的位置,org.apache.log4j.net.SocketServer需要三個參數(shù):

    1)4560 是監(jiān)聽的端口號

    2)/log4jsocket/log4jserver.properties 是記錄日志服務(wù)器的日志的配置文件

    3)/log4jsocket/clientConfig 是客戶端配置文件所在的目錄(注意是目錄)。

    著重說一下org.apache.log4j.net.SocketServer的第三個參數(shù),這個文件夾下配置的是各個客戶端的日志的配置。配置文件以.lcf結(jié)尾,文件名可以用客戶端的IP命名,log4j會自己找發(fā)送請求的客戶端IP對應的那個配置文件,如172.16.2.46服務(wù)器發(fā)送的socket請求會尋找172.16.2.46.lcf配置文件,并根據(jù)配置將日志寫入對應的文件。

    1. <span style="font-size:12px;">#注意logger后面的值要與client的值相同  
    2. log4j.logger.com.test.core.filter=DEBUG,localLogs  
    3.    
    4. log4j.appender.localLogs=org.apache.log4j.DailyRollingFileAppender  
    5. log4j.appender.localLogs.File=/data/logs/request/172.16.2.46/logs.log  
    6. log4j.appender.localLogs.layout=org.apache.log4j.PatternLayout  
    7. log4j.appender.localLogs.layout.ConversionPattern=%d [%t] - %m%n  
    8. log4j.appender.localLogs.DatePattern='.'yyyy-MM-dd'.log'  
    9. </span>  


    這樣做的好處是可以根據(jù)不同客戶端,將日志寫入不同的文件夾下的。

    其實,配置過程就這么簡單,但是當你這么做之后,你會發(fā)現(xiàn)運行org.apache.log4j.net.SocketServer后,客戶端向日志服務(wù)器發(fā)送請求時,會報找不到.lcf文件的錯誤,得不到想要的結(jié)果。原因出在org.apache.log4j.net.SocketServer代碼中的一個小bug。 

    1. <span style="font-size:12px;">LoggerRepository configureHierarchy(InetAddress inetAddress)  
    2.   {  
    3.     cat.info("Locating configuration file for " + inetAddress);  
    4.   
    5.     String s = inetAddress.toString();  
    6.     int i = s.indexOf("/");  
    7.     if (i == -1) {  
    8.       cat.warn("Could not parse the inetAddress [" + inetAddress + "]. Using default hierarchy.");  
    9.   
    10.       return genericHierarchy();  
    11.     }  
    12.     String key = s.substring(0,i);  
    13.   
    14.     File configFile = new File(this.dir, key + CONFIG_FILE_EXT);  
    15.     if (configFile.exists()) {  
    16.       Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));  
    17.       this.hierarchyMap.put(inetAddress, h);  
    18.   
    19.       new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h);  
    20.   
    21.       return h;  
    22.     }  
    23.     cat.warn("Could not find config file [" + configFile + "].");  
    24.     return genericHierarchy();  
    25.   }</span>  

    String key = s.substring(0, i);換成String key = s.substring(i+1);就好了。這段代碼是解析IP地址,然后尋找對應IP命名的.lcf配置文件;如果找不到,則解析默認的generic.lcf。由于截取的錯誤,導致找不到172.16.2.46.lcf,文件夾下又沒有g(shù)eneric.lcf,所以會拋異常。

    org.apache.log4j.net.SocketServer代碼中的另外一個bug是,只能接收來自一臺客戶端的日志請求,一旦客戶端停止運行,SocketServer也將關(guān)閉。查看代碼:

    1. public static void main(String[] argv)  
    2.   {     
    3.       if (argv.length == 3)  
    4.       init(argv[0], argv[1], argv[2]);  
    5.     else  
    6.       usage("Wrong number of arguments.");  
    7.     try  
    8.     {  
    9.         cat.info("Listening on port " + port);  
    10.         ServerSocket serverSocket = new ServerSocket(port);  
    11.         cat.info("Waiting to accept a new client.");  
    12.     Socket socket = serverSocket.accept();  
    13.     InetAddress inetAddress = socket.getInetAddress();  
    14.     cat.info("Connected to client at " + inetAddress);  
    15.       
    16.     LoggerRepository h = (LoggerRepository)server.hierarchyMap.get(inetAddress);  
    17.     if (h == null) {  
    18.             h = server.configureHierarchy(inetAddress);  
    19.     }  
    20.       
    21.     cat.info("Starting new socket node.");  
    22.     new Thread(new SocketNode(socket, h)).start();  
    23.       }  
    24.     catch (Exception e)  
    25.     {  
    26.       e.printStackTrace();  
    27.     }  
    28.   }  

    問題出在只建立了一個socket連接就不在accept了,加上while循環(huán)問題就解決了。

    1. ServerSocket serverSocket = new ServerSocket(port);  
    2. while(true){  
    3.  cat.info("Waiting to accept a new client.");  
    4.  Socket socket = serverSocket.accept();  
    5.  InetAddress inetAddress = socket.getInetAddress();  
    6.  cat.info("Connected to client at " + inetAddress);  
    7.   
    8.  LoggerRepository h = (LoggerRepository)server.hierarchyMap.get(inetAddress);  
    9.  if (h == null) {  
    10.    h = server.configureHierarchy(inetAddress);  
    11.  }  
    12.   
    13.  cat.info("Starting new socket node.");  
    14.  new Thread(new SocketNode(socket, h)).start();  
    15. }  



     

     

    好了。Log4j的配置到此結(jié)束。

    最后一個問題,日志服務(wù)器是linux,需要有一個統(tǒng)一的start、shutdown命令來啟動和關(guān)閉org.apache.log4j.net.SocketServer。那就需要些shell命令了,下面這段代碼參考了http://www.cnblogs.com/baibaluo/archive/2011/08/31/2160934.html

    catalina.sh

    1. <span style="font-size:12px;">#!/bin/bash  
    2. #端口  
    3. LISTEN_PORT=4560  
    4. #服務(wù)端log4j配置文件  
    5. SERVER_CONFIG=/log4jsocket/server.properties  
    6. #客戶端的配置  
    7. CLIENT_CONFIG_DIR=/log4jsocket/clientConfig  
    8.   
    9. #Java程序所在的目錄(classes的上一級目錄)  
    10. APP_HOME=/opt/log4jsocket/serverConfig   
    11. #需要啟動的Java主程序(main方法類)  
    12. APP_MAINCLASS=org.apache.log4j.net.SocketServer  
    13.    
    14. #拼湊完整的classpath參數(shù),包括指定lib目錄下所有的jar  
    15. CLASSPATH=$APP_HOME  
    16. for i in "$APP_HOME"/*.jar; do     
    17.     CLASSPATH="$CLASSPATH":"$i"  
    18. done  
    19.   
    20. #JDK所在路徑  
    21. JAVA_HOME="/opt/jdk1.6.0_30"   
    22. #執(zhí)行程序啟動所使用的系統(tǒng)用戶,考慮到安全,推薦不使用root帳號  
    23. RUNNING_USER=root  
    24.    
    25. #java虛擬機啟動參數(shù)  
    26. JAVA_OPTS="-ms512m -mx512m -Xmn256m -Djava.awt.headless=true -XX:MaxPermSize=128m"   
    27.   
    28. #初始化psid變量(全局)  
    29. psid=0  
    30.    
    31. checkpid() {  
    32.    javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAINCLASS`  
    33.    
    34.    if [ -n "$javaps" ]; then  
    35.       psid=`echo $javaps | awk '{print $1}'`  
    36.    else  
    37.       psid=0  
    38.    fi  
    39. }  
    40.   
    41. start() {  
    42.    checkpid  
    43.    
    44.    if [ $psid -ne 0 ]; then  
    45.       echo "================================"  
    46.       echo "warn: $APP_MAINCLASS already started! (pid=$psid)"  
    47.       echo "================================"  
    48.    else  
    49.       echo -n "Starting $APP_MAINCLASS ..."  
    50.       JAVA_CMD="nohup $JAVA_HOME/bin/java -classpath $CLASSPATH $APP_MAINCLASS $LISTEN_PORT $SERVER_CONFIG $CLIENT_CONFIG_DIR >/dev/null 2>&1 &"  
    51.       su - $RUNNING_USER -c "$JAVA_CMD"  
    52.       checkpid  
    53.       if [ $psid -ne 0 ]; then  
    54.          echo "(pid=$psid) [OK]"  
    55.       else  
    56.          echo "[Failed]"  
    57.       fi  
    58.    fi  
    59. }  
    60.   
    61. stop() {  
    62.    checkpid  
    63.    
    64.    if [ $psid -ne 0 ]; then  
    65.       echo -n "Stopping $APP_MAINCLASS ...(pid=$psid) "  
    66.       su - $RUNNING_USER -c "kill -9 $psid"  
    67.       if [ $? -eq 0 ]; then  
    68.          echo "[OK]"  
    69.       else  
    70.          echo "[Failed]"  
    71.       fi  
    72.    
    73.       checkpid  
    74.       if [ $psid -ne 0 ]; then  
    75.          stop  
    76.       fi  
    77.    else  
    78.       echo "================================"  
    79.       echo "warn: $APP_MAINCLASS is not running"  
    80.       echo "================================"  
    81.    fi  
    82. }  
    83.   
    84. status() {  
    85.    checkpid  
    86.    
    87.    if [ $psid -ne 0 ];  then  
    88.       echo "$APP_MAINCLASS is running! (pid=$psid)"  
    89.    else  
    90.       echo "$APP_MAINCLASS is not running"  
    91.    fi  
    92. }  
    93. info() {  
    94.    echo "System Information:"  
    95.    echo "****************************"  
    96.    echo `head -n 1 /etc/issue`  
    97.    echo `uname -a`  
    98.    echo  
    99.    echo "JAVA_HOME=$JAVA_HOME"  
    100.    echo `$JAVA_HOME/bin/java -version`  
    101.    echo  
    102.    echo "APP_HOME=$APP_HOME"  
    103.    echo "APP_MAINCLASS=$APP_MAINCLASS"  
    104.    echo "****************************"  
    105. }  
    106. case "$1" in  
    107.   
    108.    'start')  
    109.       start  
    110.       ;;  
    111.    'stop')  
    112.      stop  
    113.      ;;  
    114.    'restart')  
    115.      stop  
    116.      start  
    117.      ;;  
    118.    'status')  
    119.      status  
    120.      ;;  
    121.    'info')  
    122.      info  
    123.      ;;  
    124.   *)  
    125.      echo "Usage: $0 {start|stop|restart|status|info}"   
    126.      exit 0   
    127. esac  
    128. </span>  
    startup.sh

    1. <span style="font-size:12px;">#!/bin/sh  
    2. EXECUTABLE=/log4jsocket/catalina.sh  
    3. exec "$EXECUTABLE" start "$@"</span>  
    shutdown.sh

    1. <span style="font-size:12px;">EXECUTABLE=/log4jsocket/catalina.sh  
    2. exec "$EXECUTABLE" stop "$@"</span>  


    PS: csdn博客啥時可以上傳附件啊


    版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。

    posted @ 2015-08-17 11:37 小馬歌 閱讀(380) | 評論 (0)編輯 收藏
     
    from:http://javatar.iteye.com/blog/981787
    關(guān)于Dubbo服務(wù)框架的分布式事務(wù),雖然現(xiàn)在不急著做,但可以討論一下。 

    我覺得事務(wù)的管理不應該屬于Dubbo框架, 
    Dubbo只需實現(xiàn)可被事務(wù)管理即可, 
    像JDBC和JMS都是可被事務(wù)管理的分布式資源, 
    Dubbo只要實現(xiàn)相同的可被事務(wù)管理的行為,比如可以回滾, 
    其它事務(wù)的調(diào)度,都應該由專門的事務(wù)管理器實現(xiàn)。 

    在Java中,分布式事務(wù)主要的規(guī)范是JTA/XA, 
    其中:JTA是Java的事務(wù)管理器規(guī)范, 
    XA是工業(yè)標準的X/Open CAE規(guī)范,可被兩階段提交及回滾的事務(wù)資源定義, 
    比如某數(shù)據(jù)庫實現(xiàn)了XA規(guī)范,則不管是JTA,還是MSDTC,都可以基于同樣的行為對該數(shù)據(jù)庫進行事務(wù)處理。 

    在JTA/XA中,主要有兩個擴展點: 

    (1) TransactionManager 
    JTA事務(wù)管理器接口,實現(xiàn)該接口,即可完成對所有XA資源的事務(wù)調(diào)度,比如BEA的Tuxedo,JBossJTA等。 

    (2) XAResource 
    XA資源接口,實現(xiàn)該接口,即可被任意TransactionManager調(diào)度,比如:JDBC的XAConnection,JMS的XAMQ等。 

    而Dubbo的遠程服務(wù),也應該是一個XAResource,比如:XAInvoker和XAExporter, 
    Dubbo只需在第一次提交時,將請求發(fā)到服務(wù)提供方進行緩存和寫盤, 
    在第二次提交時,再基于緩存調(diào)用服務(wù)的Impl實現(xiàn), 
    當然一些健狀性分支流程要考慮清楚。 

    JTA/XA的基本原理如下: 

     

    1. 用戶啟動一個事務(wù): 
    Java代碼  收藏代碼
    1. transactionManager.begin();   


    2. 事務(wù)管理器在當前線程中初始化一個事務(wù)實例: 
    Java代碼  收藏代碼
    1. threadLocal.set(new TransactionImpl());  


    3. 用戶調(diào)用JDBC或JMS或Dubbo請求,請求內(nèi)部初始化一個XAResource實例: 
    Java代碼  收藏代碼
    1. XAResource xaResource = new XAResourceImpl(); // 比如:XAConnection  


    4. JDBC或JMS或Dubbo內(nèi)部從當前線程獲取事務(wù): 
    Java代碼  收藏代碼
    1. Transaction transaction = transactionManager.getTransaction(); // 其內(nèi)部為:threadLocal.get();  


    5. 將當前XAResource注冊到事務(wù)中: 
    Java代碼  收藏代碼
    1. transaction.enlistResource(xaResource);  


    6. 用戶提交一個事務(wù): 
    Java代碼  收藏代碼
    1. transactionManager.commit(); // 其內(nèi)部為:getTransaction().commit();  


    7. 事務(wù)for循環(huán)調(diào)用所有注冊的XAResource的兩階段提交: 
    Java代碼  收藏代碼
    1. Xid xid = new XidImpl();  
    2. for (XAResource xaResource: xaResources) {  
    3. xaResource.prepare(xid);  
    4. xaResource.commit(xid, true);  
    5. xaResource.commit(xid, false);  
    6. }  


    8. 當然,還有一些異常流程,比如rollback和forget等。 

    舉例: 
    Java代碼  收藏代碼
    1. TransactionManager transactionManager = ...; // 從JNDI進行l(wèi)ookup等方式獲取  
    2. transactionManager.begin(); // 啟動事務(wù)  
    3. try {  
    4.     jdbcConn.executeUpdate(sql); // 執(zhí)行SQL語句,DB寫入binlog,但不更新表  
    5.     jmsMQ.send(message); // 發(fā)送消息,MQ記錄消息,但不進入隊列  
    6.     dubboService.invoke(parameters); // 調(diào)用遠程服務(wù),Provider緩存請求信息,但不執(zhí)行  
    7.     transactionManager.commit(); // 提交事務(wù),數(shù)據(jù)庫,消息隊列,遠程服務(wù)同時提交  
    8. catch(Throwable t) {  
    9.     transactionManager.rollback(); // 回滾事務(wù),數(shù)據(jù)庫,消息隊列,遠程服務(wù)同時回滾  
    10. }  
      posted @ 2015-08-04 11:19 小馬歌 閱讀(3230) | 評論 (0)編輯 收藏
       
      轉(zhuǎn)自:http://javatar.iteye.com/blog/1056664

      最近一直擔心Dubbo分布式服務(wù)框架后續(xù)如果維護人員增多或變更,會出現(xiàn)質(zhì)量的下降, 
      我在想,有沒有什么是需要大家共同遵守的, 
      根據(jù)平時寫代碼時的一習慣,總結(jié)了一下在寫代碼過程中,尤其是框架代碼,要時刻牢記的細節(jié), 
      可能下面要講的這些,大家都會覺得很簡單,很基礎(chǔ),但要做到時刻牢記, 
      在每一行代碼中都考慮這些因素,是需要很大耐心的, 
      大家經(jīng)常說,魔鬼在細節(jié)中,確實如此。 

      1. 防止空指針和下標越界 
      這是我最不喜歡看到的異常,尤其在核心框架中,我更愿看到信息詳細的參數(shù)不合法異常, 
      這也是一個健狀的程序開發(fā)人員,在寫每一行代碼都應在潛意識中防止的異常, 
      基本上要能確保一次寫完的代碼,在不測試的情況,都不會出現(xiàn)這兩個異常才算合格。 

      2. 保證線程安全性和可見性 
      對于框架的開發(fā)人員,對線程安全性和可見性的深入理解是最基本的要求, 
      需要開發(fā)人員,在寫每一行代碼時都應在潛意識中確保其正確性, 
      因為這種代碼,在小并發(fā)下做功能測試時,會顯得很正常, 
      但在高并發(fā)下就會出現(xiàn)莫明其妙的問題,而且場景很難重現(xiàn),極難排查。 

      3. 盡早失敗和前置斷言 
      盡早失敗也應該成為潛意識,在有傳入?yún)?shù)和狀態(tài)變化時,均在入口處全部斷言, 
      一個不合法的值和狀態(tài),在第一時間就應報錯,而不是等到要用時才報錯, 
      因為等到要用時,可能前面已經(jīng)修改其它相關(guān)狀態(tài),而在程序中很少有人去處理回滾邏輯, 
      這樣報錯后,其實內(nèi)部狀態(tài)可能已經(jīng)混亂,極易在一個隱蔽分支上引發(fā)程序不可恢復。 

      4. 分離可靠操作和不可靠操作 
      這里的可靠是狹義的指是否會拋出異常或引起狀態(tài)不一致, 
      比如,寫入一個線程安全的Map,可以認為是可靠的, 
      而寫入數(shù)據(jù)庫等,可以認為是不可靠的, 
      開發(fā)人員必須在寫每一行代碼時,都注意它的可靠性與否, 
      在代碼中盡量劃分開,并對失敗做異常處理, 
      并為容錯,自我保護,自動恢復或切換等補償邏輯提供清晰的切入點, 
      保證后續(xù)增加的代碼不至于放錯位置,而導致原先的容錯處理陷入混亂。 

      5. 異常防御,但不忽略異常 
      這里講的異常防御,指的是對非必須途徑上的代碼進行最大限度的容忍, 
      包括程序上的BUG,比如:獲取程序的版本號,會通過掃描Manifest和jar包名稱抓取版本號, 
      這個邏輯是輔助性的,但代碼卻不少,初步測試也沒啥問題, 
      但應該在整個getVersion()中加上一個全函數(shù)的try-catch打印錯誤日志,并返回基本版本, 
      因為getVersion()可能存在未知特定場景異常,或被其他的開發(fā)人員誤修改邏輯(但一般人員不會去掉try-catch), 
      而如果它拋出異常會導致主流程異常,這是我們不希望看到的, 
      但這里要控制個度,不要隨意try-catch,更不要無聲無息的吃掉異常。 

      6. 縮小可變域和盡量final 
      如果一個類可以成為不變類(Immutable Class),就優(yōu)先將它設(shè)計成不變類, 
      不變類有天然的并發(fā)共享優(yōu)勢,減少同步或復制,而且可以有效幫忙分析線程安全的范圍, 
      就算是可變類,對于從構(gòu)造函數(shù)傳入的引用,在類中持有時,最好將字段final,以免被中途誤修改引用, 
      不要以為這個字段是私有的,這個類的代碼都是我自己寫的,不會出現(xiàn)對這個字段的重新賦值, 
      要考慮的一個因素是,這個代碼可能被其他人修改,他不知道你的這個弱約定,final就是一個不變契約。 

      7. 降低修改時的誤解性,不埋雷 
      前面不停的提到代碼被其他人修改,這也開發(fā)人員要隨時緊記的, 
      這個其他人包括未來的自己,你要總想著這個代碼可能會有人去改它, 
      我應該給修改的人一點什么提示,讓他知道我現(xiàn)在的設(shè)計意圖, 
      而不要在程序里面加潛規(guī)則,或埋一些容易忽視的雷, 
      比如:你用null表示不可用,size等于0表示黑名單, 
      這就是一個雷,下一個修改者,包括你自己,都不會記得有這樣的約定, 
      可能后面為了改某個其它BUG,不小心改到了這里,直接引爆故障。 
      對于這個例子,一個原則就是永遠不要區(qū)分null引用和empty值。 

      8. 提高代碼的可測性 
      這里的可測性主要指Mock的容易程度,和測試的隔離性, 
      至于測試的自動性,可重復性,非偶然性,無序性,完備性(全覆蓋),輕量性(可快速執(zhí)行), 
      一般開發(fā)人員,加上JUnit等工具的輔助基本都能做到,也能理解它的好處,只是工作量問題, 
      這里要特別強調(diào)的是測試用例的單一性(只測目標類本身)和隔離性(不傳染失敗), 
      現(xiàn)在的測試代碼,過于強調(diào)完備性,大量重復交叉測試, 
      看起來沒啥壞處,但測試代碼越多,維護代價越高, 
      經(jīng)常出現(xiàn)的問題是,修改一行代碼或加一個判斷條件,引起100多個測試用例不通過, 
      時間一緊,誰有這個閑功夫去改這么多形態(tài)各異的測試用例? 
      久而久之,這個測試代碼就已經(jīng)不能真實反應代碼現(xiàn)在的狀況,很多時候會被迫繞過, 
      最好的情況是,修改一行代碼,有且只有一行測試代碼不通過, 
      如果修改了代碼而測試用例還能通過,那也不行,表示測試沒有覆蓋到, 
      另外,可Mock性是隔離的基礎(chǔ),把間接依賴的邏輯屏蔽掉, 
      可Mock性的一個最大的殺手就是靜態(tài)方法,盡量少用。 
      posted @ 2015-08-04 11:11 小馬歌 閱讀(437) | 評論 (0)編輯 收藏
       
           摘要: 2015-06-30 (點擊上方藍字,可快速關(guān)注我們)通常來說,一個優(yōu)化良好的 Nginx Linux 服務(wù)器可以達到 500,000 – 600,000 次/秒 的請求處理性能,然而我的 Nginx 服務(wù)器可以穩(wěn)定地達到 904,000 次/秒 的處理性能,并且我以此高負載測試超過 12 小時,服務(wù)器工作穩(wěn)定。這里需要特別說明的是,本文中所有列出來的配置都是在我的測試環(huán)境驗...  閱讀全文
      posted @ 2015-07-10 10:23 小馬歌 閱讀(347) | 評論 (0)編輯 收藏
       

      from:http://blog.csdn.net/yanzi1225627/article/details/42040629

      由于Google官方已經(jīng)不提供Adt-Bundle下載了,主推AndroidStudio。可以從這個鏈接下載http://www.androiddevtools.cn。上面不光有adt-bundle,還有最新的AndroidStudio。由于對OS X還不是很熟悉,本次采用adt-bundle安裝。

      一,下載JDK

      下載方式有兩種,其一是從鏈接http://www.androiddevtools.cn處下載,選擇Mac OSX的1.8u5版本即可。截圖如下:


      其二是從JDK的官網(wǎng)下載,文件名為jdk-8u25-macosx-x64.dmg,大小219.3M。這個稍后我上傳至百度網(wǎng)盤供大家下載。我就是通過這種方式下載的。下載完畢后,點擊打開,接著出現(xiàn)如下:


      再點擊一下就開始安裝了,中間會讓輸入用戶名和密碼。安裝完畢后,打開個終端,輸入javac -version查看是否安裝成功。


      像上圖所示就是安裝成功了。哈哈,其實OSX沒什么神秘的,全當它是linux就好了。

      二,下載Adt-Bundle

      從http://www.androiddevtools.cn處下載,選擇Mac OSX的64位23.0.2即可。


      文件名為adt-bundle-mac-x86_64-20140702.zip,大小320.6M。是個zip格式的壓縮包,打開解壓后看到根windows上一樣是熟悉的eclipse和sdk文件夾。這就表示安裝完畢了。

      三,打開Eclipse

      打開之后就傻眼了:


      為此,我研究了N種解決辦法:

      1,http://www.cnblogs.com/zhouyinhui/p/3750836.html 讓修改Info.plist,沒有任何效果。

      2,還有的讓配置jdk環(huán)境變量,sudo vim /etc/profile,之后輸入:

      JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home

      export JAVA_HOME

      然后source /etc/profile,沒有效果。注意此處,jdk的環(huán)境變量設(shè)置一定要使用sudo vim,退出后用wq! 否則是保存不了的。JDK的路徑貌似高低版本的OSX還不太一樣,應以終端里輸入:/usr/libexec/java_home 打印出來的變量為準。可以參考:http://han.guokai.blog.163.com/blog/static/136718271201301183938165/

      3,最終找到了鏈接:http://java.com/zh_CN/download/faq/java_mac.xml#mac1010 


      上面的鏈接也就是打開eclipse,彈窗上的“更多信息”,http://support.apple.com/kb/DL1572?viewlocale=zh_CN&locale=en_US,截圖如下:


      下載的文件名為JavaForOSX2014-001.dmg,60多M。點擊安裝。終于Eclipse啟動起來了,后面的就不啰嗦了。


      總結(jié):一定要先在蘋果官網(wǎng)上下載java1.6的安裝包,然后java官網(wǎng)上下載安裝jdk8u25. 后者是否是必須我沒有測試,我mac上安裝了這兩個東西未見不良現(xiàn)象。

      備注:最新的jdk8u25已經(jīng)可以順利在OSX10.10上安裝了,無需按照http://www.krislq.com/2014/07/mac-x-yosemide10-10-update-jdk-7-jdk-8/ 進行處理。

      posted @ 2015-06-15 09:01 小馬歌 閱讀(378) | 評論 (0)編輯 收藏
       
      <?xml version="1.0" encoding="UTF-8" ?>
      <!--
             Copyright 2009-2013 the original author or authors.
             Licensed under the Apache License, Version 2.0 (the "License");
             you may not use this file except in compliance with the License.
             You may obtain a copy of the License at
                http://www.apache.org/licenses/LICENSE-2.0
             Unless required by applicable law or agreed to in writing, software
             distributed under the License is distributed on an "AS IS" BASIS,
             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
             See the License for the specific language governing permissions and
             limitations under the License.
      -->
      <!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select* )+>
      <!ATTLIST mapper
      xmlns:fo CDATA #IMPLIED
      namespace CDATA #IMPLIED
      >
      <!ELEMENT cache-ref EMPTY>
      <!ATTLIST cache-ref
      namespace CDATA #REQUIRED
      >
      <!ELEMENT cache (property*)>
      <!ATTLIST cache
      type CDATA #IMPLIED
      eviction CDATA #IMPLIED
      flushInterval CDATA #IMPLIED
      size CDATA #IMPLIED
      readOnly CDATA #IMPLIED
      blocking CDATA #IMPLIED
      >
      <!ELEMENT parameterMap (parameter+)?>
      <!ATTLIST parameterMap
      id CDATA #REQUIRED
      type CDATA #REQUIRED
      >
      <!ELEMENT parameter EMPTY>
      <!ATTLIST parameter
      property CDATA #REQUIRED
      javaType CDATA #IMPLIED
      jdbcType CDATA #IMPLIED
      mode (IN | OUT | INOUT) #IMPLIED
      resultMap CDATA #IMPLIED
      scale CDATA #IMPLIED
      typeHandler CDATA #IMPLIED
      >
      <!ELEMENT resultMap (constructor?,id*,result*,association*,collection*, discriminator?)>
      <!ATTLIST resultMap
      id CDATA #REQUIRED
      type CDATA #REQUIRED
      extends CDATA #IMPLIED
      autoMapping (true|false) #IMPLIED
      >
      <!ELEMENT constructor (idArg*,arg*)>
      <!ELEMENT id EMPTY>
      <!ATTLIST id
      property CDATA #IMPLIED
      javaType CDATA #IMPLIED
      column CDATA #IMPLIED
      jdbcType CDATA #IMPLIED
      typeHandler CDATA #IMPLIED
      >
      <!ELEMENT result EMPTY>
      <!ATTLIST result
      property CDATA #IMPLIED
      javaType CDATA #IMPLIED
      column CDATA #IMPLIED
      jdbcType CDATA #IMPLIED
      typeHandler CDATA #IMPLIED
      >
      <!ELEMENT idArg EMPTY>
      <!ATTLIST idArg
      javaType CDATA #IMPLIED
      column CDATA #IMPLIED
      jdbcType CDATA #IMPLIED
      typeHandler CDATA #IMPLIED
      select CDATA #IMPLIED
      resultMap CDATA #IMPLIED
      >
      <!ELEMENT arg EMPTY>
      <!ATTLIST arg
      javaType CDATA #IMPLIED
      column CDATA #IMPLIED
      jdbcType CDATA #IMPLIED
      typeHandler CDATA #IMPLIED
      select CDATA #IMPLIED
      resultMap CDATA #IMPLIED
      >
      <!ELEMENT collection (constructor?,id*,result*,association*,collection*, discriminator?)>
      <!ATTLIST collection
      property CDATA #REQUIRED
      column CDATA #IMPLIED
      javaType CDATA #IMPLIED
      ofType CDATA #IMPLIED
      jdbcType CDATA #IMPLIED
      select CDATA #IMPLIED
      resultMap CDATA #IMPLIED
      typeHandler CDATA #IMPLIED
      notNullColumn CDATA #IMPLIED
      columnPrefix CDATA #IMPLIED
      resultSet CDATA #IMPLIED
      foreignColumn CDATA #IMPLIED
      autoMapping (true|false) #IMPLIED
      fetchType (lazy|eager) #IMPLIED
      >
      <!ELEMENT association (constructor?,id*,result*,association*,collection*, discriminator?)>
      <!ATTLIST association
      property CDATA #REQUIRED
      column CDATA #IMPLIED
      javaType CDATA #IMPLIED
      jdbcType CDATA #IMPLIED
      select CDATA #IMPLIED
      resultMap CDATA #IMPLIED
      typeHandler CDATA #IMPLIED
      notNullColumn CDATA #IMPLIED
      columnPrefix CDATA #IMPLIED
      resultSet CDATA #IMPLIED
      foreignColumn CDATA #IMPLIED
      autoMapping (true|false) #IMPLIED
      fetchType (lazy|eager) #IMPLIED
      >
      <!ELEMENT discriminator (case+)>
      <!ATTLIST discriminator
      column CDATA #IMPLIED
      javaType CDATA #REQUIRED
      jdbcType CDATA #IMPLIED
      typeHandler CDATA #IMPLIED
      >
      <!ELEMENT case (constructor?,id*,result*,association*,collection*, discriminator?)>
      <!ATTLIST case
      value CDATA #REQUIRED
      resultMap CDATA #IMPLIED
      resultType CDATA #IMPLIED
      >
      <!ELEMENT property EMPTY>
      <!ATTLIST property
      name CDATA #REQUIRED
      value CDATA #REQUIRED
      >
      <!ELEMENT typeAlias EMPTY>
      <!ATTLIST typeAlias
      alias CDATA #REQUIRED
      type CDATA #REQUIRED
      >
      <!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST select
      id CDATA #REQUIRED
      parameterMap CDATA #IMPLIED
      parameterType CDATA #IMPLIED
      resultMap CDATA #IMPLIED
      resultType CDATA #IMPLIED
      resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
      statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
      fetchSize CDATA #IMPLIED
      timeout CDATA #IMPLIED
      flushCache (true|false) #IMPLIED
      useCache (true|false) #IMPLIED
      databaseId CDATA #IMPLIED
      lang CDATA #IMPLIED
      resultOrdered (true|false) #IMPLIED
      resultSets CDATA #IMPLIED 
      >
      <!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST insert
      id CDATA #REQUIRED
      parameterMap CDATA #IMPLIED
      parameterType CDATA #IMPLIED
      timeout CDATA #IMPLIED
      flushCache (true|false) #IMPLIED
      statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
      keyProperty CDATA #IMPLIED
      useGeneratedKeys (true|false) #IMPLIED
      keyColumn CDATA #IMPLIED
      databaseId CDATA #IMPLIED
      lang CDATA #IMPLIED
      >
      <!ELEMENT selectKey (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST selectKey
      resultType CDATA #IMPLIED
      statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
      keyProperty CDATA #IMPLIED
      keyColumn CDATA #IMPLIED
      order (BEFORE|AFTER) #IMPLIED
      databaseId CDATA #IMPLIED
      >
      <!ELEMENT update (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST update
      id CDATA #REQUIRED
      parameterMap CDATA #IMPLIED
      parameterType CDATA #IMPLIED
      timeout CDATA #IMPLIED
      flushCache (true|false) #IMPLIED
      statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
      keyProperty CDATA #IMPLIED
      useGeneratedKeys (true|false) #IMPLIED
      keyColumn CDATA #IMPLIED
      databaseId CDATA #IMPLIED
      lang CDATA #IMPLIED
      >
      <!ELEMENT delete (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST delete
      id CDATA #REQUIRED
      parameterMap CDATA #IMPLIED
      parameterType CDATA #IMPLIED
      timeout CDATA #IMPLIED
      flushCache (true|false) #IMPLIED
      statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
      databaseId CDATA #IMPLIED
      lang CDATA #IMPLIED
      >
      <!-- Dynamic -->
      <!ELEMENT include (property+)?>
      <!ATTLIST include
      refid CDATA #REQUIRED
      >
      <!ELEMENT bind EMPTY>
      <!ATTLIST bind
       name CDATA #REQUIRED
       value CDATA #REQUIRED
      >
      <!ELEMENT sql (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST sql
      id CDATA #REQUIRED
      lang CDATA #IMPLIED
      databaseId CDATA #IMPLIED
      >
      <!ELEMENT trim (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST trim
      prefix CDATA #IMPLIED
      prefixOverrides CDATA #IMPLIED
      suffix CDATA #IMPLIED
      suffixOverrides CDATA #IMPLIED
      >
      <!ELEMENT where (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ELEMENT set (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST foreach
      collection CDATA #REQUIRED
      item CDATA #IMPLIED
      index CDATA #IMPLIED
      open CDATA #IMPLIED
      close CDATA #IMPLIED
      separator CDATA #IMPLIED
      >
      <!ELEMENT choose (when* , otherwise?)>
      <!ELEMENT when (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST when
      test CDATA #REQUIRED
      >
      <!ELEMENT otherwise (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ELEMENT if (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
      <!ATTLIST if
      test CDATA #REQUIRED
      >
      posted @ 2015-06-05 11:36 小馬歌 閱讀(534) | 評論 (0)編輯 收藏
       
      看了dataguru網(wǎng)站的介紹,這個 《大型分布式系統(tǒng)案例實戰(zhàn)學習》貌似還可以,2015.5.6開課,已經(jīng)報名,系統(tǒng)理一下知識點。

      也作為使用了mycat開源工具的回報:) 另外這個逆向?qū)W習收費模式還挺有意思的

      如果對Dataguru的課程有興趣,報名的時候可以填寫我的優(yōu)惠碼 V5M5,能立減50%的固定學費!

      課程大綱:
      第1課 大型分布式系統(tǒng)原理概述
      結(jié)合業(yè)界主流的那些開源軟件,介紹和分析分布式系統(tǒng)的基本架構(gòu),組成部分,和實現(xiàn)原理。幾款常用的軟件以及功能功能性對比。

      第2課 分布式系統(tǒng)之網(wǎng)絡(luò)篇
      Zookeeper入門
      Zookeeper原理: Zookeeper原理介紹
      Curator客戶端 : 對Zookeeper知名客戶端Curator進行介紹,初步掌握其編程方式和用法。
      迷你P2P網(wǎng)絡(luò)服務(wù)案例: 采用Zookeeper打造一個迷你P2P網(wǎng)絡(luò)系統(tǒng),節(jié)點之間相互交換名片,并且實現(xiàn)動態(tài)路由(節(jié)點宕機后其他節(jié)點自動感知并更新鏈路狀態(tài)),

      第3課  分布式存儲-文件系統(tǒng)篇
      傳統(tǒng)的分布式文件系統(tǒng):Lustre、GlusterFS等經(jīng)典分布式文件系統(tǒng)分析
      新型分布式文件系統(tǒng):介紹Ceph以及它跟Openstack的關(guān)系
      互聯(lián)網(wǎng)領(lǐng)域中的小文件系統(tǒng):GridFS、FastDFS、TFS等分析學習

      第4課 分布式存儲-內(nèi)存篇
      Hazelcast 詳解與分析
      GridGain詳解與分析
      MemCache詳解與分析
      案例集錦:分布式系統(tǒng)存儲之基于內(nèi)存的兩表Join演示

      第5課 分布式存儲-數(shù)據(jù)庫篇
      分布式數(shù)據(jù)庫原理 :介紹分布式數(shù)據(jù)庫的實現(xiàn)原理,特性、優(yōu)缺點、以及難點、熱點問題
      Mycat前世今生:介紹目前基于MYSQL的熱門開源數(shù)據(jù)庫血統(tǒng),包括Cobar、tddl、Amoeba、以及目前很火的Mycat
      案例集錦:某大型網(wǎng)站每天1億數(shù)據(jù)處的案例剖析

      第6課 分布式系統(tǒng)之云計算篇
      主機虛擬化:介紹主機虛擬化的技術(shù)
      網(wǎng)絡(luò)虛擬化:介紹網(wǎng)絡(luò)虛擬化的技術(shù)
      存儲虛擬化:介紹存儲虛擬化的技術(shù)
      云計算實踐:VirtualBox虛機集群搭建
      Openstack原理介紹:介紹Openstack的體系、架構(gòu)、以及基本功能
      案例集錦:基于RDO實現(xiàn)Openstack的安裝、部署等。

      第7課 分布式計算框架
      Map-Reduce原理:介紹Map-Reduce的原理以及限制問題
      Apache Storm應用:學習Storm的原理并搭建測試環(huán)境,掌握基本編程
      案例集錦:實現(xiàn)基于Storm的1000萬×1000萬的SQL Join和排序分頁

      第8課 通信機制的設(shè)計與實現(xiàn)
      分布式通信機制概述:講解分布式通信的幾種常見機制,RPC調(diào)用、共享遠程數(shù)據(jù)、消息隊列等。
      RPC通信機制的原理 講解RPC通信機制的原理和實現(xiàn)方式:
      案例集錦:設(shè)計并實現(xiàn)一個XML-RPC框架 動手設(shè)計和實現(xiàn)一個簡單的XML-RPC框架

      第9課 消息隊列
      消息隊列機制介紹: 介紹古典的和新型的消息隊列機制的相同點和不同點
      消息隊列通信的案例分析: 對一些采用消息隊列通信的系統(tǒng)做分析,掌握消息隊列用作分布式通信的一般設(shè)計原則
      案例集錦:對知名開源消息中間件Kafka做一個入門學習,并動手完成一個實際編程案例

      第10課 打造高可用系統(tǒng)(上)
      高可用系統(tǒng)常規(guī)方案:介紹高可用系統(tǒng)的一些原理、實現(xiàn)機制、常規(guī)實現(xiàn)方案,包括基于硬件、軟件中間件、系統(tǒng)架構(gòu)等一些典型方案的實現(xiàn)
      HA Proxy入門:介紹業(yè)界常規(guī)的HA Proxy的原理以及用法
      實踐篇:Java開發(fā)一個類似HA Proxy的代理中間件

      第11課 打造高可用系統(tǒng)(下)
      高可用集群套件中間件:介紹基于Corosync+Pacemaker的高可用集群套件中間件系統(tǒng)的原理、配置以及常見案例
      Corosync技術(shù); 
      Pacemaker技術(shù);
      Pacemaker實踐:實現(xiàn)基于Pacemaker的MYSQL高可用方案。

      第12課 Mycat架構(gòu)的分布式演進背后的秘密
      配置文件的分布式訪問問題:為什么最終選擇了Zookeeper
      Mycat負載均衡的特殊性:為什么標準的HA Proxy還無法滿足Mycat的負載均衡要求
      大數(shù)據(jù)Join背后的難題:數(shù)據(jù)、網(wǎng)絡(luò)、內(nèi)存和計算能力的矛盾和調(diào)和

      第13課 Java分布式系統(tǒng)中的高性能難題
      高性能網(wǎng)絡(luò)框架的難題:AIO,NIO,Netty還是自己開發(fā)框架
      堆內(nèi)和堆外存儲:堆內(nèi)與堆外存儲的差別,開源的堆外存儲組件為何鳳毛麟角
      高性能事件派發(fā)機制:線程池模型的性能問題以及不為人知的Disruptor模型

      第14課 挑戰(zhàn)自我——全棧架構(gòu)師實踐
      本節(jié)課程的目標,是挑戰(zhàn)自我,開發(fā)一個基于Zeroc ICE+Zookeeper+Mycat+Android App+ Web系統(tǒng)的“身邊購”平臺,目標是支持1億用戶,每天交易訂單為1億,商家自己在手機上通過Appp注冊自己的店鋪,店鋪包括地理位置信息,后臺審批通過,然后可以拍照上架自己的貨物,定價,發(fā)售。用戶登錄App以后,根據(jù)其地理位置信息,顯示附近的(默認3公里)新品、熱門商品、二手商品等,并可以下單。

      授課時間:
      第一期課程預計2015年5月6日開課,預計課程持續(xù)時間為16周。
      posted @ 2015-04-13 10:23 小馬歌 閱讀(902) | 評論 (0)編輯 收藏
       
           摘要: from:http://blog.csdn.net/myrainblues/article/details/25881535最近研究redis-cluster,正好搭建了一個環(huán)境,遇到了很多坑,系統(tǒng)的總結(jié)下,等到redis3 release出來后,換掉memCache 集群.一:關(guān)于redis cluster1:redis cluster的現(xiàn)狀reids-cluster計劃在redis3.0中推出...  閱讀全文
      posted @ 2015-04-03 11:00 小馬歌 閱讀(899) | 評論 (0)編輯 收藏
       
      from :
      http://git.oschina.net/yeetrack/weixin-alert

      很簡單的步驟:

      1. 登錄web微信,打開開發(fā)者工具,切到http請求tab
      2. 給要報警的人、或者群組發(fā)送一條微信
      3. 在開發(fā)者工具中,copy出兩條curl請求,一個是發(fā)送心跳的,一個是發(fā)送消息內(nèi)容的。
      4. 編譯jar包,mvn clean pakcage
      5. 將心跳curl保存成**weixin-heart.sh**,將發(fā)送curl保存成**weixin-send.sh**。
      6. 將jar包和兩個shell文件放在統(tǒng)一路徑中,執(zhí)行java -jar weixinalert-1.0-SNAPSHOT-jar-with-dependencies.jar "Hello world!"即可。

      ps:心跳的那個請求,最好有個定時任務(wù),crontab之類,一分鐘一次,防止服務(wù)器踢掉該會話。缺點:不能退出手機上的微信(斷網(wǎng)可以,只要不手動退出),不能再使用該微信號登錄其他客戶端的微信,如windows,mac,網(wǎng)頁微信等,也就是說一個手機上的微信賬號,最多額外產(chǎn)生一個sid,多了會踢掉之前的sid。

      posted @ 2015-04-03 10:50 小馬歌 閱讀(379) | 評論 (0)編輯 收藏
      僅列出標題
      共95頁: First 上一頁 10 11 12 13 14 15 16 17 18 下一頁 Last 
       
      主站蜘蛛池模板: 国产人成免费视频| 99久在线国内在线播放免费观看 | 国产成人A在线观看视频免费| 99精品免费观看| 一级毛片在线免费看| 国产自国产自愉自愉免费24区| 久久国产精品免费一区二区三区| 一级女性全黄久久生活片免费| 猫咪免费观看人成网站在线| 男人j进女人p免费视频| 国产99久久久久久免费看| 中文字幕在线成人免费看| 日韩精品无码免费专区午夜| 黄页免费在线观看| 97视频免费观看2区| 1000部羞羞禁止免费观看视频 | 国产成人 亚洲欧洲| 无人视频免费观看免费视频 | 久久精品国产亚洲av麻豆色欲| 91久久亚洲国产成人精品性色| 亚洲欧洲精品在线| 亚洲国产成人久久精品大牛影视| 日韩精品亚洲专区在线影视| 粉色视频免费入口| a毛片全部播放免费视频完整18| 免费91最新地址永久入口| 蜜桃AV无码免费看永久| 精品久久久久国产免费| 全部免费毛片在线| 亚洲精品无码国产| 亚洲校园春色小说| 在线观看亚洲专区| 色www永久免费| 91精品国产免费久久久久久青草| 天天摸夜夜摸成人免费视频| 亚洲人成影院在线无码观看| 中文字幕亚洲精品| 亚洲精品久久无码| 免费无码av片在线观看| 成人黄页网站免费观看大全| 国产偷国产偷亚洲高清日韩|