成人性生交大片免费看视频r_亚洲综合极品香蕉久久网_在线视频免费观看一区_亚洲精品亚洲人成人网在线播放_国产精品毛片av_久久久久国产精品www_亚洲国产一区二区三区在线播_日韩一区二区三区四区区区_亚洲精品国产无套在线观_国产免费www

主頁 > 知識(shí)庫 > Golang 實(shí)現(xiàn)Thrift客戶端連接池方式

Golang 實(shí)現(xiàn)Thrift客戶端連接池方式

熱門標(biāo)簽:仙桃400電話辦理 鄭州智能語音電銷機(jī)器人價(jià)格 重慶慶云企業(yè)400電話到哪申請(qǐng) 地圖標(biāo)注免費(fèi)定制店 不封卡外呼系統(tǒng) 宿遷便宜外呼系統(tǒng)代理商 上海極信防封電銷卡價(jià)格 寧波語音外呼系統(tǒng)公司 湛江crm外呼系統(tǒng)排名

1 前言

閱讀文章之前,請(qǐng)先了解一下thrift相關(guān)知識(shí)。thrift官方并沒有提供客戶端連接池的實(shí)現(xiàn)方案,而我們?cè)趯?shí)際使用時(shí),thrift客戶端必須復(fù)用,來保證較為可觀的吞吐量,并避免在高QPS調(diào)用情況下,不斷的創(chuàng)建、釋放客戶端所帶來的機(jī)器端口耗盡問題。

本文會(huì)詳細(xì)講解如何實(shí)現(xiàn)一個(gè)簡單可靠的thrift客戶端連接池,并通過對(duì)照實(shí)驗(yàn)來說明thrift客戶端連接池所帶來的好處。

由于篇幅的原因,本文只粘出關(guān)鍵代碼,源代碼請(qǐng)查看Thrift Client Pool Demo

1.1 運(yùn)行環(huán)境

Golang版本: go1.14.3 darwin/amd64

Thrift Golang庫版本: 0.13.0

Thrift IDL編輯器版本: 0.13.0

1.2 .thrift文件

namespace java com.czl.api.thrift.model
namespace cpp com.czl.api
namespace php com.czl.api
namespace py com.czl.api
namespace js com.czl.apixianz
namespace go com.czl.api
struct ApiRequest {
 1: required i16 id;
}
struct ApiResponse{
 1:required string name;
}
// service1
service ApiService1{
 ApiResponse query(1:ApiRequest request)
}
// service2
service ApiService2{
 ApiResponse query(1:ApiRequest request)
}

注:請(qǐng)通過安裝Thrift IDL編譯器,并生成客戶端、服務(wù)端代碼。

1.3 對(duì)照實(shí)驗(yàn)說明

通過腳本開啟100個(gè)協(xié)程并發(fā)調(diào)用rpc服務(wù)10分鐘,統(tǒng)計(jì)這段時(shí)間內(nèi),未使用thrift客戶端連接池與使用客戶端連接池服務(wù)的平均吞吐量、Thrift API調(diào)用平均延遲、機(jī)器端口消耗等數(shù)據(jù)進(jìn)行性能對(duì)比。

實(shí)驗(yàn)一: 未使用thrift客戶端連接池

實(shí)驗(yàn)二: 使用thrift客戶端連接池

2 Thrift客戶端連接池實(shí)現(xiàn)

2.1 連接池的功能

首先,我們要明確一下連接池的職責(zé),這里我簡單的總結(jié)一下,連接池主要功能是維護(hù)連接的創(chuàng)建、釋放,通過緩存連接來復(fù)用連接,減少創(chuàng)建連接所帶來的開銷,提高系統(tǒng)的吞吐量,一般連接池還會(huì)有連接斷開的重連機(jī)制、超時(shí)機(jī)制等。這里我們可以先定義出大部分連接池都會(huì)有的功能,只是定義,可以先不管每個(gè)功能的具體實(shí)現(xiàn)。每一個(gè)空閑Thrift客戶端其實(shí)底層都維護(hù)著一條空閑TCP連接,空閑Thrift客戶端與空閑連接在這里其實(shí)是同一個(gè)概念。

