天天看點

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

HikariCP資料庫連接配接池實戰 點選檢視第二章 點選檢視第三章

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

朱政科 著

第1章

阿裡中間件實戰,第一個案例

在應用系統開發過程中,池化技術,如對象池、連接配接池、線程池等,通過複用對象以減少建立、釋放連接配接的消耗來提升性能。由于TCP連接配接的建立開支十分昂貴,資料庫所能承載的TCP并發連接配接數也有限制,針對這種場景,資料庫連接配接池應運而生。資料庫連接配接池的實作有很多,如c3p0、DBCP、Druid等,也包括本書将詳細介紹的、号稱性能最好的資料庫連接配接池——HikariCP。

本章首先從一個有趣的執行個體說起。在此過程中,我将告訴你很多和連接配接相關的知識,并且讓你對系統的調優過程産生一點共鳴,然後我才能按通常的方式向你展開原理介紹。這個例子是我在阿裡巴巴多年間對一款中間件調優的真實經曆,這款中間件經曆過很多打磨,比如全鍊路壓測、3年雙十一大促、多種監控系統接入、數次整體結構重構改造、重大問題修複等。這款中間件是純TCP的,再回首,我并不為當年所做的那些TCP性能優化感到自豪,相反,我認為一款池化的中間件如果能提煉出來,會更有價值,因為它更加有利于技術産品的快速落地及運維,進而更好地提升研發效能。

希望讀者通過這個執行個體可以了解到在沒有池化技術的情況下TCP調優是如何進行的,同時也能了解TCP調優的複雜性,體會到池化技術出現的必然性。資料庫連接配接池同樣是池化技術,大家也可以深入思考并想象一下資料庫連接配接池中間件需要解決哪些問題。

1.1 物聯網MQTT單機壓測130萬參數調優

2019年1月,我的工作之一就是為公司搭建一套獨立于阿裡雲之外的自己的物聯網MQTT叢集。MQTT非常适合物聯網場景,它可以保持裝置與伺服器的長連接配接,避免反複輪詢,并支援推送。和資料庫連接配接池一樣,它同樣運作于TCP協定之上,MQTT相比于HTTP具有協定開銷低、容忍弱網絡、低功耗、百萬并發等優點。但是由于目前的阿裡雲MQTT不支援will、retain msg、QOS2,并且存在限流、斷連等不穩定因素,自建MQTT叢集就顯得格外重要。

在調研了Eclipse Mosquitto後,我又在EMQ(Erlang/Enterprise/Elastic MQTT Broker)的官網上發現了下列描述:“EMQ消息伺服器1.x版本MQTT連接配接壓力測試到130萬,在1台8核心、32G記憶體的CentOS伺服器上。”經過梳理後我認為,它的服務端調優主要分為Linux作業系統參數調優和TCP協定棧網絡參數調優兩部分。

Linux作業系統參數調優如下:

1)系統全局允許配置設定的最大檔案句柄數。

sysctl -w fs.file-max=2097152

sysctl -w fs.nr_open=2097152

echo 2097152 > /proc/sys/fs/nr_open

2)允許目前會話或程序打開檔案句柄數。

ulimit -n 1048576

TCP協定棧網絡參數調優如下:

1)并發連接配接backlog設定。

sysctl -w net.core.somaxconn=32768

sysctl -w net.ipv4.tcp_max_syn_backlog=16384

sysctl -w net.core.netdev_max_backlog=16384

2)TCP Socket 讀寫Buffer設定。

sysctl -w net.core.rmem_default=262144

sysctl -w net.core.wmem_default=262144

sysctl -w net.core.rmem_max=16777216

sysctl -w net.core.wmem_max=16777216

sysctl -w net.core.optmem_max=16777216

sysctl -w net.ipv4.tcp_mem='16777216 16777216 16777216'

sysctl -w net.ipv4.tcp_rmem='1024 4096 16777216'

sysctl -w net.ipv4.tcp_wmem='1024 4096 16777216'

