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


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

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

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

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

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

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

          兵貴精,不在多。分布式鎖,我們就依靠這四個命令。但在具體實(shí)現(xiàn),還有很多細(xì)節(jié),需要仔細(xì)斟酌,因?yàn)樵诜植际讲l(fā)多進(jìn)程中,任何一點(diǎn)出現(xiàn)差錯,都會導(dǎo)致死鎖,hold住所有進(jìn)程。

          加鎖實(shí)現(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)用,就需要進(jìn)入以下個重試循環(huán),直至成功獲得鎖或者重試超時。理想是美好的,現(xiàn)實(shí)是殘酷的。僅僅使用SETNX加鎖帶有競爭條件的,在某些特定的情況會造成死鎖錯誤。

          處理死鎖

          在上面的處理方式中,如果獲取鎖的客戶端端執(zhí)行時間過長,進(jìn)程被kill掉,或者因?yàn)槠渌惓1罎ⅲ瑢?dǎo)致無法釋放鎖,就會造成死鎖。所以,需要對加鎖要做時效性檢測。因此,我們在加鎖時,把當(dāng)前時間戳作為value存入此鎖中,通過當(dāng)前時間戳和Redis中的時間戳進(jìn)行對比,如果超過一定差值,認(rèn)為鎖已經(jīng)時效,防止鎖無限期的鎖下去,但是,在大并發(fā)情況,如果同時檢測鎖失效,并簡單粗暴的刪除死鎖,再通過SETNX上鎖,可能會導(dǎo)致競爭條件的產(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時,其實(shí)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下,進(jìn)入下次循環(huán)中。

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

          GET返回nil時應(yīng)該走那種邏輯?

          第一種走超時邏輯
          C1客戶端獲取鎖,并且處理完后,DEL掉鎖,在DEL鎖之前。C2通過SETNX向foo.lock設(shè)置時間戳T0 發(fā)現(xiàn)有客戶端獲取鎖,進(jìn)入GET操作。
          C2 向foo.lock發(fā)送GET命令,獲取返回值T1(nil)。
          C2 通過T0>T1+expire對比,進(jìn)入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)有客戶端獲取鎖,進(jìn)入GET操作。
          C2 向foo.lock發(fā)送GET命令,獲取返回值T1(nil)。
          C2 循環(huán),進(jìn)入下一次SETNX邏輯

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

          GETSET返回nil時應(yīng)該怎么處理?

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

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

          時間戳的問題

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

          分布式鎖的問題

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

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

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


          可以看到加鎖和解鎖時間都很快,當(dāng)我們使用

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


          我們會發(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),當(dāng)我們并發(fā)100總共100次請求時,QPS得到110多。當(dāng)我們使用sleep時

          使用Sleep時

          單次執(zhí)行請求時

          我們看到,和不使用sleep機(jī)制時,性能相當(dāng)。當(dāng)時用相同的壓測條件進(jìn)行壓縮時 

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

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

          參考資料

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

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

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

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

          1、客戶端的配置

          客戶端的配置比較簡單,只需要告訴log4j需要監(jiān)聽哪個遠(yuǎ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. #將日志寫入本地和遠(yuǎn)程日志服務(wù)器  
          16. log4j.logger.com.test.core.filter =DEBUG,socket,logs</span>  
           

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

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

          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對應(yīng)的那個配置文件,如172.16.2.46服務(wù)器發(fā)送的socket請求會尋找172.16.2.46.lcf配置文件,并根據(jù)配置將日志寫入對應(yīng)的文件。

          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ù)不同客戶端,將日志寫入不同的文件夾下的。

          其實(shí),配置過程就這么簡單,但是當(dāng)你這么做之后,你會發(fā)現(xiàn)運(yùn)行org.apache.log4j.net.SocketServer后,客戶端向日志服務(wù)器發(fā)送請求時,會報(bào)找不到.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地址,然后尋找對應(yīng)IP命名的.lcf配置文件;如果找不到,則解析默認(rèn)的generic.lcf。由于截取的錯誤,導(dǎo)致找不到172.16.2.46.lcf,文件夾下又沒有g(shù)eneric.lcf,所以會拋異常。

          org.apache.log4j.net.SocketServer代碼中的另外一個bug是,只能接收來自一臺客戶端的日志請求,一旦客戶端停止運(yùn)行,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虛擬機(jī)啟動參數(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 小馬歌 閱讀(383) | 評論 (0)編輯 收藏
           
          from:http://javatar.iteye.com/blog/981787
          關(guān)于Dubbo服務(wù)框架的分布式事務(wù),雖然現(xiàn)在不急著做,但可以討論一下。 

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

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

          在JTA/XA中,主要有兩個擴(kuò)展點(diǎn): 

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

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

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

          JTA/XA的基本原理如下: 

           

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


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


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


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


          5. 將當(dāng)前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. 當(dāng)然,還有一些異常流程,比如rollback和forget等。 

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

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

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

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

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

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

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

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

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

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


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


            像上圖所示就是安裝成功了。哈哈,其實(shí)OSX沒什么神秘的,全當(dāng)它是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還不太一樣,應(yīng)以終端里輸入:/usr/libexec/java_home 打印出來的變量為準(zhǔn)。可以參考: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。點(diǎn)擊安裝。終于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/ 進(jìn)行處理。

            posted @ 2015-06-15 09:01 小馬歌 閱讀(380) | 評論 (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 小馬歌 閱讀(536) | 評論 (0)編輯 收藏
             
            看了dataguru網(wǎng)站的介紹,這個 《大型分布式系統(tǒng)案例實(shí)戰(zhàn)學(xué)習(xí)》貌似還可以,2015.5.6開課,已經(jīng)報(bào)名,系統(tǒng)理一下知識點(diǎn)。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            授課時間:
            第一期課程預(yù)計(jì)2015年5月6日開課,預(yù)計(jì)課程持續(xù)時間為16周。
            posted @ 2015-04-13 10:23 小馬歌 閱讀(905) | 評論 (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計(jì)劃在redis3.0中推出...  閱讀全文
            posted @ 2015-04-03 11:00 小馬歌 閱讀(903) | 評論 (0)編輯 收藏
             
            from :
            http://git.oschina.net/yeetrack/weixin-alert

            很簡單的步驟:

            1. 登錄web微信,打開開發(fā)者工具,切到http請求tab
            2. 給要報(bào)警的人、或者群組發(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ù)器踢掉該會話。缺點(diǎn):不能退出手機(jī)上的微信(斷網(wǎng)可以,只要不手動退出),不能再使用該微信號登錄其他客戶端的微信,如windows,mac,網(wǎng)頁微信等,也就是說一個手機(jī)上的微信賬號,最多額外產(chǎn)生一個sid,多了會踢掉之前的sid。

            posted @ 2015-04-03 10:50 小馬歌 閱讀(384) | 評論 (0)編輯 收藏
            僅列出標(biāo)題
            共95頁: First 上一頁 10 11 12 13 14 15 16 17 18 下一頁 Last 
             
            主站蜘蛛池模板: 英吉沙县| 右玉县| 清水河县| 永顺县| 商丘市| 应用必备| 白河县| 泌阳县| 会东县| 莎车县| 西丰县| 庐江县| 定远县| 文登市| 湖北省| 乐平市| 安图县| 吕梁市| 囊谦县| 武山县| 陆河县| 嵊泗县| 黔东| 余庆县| 松溪县| 吉安市| 三都| 孝感市| 武强县| 金沙县| 桐乡市| 金川县| 获嘉县| 渭南市| 平顶山市| 安岳县| 皋兰县| 泾阳县| 浙江省| 德兴市| 永州市|