......
// Thrift客戶端創(chuàng)建方法,留給業(yè)務(wù)去實(shí)現(xiàn)
type ThriftDial func(addr string, connTimeout time.Duration) (*IdleClient, error)
// 關(guān)閉Thrift客戶端,留給業(yè)務(wù)實(shí)現(xiàn)
type ThriftClientClose func(c *IdleClient) error
// Thrift客戶端連接池
type ThriftPool struct {
 // Thrift客戶端創(chuàng)建邏輯,業(yè)務(wù)自己實(shí)現(xiàn)
 Dial ThriftDial
 // Thrift客戶端關(guān)閉邏輯,業(yè)務(wù)自己實(shí)現(xiàn)
 Close ThriftClientClose
 // 空閑客戶端,用雙端隊(duì)列存儲(chǔ)
 idle list.List
 // 同步鎖,確保count、status、idle等公共數(shù)據(jù)并發(fā)操作安全
 lock *sync.Mutex
 // 記錄當(dāng)前已經(jīng)創(chuàng)建的Thrift客戶端,確保MaxConn配置
 count int32
 // Thrift客戶端連接池狀態(tài),目前就open和stop兩種
 status uint32
 // Thrift客戶端連接池相關(guān)配置
 config *ThriftPoolConfig
}
// 連接池配置
type ThriftPoolConfig struct {
 // Thrfit Server端地址
 Addr string
 // 最大連接數(shù)
 MaxConn int32
 // 創(chuàng)建連接超時(shí)時(shí)間
 ConnTimeout time.Duration
 // 空閑客戶端超時(shí)時(shí)間,超時(shí)主動(dòng)釋放連接,關(guān)閉客戶端
 IdleTimeout time.Duration
 // 獲取Thrift客戶端超時(shí)時(shí)間
 Timeout time.Duration
 // 獲取Thrift客戶端失敗重試間隔
 interval time.Duration
}
// Thrift客戶端
type IdleClient struct {
 // Thrift傳輸層,封裝了底層連接建立、維護(hù)、關(guān)閉、數(shù)據(jù)讀寫等細(xì)節(jié)
 Transport thrift.TTransport
 // 真正的Thrift客戶端,業(yè)務(wù)創(chuàng)建傳入
 RawClient interface{}
}
// 封裝了Thrift客戶端
type idleConn struct {
 // 空閑Thrift客戶端
 c *IdleClient
 // 最近一次放入空閑隊(duì)列的時(shí)間
 t time.Time
}
// 獲取Thrift空閑客戶端
func (p *ThriftPool) Get() (*IdleClient, error) {
 // 1. 從空閑池中獲取空閑客戶端,獲取到更新數(shù)據(jù),返回,否則執(zhí)行第2步
 // 2. 創(chuàng)建新到Thrift客戶端,更新數(shù)據(jù),返回Thrift客戶端
 ......
}
// 歸還Thrift客戶端
func (p *ThriftPool) Put(client *IdleCLient) error {
 // 1. 如果客戶端已經(jīng)斷開,更新數(shù)據(jù),返回,否則執(zhí)行第2步
 // 2. 將Thrift客戶端丟進(jìn)空閑連接池,更新數(shù)據(jù),返回
 ......
}
// 超時(shí)管理,定期釋放空閑太久的連接
func (p *ThriftPool) CheckTimeout() {
 // 掃描空閑連接池,將空閑太久的連接主動(dòng)釋放掉,并更新數(shù)據(jù)
 ......
}
// 異常連接重連
func (p *ThriftPool) Reconnect(client *IdleClient) (newClient *IdleClient, err error) {
 // 1. 關(guān)閉舊客戶端
 // 2. 創(chuàng)建新的客戶端,并返回
 ......
}
// 其他方法
......

這里有兩個(gè)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu),ThriftPool和IdleClient,ThriftPool負(fù)責(zé)實(shí)現(xiàn)整個(gè)連接池的功能,IdleClient封裝了真正的Thrift客戶端。

先看一下ThriftPool的定義:

// Thrift客戶端創(chuàng)建方法,留給業(yè)務(wù)去實(shí)現(xiàn)
type ThriftDial func(addr string, connTimeout time.Duration) (*IdleClient, error)
// 關(guān)閉Thrift客戶端,留給業(yè)務(wù)實(shí)現(xiàn)
type ThriftClientClose func(c *IdleClient) error
// Thrift客戶端連接池
type ThriftPool struct {
 // Thrift客戶端創(chuàng)建邏輯,業(yè)務(wù)自己實(shí)現(xiàn)
 Dial ThriftDial
 // Thrift客戶端關(guān)閉邏輯,業(yè)務(wù)自己實(shí)現(xiàn)
 Close ThriftClientClose
 // 空閑客戶端,用雙端隊(duì)列存儲(chǔ)
 idle list.List
 // 同步鎖,確保count、status、idle等公共數(shù)據(jù)并發(fā)操作安全
 lock *sync.Mutex
 // 記錄當(dāng)前已經(jīng)創(chuàng)建的Thrift客戶端,確保MaxConn配置
 count int32
 // Thrift客戶端連接池狀態(tài),目前就open和stop兩種
 status uint32
 // Thrift客戶端連接池相關(guān)配置
 config *ThriftPoolConfig
}
// 連接池配置
type ThriftPoolConfig struct {
 // Thrfit Server端地址
 Addr string
 // 最大連接數(shù)
 MaxConn int32
 // 創(chuàng)建連接超時(shí)時(shí)間
 ConnTimeout time.Duration
 // 空閑客戶端超時(shí)時(shí)間,超時(shí)主動(dòng)釋放連接,關(guān)閉客戶端
 IdleTimeout time.Duration
 // 獲取Thrift客戶端超時(shí)時(shí)間
 Timeout time.Duration
 // 獲取Thrift客戶端失敗重試間隔
 interval time.Duration
}