3)TCP連接配接追蹤設定。

sysctl -w net.nf_conntrack_max=1000000

sysctl -w net.netfilter.nf_conntrack_max=1000000

sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30

4)TIME-WAITSocket最大數量、回收與重用設定。

net.ipv4.tcp_max_tw_buckets=1048576

注意: 不建議開啟該設定,NAT模式下可能引起RST連接配接

net.ipv4.tcp_tw_recycle = 1

net.ipv4.tcp_tw_reuse = 1

5)FIN-WAIT-2 Socket逾時設定。

net.ipv4.tcp_fin_timeout = 15

TCP/IP參考模型可以分為應用層、傳輸層、網絡層和鍊路層。TCP和UDP在傳輸層,應用層除了剛才我們介紹的MQTT,還有HTTP、FTP等。因為MQTT運作于TCP協定之上,是以它的調優也離不開TCP的參數調優,資料庫連接配接池的出現也是為了解決應用與資料庫之間TCP的性能問題。目前很多應用,比如服務端和算法服務的互動也離不開TCP的傳輸。是以了解TCP的調優方式可以夯實這個技術領域調優的基礎,一通百通。

1.2 阿裡中間件TCP四次揮手性能調優實戰

重用資料庫連接配接最主要的原因是可以減少應用程式與資料庫之間建立或銷毀TCP連接配接的開銷,資料庫連接配接池的概念應運而生。如果不使用連接配接池,TCP四次揮手過程中TIME_WAIT的性能調優是相對比較複雜的,執行個體如下所示。

1.2.1 億級消息網關Rowan架構

Rowan是一個億級企業消息網關中間件服務,如圖1-1所示。它在業務上為B2B、Aliexpress、集團安全、共享事業部、淘寶等大部門提供郵件、短信、旺旺、站内信、釘釘等消息的持續發送能力,支援的業務包括會員注冊、評價、仲裁、使用者觸達EDM、資金中心對賬、交易、營銷、物流追蹤、賣家認證、CRM、風控合規、反欺詐、風險評測、處罰等。

技術上,Rowan由多個服務和中間件構成,具有模闆管理、使用者觸達、消息管理、EDM無線引流、打點追蹤等功能,每天産生的消息條數過千萬,僅2016年“雙十一大促”當天産生的消息,僅郵件類就超過6億件。

以郵件子產品為例,Rowan技術架構有過兩個時期。第一個時期,業務方請求通過HSF(一種阿裡内部類似Dubbo的RPC)請求調用Rowan,Rowan再透傳調用阿裡雲郵SMTP服務。當業務洪峰抵達的時候,海外叢集會經常出現逾時的情況。這是因為,當時阿裡雲國内叢集的系統設計能力比較強,基本上可以支撐國内的郵件發送;相對于國内有5個資料庫叢集,國外阿裡雲叢集則稍顯薄弱,僅有一個資料庫叢集。國内叢集的系統設計能力是1億次請求/天,日常2000多萬次請求/天;海外日常1000多萬次請求/天,峰值1500多萬次請求/天。阿裡雲使用寫磁盤的方式,若投遞資訊失敗,重試5次,大緻時間點是2分鐘、5分鐘、30分鐘、1小時和2小時,若2小時後仍發送失敗則丢棄資訊。但是,隻要是被阿裡雲SMTP攔截的,如傳回給Rowan的connection reset、451、526等異常,不進入阿裡雲隊列,這就會造成隻有Rowan能列印出異常資訊,而阿裡雲服務端則列印不出具體的異常資訊,排查問題變得相當棘手。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

由于國内外知名的郵件伺服器,如亞馬遜AWS、搜狐SENDCLOUD等都是支援抗堆積的,在無法推動阿裡雲從SMTP支援到HTTP的背景下,我進行了第二個時期的技術改造設計,如圖1-2所示。

