欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Go實現(xiàn)TCP長連接上的請求數(shù)控制

 更新時間:2024年05月31日 09:04:31   作者:二郎腿  
在服務(wù)端開啟長連接的情況下,四層負(fù)載均衡轉(zhuǎn)發(fā)請求時,會出現(xiàn)服務(wù)端收到的請求qps不均勻的情況或是服務(wù)器無法接受到請求,因此需要服務(wù)端定期主動斷開一些長連接,所以本文給大家介紹了基于Go實現(xiàn)TCP長連接上的請求數(shù)控制,需要的朋友可以參考下

一、背景

  • 在服務(wù)端開啟長連接的情況下,四層負(fù)載均衡轉(zhuǎn)發(fā)請求時,會出現(xiàn)服務(wù)端收到的請求qps不均勻的情況,或是服務(wù)重啟后會長時間無法接受到請求,導(dǎo)致不同服務(wù)端機(jī)器的負(fù)載不一致,qps高的機(jī)器過載的問題;
  • 該問題的原因是只有在新建連接時才會觸發(fā)負(fù)載四層負(fù)載均衡器的再均衡策略,客戶端隨機(jī)與不同的服務(wù)器新建 TCP 連接,否則現(xiàn)有的 TCP 連接夠用時,會一致被復(fù)用,在現(xiàn)有的 TCP 連接上傳輸請求,出現(xiàn) qps 不均勻的情況;
  • 因此需要服務(wù)端定期主動斷開一些長連接,觸發(fā)四層轉(zhuǎn)發(fā)連接的再均衡策略,實現(xiàn)類似于七層負(fù)載均衡 Nginx 中的 keepalive_requests 字段的功能,即同一個 TCP 長連接上的請求數(shù)達(dá)到一定數(shù)量時,服務(wù)端主動斷開 TCP 長連接。

二、基本介紹

1,TCP 的 Keepalive:

  • 即 TCP 保活機(jī)制,是由 TCP 層(內(nèi)核態(tài)) 實現(xiàn)的,位于傳輸層,相關(guān)配置參數(shù)在 /proc/sys/net/ipv4目錄下:
    • tcp_keepalive_intvl:?;钐綔y報文發(fā)送時間間隔,75s;
    • tcp_keepalive_probes:?;钐綔y報文發(fā)送次數(shù),9次,9次之后直接關(guān)閉;
    • tcp_keepalive_time:?;畛瑫r時間,7200s,即該 TCP 連接空閑兩小時后開始發(fā)送?;钐綔y報文;
  • TCP 連接傳輸完數(shù)據(jù)后,不會立馬主動關(guān)閉,會先存活一段時間,超過存活時間后,會觸發(fā)?;顧C(jī)制發(fā)送探測報文,多次探測確認(rèn)沒有數(shù)據(jù)繼續(xù)傳輸后,再進(jìn)行 TCP 四次揮手,關(guān)閉 TCP 連接;
  • 在 go 語言中,建立 TCP 連接時,默認(rèn)設(shè)置的 keep-alive 為 15s,詳見 go1.21 src/net/tcp/tcpsocket.go

2,HTTP 的 Keep-Alive

  • 即 HTTP 長連接,是由應(yīng)用層(用戶態(tài)) 實現(xiàn)的,位于應(yīng)用層;

  • 需要在 HTTP 報文的頭部設(shè)置以下信息:

* Connection: keep-alive
* Keep-Alive: timeout=7200
  • 上面信息表示,http 采用長連接,且超時時間為7200s;

  • http 協(xié)議 1.0 默認(rèn)采用短連接,即每次發(fā)送完數(shù)據(jù)后會設(shè)置 Connection: close 表示需要主動關(guān)閉當(dāng)前 TCP 連接,進(jìn)行四次揮手后關(guān)閉;下次再發(fā)送數(shù)據(jù)前,又需要先進(jìn)行三次握手建立 TCP 連接,才能發(fā)送數(shù)據(jù);循環(huán)往復(fù),每次建立的 TCP 連接都只能發(fā)送一次數(shù)據(jù),每次發(fā)送數(shù)據(jù)都需要進(jìn)行三次握手與四次揮手,每次建立連接與斷開連接會導(dǎo)致網(wǎng)絡(luò)耗時變長,如下圖;

  • http 協(xié)議 1.1 開始默認(rèn)采用長連接;即每次發(fā)送完數(shù)據(jù)后會設(shè)置 Connection: keep-alive 表示需要復(fù)用當(dāng)前 TCP 連接,建立一次 TCP 連接后,可以發(fā)送多次的 HTTP 報文,即多次發(fā)送數(shù)據(jù)也只需要一遍三次握手與四次揮手,省去了每次建立連接與斷開連接的時間,如下圖:

3,四層負(fù)載均衡

  • 四層負(fù)載均衡是一種在網(wǎng)絡(luò)層(第四層)上進(jìn)行負(fù)載均衡的技術(shù),通過傳輸層協(xié)議 TCP 或 UDP,將傳入的請求分發(fā)到多個服務(wù)器上,以實現(xiàn)請求的負(fù)載均衡和高可用性;
  • 四層負(fù)載均衡主要基于目標(biāo)IP地址和端口號對請求進(jìn)行分發(fā),不深入分析請求的內(nèi)容和應(yīng)用層協(xié)議,通常使用負(fù)載均衡器作為中間設(shè)備,接收客戶端請求,并將請求轉(zhuǎn)發(fā)到后端服務(wù)器;
  • 負(fù)載均衡器可以根據(jù)預(yù)定義的算法(例如輪詢、最小連接數(shù)、哈希、隨機(jī)、加權(quán)隨機(jī)等)選擇后端服務(wù)器來處理請求;
  • 四層負(fù)載均衡只需要解析到傳輸層協(xié)議即可進(jìn)行請求轉(zhuǎn)發(fā),且是直接和真實服務(wù)器建立 TCP 連接,所以整體耗時比較??;

4,七層負(fù)載均衡

  • Nginx 可以用于七層負(fù)載均衡器,客戶端與 Nginx 所在的服務(wù)器建立起 TCP 連接,通過解析應(yīng)用層中的內(nèi)容,選擇對應(yīng)的后端服務(wù)器,Nginx 所在的機(jī)器再與后端服務(wù)器建立起 TCP 連接,將應(yīng)用層數(shù)據(jù)轉(zhuǎn)發(fā)后端服務(wù)器上,這就是所謂的七層負(fù)載均衡,即根據(jù)應(yīng)用層的信息進(jìn)行轉(zhuǎn)發(fā);
  • 在 Nginx 中,keepalive_requests 指令用于設(shè)置在長連接上可以處理的最大請求數(shù)量,一旦達(dá)到這個數(shù)量,Nginx 將關(guān)閉當(dāng)前連接并等待客戶端建立新的連接以繼續(xù)處理請求;
  • 通過限制每個持久連接上處理的請求數(shù)量,keepalive_requests 可以幫助控制服務(wù)器資源的使用,并防止連接過度占用服務(wù)器資源,也可以幫助避免潛在的連接泄漏和提高服務(wù)器的性能;
  • 該值設(shè)置得過小,會導(dǎo)致經(jīng)常需要 TCP 三次握手和四次揮手,無法有效發(fā)揮長連接的性能;該值設(shè)置得過大,會無法發(fā)揮該值的作用,導(dǎo)致長連接上的請求過多;具體的大小,要根據(jù)實際請求的 QPS 和響應(yīng)耗時來設(shè)置;
  • 七層負(fù)載均衡能夠根據(jù)應(yīng)用層的請求內(nèi)容實現(xiàn)更驚喜的請求分發(fā)和處理,但是需要建立兩次 TCP 連接,以及每次將報文逐步解析到應(yīng)用層再又逐步封裝鏈路層,會導(dǎo)致耗時和失敗率上漲;
  • 四層負(fù)載均衡與七層負(fù)載均衡的比較如下:

三、具體實現(xiàn)

1,代碼示例

package main

import (
	"sync"
	"time"

	"github.com/labstack/echo"
)

type QpsBalance struct {
	mu   sync.Mutex
	data map[string]int // key: ip:port
	num  int            // 通過配置文件來配置
}

// Update 返回 true 表示當(dāng)前的 tcp 連接上的請求數(shù)超過限制,需要斷開連接
// 如果是某個 tcp 連接長時間沒有后續(xù)請求了,默認(rèn) 15s 之后會發(fā)送?;顖笪?,
func (q *QpsBalance) Update(k string) bool {
	q.mu.Lock()
	defer q.mu.Unlock()

	num := q.data[k] + 1
	if num >= q.num {
		q.data[k] = 0
		return true
	}
	q.data[k] = num
	return false
}

// Reset 通過定時任務(wù)每天3點重置,避免上游多次不同的擴(kuò)容ip形成臟數(shù)據(jù)
func (q *QpsBalance) Reset() {
	q.mu.Lock()
	defer q.mu.Unlock()

	q.data = make(map[string]int)
}

func (q *QpsBalance) Init(n int) {
	q.mu.Lock()
	defer q.mu.Unlock()

	q.data = make(map[string]int)
	q.num = n
}

func main() {
	balancer := &QpsBalance{}
	balancer.Init(200)
	e := echo.New()
	e.PUT("/handle", func(c echo.Context) error {
		if balancer.Update(c.Request().RemoteAddr) {
			c.Response().Header().Set("Connection", "close")
		}
		// do other
		return nil
	})

	go func(b *QpsBalance) {
		ticker := time.NewTicker(time.Hour)
		for {
			t := <-ticker.C
			if t.Hour() == 3 {
				b.Reset()
			}
		}
	}(balancer)
}

2,基本原理

  • 當(dāng)同一個 TCP 連接上的請求數(shù)達(dá)到一定限制時,設(shè)置返回頭部為 Connection: close,主動關(guān)閉 TCP 連接,并重置計數(shù)器;
  • 需要注意的是,客戶端如果也是服務(wù)器,并且存在自動擴(kuò)容,那么需要定期清理計數(shù)的 map,避免多次不同的擴(kuò)容ip形成臟數(shù)據(jù);
  • 以及某些 TCP 連接可能沒有達(dá)到計數(shù)的閾值,便不再被復(fù)用了,經(jīng)過一段時間后會主動斷開,這些 TCP 的計數(shù)依然存在 map 中,形成了臟數(shù)據(jù);
  • 在 TCP 連接中,使用四元組來標(biāo)記的一個唯一的 TCP 連接,即源ip、源端口、目的ip、目的端口,現(xiàn)在是在服務(wù)端進(jìn)行計數(shù)的,所以目的ip和目的端口都是一樣的,僅僅通過源ip和源端口便可以分別出 TCP 連接;

四、數(shù)據(jù)驗證

  • 通過服務(wù)記錄的監(jiān)控,查看上游請求過來的平均耗時、P99 耗時、失敗率是否有明顯的變化,以及不同服務(wù)器收到的請求 QPS 是否均勻;
  • 通過 tcpdump src port 28080 -A | grep Connection 命令查看服務(wù)端響應(yīng)的HTTP報文頭部是否有 Connection: close 字段,即是否會主動關(guān)閉 TCP 連接;
  • 通過 netstat -antp | grep main | grep :28080 命令查看不同服務(wù)器上的服務(wù)建立的長連接數(shù)是否均勻;
  • 選取某個長連接,多次查看其存在的時間,是否符合預(yù)期;
    • 預(yù)期時間:假如 keepalive_requests 設(shè)置為 100,客戶端記錄的響應(yīng)耗時 20ms(包括網(wǎng)絡(luò)耗時和服務(wù)端耗時),那么平均一個長連接一秒能夠發(fā)送 5 個請求,約 20s 后能夠處理 100 個,那么該長連接能夠存活 20s;
    • 注意不能查看某個長連接對應(yīng)的 socket 創(chuàng)建的時間,因為同一個 socket 會被不同的長連接復(fù)用,一般不會被關(guān)閉;

