ELB配合Unicorn集群導致響應緩慢的問題

前一段時間,我們在遷移Unicorn服務到Docker集群中。並且在前端使用ELB作為反向代理。這樣利用ELB高可用的特點,實現Docker集群任何一個節點掛掉時,即時屏蔽該節點的同時不會影響到服務的整體可用性。當然也可以採用DNS中設置多IP地址的A記錄的方式,不過DNS的更新生效時間受終端緩存影響較大,比較不可控。當然,為什麼採用ELB並不是我討論的重點,而採用ELB直接和Unicorn通信的問題才是本文要說的問題。

當我們上線集群和ELB運行一段時間後發現,ELB經常會檢測到後端OutOfService,導致終端無法訪問服務。在ELB日誌里體現出來就是眾多的5xx錯誤。而進入到節點中直接訪問對應埠的Unicorn服務,則會很長時間才返回(並不會超時或者錯誤)。

經過一大圈的排查,最後問題鎖定到Unicorn和ELB的兼容性問題。最後還是Unicorn和ELB的官方文檔給了我們最有力的證據。

在Unicorn的官方文檔中有一段設計準則,說的是Unicorn如何符合Unix哲學,如何balabala。簡單說就一句話:老子不負責高並發,只負責高並發業務。聽起來很拗口是不。那我們具體來說說:

  1. Unicorn認為一個完整的高效率的WEB伺服器,包含兩部分工作:一是儘可能多的勾搭客戶端實現高並發,一是儘可能快的處理單個客戶端的業務實現高效率。
  2. 同時他覺得客戶端都是矮窮挫,發請求收響應什麼的網路IO操作太慢,配不上高大上的Unicorn。
  3. 所以應該找個傻逼,干著和一群群客戶保持連接的臟活累活。把收集客戶端的請求(HTTP Request),火速的通過內網或者呈送給Unicorn。等待Unicorn處理完後接住結果(HTTP Response),然後慢慢的發回給客戶端。
  4. 而自己只需要秒讀取請求數據,然後超牛逼超快的處理完就可以了。

這樣的設計準則,就造成了在伺服器並發處理中,Unicorn不會負責或者不擅長負責一個重要的東西——客戶端連接池。

而再來看看另外一個主角ELB。這貨就是Unicorn所需要的傻逼,負責保持客戶端的連接池。然而這並不是Unicorn所推薦的滿足他幾大要求的傻逼,因為ELB干著一件讓Unicorn不爽的事情——保持後端連接。

根據ELB的文檔,ELB主機會嘗試和被代理的後端應用建立持久化連接。一方面可以簡單的用來作為健康檢查。另一方面在有客戶端請求的時候,能夠直接復用這個鏈接,從而更快速的處理請求。

當Unicorn用在集群上的時候,通常我們會配備多個節點。多個節點都會和ELB勾兌起自己的「持久化鏈接」。而這些鏈接最後都會落實到Unicorn身上。當節點數足夠多的時候,就會導致光是ELB的「持久化鏈接」數量,就達到了Unicorn的上限。

按理說如果只是鏈接多,Unicorn幾刷子處理完也就罷了。關鍵是ELB的目的並不是Unicorn期待的「火速呈上請求並拿走響應斷開鏈接」,而是一直保持著鏈接不放。這樣就基本榨乾了Unicorn有限的連接池。造成正常的請求,甚至部分ELB的健康檢查請求排隊,直到Unicorn受不了此前的請求逐個斷開才能處理。

所以,總結起來,還是Unicorn自己「設計準則」里的一句話比較管用——符合他們xxxx需求的前端連接池管理方案,只有Nginx這個腦殘。Nginx既可以怒懟海量的客戶端,所以區區幾個ELB的持久話鏈接不在話下。同時,Nginx只會開鏈接——發請求——等響應——斷鏈接,不會對背後的Unicorn造成什麼壓力。

最後我們的解決方案就是,把這一堆Unicorn容器和一個Nginx容器(以Nginx的並發處理能力,一個足矣)加入到同一個Overlay網路。對外由Nginx去Public埠,接收請求。對內,Nginx再把請求交由Unicorn處理。然後Nginx外層,再直接連客戶端或者ELB都是極好的。


推薦閱讀:

BearyChat 技術棧是什麼?
Windows Azure 為何比國內其他雲服務貴這麼多?
有哪些雲計算平台好用又實在?
無意中被亞馬遜AWS扣費怎麼辦啊?
有沒有支持MATLAB的雲計算平台?

TAG:集群 | Docker | AmazonWebServicesAWS |