在這個架構中,所有的外部服務通過Rowan Service調用對應的服務,如“郵件”等。首先會根據Cache緩存中的模闆資訊組裝成消息體,并存儲在HBASE中進行發送狀态的記載;其次請求的消息會直接丢到對應的中美MQ(阿裡内部叫Metaq,對外開源叫RocketMQ)消息隊列叢集中,線上流式實時計算Jstorm會分别處理MQ中的郵件、短信、站内信、HTTP、旺旺、釘釘等消息,來進行消息的投遞。

由于杭州的阿裡雲郵系統能力遠遠高于美國叢集,是以美國叢集若命中了國内郵箱,比如126、163、QQ等,會直接路由到國内阿裡雲網關進行發送。在圖1-2中,Rowan服務是MQ的生産者,Jstorm是MQ的消費者,故流控和暫停功能放在了消費者這裡。流控功能是基于阿裡配置中心Diamond定制化開發的,Jstorm啟動時直接從配置項讀取MQ最大線程數和最小線程數。在Diamond中還可以配置海外叢集的壓力疏導功能,支援按照百分比将流量轉移到國内。利用Diamond配置中心實時更新的特性,當Diamond修改時可以實時推送給對應機房以指定的流控線程數重新開機MQ消費者用戶端。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

阿裡雲可以配置發件賬号的優先級,并暫停低優先級的賬号。Rowan這套新架構在此基礎上額外支援兩個類似的擴充功能:自帶無效位址,可以關掉對指定收件人的發送;模闆禁用也可以直接關掉此類模闆的發送。

這套架構采用疏導的政策,旨在将海外叢集的壓力轉移到國内分擔,進而大幅度提高系統的整體QPS。這套架構需要業務進行配合,通過采取統計國内外郵件賬号的QPS名額、進行國内外發件賬号的優先級排序、準備發送故障預案、精簡合并系統通知郵件、均勻化編排大促營銷郵件發送等一系列措施,來保障整體系統“雙十一大促”的穩定性。這也是我在阿裡巴巴公司工作期間學到的——技術驅動業務,團隊合作,“貼着業務走,以結果為導向”是中間件團隊的職責。

1.2.2 人臉識别服務:異曲同工的架構

提到疏導的架構和QPS的提升,讓我想起了2019年我設計的大幅度提升QPS的技術方案。

如圖1-3所示,FaceServer是基于Java的人臉識别服務,通過調用Python的算法服務,為業務服務提供人臉識别的Dubbo API接口。算法服務包括多個原子服務,如人臉、品質、屬性、活體、特征等。據統計,FaceServer服務調用算法人臉識别服務,平均每天有40~50次逾時異常,最多的時候有100次。技術團隊是以對算法伺服器單機進行了壓測,單機10個線程,平均響應時間在2.878秒左右(FaceServer認為3秒即逾時),最長響應時間為13.814秒,QPS為1。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

FaceServer在物聯網時代,就類似于淘寶網等網站的會員登入,是網際網路的門面,QPS為1顯然不能滿足商業化需求。很多做研發的資深人士可能會想,是否可以采用堆機器的方式來提高QPS呢?這是不可行的,因為算法伺服器非常特别,不同于普通的伺服器,它是昂貴的GPU密集型伺服器:一台阿裡雲GPU伺服器單機每月要6000元,一年7.2萬元,按照圖1-3采購兩台,一年總計14.4萬元。也就是說,目前單機隻有1QPS,兩台機器2QPS,要支援200QPS,如果堆機器,一年就需要花1400多萬元。為了使QPS達到200,一年需要多支出1400多萬元的開銷,這絕對不是中小型創業公司能夠承擔的,在降成本的大型BAT,想必也是不能接受的。

目前的算法伺服器是單點,另一台算法GPU伺服器由于曆史原因一直沒有啟用,是以第1步改造我決定先高效利用好兩台已有的算法GPU伺服器。在進行了将單機部署提升為叢集部署、算法服務内部的串行改并行、耗時原子服務多開程序等工作後,FaceServer的維護者表示再也沒有出現過線上的逾時異常的情況,此時的架構如圖1-4所示。經團隊壓測後,單機的QPS也從1提升到了7,由于線上正在使用人臉識别服務,整體叢集QPS沒有進行壓測,估計應該是1+1>2的效果(叢集14QPS)。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