以上就是基于Go實現(xiàn)TCP長連接上的請求數(shù)控制的詳細(xì)內(nèi)容,更多關(guān)于Go TCP請求數(shù)控制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語言開發(fā)中有了net/http為什么還要有g(shù)in的原理及使用場景解析

    Go語言開發(fā)中有了net/http為什么還要有g(shù)in的原理及使用場景解析

    這篇文章主要為大家介紹了Go語言有了net/http標(biāo)準(zhǔn)庫為什么還要有g(shù)in第三方庫的原理及使用場景詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • golang的時區(qū)和神奇的time.Parse的使用方法

    golang的時區(qū)和神奇的time.Parse的使用方法

    這篇文章主要介紹了golang的時區(qū)和神奇的time.Parse的使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Golang中Gin框架中如何定義路由詳解

    Golang中Gin框架中如何定義路由詳解

    Gin是一個用Go語言編寫的Web框架,具有高性能和易于使用的特點,本文將結(jié)合實際案例,詳細(xì)介紹Gin框架的路由用法,有需要的小伙伴可以參考下
    2024-10-10
  • Go 語言數(shù)組和切片的區(qū)別詳解

    Go 語言數(shù)組和切片的區(qū)別詳解

    本文主要介紹了Go 語言數(shù)組和切片的區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Go 實現(xiàn)熱重啟的詳細(xì)介紹

    Go 實現(xiàn)熱重啟的詳細(xì)介紹

    這篇文章主要介紹了Go 實現(xiàn)熱重啟的詳細(xì)介紹,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Go語言實現(xiàn)并發(fā)控制的常見方式詳解

    Go語言實現(xiàn)并發(fā)控制的常見方式詳解

    這篇文章主要為大家詳細(xì)介紹了Go語言實現(xiàn)并發(fā)控制的幾種常見方式,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以參考一下
    2024-03-03
  • 基于Go語言實現(xiàn)高性能文件上傳下載系統(tǒng)

    基于Go語言實現(xiàn)高性能文件上傳下載系統(tǒng)

    在Web應(yīng)用開發(fā)中,文件上傳下載是一個非常常見的需求,本文將介紹如何使用Go語言實現(xiàn)一個安全、高效的本地文件存儲系統(tǒng),感興趣的小伙伴可以了解下
    2025-03-03
  • 詳解Go多協(xié)程并發(fā)環(huán)境下的錯誤處理

    詳解Go多協(xié)程并發(fā)環(huán)境下的錯誤處理

    這篇文章主要介紹了詳解Go多協(xié)程并發(fā)環(huán)境下的錯誤處理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Go語言中序列化與反序列化示例詳解

    Go語言中序列化與反序列化示例詳解

    我們的數(shù)據(jù)對象要在網(wǎng)絡(luò)中傳輸或保存到文件,就需要對其編碼和解碼動作,Go語言當(dāng)然也支持所有這些編碼格式,下面這篇文章主要給大家介紹了關(guān)于Go語言中序列化與反序列化的相關(guān)資料,需要的朋友可以參考下
    2022-07-07
  • golang實現(xiàn)LRU緩存淘汰算法的示例代碼

    golang實現(xiàn)LRU緩存淘汰算法的示例代碼

    這篇文章主要介紹了golang實現(xiàn)LRU緩存淘汰算法的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12

最新評論