自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

gRPC網(wǎng)關(guān)如何針對HTTP 2.0長連接進行性能優(yōu)化,提升吞吐量

網(wǎng)絡(luò) 通信技術(shù)
本文主要是解決gRPC的請求轉(zhuǎn)發(fā)問題,構(gòu)建一個網(wǎng)關(guān)系統(tǒng),技術(shù)選型OpenResty,既保留了Nginx的高性能又兼具了OpenResty動態(tài)易擴展。然后針對編寫的LUA代碼,性能壓測,不斷調(diào)整優(yōu)化,解決各個鏈路區(qū)間的TCP連接保證可重復(fù)使用。

最近要搞個網(wǎng)關(guān)GateWay,由于系統(tǒng)間請求調(diào)用是基于gRPC框架,所以網(wǎng)關(guān)第一職責(zé)就是能接收并轉(zhuǎn)發(fā)gRPC請求,大致的系統(tǒng)架構(gòu)如下所示:

簡單看下即可,由于含有定制化業(yè)務(wù)背景,架構(gòu)圖看不懂也沒關(guān)系,后面我會對里面的核心技術(shù)點單獨剖析講解。

為什么要引入網(wǎng)關(guān)?請求鏈路多了一跳,性能有損耗不說,一旦宕機就全部玩完了!

但現(xiàn)實就是這樣,不是你想怎么樣,就能怎么樣!

有時技術(shù)方案繞一個大圈子,就是為了解決一個無法避開的因素。這個因素可能是多方面:

  • 可能是技術(shù)上的需求,比如要做監(jiān)控統(tǒng)計,需要在上層某個位置加個攔截層,收集數(shù)據(jù),統(tǒng)一處理
  • 可能是技術(shù)實現(xiàn)遇到巨大挑戰(zhàn),至少是當(dāng)前技術(shù)團隊研發(fā)實力解決不了這個難題
  • 可能上下文會話關(guān)聯(lián),一個任務(wù)要觸發(fā)多次請求,但始終要在一臺機器上完成全部處理
  • 可能是政策因素,為了數(shù)據(jù)安全,你必須走這一繞。

本文引入的網(wǎng)關(guān)就是安全原因,由于一些公司的安全限制,外部服務(wù)無法直接訪問公司內(nèi)部的計算節(jié)點,需要引入一個前置網(wǎng)關(guān),負責(zé)反向代理、請求路由轉(zhuǎn)發(fā)、數(shù)據(jù)通信、調(diào)用監(jiān)控等。

一、問題抽象,技術(shù)選型

上面的業(yè)務(wù)架構(gòu)可能比較復(fù)雜,不了解業(yè)務(wù)背景同學(xué)很容易被繞暈。那么我們簡化一些,抽象出一個具體要解決的問題,簡化描述。

過程分為三步:

1、client端發(fā)起gPRC調(diào)用(基于HTTP2),請求打到gRPC網(wǎng)關(guān)

2、網(wǎng)關(guān)接到請求,根據(jù)請求約定的參數(shù)標(biāo)識,從Redis緩存里查詢目標(biāo)服務(wù)器的映射關(guān)系

3、最后,網(wǎng)關(guān)將請求轉(zhuǎn)發(fā)給目標(biāo)服務(wù)器,獲取響應(yīng)結(jié)果,將數(shù)據(jù)原路返回。

gRPC必須使用 HTTP/2 傳輸數(shù)據(jù),支持明文和TLS加密數(shù)據(jù),支持流數(shù)據(jù)的交互。充分利用 HTTP/2 連接的多路復(fù)用和流式特性。

技術(shù)選型

1、最早計劃采用Netty來做,但由于gRPC的proto模板不是我們定義的,所以解析成本很高,另外還要讀取請求Header中的數(shù)據(jù),開發(fā)難度較大,所以這個便作為了備選方案。

2、另一種改變思路,往反向代理框架方向?qū)ふ?,重新回到主流的Nginx這條線,但是nginx采用C語言開發(fā),如果是基于常規(guī)的負載均衡策略轉(zhuǎn)發(fā)請求,倒是沒什么大的問題。但是,我們內(nèi)部有依賴任務(wù)資源關(guān)系,也間接決定著要依賴外部的存儲系統(tǒng)。