基于百度人臉識别算法,為企業認證的使用者免費提供每月15QPS的服務,據此推測,百度應該也有一些性能優化的技巧來高效利用GPU伺服器,是以目前這樣的架構還可以繼續提高QPS。于是,我又和算法團隊進行了進一步的溝通,有收獲的是,據算法團隊回報,算法伺服器内部單獨處理一張人臉圖檔和批量處理其實是一樣的。算法團隊表示,在單模型情況下,目前人臉特征模型單機程序如果使用批處理至少可以将QPS提高到35,而這恰恰就是可以大幅提升QPS的切入點。根據算法團隊批處理的需求,我想到了一個請求合并的架構。

如圖1-5所示就是第2版優化架構。這是一種請求合并的處理架構,也是中間件性能提升中較為常見的方案。對于業務服務來說,依然是通過Dubbo同步調用FaceServer服務,而FaceServer則是将每次請求丢到一個隊列中去,當這個隊列請求收集到一定數量(比如算法服務的35QPS)時則進行送出或者每秒進行送出,這種同步轉異步的方式可以充分利用算法GPU伺服器的資源,進而大幅提升QPS。在圖1-5的架構中,我采用了Guava而不是Redis,原因是Redis進行人臉圖檔的讀寫會有網絡開銷,采用Guava會在更大程度上提升響應時間。另外,關于圖1-5重試的部分我也使用了GuavaRetry,充分利用了Guava的緩存和重試工具特性。

這種Dubbo同步轉異步、請求合并的方案,其實和Rowan的抗堆積、流控與暫停有着異曲同工之妙,都是在有限資源(人力、物力)的基礎上充分合理利用已有資源,進而達到更為極緻的QPS。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

1.2.3 “雙十一大促”全鍊路壓測發現TCP問題

聊完架構,我們繼續回到Rowan和TCP的話題。

Rowan郵件功能使用的是SMTP協定,SMTP在七層協定中屬于應用層協定,屬TCP/IP協定簇,它幫助每台計算機在發送或中轉信件時找到下一個目的地。Rowan對接阿裡雲雲郵網關,阿裡雲雲郵再對接Hotmail、Gmail、Yahoo等郵件服務商。在我日常運維的過程中,會偶發性地出現“Could not connect to SMTP host”的異常提示,尤其是在業務方進行EDM(Email Direct Marketing)郵件大規模營銷時會頻繁發生。

備戰“雙十一”過程中,我對Rowan進行了全鍊路技術改造和壓測,在進行線上壓測郵件服務過程中發現美國叢集(US代表美國,HZ代表杭州)在達到1萬QPS壓力時,Rowan服務端大量産生60多萬次的SMTP連接配接不上阿裡雲網關的異常,如圖1-6所示。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

阿裡的中間件存在百花齊放、百家争鳴的情況,也可以說是野蠻生長、重複造輪子、适者生存。當時還有一款同樣的功能的中間件叫作EVE,這是一款從阿裡巴巴B2B時代就存在的曆史悠久的中間件,而Rowan則是一款年輕的中間件。和EVE不同的是,Rowan并不是通過堆機器和增加系統複雜度來換取高吞吐量的,其隻使用了中美(各4台)共計8台Server伺服器,Rowan引入Apache的Jstorm技術,SMTP的郵件發送邏輯放在了Jstorm中,引入Jstorm就是想用最少的機器做極緻的事。在Jstorm的日志中,我們又拿到了更加詳細的異常資訊。

