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

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

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

         摘要: 1、 為什么使用Nexus        如果沒有私服,我們所需的所有構件都需要通過maven的中央倉庫和第三方的Maven倉庫下載到本地,而一個團隊中的所有人都重復的從maven倉庫下載構件無疑加大了倉庫的負載和浪費了外網帶寬,如果網速慢的話,還會影響項目的進程。很多情況下項目的開發都是在內網進行的,連接不到maven倉庫怎么...  閱讀全文
    posted @ 2014-05-13 11:18 小馬歌 閱讀(34952) | 評論 (0)編輯 收藏
     

    生產環境下,jvm參數可以設置如下:

    -server –Xms256m –Xmx2G -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -XX:+UseParallelOldGC -XX:+UseParallelGC -XX:CompileThreshold=10 -XX:MaxInlineSize=1024 -Djava.awt.headless=true -Djmagick.systemclassloader=no -Djava.net.preferIPv4Stack=true -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 -Dnetworkaddress.cache.ttl=300 -Dsun.net.inetaddr.ttl=300


    順便說一句:–Xms和–Xmx如果配置成一樣容易出現fullgc
    posted @ 2014-05-09 17:02 小馬歌 閱讀(575) | 評論 (0)編輯 收藏
     

    Redis常用數據類型

    Redis最為常用的數據類型主要有以下五種:

    • String
    • Hash
    • List
    • Set
    • Sorted set

    在具體描述這幾種數據類型之前,我們先通過一張圖了解下Redis內部內存管理中是如何描述這些不同數據類型的:

    首先Redis內部使用一個redisObject對象來表示所有的key和value,redisObject最主要的信息如上圖所示:type代表一個value對象具體是何種數據類型,encoding是不同數據類型在redis內部的存儲方式,比如:type=string代表value存儲的是一個普通字符串,那么對應的encoding可以是raw或者是int,如果是int則代表實際redis內部是按數值型類存儲和表示這個字符串的,當然前提是這個字符串本身可以用數值表示,比如:"123" "456"這樣的字符串。

    這里需要特殊說明一下vm字段,只有打開了Redis的虛擬內存功能,此字段才會真正的分配內存,該功能默認是關閉狀態的,該功能會在后面具體描述。通過上圖我們可以發現Redis使用redisObject來表示所有的key/value數據是比較浪費內存的,當然這些內存管理成本的付出主要也是為了給Redis不同數據類型提供一個統一的管理接口,實際作者也提供了多種方法幫助我們盡量節省內存使用,我們隨后會具體討論。

    下面我們先來逐一的分析下這五種數據類型的使用和內部實現方式:

    • String

      常用命令:

      set,get,decr,incr,mget 等。

      應用場景:

      String是最常用的一種數據類型,普通的key/value存儲都可以歸為此類,這里就不所做解釋了。

      實現方式:

      String在redis內部存儲默認就是一個字符串,被redisObject所引用,當遇到incr,decr等操作時會轉成數值型進行計算,此時redisObject的encoding字段為int。

    • Hash

      常用命令:

      hget,hset,hgetall 等。

      應用場景:

      我們簡單舉個實例來描述下Hash的應用場景,比如我們要存儲一個用戶信息對象數據,包含以下信息:

      用戶ID為查找的key,存儲的value用戶對象包含姓名,年齡,生日等信息,如果用普通的key/value結構來存儲,主要有以下2種存儲方式:

      第一種方式將用戶ID作為查找key,把其他信息封裝成一個對象以序列化的方式存儲,這種方式的缺點是,增加了序列化/反序列化的開銷,并且在需要修改其中一項信息時,需要把整個對象取回,并且修改操作需要對并發進行保護,引入CAS等復雜問題。

      第二種方法是這個用戶信息對象有多少成員就存成多少個key-value對兒,用用戶ID+對應屬性的名稱作為唯一標識來取得對應屬性的值,雖然省去了序列化開銷和并發問題,但是用戶ID為重復存儲,如果存在大量這樣的數據,內存浪費還是非常可觀的。

      那么Redis提供的Hash很好的解決了這個問題,Redis的Hash實際是內部存儲的Value為一個HashMap,并提供了直接存取這個Map成員的接口,如下圖:

      也就是說,Key仍然是用戶ID, value是一個Map,這個Map的key是成員的屬性名,value是屬性值,這樣對數據的修改和存取都可以直接通過其內部Map的Key(Redis里稱內部Map的key為field), 也就是通過 key(用戶ID) + field(屬性標簽) 就可以操作對應屬性數據了,既不需要重復存儲數據,也不會帶來序列化和并發修改控制的問題。很好的解決了問題。

      這里同時需要注意,Redis提供了接口(hgetall)可以直接取到全部的屬性數據,但是如果內部Map的成員很多,那么涉及到遍歷整個內部Map的操作,由于Redis單線程模型的緣故,這個遍歷操作可能會比較耗時,而另其它客戶端的請求完全不響應,這點需要格外注意。

      實現方式:

      上面已經說到Redis Hash對應Value內部實際就是一個HashMap,實際這里會有2種不同實現,這個Hash的成員比較少時Redis為了節省內存會采用類似一維數組的方式來緊湊存儲,而不會采用真正的HashMap結構,對應的value redisObject的encoding為zipmap,當成員數量增大時會自動轉成真正的HashMap,此時encoding為ht。

    • List

      常用命令:

      lpush,rpush,lpop,rpop,lrange等。

      應用場景:

      Redis list的應用場景非常多,也是Redis最重要的數據結構之一,比如twitter的關注列表,粉絲列表等都可以用Redis的list結構來實現,比較好理解,這里不再重復。

      實現方式:

      Redis list的實現為一個雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷,Redis內部的很多實現,包括發送緩沖隊列等也都是用的這個數據結構。

    • Set

      常用命令:

      sadd,spop,smembers,sunion 等。

      應用場景:

      Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在于set是可以自動排重的,當你需要存儲一個列表數據,又不希望出現重復數據時,set是一個很好的選擇,并且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。

      實現方式:

      set 的內部實現是一個 value永遠為null的HashMap,實際就是通過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的原因。

    • Sorted set

      常用命令:

      zadd,zrange,zrem,zcard等

      使用場景:

      Redis sorted set的使用場景與set類似,區別是set不是自動有序的,而sorted set可以通過用戶額外提供一個優先級(score)的參數來為成員排序,并且是插入有序的,即自動排序。當你需要一個有序的并且不重復的集合列表,那么可以選擇sorted set數據結構,比如twitter 的public timeline可以以發表時間作為score來存儲,這樣獲取時就是自動按時間排好序的。

      實現方式:

      Redis sorted set的內部使用HashMap和跳躍表(SkipList)來保證數據的存儲和有序,HashMap里放的是成員到score的映射,而跳躍表里存放的是所有的成員,排序依據是HashMap里存的score,使用跳躍表的結構可以獲得比較高的查找效率,并且在實現上比較簡單。

    常用內存優化手段與參數

    通過我們上面的一些實現上的分析可以看出redis實際上的內存管理成本非常高,即占用了過多的內存,作者對這點也非常清楚,所以提供了一系列的參數和手段來控制和節省內存,我們分別來討論下。

    首先最重要的一點是不要開啟Redis的VM選項,即虛擬內存功能,這個本來是作為Redis存儲超出物理內存數據的一種數據在內存與磁盤換入換出的一個持久化策略,但是其內存管理成本也非常的高,并且我們后續會分析此種持久化策略并不成熟,所以要關閉VM功能,請檢查你的redis.conf文件中 vm-enabled 為 no。

    其次最好設置下redis.conf中的maxmemory選項,該選項是告訴Redis當使用了多少物理內存后就開始拒絕后續的寫入請求,該參數能很好的保護好你的Redis不會因為使用了過多的物理內存而導致swap,最終嚴重影響性能甚至崩潰。

    另外Redis為不同數據類型分別提供了一組參數來控制內存使用,我們在前面詳細分析過Redis Hash是value內部為一個HashMap,如果該Map的成員數比較少,則會采用類似一維線性的緊湊格式來存儲該Map, 即省去了大量指針的內存開銷,這個參數控制對應在redis.conf配置文件中下面2項:

    hash-max-zipmap-entries 64  hash-max-zipmap-value 512  hash-max-zipmap-entries 

    含義是當value這個Map內部不超過多少個成員時會采用線性緊湊格式存儲,默認是64,即value內部有64個以下的成員就是使用線性緊湊存儲,超過該值自動轉成真正的HashMap。

    hash-max-zipmap-value 含義是當 value這個Map內部的每個成員值長度不超過多少字節就會采用線性緊湊存儲來節省空間。

    以上2個條件任意一個條件超過設置值都會轉換成真正的HashMap,也就不會再節省內存了,那么這個值是不是設置的越大越好呢,答案當然是否定的,HashMap的優勢就是查找和操作的時間復雜度都是O(1)的,而放棄Hash采用一維存儲則是O(n)的時間復雜度,如果

    成員數量很少,則影響不大,否則會嚴重影響性能,所以要權衡好這個值的設置,總體上還是最根本的時間成本和空間成本上的權衡。

    同樣類似的參數還有:

    list-max-ziplist-entries 512

    說明:list數據類型多少節點以下會采用去指針的緊湊存儲格式。

    list-max-ziplist-value 64 

    說明:list數據類型節點值大小小于多少字節會采用緊湊存儲格式。

    set-max-intset-entries 512 

    說明:set數據類型內部數據如果全部是數值型,且包含多少節點以下會采用緊湊格式存儲。

    最后想說的是Redis內部實現沒有對內存分配方面做過多的優化,在一定程度上會存在內存碎片,不過大多數情況下這個不會成為Redis的性能瓶頸,不過如果在Redis內部存儲的大部分數據是數值型的話,Redis內部采用了一個shared integer的方式來省去分配內存的開銷,即在系統啟動時先分配一個從1~n 那么多個數值對象放在一個池子中,如果存儲的數據恰好是這個數值范圍內的數據,則直接從池子里取出該對象,并且通過引用計數的方式來共享,這樣在系統存儲了大量數值下,也能一定程度上節省內存并且提高性能,這個參數值n的設置需要修改源代碼中的一行宏定義REDIS_SHARED_INTEGERS,該值默認是10000,可以根據自己的需要進行修改,修改后重新編譯就可以了。

    Redis的持久化機制

    Redis由于支持非常豐富的內存數據結構類型,如何把這些復雜的內存組織方式持久化到磁盤上是一個難題,所以Redis的持久化方式與傳統數據庫的方式有比較多的差別,Redis一共支持四種持久化方式,分別是:

    • 定時快照方式(snapshot)
    • 基于語句追加文件的方式(aof)
    • 虛擬內存(vm)
    • Diskstore方式

    在設計思路上,前兩種是基于全部數據都在內存中,即小數據量下提供磁盤落地功能,而后兩種方式則是作者在嘗試存儲數據超過物理內存時,即大數據量的數據存儲,截止到本文,后兩種持久化方式仍然是在實驗階段,并且vm方式基本已經被作者放棄,所以實際能在生產環境用的只有前兩種,換句話說Redis目前還只能作為小數據量存儲(全部數據能夠加載在內存中),海量數據存儲方面并不是Redis所擅長的領域。下面分別介紹下這幾種持久化方式:

    定時快照方式(snapshot):

    該持久化方式實際是在Redis內部一個定時器事件,每隔固定時間去檢查當前數據發生的改變次數與時間是否滿足配置的持久化觸發的條件,如果滿足則通過操作系統fork調用來創建出一個子進程,這個子進程默認會與父進程共享相同的地址空間,這時就可以通過子進程來遍歷整個內存來進行存儲操作,而主進程則仍然可以提供服務,當有寫入時由操作系統按照內存頁(page)為單位來進行copy-on-write保證父子進程之間不會互相影響。

    該持久化的主要缺點是定時快照只是代表一段時間內的內存映像,所以系統重啟會丟失上次快照與重啟之間所有的數據。

    基于語句追加方式(aof):

    aof方式實際類似mysql的基于語句的binlog方式,即每條會使Redis內存數據發生改變的命令都會追加到一個log文件中,也就是說這個log文件就是Redis的持久化數據。

    aof的方式的主要缺點是追加log文件可能導致體積過大,當系統重啟恢復數據時如果是aof的方式則加載數據會非常慢,幾十G的數據可能需要幾小時才能加載完,當然這個耗時并不是因為磁盤文件讀取速度慢,而是由于讀取的所有命令都要在內存中執行一遍。另外由于每條命令都要寫log,所以使用aof的方式,Redis的讀寫性能也會有所下降。

    虛擬內存方式:

    虛擬內存方式是Redis來進行用戶空間的數據換入換出的一個策略,此種方式在實現的效果上比較差,主要問題是代碼復雜,重啟慢,復制慢等等,目前已經被作者放棄。

    diskstore方式:

    diskstore方式是作者放棄了虛擬內存方式后選擇的一種新的實現方式,也就是傳統的B-tree的方式,目前仍在實驗階段,后續是否可用我們可以拭目以待。

    Redis持久化磁盤IO方式及其帶來的問題

    有Redis線上運維經驗的人會發現Redis在物理內存使用比較多,但還沒有超過實際物理內存總容量時就會發生不穩定甚至崩潰的問題,有人認為是基于快照方式持久化的fork系統調用造成內存占用加倍而導致的,這種觀點是不準確的,因為fork 調用的copy-on-write機制是基于操作系統頁這個單位的,也就是只有有寫入的臟頁會被復制,但是一般你的系統不會在短時間內所有的頁都發生了寫入而導致復制,那么是什么原因導致Redis崩潰的呢?

    答案是Redis的持久化使用了Buffer IO造成的,所謂Buffer IO是指Redis對持久化文件的寫入和讀取操作都會使用物理內存的Page Cache,而大多數數據庫系統會使用Direct IO來繞過這層Page Cache并自行維護一個數據的Cache,而當Redis的持久化文件過大(尤其是快照文件),并對其進行讀寫時,磁盤文件中的數據都會被加載到物理內存中作為操作系統對該文件的一層Cache,而這層Cache的數據與Redis內存中管理的數據實際是重復存儲的,雖然內核在物理內存緊張時會做Page Cache的剔除工作,但內核很可能認為某塊Page Cache更重要,而讓你的進程開始Swap ,這時你的系統就會開始出現不穩定或者崩潰了。我們的經驗是當你的Redis物理內存使用超過內存總容量的3/5時就會開始比較危險了。

    下圖是Redis在讀取或者寫入快照文件dump.rdb后的內存數據圖:

    總結:

    1. 根據業務需要選擇合適的數據類型,并為不同的應用場景設置相應的緊湊存儲參數。
    2. 當業務場景不需要數據持久化時,關閉所有的持久化方式可以獲得最佳的性能以及最大的內存使用量。
    3. 如果需要使用持久化,根據是否可以容忍重啟丟失部分數據在快照方式與語句追加方式之間選擇其一,不要使用虛擬內存以及diskstore方式。
    4. 不要讓你的Redis所在機器物理內存使用超過實際內存總量的3/5。

    轉載自:http://www.infoq.com/cn/articles/tq-redis-memory-usage-optimization-storage

    posted @ 2014-05-09 17:00 小馬歌 閱讀(321) | 評論 (0)編輯 收藏
     

    由于Dubbo底層采用Socket進行通信,自己對通信理理論也不是很清楚,所以順便把通信的知識也學習一下。

    n  通信理論

    計算機與外界的信息交換稱為通信。基本的通信方法有并行通信和串行通信兩種。

    1.一組信息(通常是字節)的各位數據被同時傳送的通信方法稱為并行通信。并行通信依靠并行I/O接口實現。并行通信速度快,但傳輸線根數多,只適用于近距離(相距數公尺)的通信。

    2.一組信息的各位數據被逐位順序傳送的通信方式稱為串行通信。串行通信可通過串行接口來實現。串行通信速度慢,但傳輸線少,適宜長距離通信。

    串行通信按信息傳送方向分為以下3種:

    1)   單工

    只能一個方向傳輸數據

    【原創】Alibaba Dubbo框架同步調用原理分析-1 - sun - 學無止境

    2)   半雙工

    信息能雙向傳輸,但不能同時雙向傳輸

    【原創】Alibaba Dubbo框架同步調用原理分析-1 - sun - 學無止境

    3)   全雙工

    能雙向傳輸并且可以同時雙向傳輸

    【原創】Alibaba Dubbo框架同步調用原理分析-1 - sun - 學無止境 

    n  Socket

    Socket 是一種應用接口, TCP/IP 是網絡傳輸協議,雖然接口相同, 但是不同的協議會有不同的服務性質。創建Socket 連接時,可以指定使用的傳輸層協議,Socket 可以支持不同的傳輸層協議(TCP 或UDP ),當使用TCP 協議進行連接時,該Socket 連接就是一個TCP 連接。Soket 跟TCP/IP 并沒有必然的聯系。Socket 編程接口在設計的時候,就希望也能適應其他的網絡協議。所以,socket 的出現只是可以更方便的使用TCP/IP 協議棧而已。

    引自:http://hi.baidu.com/lewutian/blog/item/b28e27fd446d641d09244d08.html

    上一個通信理論其實是想說Socket(TCP)通信是全雙工的方式

    n  Dubbo遠程同步調用原理分析

    從Dubbo開源文檔上了解到一個調用過程如下圖

    http://code.alibabatech.com/wiki/display/dubbo/User+Guide#UserGuide-APIReference

    另外文檔里有說明:Dubbo缺省協議采用單一長連接和NIO異步通訊,適合于小數據量大并發的服務調用,以及服務消費者機器數遠大于服務提供者機器數的情況。

    【原創】Alibaba Dubbo框架同步調用原理分析-1 - sun - 學無止境

    Dubbo缺省協議,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。

    • 連接個數:單連接
    • 連接方式:長連接
    • 傳輸協議:TCP
    • 傳輸方式:NIO異步傳輸
    • 序列化:Hessian二進制序列化
    • 適用范圍:傳入傳出參數數據包較小(建議小于100K),消費者比提供者個數多,單一消費者無法壓滿提供者,盡量不要用dubbo協議傳輸大文件或超大字符串
    • 適用場景:常規遠程服務方法調用

     通常,一個典型的同步遠程調用應該是這樣的:

    【原創】Alibaba Dubbo框架同步調用原理分析-1 - sun - 學無止境

    1, 客戶端線程調用遠程接口,向服務端發送請求,同時當前線程應該處于“暫停“狀態,即線程不能向后執行了,必需要拿到服務端給自己的結果后才能向后執行

    2, 服務端接到客戶端請求后,處理請求,將結果給客戶端
    3, 客戶端收到結果,然后當前線程繼續往后執行

    Dubbo里使用到了Socket(采用apache mina框架做底層調用)來建立長連接,發送、接收數據,底層使用apache mina框架的IoSession進行發送消息。

    查看Dubbo文檔及源代碼可知,Dubbo底層使用Socket發送消息的形式進行數據傳遞,結合了mina框架,使用IoSession.write()方法,這個方法調用后對于整個遠程調用(從發出請求到接收到結果)來說是一個異步的,即對于當前線程來說,將請求發送出來,線程就可以往后執行了,至于服務端的結果,是服務端處理完成后,再以消息的形式發送給客戶端的。于是這里出現了2個問題:
    • 當前線程怎么讓它“暫停”,等結果回來后,再向后執行?
    • 正如前面所說,Socket通信是一個全雙工的方式,如果有多個線程同時進行遠程方法調用,這時建立在client server之間的socket連接上會有很多雙方發送的消息傳遞,前后順序也可能是亂七八糟的,server處理完結果后,將結果消息發送給client,client收到很多消息,怎么知道哪個消息結果是原先哪個線程調用的?

    分析源代碼,基本原理如下:
    1. client一個線程調用遠程接口,生成一個唯一的ID(比如一段隨機字符串,UUID等),Dubbo是使用AtomicLong從0開始累計數字的
    2. 將打包的方法調用信息(如調用的接口名稱,方法名稱,參數值列表等),和處理結果的回調對象callback,全部封裝在一起,組成一個對象object
    3. 向專門存放調用信息的全局ConcurrentHashMap里面put(ID, object)
    4. 將ID和打包的方法調用信息封裝成一對象connRequest,使用IoSession.write(connRequest)異步發送出去
    5. 當前線程再使用callback的get()方法試圖獲取遠程返回的結果,在get()內部,則使用synchronized獲取回調對象callback的鎖, 再先檢測是否已經獲取到結果,如果沒有,然后調用callback的wait()方法,釋放callback上的鎖,讓當前線程處于等待狀態。
    6. 服務端接收到請求并處理后,將結果(此結果中包含了前面的ID,即回傳)發送給客戶端,客戶端socket連接上專門監聽消息的線程收到消息,分析結果,取到ID,再從前面的ConcurrentHashMap里面get(ID),從而找到callback,將方法調用結果設置到callback對象里。
    7. 監聽線程接著使用synchronized獲取回調對象callback的鎖(因為前面調用過wait(),那個線程已釋放callback的鎖了),再notifyAll(),喚醒前面處于等待狀態的線程繼續執行(callback的get()方法繼續執行就能拿到調用結果了),至此,整個過程結束。
    這里還需要畫一個大圖來描述,后面再補了
    需要注意的是,這里的callback對象是每次調用產生一個新的,不能共享,否則會有問題;另外ID必需至少保證在一個Socket連接里面是唯一的。

    現在,前面兩個問題已經有答案了,
    • 當前線程怎么讓它“暫停”,等結果回來后,再向后執行?
         答:先生成一個對象obj,在一個全局map里put(ID,obj)存放起來,再用synchronized獲取obj鎖,再調用obj.wait()讓當前線程處于等待狀態,然后另一消息監聽線程等到服務端結果來了后,再map.get(ID)找到obj,再用synchronized獲取obj鎖,再調用obj.notifyAll()喚醒前面處于等待狀態的線程。
    • 正如前面所說,Socket通信是一個全雙工的方式,如果有多個線程同時進行遠程方法調用,這時建立在client server之間的socket連接上會有很多雙方發送的消息傳遞,前后順序也可能是亂七八糟的,server處理完結果后,將結果消息發送給client,client收到很多消息,怎么知道哪個消息結果是原先哪個線程調用的?
         答:使用一個ID,讓其唯一,然后傳遞給服務端,再服務端又回傳回來,這樣就知道結果是原先哪個線程的了。

    這種做法不是第一次見了,10年在上一公司里,也是遠程接口調用,不過走的消息中間件rabbitmq,同步調用的原理跟這類似,詳見:rabbitmq 學習-9- RpcClient發送消息和同步接收消息原理

    關鍵代碼:

    com.taobao.remoting.impl.DefaultClient.java

    //同步調用遠程接口

    public Object invokeWithSync(Object appRequest, RequestControl control) throws RemotingException, InterruptedException {

            byte protocol = getProtocol(control);

            if (!TRConstants.isValidProtocol(protocol)) {

                throw new RemotingException("Invalid serialization protocol [" + protocol + "] on invokeWithSync.");

            }

            ResponseFuture future = invokeWithFuture(appRequest, control);

            return future.get();  //獲取結果時讓當前線程等待,ResponseFuture其實就是前面說的callback

    }

    public ResponseFuture invokeWithFuture(Object appRequest, RequestControl control) {

             byte protocol = getProtocol(control);

             long timeout = getTimeout(control);

             ConnectionRequest request = new ConnectionRequest(appRequest);

             request.setSerializeProtocol(protocol);

             Callback2FutureAdapter adapter = new Callback2FutureAdapter(request);

             connection.sendRequestWithCallback(request, adapter, timeout);

             return adapter;

    }

     

    Callback2FutureAdapter implements ResponseFuture

    public Object get() throws RemotingException, InterruptedException {

    synchronized (this) {  // 旋鎖

       while (!isDone) {  // 是否有結果了

    wait(); //沒結果是釋放鎖,讓當前線程處于等待狀態

       }

    }

    if (errorCode == TRConstants.RESULT_TIMEOUT) {

       throw new TimeoutException("Wait response timeout, request["

       + connectionRequest.getAppRequest() + "].");

    }

    else if (errorCode > 0) {

       throw new RemotingException(errorMsg);

    }

    else {

       return appResp;

    }

    }

    客戶端收到服務端結果后,回調時相關方法,即設置isDone = true并notifyAll()

    public void handleResponse(Object _appResponse) {

             appResp = _appResponse; //將遠程調用結果設置到callback中來

             setDone();

    }

    public void onRemotingException(int _errorType, String _errorMsg) {

             errorCode = _errorType;

             errorMsg = _errorMsg;

             setDone();

    }

    private void setDone() {

             isDone = true;

             synchronized (this) { //獲取鎖,因為前面wait()已經釋放了callback的鎖了

                 notifyAll(); // 喚醒處于等待的線程

             }

    }

     

    com.taobao.remoting.impl.DefaultConnection.java

     

    // 用來存放請求和回調的MAP

    private final ConcurrentHashMap<Long, Object[]> requestResidents;

     

    //發送消息出去

    void sendRequestWithCallback(ConnectionRequest connRequest, ResponseCallback callback, long timeoutMs) {

             long requestId = connRequest.getId();

             long waitBegin = System.currentTimeMillis();

             long waitEnd = waitBegin + timeoutMs;

             Object[] queue = new Object[4];

             int idx = 0;

             queue[idx++] = waitEnd;

             queue[idx++] = waitBegin;   //用于記錄日志

             queue[idx++] = connRequest; //用于記錄日志

             queue[idx++] = callback;

             requestResidents.put(requestId, queue); // 記錄響應隊列

             write(connRequest);

     

             // 埋點記錄等待響應的Map的大小

             StatLog.addStat("TBRemoting-ResponseQueues", "size", requestResidents.size(),

                       1L);

    }

    public void write(final Object connectionMsg) {

    //mina里的IoSession.write()發送消息

             WriteFuture writeFuture = ioSession.write(connectionMsg);

             // 注冊FutureListener,當請求發送失敗后,能夠立即做出響應

             writeFuture.addListener(new MsgWrittenListener(this, connectionMsg));

    }

     

    /**

    * 在得到響應后,刪除對應的請求隊列,并執行回調

    * 調用者:MINA線程

    */

    public void putResponse(final ConnectionResponse connResp) {

             final long requestId = connResp.getRequestId();

             Object[] queue = requestResidents.remove(requestId);

             if (null == queue) {

                 Object appResp = connResp.getAppResponse();

                 String appRespClazz = (null == appResp) ? "null" : appResp.getClass().getName();

                 StringBuilder sb = new StringBuilder();

                 sb.append("Not found response receiver for requestId=[").append(requestId).append("],");

                 sb.append("from [").append(connResp.getHost()).append("],");

                 sb.append("response type [").append(appRespClazz).append("].");

                 LOGGER.warn(sb.toString());

                 return;

             }

             int idx = 0;

             idx++;

             long waitBegin = (Long) queue[idx++];

             ConnectionRequest connRequest = (ConnectionRequest) queue[idx++];

             ResponseCallback callback = (ResponseCallback) queue[idx++];

             // ** 把回調任務交給業務提供的線程池執行 **

             Executor callbackExecutor = callback.getExecutor();

             callbackExecutor.execute(new CallbackExecutorTask(connResp, callback));

     

             long duration = System.currentTimeMillis() - waitBegin; // 實際讀響應時間

             logIfResponseError(connResp, duration, connRequest.getAppRequest());

    }

     

    CallbackExecutorTask

    static private class CallbackExecutorTask implements Runnable {

             final ConnectionResponse resp;

             final ResponseCallback callback;

             final Thread createThread;

     

             CallbackExecutorTask(ConnectionResponse _resp, ResponseCallback _cb) {

                 resp = _resp;

                 callback = _cb;

                 createThread = Thread.currentThread();

             }

     

             public void run() {

                 // 預防這種情況:業務提供的Executor,讓調用者線程來執行任務

                 if (createThread == Thread.currentThread()

                           && callback.getExecutor() != DIYExecutor.getInstance()) {

                       StringBuilder sb = new StringBuilder();

                       sb.append("The network callback task [" + resp.getRequestId() + "] cancelled, cause:");

                       sb.append("Can not callback task on the network io thhread.");

                       LOGGER.warn(sb.toString());

                       return;

                 }

     

                 if (TRConstants.RESULT_SUCCESS == resp.getResult()) {

                       callback.handleResponse(resp.getAppResponse()); //設置調用結果

                 }

                 else {

                       callback.onRemotingException(resp.getResult(), resp

                                .getErrorMsg());  //處理調用異常

                 }

             }

    }

     

    另外:

    1, 服務端在處理客戶端的消息,然后再處理時,使用了線程池來并行處理,不用一個一個消息的處理

    同樣,客戶端接收到服務端的消息,也是使用線程池來處理消息,再回調

     

    轉載自:http://sunjun041640.blog.163.com/blog/static/256268322011111882453405/

    posted @ 2014-05-09 15:49 小馬歌 閱讀(4681) | 評論 (0)編輯 收藏
     
         摘要: 一、Redis服務器端的安裝和客戶端Jedis的安裝1.下載Redis   下載地址:http://redis.googlecode.com/files/redis-2.4.8.tar.gz 2.安裝Redis在linux下運行如下命令進行安裝。Shell代碼  $ tar xzf redis-2.4.8.tar.gz...  閱讀全文
    posted @ 2014-05-09 15:21 小馬歌 閱讀(2196) | 評論 (0)編輯 收藏
     

    javapns是一個java實現的APNs的provider庫,利用這個庫可以向apple的APNs服務器推送基本的以及自定義的APNs消息、從APNs服務器接收詳細發送情況報告(error packet)和查詢反饋信息(feedback)。下面介紹其基本的用法。

    一、下載javapns庫和其依賴的庫:

    [codesyntax lang="bash"]

    svn checkout http://javapns.googlecode.com/svn/trunk/ javapns-read-only

    [/codesyntax]

    依賴庫:

    · commons-lang-2.4.jar

    · commons-io-1.4.jar

    · bcprov-jdk15-146.jar

    · log4j-1.2.15.jar

    這幾個都是開源庫。

    在工程中導入這幾個庫。

     

    二、推送通知的方法:

    Push.alert:推送一條僅包含alert的消息

    Push.sound:推送一條僅包含sound的消息

    Push.badge:推送一條僅包含badge的消息

    Push.combine:推送一條包含alertsoundbadge的消息

    也可以自己構造Payload,然后傳遞給Push.payload方法發送一個payload,給一個或多個設備。或者調用Push.payloads方法把payload一對一的發送給設備,這個需要預先構造PayloadPerDevice類的實例。

    自己構造構造Payload的實例的基本方法:

     

    [codesyntax lang="java5" lines="fancy"]

    PushNotificationPayload payload = new PushNotificationPayload();  //聲明一個空的payload  payload.setExpiry(1);  //設置payload的過期時間  payload.addAlert("alert message");  //設置alert消息  payload.addBadge(3);  //設置badge值  payload.addSound("beep.wav");  //設置聲音  payload.addCustomAlertActionLocKey("launch apns");  //設置ActionLocKey  payload.addCustomAlertLocKey("locKey");  //設置LocKey  payload.addCustomDictionary("custom1", "value1");  //增加一個自定義鍵值對  List<PushedNotification> notifications = Push.payload(payload, "apns-key+cert-exported-from-mac.p12", "hadoop",   false,   "def981279b88b3a858b9dc9ea35b893175d5d190e2a271d448ee0679ad5bd880");  //調用Push.payload方法發送這個payload,發回一個已發送的notification的列表

    [/codesyntax]

     

    三、處理APNs服務器的反饋

    蘋果的推送服務器提供兩個的反饋系統,實際生產過程中,要對兩個反饋系統中的反饋消息都進行檢查,不能只用其一。這兩個反饋系統是:Feedback Service vs Error-response packets 。

    javapns系統已經對這兩種反饋系統提供的良好的支持。

    (1)Error-response packets

    在發送消息之后返回的PushedNotificationresponse成員中,會保存有蘋果返回的Error-response packets的信息,若消息推送為發生錯誤,則該成員為空null。可通過如下方法使用:

     

    [codesyntax lang="java5" lines="fancy"]

    for (PushedNotification notification : notifications) {  response = notification.getResponse();  if(response != null)  {  response.getMessage();  System.out.println(response.getMessage());  }              if (notification.isSuccessful())   {                    System.out.println("Push notification sent successf ully to: " + notification.getDevice().getToken());              }   else   {                      String invalidToken =   notification.getDevice().getToken();              }  }

    [/codesyntax]

    (2)feedback service

    feedback service會列出apple 服務器認為永遠不可達的設備(可能是由于你的client端的應用程序已被刪除等原因)。可通過如下方法使用feedback:

     

    [codesyntax lang="java5" lines="fancy"]

    List<Device> devList = Push.feedback("apns-key+cert-exported-from- mac.p12", "hadoop", false);  for(Device basicDevice: devList)  {  System.out.println(basicDevice.getToken());  System.out.println(basicDevice.getDeviceId());  }

    [/codesyntax]

    posted @ 2014-05-06 18:08 小馬歌 閱讀(458) | 評論 (0)編輯 收藏
     

    from:http://www.oschina.net/p/atlas/

    Atlas是由 Qihoo 360,  Web平臺部基礎架構團隊開發維護的一個基于MySQL協議的數據中間層項目。它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基礎上,修改了大量bug,添加了很多功能特性。目前該項目在360公司內部得到了廣泛應用,很多MySQL業務已經接入了Atlas平臺,每天承載的讀寫請求數達幾十億條。

    主要功能:
    * 讀寫分離
    * 從庫負載均衡
    * IP過濾
    * SQL語句黑白名單
    * 自動分表

    Q & A
    -------------------
    Q: 是否支持多字符集?
    A: 這是我們對原版MySQL-Proxy的第一項改進,符合國情是必須的

    Q: 自動讀寫分離挺好,但有時候我寫完馬上就想讀,萬一主從同步延遲怎么辦?
    A: SQL語句前增加 /*master*/ 就可以將讀請求強制發往主庫

    Q: 主庫宕機,讀操作受影響么?
    A: 在atlas中是不會的! 能問這樣的問題, 說明你用過官方的mysql-proxy, 很遺憾官方版本并未解決這個問題

    Q: 檢測后端DB狀態會阻塞正常請求么?
    A: 不會, atlas中檢測線程是異步進行檢測的,即使有db宕機,也不會阻塞主流程。在atlas中沒有什么異常會讓主流程阻塞! 同上,官方版本也會讓你失望

    Q: 想下線一臺DB, 又不想停掉mysql server, 怎么辦?
    A: 可以通過管理接口手動上下線后端db, atlas會優先考慮管理員的意愿

    Q: 想給集群中增加一臺DB, 不想影響線上正常訪問可以嗎?
    A: 通過管理接口可以輕松實現

    Q: 相比官方mysql-proxy, atlas還有哪些改進?
    A: 這實在是個難以回答的問題,性能,穩定性,可靠性,易維護性,我們做過幾十項的改進,下面會盡量列一些較大的改動

    VS 官方MySQL-Proxy
    -------------------
    1. 將主流程中所有Lua代碼改為純C實現,Lua僅用在管理接口
    2. 重寫網絡模型、線程模型
    3. 實現了真正意義的連接池
    4. 優化了鎖機制,性能提高數十倍
    ......

    附名字來源:
        Atlas,希臘神話中雙肩撐天的巨人,普羅米修斯的兄弟,最高大強壯的神之一,因反抗宙斯失敗而被罰頂天。我們期望這個系統能夠腳踏后端DB,為前端應用撐起一片天。

    二、配置文件示例
    -------------------
    [mysql-proxy]    #不需要改
    plugins = admin, proxy    #Atlas加載的模塊名稱,不需要改

    admin-username = user    #管理接口的用戶名
    admin-password = pwd    #管理接口的密碼
    admin-lua-script = /usr/local/mysql-proxy/lib/mysql-proxy/lua/admin.lua    #實現管理接口的Lua腳本所在路徑

    proxy-backend-addresses = 127.0.0.1:3306    #Atlas后端連接的MySQL主庫的IP和端口,可設置多項,用逗號分隔
    proxy-read-only-backend-addresses = 127.0.0.1:3305@2    #Atlas后端連接的MySQL從庫的IP和端口,2代表權重,用來作負載均衡,若省略則默認為1,可設置多項,用逗號分隔

    daemon = false    #設置Atlas的運行方式,設為true時為守護進程方式,設為false時為前臺方式,一般開發調試時設為false,線上運行時設為true
    keepalive = false    #設置Atlas的運行方式,設為true時Atlas會啟動兩個進程,一個為monitor,一個為worker,monitor在worker意外退出后會自動將其重啟,設為false時只有worker,沒有monitor,一般開發調試時設為false,線上運行時設為true

    event-threads = 4    #工作線程數,推薦設置與系統的CPU核數相等
    log-level = message    #日志級別,分為message、warning、critical、error、debug五個級別
    log-path = /usr/local/mysql-proxy/log    #日志存放的路徑
    instance = test    #實例名稱,用于同一臺機器上多個Atlas實例間的區分

    proxy-address = 0.0.0.0:1234    #Atlas監聽的工作接口IP和端口
    admin-address = 0.0.0.0:2345    #Atlas監聽的管理接口IP和端口

    min-idle-connections = 128    #連接池的最小空閑連接數,可根據業務請求量大小適當調大或調小
    tables = person.mt.id.3    #分表設置,此例中person為庫名,mt為表名,id為分表字段,3為子表數量,可設置多項,以逗號分隔
    pwds = user1:+jKsgB3YAG8=, user2:GS+tr4TPgqc=    #用戶名與其對應的加密過的密碼,密碼使用加密程序encrypt加密,此設置項用于多個用戶名同時訪問同一個Atlas實例的情況,若只有一個用戶名則不需要設置該項
    charset = utf8    #默認字符集,若不設置該項,則默認字符集為latin1

    三、編譯安裝
    -------------------
    依賴:glib(2.32.0以上)、libevent(1.4以上)、Lua(5.1以上)、OpenSSL(0.9.8以上)

    ./bootstrap.sh    #可能需要修改其中的路徑
    make
    sudo make install

    四、啟動與停止
    -------------------
    進入PREFIX/conf目錄,編輯instance.conf,此處instance的實際名稱應與其中instance設置項相同,其他設置項含義見第二節。

    啟動:
    PREFIX/bin/mysql-proxyd instance start

    停止:
    PREFIX/bin/mysql-proxyd instance stop

    重啟:
    PREFIX/bin/mysql-proxyd instance restart

    查看運行狀態:
    PREFIX/bin/mysql-proxyd instance status

    posted @ 2014-05-06 13:33 小馬歌 閱讀(254) | 評論 (0)編輯 收藏
     
    先來看下MYSQL異步復制的概念: 
      異步復制:MySQL本身支持單向的、異步的復制。異步復制意味著在把數據從一臺機器拷貝到另一臺機器時有一個延時 – 最重要的是這意味著當應用系統的事務提交已經確認時數據并不能在同一時刻拷貝/應用到從機。通常這個延時是由網絡帶寬、資源可用性和系統負載決定的。然而,使用正確的組件并且調優,復制能做到接近瞬時完成。 
      
       當主庫有更新的時候,主庫會把更新操作的SQL寫入二進制日志(Bin log),并維護一個二進制日志文件的索引,以便于日志文件輪回(Rotate)。在從庫啟動異步復制的時候,從庫會開啟兩個I/O線程,其中一個線程連接主庫,要求主庫把二進制日志的變化部分傳給從庫,并把傳回的日志寫入本地磁盤。另一個線程則負責讀取本地寫入的二進制日志,并在本地執行,以反映出這種變化。較老的版本在復制的時候只啟用一個I/O線程,實現這兩部分的功能。 




    同步復制:同步復制可以定義為數據在同一時刻被提交到一臺或多臺機器,通常這是通過眾所周知的“兩階段提交”做到的。雖然這確實給你在多系統中保持一致性,但也由于增加了額外的消息交換而造成性能下降。 



    使用MyISAM或者InnoDB存儲引擎的MySQL本身并不支持同步復制,然而有些技術,例如分布式復制塊設備(簡稱DRBD),可以在下層的文件系統提供同步復制,允許第二個MySQL服務器在主服務器丟失的情況下接管(使用第二服務器的復本)。要了解更多信息, 

      MYSQL 5。5開始,支持半自動復制。之前版本的MySQL Replication都是異步(asynchronous)的,主庫在執行完一些事務后,是不會管備庫的進度的。如果備庫不幸落后,而更不幸的是主庫此時又出現Crash(例如宕機),這時備庫中的數據就是不完整的。簡而言之,在主庫發生故障的時候,我們無法使用備庫來繼續提供數據一致的服務了。 

    Semisynchronous Replication則一定程度上保證提交的事務已經傳給了至少一個備庫。 

       
    Semi synchronous中,僅僅保證事務的已經傳遞到備庫上,但是并不確保已經在備庫上執行完成了。 

    此外,還有一種情況會導致主備數據不一致。在某個session中,主庫上提交一個事務后,會等待事務傳遞給至少一個備庫,如果在這個等待過程中主庫Crash,那么也可能備庫和主庫不一致,這是很致命的。(在主庫恢復后,可以通過參數Rpl_semi_sync_master_no_tx觀察) 

         如果主備網絡故障或者備庫掛了,主庫在事務提交后等待10秒(rpl_semi_sync_master_timeout的默認值)后,就會繼續。這時,主庫就會變回原來的異步狀態。 

        MySQL在加載并開啟Semi-sync插件后,每一個事務需等待備庫接收日志后才返回給客戶端。如果做的是小事務,兩臺主機的延遲又較小,則Semi-sync可以實現在性能很小損失的情況下的零數據丟失。 

       主機Crash時的處理 

    備庫Crash時,主庫會在某次等待超時后,關閉Semi-sync的特性,降級為普通的異步復制,這種情況比較簡單。 

    主庫Crash后,那么可能存在一些事務已經在主庫Commit,但是還沒有傳給任何備庫,我們姑且稱這類事務為"墻頭事務"。"墻頭事務"都是沒有返回給客戶端的,所以發起事務的客戶端并不知道這個事務是否已經完成。 

    這時,如果客戶端不做切換,只是等Crash的主庫恢復后,繼續在主庫進行操作,客戶端會發現前面的"墻頭事務"都已經完成,可以繼續進行后續的業務處理;另一種情況,如果客戶端Failover到備庫上,客戶端會發現前面的“墻頭事務”都沒有成功,則需要重新做這些事務,然后繼續進行后續的業務處理。 

       可以做多個備庫,任何一個備庫接收完成日志后,主庫就可以返回給客戶端了。 

    網絡傳輸在并發線程較多時,一次可能傳輸很多日志,事務的平均延遲會降低。 

    "墻頭事務"在墻頭上的時候,是可以被讀取的,但是這些事務在上面Failover的場景下,是被認為沒有完成的。 

         默認情況下MySQL的復制是異步的,Master上所有的更新操作寫入Binlog之后并不確保所有的更新都被復制到Slave之上。異步操作雖然效率高,但是在Master/Slave出現問題的時候,存在很高數據不同步的風險,甚至可能丟失數據。 
    MySQL5.5引入半同步復制功能的目的是為了保證在master出問題的時候,至少有一臺Slave的數據是完整的。在超時的情況下也可以臨時轉入異步復制,保障業務的正常使用,直到一臺salve追趕上之后,繼續切換到半同步模式。 
    Master: 
    INSTALL PLUGIN rpl_semi_sync_master SONAME ‘semisync_master.so’; 
    SET GLOBAL rpl_semi_sync_master_enabled=1; 
    SET GLOBAL rpl_semi_sync_master_timeout=1000; (1s, default 10s) 
    Slave: 
    INSTALL PLUGIN rpl_semi_sync_slave SONAME ‘semisync_slave.so’; 
    SET GLOBAL rpl_semi_sync_slave_enabled=1; 
    復制心跳(用戶檢測復制是否中斷) 

    MySQL5.5提供的新的配置master_heartbeat_period,能夠在復制停止工作和出現網絡中斷的時候幫助我們迅速發現問題。 

    啟用方法: 

    STOP SLAVE; 

    CHANGE MASTER TO master_heartbeat_period= milliseconds; 

    START SLAVE; 

    Slave自動恢復同步 

    在MySQL5.5版本之前,MySQL Slave實例在異常終止服務之后,可能導致復制中斷,并且relay binlog可能損壞,在MySQL再次啟動之后并不能正常恢復復制。在MySQL5.5中這一問題得到了解決,MySQL可以自行丟棄順壞的而未處理的數據,重新從master上獲取源數據,進而回復復制。 

    跳過指定復制事件 

    在多Master或環形復制的情況下,處于復制鏈條中間的服務器異常,可以通過 

    CHANGE MASTER TO MASTER_HOST=xxx IGNORE_SERVER_IDS=y 

    跳過出問題的MySQL實例。 

    自動轉換字段類型 

    MySQL5.1在基于語句的復制下,支持部分的字段轉換,但是行級的會報錯。MySQL5.5語句和行級復制都已支持。還可以通過 SLAVE_TYPE_CONVERSIONS 控制轉換的方向。 
    posted @ 2014-05-06 13:21 小馬歌 閱讀(272) | 評論 (0)編輯 收藏
     

    摘要

    超級負載均衡旨在為解決服務不斷擴展、機器不斷增多、機器性能差異等問題,以增強系統的穩定性,自動分配請求壓力。算法實現了多個模型和均衡策略,能通過配置實現隨機、輪詢、一致hash等。同時也能實現跨機房的相關分配。現已經在多個系統中使用。

    TAG

    負載均衡

    內容

    現有系統中存在的問題:

    1. 慢連接、瞬時訪問慢。

    場景一:

    如果后端新增加機器,cache命中率低,因此響應速度慢,但是能連接上且不超時。如果ui持續訪問就會把ui夯住。

    場景二:

    如果后端模塊某一臺機器響應較慢。如果前端持續訪問就會被夯住。

    2. 死機。

    場景一:

    能斷斷續續響應請求,不過速度很慢。造成ui夯住。

    3. 混合部署。

    場景一:

    多個模塊在同一機器上,項目影響。

    4. 機器權重。

    場景一:

    老機器,性能差;新機器,性能彪悍。因此他們應該承載不同的壓力。

    5. 跨機房冗余。

    場景一:

    后端對cache依賴很高的模塊,因為采用的是一致hash算法,如果掛掉一臺機器,對另外的機器cache命中率沖擊很大。因此希望將對這個機器的請求均衡到另外一個機房。

    6. php和c使用同樣的策略。

    現在php和c希望能使用的策略實際上是有很大的一致。為了避免重復開發,php和c希望采用同樣的負載均衡庫。

    要解決的問題:

    設計思路:

    1. 根據均衡策略計算出的均衡值對Server進行逆序排序。

    2. 負載選擇。對步驟1排序后的Server按以下順序進行選擇:

    a、按連接失敗概率進行選擇。

    注:橫軸代表失敗次數,縱軸代表選擇的概率。

    Cconn:一段區間內失敗次數

    f(Cconn):連接概率,取值范圍在(0,100]

    b、按健康狀態選擇。

    整個模型基于服務處理時間的收斂性。

    分析:

    1) 如果機器狀態良好,則平均處理時間會保持在一個穩定水平;即使是小波動,也會較快平穩在一個狀態。

    2) 如果機器開始出現問題,處理時間會開始增長。如果增長持續超過一段時間,則說明有可能會影響服務;如果一段時間后穩定了,說明對請求沒有太多影響。

    f(healthy):機器健康狀態,取值范圍[0,1]

    select(healthy):機器選擇概率,取值范圍[R,1]

    c、如果所有機器都沒選中,則隨機選擇一臺機器進行服務。

    3. 機器流量均分。

    不同的機器處理能力是不一樣的。當按照步驟2選擇了某臺機器,需要將其他處理時間為他的1/T(T>=2)的機器也選取出來,將部分壓力分給對應的機器。

    設k臺機器的處理時間分別是t1, t2,…,tk, 選中的機器id=i,比該機器處理能力高的機器時間分別為p1,p2,..,pr, (其中pj × T <= ti)。設一段時間總訪問量為Y,每臺機器理論上的訪問量應該為Vg=Y/k。而實際的Vr=Y/(ti * (1/t1+1/t2+…+1/tk))。則應該分出Vg-Vr的流量給pj。pj的流量比例為1/p1:1/p2:…:1/pr

    算法設計:

    A、均衡算法

    1. 一致hash算法。

    將每個server的ip和port加上balance_key三者做字符串拼接后,做md5簽名。

    value(server) = md5(server_ip + server_port + balance_key)

    2. 隨機算法。

    value(server) = random();

    3. 輪詢算法。

    value(server) =((server.id – (rounds % server_count)) + server_count) % server_count

    4. 多個選一算法。

    rank初始化為1, 如果默認的server失敗,則rank+1

    value(server) =((server.id – (rank % server_count)) + server_count) % server_count

    B、負載算法

    1. 連接狀態算法。

    a、對每一個server開辟一個狀態隊列。bool queue[K] 用來統計失敗次數。每次有壞狀態進隊,計數加一。如果有壞狀態出隊,則計數減一。

    b、按照f(Cconn)公式計算出選擇概率。

    c、利用rand()%100是否在[0,f(Cconn)]來決定是否選擇該機器。

    2. 健康狀態算法。

    a、每臺機器維持一個一秒鐘內的處理時間T和次數C。

    b、當一秒過去以后,將T、C計算為平均處理時間R。

    c、每M秒,統計每臺機器最近一段時間的平均處理時間, 按照公式select(healthy)算出選擇概率。

    d、利用rand()%100是否在[0, select(healthy)*100]來決定是否選擇該機器。

    C、流量均分

    按照策略選出滿足要求的機器,按照流量均分公式進行流量分配。

    分配時按照balance_key+server方式和random()來分配機器, 盡量保證請求落在同一臺機器。

    by wangbo

    posted @ 2014-05-06 10:22 小馬歌 閱讀(218) | 評論 (0)編輯 收藏
     
         摘要: 2005年,我開始和朋友們開始拉活兒做網站,當時第一個網站是在linux上用jsp搭建的,到后來逐步的引入了多種框架,如webwork、hibernate等。在到后來,進入公司,開始用c/c++,做分布式計算和存儲。(到那時才解開了我的一個疑惑:C語言除了用來寫HelloWorld,還能干嘛?^_^)。總而言之,網站根據不同的需求,不同的請求壓力,不同的業務模型,需要不同的架構來給予支持。我從我的...  閱讀全文
    posted @ 2014-05-06 10:07 小馬歌 閱讀(473) | 評論 (1)編輯 收藏
    僅列出標題
    共95頁: First 上一頁 20 21 22 23 24 25 26 27 28 下一頁 Last 
     
    主站蜘蛛池模板: 亚洲成a人片在线观看播放| 亚洲av无码成人精品区一本二本 | 精品女同一区二区三区免费播放| 四虎成人免费影院网址| 日韩毛片免费一二三| 久久夜色精品国产亚洲| 免费大片黄在线观看yw| 黄网站色视频免费观看45分钟 | 亚洲人成网站色在线观看| 免费jlzzjlzz在线播放视频| 91视频精品全国免费观看| 一级做a爱过程免费视频高清| 日本亚洲欧洲免费天堂午夜看片女人员 | 国产人妖ts在线观看免费视频| a毛看片免费观看视频| 456亚洲人成影院在线观| 狠狠亚洲狠狠欧洲2019| 一色屋成人免费精品网站 | 永久看日本大片免费35分钟| 国产成人精品亚洲| 亚洲蜜芽在线精品一区| 免费一级大黄特色大片| 日韩在线播放全免费| 一级毛片免费毛片毛片| 亚洲中文字幕AV在天堂| 精品国产亚洲一区二区三区| 成年人视频在线观看免费| 日韩免费在线视频| 四虎影视久久久免费观看| 亚洲综合色婷婷在线观看| 成人免费777777| 午夜精品一区二区三区免费视频| 亚洲.国产.欧美一区二区三区| 亚洲一区二区三区高清| 亚洲熟女乱综合一区二区| 成人黄动漫画免费网站视频 | 免费一级大黄特色大片| 成人奭片免费观看| 99视频有精品视频免费观看| 国产99精品一区二区三区免费| 亚洲国产AV一区二区三区四区 |