Thrift客戶端創(chuàng)建與關(guān)閉,涉及到業(yè)務(wù)細(xì)節(jié),這里抽離成Dial方法和Close方法。

連接池需要維護(hù)空閑客戶端,這里用雙端隊(duì)列來存儲(chǔ)。

一般的連接池,都應(yīng)該支持最大連接數(shù)配置,MaxConn可以配置連接池最大連接數(shù),同時(shí)我們用count來記錄連接池當(dāng)前已經(jīng)創(chuàng)建的連接。

為了實(shí)現(xiàn)連接池的超時(shí)管理,當(dāng)然也得有相關(guān)超時(shí)配置。

連接池的狀態(tài)、當(dāng)前連接數(shù)等這些屬性,是多協(xié)程并發(fā)操作的,這里用同步鎖lock來確保并發(fā)操作安全。

在看一下IdleClient實(shí)現(xiàn):

// Thrift客戶端
type IdleClient struct {
 // Thrift傳輸層,封裝了底層連接建立、維護(hù)、關(guān)閉、數(shù)據(jù)讀寫等細(xì)節(jié)
 Transport thrift.TTransport
 // 真正的Thrift客戶端,業(yè)務(wù)創(chuàng)建傳入
 RawClient interface{}
}
// 封裝了Thrift客戶端
type idleConn struct {
 // 空閑Thrift客戶端
 c *IdleClient
 // 最近一次放入空閑隊(duì)列的時(shí)間
 t time.Time
}

RawClient是真正的Thrift客戶端,與實(shí)際邏輯相關(guān)。

Transport Thrift傳輸層,Thrift傳輸層,封裝了底層連接建立、維護(hù)、關(guān)閉、數(shù)據(jù)讀寫等細(xì)節(jié)。

idleConn封裝了IdleClient,用來實(shí)現(xiàn)空閑連接超時(shí)管理,idleConn記錄一個(gè)時(shí)間,這個(gè)時(shí)間是Thrift客戶端最近一次被放入空閑隊(duì)列的時(shí)間。

2.2 獲取連接

......
var nowFunc = time.Now
......
// 獲取Thrift空閑客戶端
func (p *ThriftPool) Get() (*IdleClient, error) {
 return p.get(nowFunc().Add(p.config.Timeout))
}
// 獲取連接的邏輯實(shí)現(xiàn)
// expire設(shè)定了一個(gè)超時(shí)時(shí)間點(diǎn),當(dāng)沒有可用連接時(shí),程序會(huì)休眠一小段時(shí)間后重試
// 如果一直獲取不到連接,一旦到達(dá)超時(shí)時(shí)間點(diǎn),則報(bào)ErrOverMax錯(cuò)誤
func (p *ThriftPool) get(expire time.Time) (*IdleClient, error) {
 if atomic.LoadUint32(p.status) == poolStop {
 return nil, ErrPoolClosed
 }
 // 判斷是否超額
 p.lock.Lock()
 if p.idle.Len() == 0  atomic.LoadInt32(p.count) >= p.config.MaxConn {
 p.lock.Unlock()
 // 不采用遞歸的方式來實(shí)現(xiàn)重試機(jī)制,防止棧溢出,這里改用循環(huán)方式來實(shí)現(xiàn)重試
 for {
 // 休眠一段時(shí)間再重試
 time.Sleep(p.config.interval)
 // 超時(shí)退出
 if nowFunc().After(expire) {
 return nil, ErrOverMax
 }
 p.lock.Lock()
 if p.idle.Len() == 0  atomic.LoadInt32(p.count) >= p.config.MaxConn {
 p.lock.Unlock()
 } else { // 有可用鏈接,退出for循環(huán)
 break
 }
 }
 }
 if p.idle.Len() == 0 {
 // 先加1,防止首次創(chuàng)建連接時(shí),TCP握手太久,導(dǎo)致p.count未能及時(shí)+1,而新的請(qǐng)求已經(jīng)到來
 // 從而導(dǎo)致短暫性實(shí)際連接數(shù)大于p.count(大部分鏈接由于無法進(jìn)入空閑鏈接隊(duì)列,而被關(guān)閉,處于TIME_WATI狀態(tài))
 atomic.AddInt32(p.count, 1)
 p.lock.Unlock()
 client, err := p.Dial(p.config.Addr, p.config.ConnTimeout)
 if err != nil {
 atomic.AddInt32(p.count, -1)
 return nil, err
 }
 // 檢查連接是否有效
 if !client.Check() {
 atomic.AddInt32(p.count, -1)
 return nil, ErrSocketDisconnect
 }
 return client, nil
 }
 // 從隊(duì)頭中獲取空閑連接
 ele := p.idle.Front()
 idlec := ele.Value.(*idleConn)
 p.idle.Remove(ele)
 p.lock.Unlock()
 // 連接從空閑隊(duì)列獲取,可能已經(jīng)關(guān)閉了,這里再重新檢查一遍
 if !idlec.c.Check() {
 atomic.AddInt32(p.count, -1)
 return nil, ErrSocketDisconnect
 }
 return idlec.c, nil
}