Caused by: javax.mail.MessagingException: Could not connect to SMTP host: smtp-usa.ocm.aliyun.com, port: 25; nested exception is: java.net.NoRouteToHostException: Cannot assign requested address at com.sun.mail.smtp.AliyunSMTPTransport.openServer(AliyunSMTPTransport.java:1764) at com.sun.mail.smtp.AliyunSMTPTransport.protocolConnect(AliyunSMTPTransport.java:587) Caused by: java.net.NoRouteToHostException: Cannot assign requested address at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)

然而登入阿裡雲的伺服器檢視系統名額,以及檢查阿裡雲雲郵系統的神農監控、盤古等名額都沒有問題。檢視Jstorm伺服器套接字socket使用概況,可以根據上述異常SMTP 25端口使用netstat -tn |grep :25 |wc -l 指令,或者使用ss –s(Socket Statistics縮寫)指令。如圖1-7和圖1-8所示是連續兩次截圖記錄,可以看出timewait非常多。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例
帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

種種迹象表明,問題發生在Client端而不是Server端,這是一起典型的TCP調優案例。

1.2.4 Linux核心網絡參數調優

首先我們調整一下Linux核心參數來提高伺服器并發處理能力。一般來說,這種方式可以不用更新伺服器硬體就能最大程度提高伺服器性能,是一種節省成本的做法。核心參數修改了以下4個配置:

  • ulimit –n。該檔案表示系統裡打開檔案描述符的最大值,檢視之後發現是1024,這個值偏小,我們将其調大一些。
  • somaxconnSocket。cat /proc/sys/net/core/somaxconn,該檔案表示等待隊列的長度,預設是128,我們将其調到1000。
  • netdev_max_backlog。cat /proc/sys/net/core/netdev_max_backlog,該檔案表示在每個網絡接口接收資料包的速率比核心處理這些包的速率快時,允許送到隊列的資料包的最大數目,我們将其調到1000。
  • tcp_max_syn_backlog。cat /proc/sys/net/ipv4/tcp_max_syn_backlog,該檔案表示SYN隊列的長度,預設為1024,加大隊列長度為8192,以容納更多等待中的網絡連接配接。

修改完畢,重新進行線上壓測,然而發現問題沒有解決,QPS也沒有得到提升。

小竅門

本節主要是對/proc/sys/net/進行優化,該目錄下的配置檔案主要用來控制核心和網絡層之間的互動行為,一些技巧補充如下所示。

1)/proc/sys/net/core/message_burst檔案表示寫新的警告消息所需的時間(以0.1秒為機關),在這個時間内系統接收到的其他警告消息都會被丢棄。這用于防止某些企圖用消息“淹沒”系統的人所使用的拒絕服務(Denial of Service)攻擊。預設:50(5秒)。

2)/proc/sys/net/core/message_cost檔案表示寫每個警告消息相關的成本值。該值越大,越有可能忽略警告消息。預設:5。

3)/proc/sys/net/core/netdev_max_backlog檔案表示當每個網絡接口接收資料包的速率比核心處理這些包的速率快時,允許送到隊列的資料包的最大數目。預設:300。

4)/proc/sys/net/core/optmem_max檔案表示每個套接字所允許的最大緩沖區的大小。預設:10240。

5)/proc/sys/net/core/rmem_default檔案指定了接收套接字緩沖區大小的預設值(以位元組為機關)。預設:110592。

6)/proc/sys/net/core/rmem_max檔案指定了接收套接字緩沖區大小的最大值(以位元組為機關)。預設:131071。

7)/proc/sys/net/core/wmem_default檔案指定了發送套接字緩沖區大小的預設值(以位元組為機關)。預設:110592。

8)/proc/sys/net/core/wmem_max檔案指定了發送套接字緩沖區大小的最大值(以位元組為機關)。預設:131071。

1.2.5 Linux TCP參數調優

TIME_WAIT是TCP中一個很重要的狀态,在大并發的短連接配接下,會産生很多TIME_WAIT,這會消耗很多的系統資源;端口的數量隻有65535,占用一個就會少一個,進而嚴重影響新連接配接。是以需要調優TCP參數,進而讓系統更快地釋放TIME_WAIT的連接配接。TCP的傳輸連接配接有連接配接建立、資料傳送和連接配接釋放3個階段,多年前與阿裡巴巴的葉軍博士在一次閑聊中得知,作為阿裡巴巴面試官的他經常會考察應聘者TCP的3次握手和4次揮手這個知識點。