Nginx適合處理靜態(tài)內(nèi)容,做一個靜態(tài)web服務(wù)器,但我們又看重其高性能,最后我們選型 Openresty

OpenResty? 是一個基于 Nginx 與 Lua 的高性能 Web 平臺,其內(nèi)部集成了大量精良的 Lua 庫、第三方模塊以及大多數(shù)的依賴項。用于方便地搭建能夠處理超高并發(fā)、擴展性極高的動態(tài) Web 應(yīng)用、Web 服務(wù)和動態(tài)網(wǎng)關(guān)。

二、Openresty 代碼 SHOW

  1. http { 
  2.     include       mime.types; 
  3.     default_type  application/octet-stream; 
  4.     access_log  logs/access.log  main; 
  5.     sendfile        on; 
  6.     keepalive_timeout  120
  7.     client_max_body_size 3000M
  8.     server { 
  9.         listen   8091   http2; 
  10.         location / { 
  11.             set $target_url  '' ; 
  12.             access_by_lua_block{ 
  13.                 local headers = ngx.req.get_headers(0
  14.                 local jobid= headers["jobid"
  15.                 local redis = require "resty.redis" 
  16.                 local red = redis:new() 
  17.                 red:set_timeouts(1000) -- 1 sec 
  18.                 local ok, err = red:connect("156.9.1.2"6379
  19.                 local res, err = red:get(jobid) 
  20.                 ngx.var.target_url = res 
  21.             } 
  22.             grpc_pass   grpc://$target_url; 
  23.         } 
  24.     } 

三、性能壓測

  • Client 端機器,壓測期間,觀察網(wǎng)絡(luò)連接:

結(jié)論:

并發(fā)壓測場景下,請求會轉(zhuǎn)發(fā)到三臺網(wǎng)關(guān)服務(wù)器,每臺服務(wù)器處于TIME_WAIT狀態(tài)的TCP連接并不多??梢姶硕芜B接基本能達到連接復(fù)用效果。

  • gRPC網(wǎng)關(guān)機器,壓測期間,觀察網(wǎng)絡(luò)連接情況:

有大量的請求連接處于TIME_WAIT狀態(tài)。按照端口號可以分為兩大類:6379和 40928

  1. [root@tf-gw-64bd9f775c-qvpcx nginx]#  netstat -na | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 
  2. LISTEN 2 
  3. ESTABLISHED 6 
  4. TIME_WAIT 27500 

通過linux shell 統(tǒng)計命令,172.16.66.46服務(wù)器有27500個TCP連接處于 TIME_WAIT

  1. [root@tf-gw-64bd9f775c-qvpcx nginx]#  netstat -na | grep 6379 |awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 
  2. ESTABLISHED 1 
  3. TIME_WAIT 13701 

其中,連接redis(redis的訪問端口 6379) 并處于 TIME_WAIT 狀態(tài)有 13701 個連接

  1. [root@tf-gw-64bd9f775c-qvpcx nginx]#  netstat -na | grep 40928 |awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 
  2. ESTABLISHED 2 
  3. TIME_WAIT 13671 

其中,連接后端Server目標(biāo)服務(wù)器 并處于 TIME_WAIT 狀態(tài)有 13671 個連接。兩者的連接數(shù)基本相等,因為每一次轉(zhuǎn)發(fā)請求都要查詢一次Redis。

結(jié)論匯總:

  • client端發(fā)送請求到網(wǎng)關(guān),目前已經(jīng)維持長連接,滿足要求。
  • gRPC網(wǎng)關(guān)連接Redis緩存服務(wù)器,目前是短連接,每次請求都去創(chuàng)建一個連接,性能開銷太大。需要單獨優(yōu)化
  • gRPC網(wǎng)關(guān)轉(zhuǎn)發(fā)請求到目標(biāo)服務(wù)器,目前也是短連接,用完即廢棄,完全沒有發(fā)揮Http2.0的長連接優(yōu)勢。需要單獨優(yōu)化

四、什么是 TIME_WAIT

統(tǒng)計服務(wù)器tcp連接狀態(tài)處于TIME_WAIT的命令腳本:

  1. netstat -anpt | grep TIME_WAIT | wc -l 

我們都知道TCP是三次握手,四次揮手。那揮手具體過程是什么?

1、主動關(guān)閉連接的一方,調(diào)用close(),協(xié)議層發(fā)送FIN包,主動關(guān)閉方進入FIN_WAIT_1狀態(tài)

2、被動關(guān)閉的一方收到FIN包后,協(xié)議層回復(fù)ACK;然后被動關(guān)閉的一方,進入CLOSE_WAIT狀態(tài),主動關(guān)閉的一方等待對方關(guān)閉,則進入FIN_WAIT_2狀態(tài);此時,主動關(guān)閉的一方 等待 被動關(guān)閉一方的應(yīng)用程序,調(diào)用close()操作

3、被動關(guān)閉的一方在完成所有數(shù)據(jù)發(fā)送后,調(diào)用close()操作;此時,協(xié)議層發(fā)送FIN包給主動關(guān)閉的一方,等待對方的ACK,被動關(guān)閉的一方進入LAST_ACK狀態(tài);

4、主動關(guān)閉的一方收到FIN包,協(xié)議層回復(fù)ACK;此時,主動關(guān)閉連接的一方,進入TIME_WAIT狀態(tài);而被動關(guān)閉的一方,進入CLOSED狀態(tài)

5、等待 2MSL(Maximum Segment Lifetime, 報文最大生存時間),主動關(guān)閉的一方,結(jié)束TIME_WAIT,進入CLOSED狀態(tài)

2MSL到底有多長呢?這個不一定,1分鐘、2分鐘或者4分鐘,還有的30秒。不同的發(fā)行版可能會不同。在Centos 7.6.1810 的3.10內(nèi)核版本上是60秒。

來張TCP狀態(tài)機大圖,一目了然:

為什么一定要有 TIME_WAIT ?

雖然雙方都同意關(guān)閉連接了,而且握手的4個報文也都協(xié)調(diào)和發(fā)送完畢,按理可以直接到CLOSED狀態(tài)。但是網(wǎng)絡(luò)是不可靠的,發(fā)起方無法確保最后發(fā)送的ACK報文一定被對方收到,比如丟包或延遲到達,對方處于LAST_ACK狀態(tài)下的SOCKET可能會因為超時未收到ACK報文,而重發(fā)FIN報文。所以TIME_WAIT狀態(tài)的作用就是用來重發(fā)可能丟失的ACK報文。

簡單講,TIME_WAIT之所以等待2MSL的時長,是為了避免因為網(wǎng)絡(luò)丟包或者網(wǎng)絡(luò)延遲而造成的tcp傳輸不可靠,而這個TIME_WAIT狀態(tài)則可以最大限度的提升網(wǎng)絡(luò)傳輸?shù)目煽啃浴?/p>

注意:一個連接沒有進入 CLOSED 狀態(tài)之前,這個連接是不能被重用的!

如何優(yōu)化 TIME_WAIT 過多的問題

1、調(diào)整系統(tǒng)內(nèi)核參數(shù)

  1. net.ipv4.tcp_syncookies = 1 表示開啟SYN Cookies。當(dāng)出現(xiàn)SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關(guān)閉; 
  2. net.ipv4.tcp_tw_reuse = 1 表示開啟重用。允許將 TIME-WAIT sockets重新用于新的TCP連接,默認為0,表示關(guān)閉; 
  3. net.ipv4.tcp_tw_recycle = 1 表示開啟TCP連接中 TIME-WAIT sockets的快速回收,默認為0,表示關(guān)閉。 
  4. net.ipv4.tcp_fin_timeout =  修改系統(tǒng)默認的 TIMEOUT 時間 
  5. net.ipv4.tcp_max_tw_buckets = 5000 表示系統(tǒng)同時保持TIME_WAIT套接字的最大數(shù)量,(默認是18000). 當(dāng)TIME_WAIT連接數(shù)量達到給定的值時,所有的TIME_WAIT連接會被立刻清除,并打印警告信息。但這種粗暴的清理掉所有的連接,意味著有些連接并沒有成功等待2MSL,就會造成通訊異常。一般不建議調(diào)整 
  6. net.ipv4.tcp_timestamps = 1(默認即為1)60s內(nèi)同一源ip主機的socket connect請求中的timestamp必須是遞增的。也就是說服務(wù)器打開了 tcp_tw_reccycle了,就會檢查時間戳,如果對方發(fā)來的包的時間戳是亂跳的或者說時間戳是滯后的,那么服務(wù)器就會丟掉不回包,現(xiàn)在很多公司都用LVS做負載均衡,通常是前面一臺LVS,后面多臺后端服務(wù)器,這其實就是NAT,當(dāng)請求到達LVS后,它修改地址數(shù)據(jù)后便轉(zhuǎn)發(fā)給后端服務(wù)器,但不會修改時間戳數(shù)據(jù),對于后端服務(wù)器來說,請求的源地址就是LVS的地址,加上端口會復(fù)用,所以從后端服務(wù)器的角度看,原本不同客戶端的請求經(jīng)過LVS的轉(zhuǎn)發(fā),就可能會被認為是同一個連接,加之不同客戶端的時間可能不一致,所以就會出現(xiàn)時間戳錯亂的現(xiàn)象,于是后面的數(shù)據(jù)包就被丟棄了,具體的表現(xiàn)通常是是客戶端明明發(fā)送的SYN,但服務(wù)端就是不響應(yīng)ACK,還可以通過下面命令來確認數(shù)據(jù)包不斷被丟棄的現(xiàn)象,所以根據(jù)情況使用 
  7.  
  8. 其他優(yōu)化: 
  9. net.ipv4.ip_local_port_range = 1024 65535 ,增加可用端口范圍,讓系統(tǒng)擁有的更多的端口來建立鏈接,這里有個問題需要注意,對于這個設(shè)置系統(tǒng)就會從1025~65535這個范圍內(nèi)隨機分配端口來用于連接,如果我們服務(wù)的使用端口比如8080剛好在這個范圍之內(nèi),在升級服務(wù)期間,可能會出現(xiàn)8080端口被其他隨機分配的鏈接給占用掉 
  10. net.ipv4.ip_local_reserved_ports = 7005,8001-8100 針對上面的問題,我們可以設(shè)置這個參數(shù)來告訴系統(tǒng)給我們預(yù)留哪些端口,不可以用于自動分配。 

2、將短連接優(yōu)化為長連接

短連接工作模式:連接->傳輸數(shù)據(jù)->關(guān)閉連接

長連接工作模式:連接->傳輸數(shù)據(jù)->保持連接 -> 傳輸數(shù)據(jù)-> 。。。->關(guān)閉連接

五、訪問 Redis 短連接優(yōu)化

高并發(fā)編程中,必須要使用連接池技術(shù),把短鏈接改成長連接。也就是改成創(chuàng)建連接、收發(fā)數(shù)據(jù)、收發(fā)數(shù)據(jù)... 拆除連接,這樣我們就可以減少大量創(chuàng)建連接、拆除連接的時間。從性能上來說肯定要比短連接好很多

在 OpenResty 中,可以設(shè)置set_keepalive 函數(shù),來支持長連接。

set_keepalive 函數(shù)有兩個參數(shù):

第一個參數(shù):連接的最大空閑時間

第二個參數(shù):連接池大小

  1. local res, err = red:get(jobid) 
  2. // redis操作完后,將連接放回到連接池中 
  3. // 連接池大小設(shè)置成40,連接最大空閑時間設(shè)置成10秒 
  4. red:set_keepalive(10000, 40) 

reload nginx配置后,重新壓測。

結(jié)論:redis的連接數(shù)基本控制在40個以內(nèi)。

其他的參數(shù)設(shè)置可以參考:

https://github.com/openresty/lua-resty-redis#set_keepalive

六、訪問目標(biāo) Server 機器短連接優(yōu)化

nginx 提供了一個upstream模塊,用來控制負載均衡、內(nèi)容分發(fā)。提供了以下幾種負載算法:

  • 輪詢(默認)。每個請求按時間順序逐一分配到不同的后端服務(wù)器,如果后端服務(wù)器down掉,能自動剔除。
  • weight(權(quán)重)。指定輪詢幾率,weight和訪問比率成正比,用于后端服務(wù)器性能不均的情況。
  • ip_hash。每個請求按訪問ip的hash結(jié)果分配,這樣每個訪客固定訪問一個后端服務(wù)器,可以解決session的問題。
  • fair(第三方)。按后端服務(wù)器的響應(yīng)時間來分配請求,響應(yīng)時間短的優(yōu)先分配。
  • url_hash(第三方)。按訪問url的hash結(jié)果來分配請求,使每個url定向到同一個后端服務(wù)器,后端服務(wù)器為緩存時比較有效。

由于 upstream提供了keepalive函數(shù),每個工作進程的高速緩存中保留的到上游服務(wù)器的空閑保持連接的最大數(shù)量,可以保持連接復(fù)用,從而減少TCP連接頻繁的創(chuàng)建、銷毀性能開銷。

缺點:

Nginx官方的upstream不支持動態(tài)修改,而我們的目標(biāo)地址是動態(tài)變化,請求時根據(jù)業(yè)務(wù)規(guī)則動態(tài)實時查詢路由。為了解決這個動態(tài)性問題,我們引入OpenResty的balancer_by_lua_block。

通過編寫Lua腳本方式,來擴展upstream功能。

修改nginx.conf的upstream,動態(tài)獲取路由目標(biāo)的IP和Port,并完成請求的轉(zhuǎn)發(fā),核心代碼如下:

  1. upstream grpcservers { 
  2.    balancer_by_lua_block{ 
  3.      local balancer = require "ngx.balancer" 
  4.      local host = ngx.var.target_ip 
  5.      local port = ngx.var.target_port 
  6.      local ok, err = balancer.set_current_peer(host, port) 
  7.      if not ok then 
  8.         ngx.log(ngx.ERR, "failed to set the current peer: ", err) 
  9.         return ngx.exit(500) 
  10.      end 
  11.    } 
  12.    keepalive 40; 

修改配置后,重啟Nginx,繼續(xù)壓測,觀察結(jié)果:

TCP連接基本都處于ESTABLISHED狀態(tài),優(yōu)化前的TIME_WAIT狀態(tài)幾乎沒有了。

  1. [root@tf-gw-64bd9f775c-qvpcx nginx]#  netstat -na | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 
  2. LISTEN 2 
  3. ESTABLISHED 86 
  4. TIME_WAIT 242 

寫在最后

本文主要是解決gRPC的請求轉(zhuǎn)發(fā)問題,構(gòu)建一個網(wǎng)關(guān)系統(tǒng),技術(shù)選型OpenResty,既保留了Nginx的高性能又兼具了OpenResty動態(tài)易擴展。然后針對編寫的LUA代碼,性能壓測,不斷調(diào)整優(yōu)化,解決各個鏈路區(qū)間的TCP連接保證可重復(fù)使用。

 

責(zé)任編輯:武曉燕 來源: 微觀技術(shù)
相關(guān)推薦

2024-05-23 16:41:40

2019-08-20 00:20:47

TCPHOL吞吐量

2024-06-28 09:39:58

2024-06-06 16:15:00

2010-04-14 16:02:09

IDF

2013-04-19 09:45:20

AMPLabHadoopHDFS

2021-12-26 00:03:27

響應(yīng)式編程異步

2024-12-13 13:58:53

2025-03-04 08:52:21

2023-08-03 14:18:29

Rust阻塞函數(shù)

2023-11-07 15:11:46

Kafka技巧

2023-02-09 08:57:11

Callable異步java

2024-11-02 10:28:03

2019-09-25 08:37:48

MySQL數(shù)據(jù)庫人生第一份工作

2019-09-29 15:36:01

吞吐量MySQL數(shù)據(jù)庫

2009-02-24 09:28:00

2013-10-11 11:22:14

GraphDBLinux內(nèi)存管理數(shù)據(jù)庫

2024-07-08 08:00:00

2022-05-26 15:17:54

訓(xùn)練模型

2021-03-24 09:06:01

MySQL長連接短連接
點贊
收藏

51CTO技術(shù)棧公眾號