p.Get()的邏輯比較清晰:如果空閑隊(duì)列沒有連接,且當(dāng)前連接已經(jīng)到達(dá)p.config.MaxConn,就休眠等待重試;當(dāng)滿足獲取連接條件時(shí)p.idle.Len() != 0 || atomic.LoadInt32(p.count) p.config.MaxConn,有空閑連接,則返回空閑連接,減少創(chuàng)建連接的開銷,沒有的話,再重新創(chuàng)建一條新的連接。

這里有兩個(gè)關(guān)鍵的地方需要注意:

等待重試的邏輯,不要用遞歸的方式來實(shí)現(xiàn),防止運(yùn)行棧溢出。

// 遞歸的方法實(shí)現(xiàn)等待重試邏輯
func (p *ThriftPool) get(expire time.Time) (*IdleClient, error) {
 // 超時(shí)退出
 if nowFunc().After(expire) {
 return nil, ErrOverMax
 }
 if atomic.LoadUint32(p.status) == poolStop {
 return nil, ErrPoolClosed
 }
 // 判斷是否超額
 p.lock.Lock()
 if p.idle.Len() == 0  atomic.LoadInt32(p.count) >= p.config.MaxConn {
 p.lock.Unlock()
 // 休眠遞歸重試
 time.Sleep(p.config.interval)
 p.get(expire)
 }
 .......
}

注意p.lock.Lock()的和p.lock.UnLock()調(diào)用時(shí)機(jī),確保公共數(shù)據(jù)并發(fā)操作安全。

2.3 釋放連接

// 歸還Thrift客戶端
func (p *ThriftPool) Put(client *IdleClient) error {
 if client == nil {
 return nil
 }
 if atomic.LoadUint32(p.status) == poolStop {
 err := p.Close(client)
 client = nil
 return err
 }
 if atomic.LoadInt32(p.count) > p.config.MaxConn || !client.Check() {
 atomic.AddInt32(p.count, -1)
 err := p.Close(client)
 client = nil
 return err
 }
 p.lock.Lock()
 p.idle.PushFront(idleConn{
 c: client,
 t: nowFunc(),
 })
 p.lock.Unlock()
 return nil
}

p.Put()邏輯也比較簡單,如果連接已經(jīng)失效,p.count需要-1,并進(jìn)行連接關(guān)閉操作。否則丟到空閑隊(duì)列里,這里還是丟到隊(duì)頭,沒錯(cuò),還是丟到隊(duì)頭,p.Get()和p.Put()都是從隊(duì)頭操作,有點(diǎn)像堆操作,為啥這么處理,等下面說到空閑連接超時(shí)管理就清楚了,這里先記住丟回空閑隊(duì)列的時(shí)候,會(huì)更新空閑連接的時(shí)間。

2.4 超時(shí)管理

獲取連接超時(shí)管理p.Get()方法已經(jīng)講過了,創(chuàng)建連接超時(shí)管理由p.Dial()去實(shí)現(xiàn),下面說的是空閑連接的超時(shí)管理,空閑隊(duì)列的連接,如果一直沒有使用,超過一定時(shí)間,需要主動(dòng)關(guān)閉掉,服務(wù)端的資源有限,不需要用的連接就主動(dòng)關(guān)掉,而且連接放太久,服務(wù)端也會(huì)主動(dòng)關(guān)掉。

// 超時(shí)管理,定期釋放空閑太久的連接
func (p *ThriftPool) CheckTimeout() {
 p.lock.Lock()
 for p.idle.Len() != 0 {
 ele := p.idle.Back()
 if ele == nil {
 break
 }
 v := ele.Value.(*idleConn)
 if v.t.Add(p.config.IdleTimeout).After(nowFunc()) {
 break
 }
 //timeout  clear
 p.idle.Remove(ele)
 p.lock.Unlock()
 p.Close(v.c) //close client connection
 atomic.AddInt32(p.count, -1)
 p.lock.Lock()
 }
 p.lock.Unlock()
 return
}

清理超時(shí)空閑連接的時(shí)候,是從隊(duì)尾開始清理掉超時(shí)或者無效的連接,直到找到第一個(gè)可用的連接或者隊(duì)列為空。p.Get()和p.Put()都從隊(duì)頭操作隊(duì)列,保證了活躍的連接都在隊(duì)頭,如果一開始創(chuàng)建的連接太多,后面業(yè)務(wù)操作變少,不需要那么多連接的時(shí)候,那多余的連接就會(huì)沉到隊(duì)尾,被超時(shí)管理所清理掉。另外,這樣設(shè)計(jì)也可以優(yōu)化操作的時(shí)間復(fù)雜度O(n)。

2.5 重連機(jī)制

事實(shí)上,thrift的transport層并沒有提供一個(gè)檢查連接是否有效的方法,一開始實(shí)現(xiàn)連接池的時(shí)候,檢測方法是調(diào)用thrift.TTransport.IsOpen()來判斷