對于TIME_WAIT,我們主要進行如下修改:

net.ipv4.tcp_tw_recycle = 1

net.ipv4.tcp_tw_reuse = 1

這兩個參數預設是關閉的,而且不建議開啟,因為在NAT模式下可能引起連接配接RST。這兩個參數的作用是主動斷開連接配接,這由于違反了TCP協定(RFC 1122),在其官方文檔中也強調:“It should not be changed without advice/request of technical”(在沒有專業的建議或請求的情況下不要修改設定)。

TCP主要核心參數的說明如下所示:

  • net.ipv4.tcp_syncookies = 1表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防範少量SYN攻擊。預設為0,表示關閉。
  • net.ipv4.tcp_tw_reuse = 1表示開啟重用。允許将TIME-WAIT sockets重新用于新的TCP連接配接。預設為0,表示關閉。
  • net.ipv4.tcp_tw_recycle = 1表示開啟TCP連接配接中TIME-WAIT sockets的快速回收。預設為0,表示關閉。
  • net.ipv4.tcp_fin_timeout修改系統預設的TIMEOUT時間。

我們可以再繼續進行并發連接配接 backlog調優,并對可用端口調優範圍、TCP Socket 讀寫 Buffer、TIME-WAIT Socket 最大數量、FIN-WAIT-2 Socket 逾時設定等進行優化。特别說明一下,tcp_max_tw_buckets用于控制并發時,TIME_WAIT的數量預設值是180 000,如果超過該值,系統會清除多餘部分,并警告“time wait bucket table overflow”。這些參數的調優不但能夠提升伺服器的負載,還可以在一定程度上防禦DDoS等攻擊。

在參數調優之後,又上了一道雙保險,重新修改了com.sun.mail.smtp源碼的SO_REUSEADDR,通過serverSocket.setReuseAddress(true)讓其在伺服器綁定端口前生效,目的是讓端口釋放後立即就可以被再次使用。

但是釋出到線上進行壓測以後,卻依然沒有效果。

1.2.6 一行代碼大幅提升QPS

經過了反複的調優以後,真正解決問題的其實隻有一行代碼“TCP option SO_LINGER”。在1.2.5節中的socket.setReuseAddress(true)代碼後增加一行代碼“socket.setSoLinger(true,0)”,重新進行線上壓測,壓測過程中啟用setSoLinger,啟用前後的資料結果如圖1-9~圖1-11所示。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例
帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例
帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

從圖1-9可以發現,啟用l_linger前連接配接很難超過700,timewait狀态的很多;啟用socket.setSoLinger(true,0)後連接配接數到1045,timewait狀态沒有了(見圖1-11)。正是因為放棄了TCP中的4次揮手,是以用戶端(Rowan整體系統)會給服務端(阿裡雲郵叢集)發出很多TCP中的RST。葉軍博士曾告訴我說,關閉4次揮手這個案例他以前調優時也做過類似處理,有需要的場景可以将4次揮手改成3次揮手,不過要經過專業的大規模測試,一旦TCP層面的Buffer資料丢掉,還是會存在一些隐患的。SO_LINGER雖然可以讓伺服器性能提升很多,但是在《UNIX網絡程式設計》卷1中提到過,TIME_WAIT的作用是在主動關閉端口後,保證資料讓對端收到,Richard.Steven的原話是:“TIME_WAIT是我們的朋友。”如果服務存在大量或者多個通信,而且之間還有一些時序關系,那麼我們就不能使用這種讓使用者丢棄一部分資料的方式,否則可能因為最後階段丢失一些伺服器的傳回指令而造成程式的錯誤。