// 檢測連接是否有效
func (c *IdleClient) Check() bool {
 if c.Transport == nil || c.RawClient == nil {
 return false
 }
 return c.Transport.IsOpen()
}

可在測試階段發(fā)現(xiàn)當(dāng)?shù)讓赢?dāng)TCP連接被異常斷開的時(shí)候(服務(wù)端重啟、服務(wù)端宕機(jī)等),c.Transport.IsOpen()并不能如期的返回false,如果我們查看thrift的源碼,可以發(fā)現(xiàn),其實(shí)c.Transport.IsOpen()只和我們是否調(diào)用了c.Transport.Open()方法有關(guān)。為了能實(shí)現(xiàn)斷開重連機(jī)制,我們只能在使用階段發(fā)現(xiàn)異常連接時(shí),重連連接。

這里我在ThriftPool上封裝了一層代理ThriftPoolAgent,來實(shí)現(xiàn)斷開重連邏輯,具體請(qǐng)參考代碼實(shí)現(xiàn)。

package pool
import (
 "fmt"
 "github.com/apache/thrift/lib/go/thrift"
 "log"
 "net"
)
type ThriftPoolAgent struct {
 pool *ThriftPool
}
func NewThriftPoolAgent() *ThriftPoolAgent {
 return ThriftPoolAgent{}
}
func (a *ThriftPoolAgent) Init(pool *ThriftPool) {
 a.pool = pool
}
// 真正的業(yè)務(wù)邏輯放到do方法做,ThriftPoolAgent只要保證獲取到可用的Thrift客戶端,然后傳給do方法就行了
func (a *ThriftPoolAgent) Do(do func(rawClient interface{}) error) error {
 var (
 client *IdleClient
 err error
 )
 defer func() {
 if client != nil {
 if err == nil {
 if rErr := a.releaseClient(client); rErr != nil {
 log.Println(fmt.Sprintf("releaseClient error: %v", rErr))
 }
 } else if _, ok := err.(net.Error); ok {
 a.closeClient(client)
 } else if _, ok = err.(thrift.TTransportException); ok {
 a.closeClient(client)
 } else {
 if rErr := a.releaseClient(client); rErr != nil {
 log.Println(fmt.Sprintf("releaseClient error: %v", rErr))
 }
 }
 }
 }()
 // 從連接池里獲取鏈接
 client, err = a.getClient()
 if err != nil {
 return err
 }
 if err = do(client.RawClient); err != nil {
 if _, ok := err.(net.Error); ok {
 log.Println(fmt.Sprintf("err: retry tcp, %T, %s", err, err.Error()))
 // 網(wǎng)絡(luò)錯(cuò)誤,重建連接
 client, err = a.reconnect(client)
 if err != nil {
 return err
 }
 return do(client.RawClient)
 }
 if _, ok := err.(thrift.TTransportException); ok {
 log.Println(fmt.Sprintf("err: retry tcp, %T, %s", err, err.Error()))
 // thrift傳輸層錯(cuò)誤,也重建連接
 client, err = a.reconnect(client)
 if err != nil {
 return err
 }
 return do(client.RawClient)
 }
 return err
 }
 return nil
}
// 獲取連接
func (a *ThriftPoolAgent) getClient() (*IdleClient, error) {
 return a.pool.Get()
}
// 釋放連接
func (a *ThriftPoolAgent) releaseClient(client *IdleClient) error {
 return a.pool.Put(client)
}
// 關(guān)閉有問題的連接,并重新創(chuàng)建一個(gè)新的連接
func (a *ThriftPoolAgent) reconnect(client *IdleClient) (newClient *IdleClient, err error) {
 return a.pool.Reconnect(client)
}
// 關(guān)閉連接
func (a *ThriftPoolAgent) closeClient(client *IdleClient) {
 a.pool.CloseConn(client)
}
// 釋放連接池
func (a *ThriftPoolAgent) Release() {
 a.pool.Release()
}
func (a *ThriftPoolAgent) GetIdleCount() uint32 {
 return a.pool.GetIdleCount()
}
func (a *ThriftPoolAgent) GetConnCount() int32 {
 return a.pool.GetConnCount()
}

3 對(duì)照實(shí)驗(yàn)

啟用100個(gè)協(xié)程,不斷調(diào)用Thrift服務(wù)端API 10分鐘,對(duì)比服務(wù)平均吞吐量、Thrift API調(diào)用平均延遲、機(jī)器端口消耗。

平均吞吐量(r/s) = 總成功數(shù) / 600

API調(diào)用平均延遲(ms/r) = 總成功數(shù) / API成功請(qǐng)求總耗時(shí)(微秒) / 1000

機(jī)器端口消耗計(jì)算:netstat -nt | grep 9444 -c

3.1 實(shí)驗(yàn)一:未使用連接池

機(jī)器端口消耗

平均吞吐量、平均延遲