那SO_LINGER是什麼呢?它是一個可以通過API設定的socket選項,僅适用于TCP和SCTP。它主要由on和linger兩個屬性決定,其資料結構如下所示:

struct linger {

int l_onoff; /* 0 = off, nozero = on */開關,零或者非零
int l_linger; /* linger time */優雅關閉最長時間           

};

如表1-1所示,socket.setSoLinger(true,0)對應的是立即傳回,即“強制關閉失效”的情況。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

如圖1-12所示,這種情況并不是4次揮手,TCP不會進入TIME_WAIT狀态。在send buffer中的資料都發送完之前,close就傳回,client向SERVER發送一個RST資訊。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

為了避免由于使用socket.setSoLinger(true,0)可能導緻的應用資料包丢失問題,在如

圖1-13所示的javamail官方文檔中,SMTP啟用了quitwait參數,可以保證應用層消息被完整地發完并收到服務端的發送響應。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

是以,關閉Socket連接配接部分的代碼調整為,如果quiteWait為true,則需要接收響應。修改完後,當天13:00以後進行了多次壓測,在Rowan服務端中“Could not connect to SMTP host”這個異常再也沒有出現,如圖1-14所示。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

同一時間,阿裡雲神農監控顯示收到郵件的QPS比之前的壓測要略大,曲線上升波動更快,完全符合預期,如圖1-15所示。

帶你讀《HikariCP資料庫連接配接池實戰》之一:阿裡中間件實戰,第一個案例阿裡中間件實戰,第一個案例

至此,整體TCP調優結束。

思考SMTP也支援一個連接配接多封郵件,發完rset指令後,再重新認證發送就可以了,TCP方面并不是與阿裡雲伺服器建立的連接配接,而是與阿裡雲的LVS建立的TCP連接配接。

阿裡雲使用的還是SMTP的協定,其實我們可以看一下業界的郵件服務,如AWS、SENDCLOUD等,不僅僅支援SMTP,也同樣支援HTTP等。其實真正解決問題的方法是将連接配接方式改進為長連接配接,或者實作client的連接配接複用(連接配接池的理念)。是以推動阿裡雲從SMTP改為HTTP也是一個方向。

1.3 技術驅動業務,結果為導向

上述内容都是高并發時對于TCP的調優,但是抗堆積也是消息網關的一個很核心的特性,如亞馬遜AMAZON SES和SENDCLOUD等都是抗堆積的。

郵件的擁堵會造成一連串的問題,如:多個阿裡事業部之間郵件緊耦合,每到大促線上互相影響,業務發展嚴重受限,詢盤導緻系統雪崩,實時性高的會員注冊郵件被整體通道堵死,投遞量越大投遞成功率越低,投遞量使用者回報郵件延遲越多等問題。我清晰地記得,當年的故障報告有記載,因為郵件擁堵導緻的P0級故障直接造成了資金損失。

對于流量洪峰,我們可以吸取古人的智慧。早在4000多年前,我國的黃河流域洪水為患,鲧采取“水來土擋”的政策治水,最終失敗。大禹确立了一條與他父親鲧的“堵”相反的方針,叫作“疏”,即“治水須順水性,水性就下,導之入海。高處就鑿通,低處就疏導”的治水思想。

當遇到國際化業務場景如“雙十一”時,在美國的伺服器系統設計能力遠不如杭州伺服器的情況下,我們可以采用多種手段進行“疏導”。

“疏”:可以采用拆分事業部、營銷和通知賬号的手段讓每個發件賬号獨立走流程,消息設定優先級(最高優先級不落盤,直接向網關通過綠色通道發送,避免延遲,如提高注冊郵件的實時性),支援整體和局部的流控暫停及停發消息等手段。

“導”:為了防止對于阿裡雲盤古的沖擊,Rowan自身做好抗堆積建設;支援灰階引流,解決阿裡雲美國系統設計能力弱而杭州系統設計能力強的問題;大促郵件發送參與業務方評估,合理排程紛發;做好充分的預案等。由于中美有13個小時的時差,“雙十一大促”也可以制定合理的規劃,通過“打時間差”的形式處理好中美“雙十一大促”的消息投遞問題。

郵件領域還有一個經典的黑名單的問題,全球最大的反垃圾郵件組織spamhaus會将發往mail.ru,microsoft(Hotmail、Outlook、Live)等位址的郵件設為接收方拒收。如果營銷郵件無法保證接收位址的有效性等,便會很容易被spamhaus封禁,spamhaus也往往不同意解封,一般隻能通過郵件和spamhaus溝通。大促預熱期對于EDM(Email Direct Marketing)營銷GMV(Gross Merchandise Volume,網站成交總額)影響很大,尤其是外國人有着看電子郵件的習慣。正是因為郵件黑名單的問題,我在2016年曾經彙總過一部分資料,發現營銷郵件的到達率從95%降到了70%左右,EDM相關GMV 就跟着下降了30%以上。

針對黑名單問題,優先尋求商務解封。除了商務解封之外,産品設計上需要建設郵件的黑白名單體系,對存量和增量使用者進行确認,發送郵件清單的黑白名單,先從Hotmail等會被屏蔽的使用者開始,在發送郵件的清單中進行過濾;如果是在郵件清單中的注冊新會員使用者,則進行郵箱驗證,隻驗證是否可以發郵件,不影響下單等流程。另外,阿裡雲需要實作更換機房IP的政策,通過IP更換跳過spamhaus的監測,比如大促前保留備用營銷網段随時準備動态切換。品質較低的任務也可以先暫停,當然這些任務暫停并不會影響我們向優質客戶(如下過單的使用者)發送的EDM郵件,是以對業務上的影響是可控的。具體的業務名額可以在這些低品質任務暫停後觀察一下網站的流量名額,會發現基本不會有什麼明顯的影響。

在完成了上述工作以後,2016年“雙十一大促”不光技術上提升到萬級QPS,在業務上也取得了可喜的結果:1.6億存量使用者驗證在大促前落地;“雙十一”當天2億營銷郵件投遞到使用者郵箱;大促前3億營銷郵件投放成功;大促期間和2016年上半年比較,在整體的發送量提升的同時,打開率和行動率有10%~15%的提升。

1.4 本章小結

春秋戰國時期,有位神醫被尊為“醫祖”,他就是扁鵲。一次,魏文王問扁鵲:“你們家兄弟三人,都精于醫術,到底哪一位最好呢?”扁鵲答:“長兄最好,中兄次之,我最差。”文王又問:“那麼為什麼你最出名呢?”扁鵲答:“長兄治病,是治病于病情發作之前,由于一般人不知道他事先能鏟除病因,是以他的名氣無法傳出去;中兄治病,是治病于病情初起時,一般人以為他隻能治輕微的小病,是以他的名氣隻及本鄉裡;而我是治病于病情嚴重之時,一般人都看到我在經脈上穿針管放血,在皮膚上敷藥等大手術,是以以為我的醫術高明,名氣是以響遍全國。”

本章介紹的例子,我沒有高明地做好事前控制、事中控制,而是陷入在事後對嚴重問題不斷地“穿針放血、敷藥刮骨”等大手術般的操作中,希望能夠讓你對于“連接配接是怎樣調優的”産生一點感覺。

通過本章執行個體,大家可以看到,TCP尤其是TIME_WAIT的調優其實是相對比較複雜的,且是一項耗時耗力的工作。此外,就算做了核心級别的調優,随着容器化Docker和K8S的普及,如果新擴容的機器沒有帶上這些調優過的配置又會讓開發人員重蹈覆轍。

正是由于TCP的複雜性,無池化的資料庫程式在高并發的場景下也必然會面臨類似的這樣或那樣的問題。而資料庫連接配接池的出現,将複雜的問題簡單化,大大友善了程式員的開發運維工作,進而提升研發效能。接下來,讓我們了解一些資料庫連接配接池的背景、原理和理論。