從結(jié)果看,API的平均延遲在77ms左右,但是服務(wù)的平均吞吐量才到360,比理論值1000 / 77 * 1000 = 1299少了很多,而且有96409次錯(cuò)誤,報(bào)錯(cuò)的主要原因是:connect can't assign request address,100個(gè)協(xié)程并發(fā)調(diào)用就已經(jīng)消耗了1.6w個(gè)端口,如果并發(fā)數(shù)更高的場景,端口消耗的情況會(huì)更加嚴(yán)重,實(shí)際上,這1.6w條TCP連接,幾乎都是TIME_WAIT狀態(tài),Thrfit客戶端用完就close掉,根據(jù)TCP三次握手可知主動(dòng)斷開連接的一方最終將會(huì)處于TIME_WAIT狀態(tài),并等待2MSL時(shí)間。

3.2 實(shí)驗(yàn)二:使用連接池

機(jī)器端口消耗

平均吞吐量、平均延遲

可以看出,用了連接池后,平均吞吐量可達(dá)到1.8w,API調(diào)用平均延遲才0.5ms,你可能會(huì)問,理論吞吐量不是可以達(dá)到1000 / 0.5 * 100 = 20w?理論歸理論,如果按照1.8w吞吐量算,一次處理過程總時(shí)間消耗是1000 / (18000 / 100) = 5.6ms,所以這里影響吞吐量的因素已經(jīng)不是API調(diào)用的耗時(shí)了,1.8w的吞吐量其實(shí)已經(jīng)挺不錯(cuò)了。

另外,消耗的端口數(shù)也才194/2 = 97(除余2是因?yàn)閟erver端也在本地跑),而且都是ESTABLISH狀態(tài),連接一直保持著,不斷的在被復(fù)用。連接被復(fù)用,少了創(chuàng)建TCP連接的三次握手環(huán)節(jié),這里也可以解釋為啥API調(diào)用的平均延遲可以從77ms降到0.5ms,不過0.5ms確實(shí)有點(diǎn)低,線上環(huán)境Server一般不會(huì)和Client在同一臺(tái)機(jī)器,而且業(yè)務(wù)邏輯也會(huì)比這里復(fù)雜,API調(diào)用的平均延遲會(huì)相對(duì)高一點(diǎn)。

4 總結(jié)

調(diào)用Thrift API必須使用Thrift客戶端連接池,否則在高并發(fā)的情況下,會(huì)有大量的TCP連接處于TIME_WAIT狀態(tài),機(jī)器端口被大量消耗,可能會(huì)導(dǎo)致部分請(qǐng)求失敗甚至服務(wù)不可用。每次請(qǐng)求都重新創(chuàng)建TCP連接,進(jìn)行TCP三次握手環(huán)節(jié),API調(diào)用的延遲會(huì)比較高,服務(wù)的吞吐量也不會(huì)很高。

使用Thrift客戶端連接池,可以提高系統(tǒng)的吞吐量,同時(shí)可以避免機(jī)器端口被耗盡的危險(xiǎn),提高服務(wù)的可靠性。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

您可能感興趣的文章:
  • golang 通過ssh代理連接mysql的操作
  • 淺談golang結(jié)構(gòu)體偷懶初始化
  • golang連接kafka消費(fèi)進(jìn)ES操作
  • golang實(shí)現(xiàn)各種情況的get請(qǐng)求操作
  • Golang 實(shí)現(xiàn)分片讀取http超大文件流和并發(fā)控制
  • 在Golang中使用http.FileServer返回靜態(tài)文件的操作
  • golang 生成定單號(hào)的操作

標(biāo)簽:海南 電子產(chǎn)品 西雙版納 青海 儋州 物業(yè)服務(wù) 遼寧 安康

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Golang 實(shí)現(xiàn)Thrift客戶端連接池方式》,本文關(guān)鍵詞  Golang,實(shí)現(xiàn),Thrift,客戶端,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Golang 實(shí)現(xiàn)Thrift客戶端連接池方式》相關(guān)的同類信息!
  • 本頁收集關(guān)于Golang 實(shí)現(xiàn)Thrift客戶端連接池方式的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    亚洲va韩国va欧美va精四季| 杨钰莹一级淫片aaaaaa播放| 欧美久久一级| 色综合久久99| 亚洲国产精品推荐| 久久av秘一区二区三区| 少妇熟女视频一区二区三区| 色婷婷综合久久久中字幕精品久久| 欧美另类69xxxx| 黄色大片在线播放| 亚洲三级电影| 亚洲成a人片综合在线| 草草久久久无码国产专区| 精品美女久久久久久免费| 看黄色免费网站| 一卡二卡三卡四卡| 热99re久久精品精品免费| 亚洲三区四区| 91最新网站| 黄色在线看片| 成人免费在线一区二区三区| 欧美福利视频| 艳妇乳肉豪妇荡乳av| 污片在线免费观看| 国产口爆吞精一区二区| 国产福利视频在线观看| 亚洲成人tv网| 国产精品成人观看视频免费| 一本色道久久综合亚洲二区三区| 日本免费在线观看视频| 狠狠综合久久av一区二区老牛| 亚洲 另类 春色 国产| 欧美性生交xxxxx久久久| 99国产欧美另类久久久精品| eeuss影院在线播放| 日韩欧美中文字幕不卡| 亚洲国产欧美在线人成| 日韩中文一区二区| 欧美少妇xxxx| 欧美日韩亚洲一区二区| 国产91丝袜在线播放九色| 9人人澡人人爽人人精品| 国产日韩欧美夫妻视频在线观看| 国产精品国产三级国产aⅴ入口| a级片在线视频| 麻豆精品精品国产自在97香蕉| 亚洲久草视频| 天堂网在线观看视频| 久久精品日韩| 日本一区二区三区四区高清视频| 魔女鞋交玉足榨精调教| 亚洲国产成人av在线| 欧美aⅴ一区二区三区视频| 黄色a级三级三级三级| 在线观看国产精品一区| 精品国内自产拍在线观看| japonensisjava老师可播放| 久久99精品国产91久久来源| 亚洲欧美亚洲| 妖精一区二区三区精品视频| 亚洲欧美另类在线| 婷婷国产在线综合| 成人黄页在线观看| 日韩中文字幕国产精品| 亚洲国产精品成人综合色在线婷婷| 黄动漫在线看| 国产精品专区一| а√中文在线8| 免费在线一区二区三区| 国产91精品看黄网站在线观看| 91青草视频久久| 亚洲三级在线观看| 欧美激情2020午夜免费观看| 国产在线麻豆精品| 777sesese| 国产精品99精品无码视亚| 91在线精品观看| 欧美14一18处毛片| 欧美一级黄色录像片| 久久99久久久久久| 国产91露脸合集magnet| 成人18视频| 国产精品夜夜夜爽阿娇| 日本欧美电影在线观看| 国产999视频| 久热精品视频在线免费观看| 欧美精品videossex性护士| 久久久久国产精品一区| 欧美—级a级欧美特级ar全黄| 亚洲国产图片| 中文字幕色av一区二区三区| 久久品道一品道久久精品| 久久久国产精品一区二区三区| 国产在线精选视频| 国产亚洲精品码| 国产在线黄色| 国产玉足脚交久久欧美| 欧美变态视频| 91亚洲国产成人精品一区二三| 欧美一级特黄高清视频| 8x海外华人永久免费日韩内陆视频| 日日夜夜网站| 97色成人综合网站| 亚洲成年人影院在线| jizz视频在线观看| 国产日韩换脸av一区在线观看| 91精品久久久久久久久久久久久久| 97影院秋霞午夜在线观看| 久久久久久九九九九九| brazzers欧美最新版视频| 欧美日韩亚洲色图| 91精品久久久久久久蜜月| 91av在线播放视频| 亚洲国产精品yw在线观看| 日本精品久久中文字幕佐佐木| 国产精品成人观看视频免费| 日日躁天天躁狠狠躁| 亚洲一区在线观看免费| 午夜爽爽爽男女免费观看| 在线欧美亚洲| 亚洲最大免费视频| 捆绑紧缚一区二区三区在线观看| 免费久久网站| 久久久久久夜| 亚洲日本成人在线观看| 97人人模人人爽人人少妇| 粉嫩av一区二区三区粉嫩| 色99之美女主播在线视频| 午夜在线播放视频欧美| 国产精品久久久久久亚洲伦| 欧美性一区二区三区| 九色porny极品| 亚洲美女少妇无套啪啪呻吟| 日韩一级二级| 国产亚洲1区2区3区| 麻豆精品免费视频| 久久人人97超碰国产公开结果| 91高清免费视频| 51精品免费网站| 国产精欧美一区二区三区白种人| 久久国产日本精品| 久久久久久久久久久视频| 欧美三电影在线| 中文字幕一区二区三区乱码图片| 中文字幕在线日本| 久久久99久久精品女同性| 色综合天天综合网天天狠天天| 男人的天堂视频在线| 韩国精品视频在线观看| 国产一区二区自拍| 国产成人精品综合久久久| 来吧亚洲综合网| 中文字幕一区二区人妻视频| 午夜激情影院在线观看| 黑人巨大亚洲一区二区久| 亚洲国产一区二区三区青草影视| 99九九精品视频| 亚洲一区二三区| 精品在线观看视频| 午夜欧美在线一二页| 亚洲天堂第二页| 国产在线精品一区二区三区不卡| 日韩黄在线观看| 精品手机在线视频| 亚洲一区在线日韩在线深爱| 午夜不卡av免费| 一区二区久久精品| 免费观看h片| 国产福利在线观看| 亚洲狼人精品一区二区三区| 国产三区二区一区久久| 国产亚洲欧美日韩美女| 日本午夜在线观看| 俄罗斯嫩小性bbwbbw| 欧美a级片免费看| 羞羞的视频在线观看| 国产三级精品在线| 国产精品久线观看视频| 国产喂奶挤奶一区二区三区| 一区二区三区日本久久久| 91国产精品一区| 三级黄视频在线观看| dj大片免费在线观看| 精品久久久久久国产91| 青青草国产成人av片免费| 国产精品毛片一区二区三区四区| 米奇.777.com| 国产高清视频免费观看| 久久免费精品视频在这里| 欧美国产日韩一区二区在线观看| 国产欧美综合在线| av一本久道久久综合久久鬼色| 每日更新成人在线视频| 精品视频无码一区二区三区| 亚洲欧美日韩国产综合精品二区| 国产精品日韩在线观看| 国产不卡一区二区三区在线观看| 中文字幕亚洲欧美在线| 99热在线精品观看| 日本女人一区二区三区| 中文字幕伦理免费在线视频| 精品入口麻豆传煤| 无码人妻精品一区二区蜜桃色欲| 亚洲综合在线一区| 成人丝袜18视频在线观看| 欧美在线播放一区二区| 亚洲久久在线观看| 久久久天堂国产精品| 成人免费看黄网址| 欧美人交a欧美精品| 首页欧美精品中文字幕| 51自拍视频在线观看| 欧美成人xxxxx| 人人爽人人爽人人片av| 天堂а√在线官网| 在线电影院国产精品| 欧美视频在线播放| 青青色在线视频| 亚洲视频小说图片| 四虎免费在线观看视频| 黄色在线观看网站| 男女午夜网站| 中文字幕久久熟女蜜桃| 日韩欧美精品在线| 久久综合九色综合97_久久久| 动漫av在线免费观看| 久久久噜噜噜久久久| 日韩中文字幕有码| 国产一区二区视频在线观看免费| 久久成人精品一区二区三区| 中文在线√天堂| 色av一区二区三区| 91网在线观看| 午夜精品一区二区三区在线观看| 日韩av高清在线播放| 天堂а√在线中文在线新版| 亚洲丁香久久久| 精品人妻大屁股白浆无码| 99久久国产综合精品麻豆| 国产一区福利| 久久综合久久美利坚合众国| 国内精品福利| 在线免费观看成年人视频| 国产精品久久久久无码av| 超碰成人久久| 精品69视频一区二区三区| 国产一区二区三区精品欧美日韩一区二区三区| 在线免费观看av的网站| 女囚岛在线观看| 亚洲成人av一区二区三区| 亚洲精品欧美专区| 91成人免费在线视频| 青青青免费在线视频| 日韩免费高清视频网站| 成人动漫一区二区| 在线日韩日本国产亚洲| 成人欧美一区二区三区视频xxx| 成人影院久久久久久影院| 国产69精品久久久久9999小说| 姬川优奈aav一区二区| 一区二区三区毛片免费| 国产伦精品一区二区三区四区视频| 免费一级电影| 亚洲自拍偷拍色片视频| 天堂网在线观看在线观看精品| 亚洲综合无码一区二区| 国内精彩免费自拍视频在线观看网址| 日本公妇乱淫免费视频一区三区| 日韩精品免费综合视频在线播放| av在线官网| 999久久久国产精品| 国产1区2区在线| 亚洲国产精品精华液网站| 亚洲男人电影天堂| 日韩欧美国产精品一区| 五月亚洲婷婷| 99精品视频一区二区| 国产欧美精品一区| 中文字幕日本精品| 国产精品久久久久免费a∨大胸| 精品国产乱码一区二区三区四区| 中文字幕一区二区三区波野结| 欧美欧美欧美欧美首页| 久久久天堂国产精品| 国内视频自拍在线视频| 99久久免费精品国产72精品九九| 亚洲欧美日韩国产成人| 伊人午夜电影| 中文字幕第一区综合| 亚洲人成电影在线观看天堂色| 99se视频在线观看| 国产精品视频一区二区三区麻豆| 九九热久久66| 午夜视频福利在线观看| 亚洲国产免费看| 久久久久久激情| 亚洲伊人精品酒店| 在线天堂中文| 日韩美女毛茸茸| 日韩精品免费一区二区三区竹菊| 国产成人av电影| 日韩欧美成人网| 欧美成人免费大片| 一本色道88久久加勒比精品| 特级毛片在线免费观看| 欧美人妖巨大在线| 国内精品免费视频精选在线观看| 中文字幕 亚洲视频| 亚洲ⅴ国产v天堂a无码二区| 极品美女一区二区三区| 91精品国产综合久久国产大片| 日韩一区国产二区欧美三区| 麻豆av一区二区三区| 成人精品一区二区三区四区| 3d性欧美动漫精品xxxx软件| 91成人天堂久久成人| 日韩中文字幕一区二区| 国产午夜手机精彩视频| 黄色网在线免费看| 亚洲精品成人悠悠色影视| 三上悠亚作品在线观看| 亚洲欧美日韩高清在线| 日本欧美中文字幕| 99精品在免费线中文字幕网站一区| 无码人妻精品一区二区三区夜夜嗨| av在线播放天堂| 久久99精品国产一区二区三区| 91av在线免费观看|