Go創建Grpc鏈接池

来源:https://www.cnblogs.com/yangjianyong-bky/archive/2023/03/02/17172976.html
-Advertisement-
Play Games

Go創建Grpc鏈接池 轉載請註明來源:https://janrs.com/2023/03/%e5%88%9b%e5%bb%bagrpc%e9%93%be%e6%8e%a5%e6%b1%a0/ 常規用法 gRPC 四種基本使用 請求響應模式 客戶端數據流模式 服務端數據流模式 雙向流模式 常見的gR ...


Go創建Grpc鏈接池

轉載請註明來源:https://janrs.com/2023/03/創建grpc鏈接池/


常規用法

gRPC 四種基本使用

  • 請求響應模式
  • 客戶端數據流模式
  • 服務端數據流模式
  • 雙向流模式

常見的gRPC調用寫法

func main(){
	//... some code
	// 鏈接grpc服務
	conn , err := grpc.Dial(":8000",grpc.WithInsecure)
	if err != nil {
		//...log
	}
	defer conn.Close()
	//...some code

存在的問題:面臨高併發的情況,性能問題很容易就會出現,例如我們在做性能測試的時候,就會發現,打一會性能測試,客戶端請求服務端的時候就會報錯:

rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial tcp xxx:xxx: connect: connection refused

實際去查看問題的時候,很明顯,這是 gRPC 的連接數被打滿了,很多連接都還未完全釋放。[#本文來源:janrs.com#]

gRPC 的通信本質上也是 TCP 的連接,那麼一次連接就需要三次握手,和四次揮手,每一次建立連接和釋放連接的時候,都需要走這麼一個過程,如果我們頻繁的建立和釋放連接,這對於資源和性能其實都是一個大大的浪費。

在服務端,gRPC 服務端的鏈接管理不用我們操心,但是 gRPC 客戶端的鏈接管理非常有必要關心,要實現復用客戶端的連接。


創建鏈接池

創建鏈接池需要考慮的問題:

  • 連接池是否支持擴縮容
  • 空閑的連接是否支持超時自行關閉,是否支持保活
  • 池子滿的時候,處理的策略是什麼樣的

創建鏈接池介面

type Pool interface {

	// 獲取一個新的連接 , 當關閉連接的時候,會將該連接放入到池子中
   Get() (Conn, error)

	// 關閉連接池,自然連接池子中的連接也不再可用
   Close() error

	//[#本文來源:janrs.com#]
   Status() string
}

實現鏈接池介面

創建鏈接池代碼

func New(address string, option Options) (Pool, error) {
   if address == "" {
      return nil, errors.New("invalid address settings")
   }
   if option.Dial == nil {
      return nil, errors.New("invalid dial settings")
   }
   if option.MaxIdle <= 0 || option.MaxActive <= 0 || option.MaxIdle > option.MaxActive {
      return nil, errors.New("invalid maximum settings")
   }
   if option.MaxConcurrentStreams <= 0 {
      return nil, errors.New("invalid maximun settings")
   }

   p := &pool{
      index:   0,
      current: int32(option.MaxIdle),
      ref:     0,
      opt:     option,
      conns:   make([]*conn, option.MaxActive),
      address: address,
      closed:  0,
   }

   for i := 0; i < p.opt.MaxIdle; i++ {
      c, err := p.opt.Dial(address)
      if err != nil {
         p.Close()
         return nil, fmt.Errorf("dial is not able to fill the pool: %s", err)
      }
      p.conns[i] = p.wrapConn(c, false)
   }
   log.Printf("new pool success: %v\n", p.Status())

   return p, nil
}

關於以上的代碼,需要特別註意每一個連接的建立也是在 New 裡面完成的,[#本文來源:janrs.com#]只要有 1 個連接未建立成功,那麼咱們的連接池就算是建立失敗,咱們會調用 p.Close() 將之前建立好的連接全部釋放掉。

關閉鏈接池代碼

// 關閉連接池
func (p *pool) Close() error {
   atomic.StoreInt32(&p.closed, 1)
   atomic.StoreUint32(&p.index, 0)
   atomic.StoreInt32(&p.current, 0)
   atomic.StoreInt32(&p.ref, 0)
   p.deleteFrom(0)
   log.Printf("[janrs.com]close pool success: %v\n", p.Status())
   return nil
}

從具體位置刪除鏈接池代碼

// 清除從 指定位置開始到 MaxActive 之間的連接
func (p *pool) deleteFrom(begin int) {
   for i := begin; i < p.opt.MaxActive; i++ {
      p.reset(i)
   }
}

銷毀具體的鏈接代碼

// 清除具體的連接
func (p *pool) reset(index int) {
   conn := p.conns[index]
   if conn == nil {
      return
   }
   conn.reset()
   p.conns[index] = nil
}

關閉鏈接

代碼

func (c *conn) reset() error {
   cc := c.cc
   c.cc = nil
   c.once = false
   // 本文博客來源:janrs.com
   if cc != nil {
      return cc.Close()
   }
   return nil
}

func (c *conn) Close() error {
   c.pool.decrRef()
   if c.once {
      return c.reset()
   }
   return nil
}

在使用連接池通過 pool.Get() 拿到具體的連接句柄 conn 之後,會使用 conn.Close()關閉連接,實際上也是會走到上述的 Close() 實現的位置,但是並未指定當然也沒有許可權顯示的指定將 once 置位為 false ,也就是對於調用者來說,是關閉了連接,對於連接池來說,實際上是將連接歸還到連接池中。


擴縮容

關鍵代碼

func (p *pool) Get() (Conn, error) {
   // the first selected from the created connections
   nextRef := p.incrRef()
   p.RLock()
   current := atomic.LoadInt32(&p.current)
   p.RUnlock()
   if current == 0 {
      return nil, ErrClosed
   }
   if nextRef <= current*int32(p.opt.MaxConcurrentStreams) {
      next := atomic.AddUint32(&p.index, 1) % uint32(current)
      return p.conns[next], nil
   }
   // 本文博客來源:janrs.com

   // the number connection of pool is reach to max active
   if current == int32(p.opt.MaxActive) {
      // the second if reuse is true, select from pool's connections
      if p.opt.Reuse {
         next := atomic.AddUint32(&p.index, 1) % uint32(current)
         return p.conns[next], nil
      }
      // the third create one-time connection
      c, err := p.opt.Dial(p.address)
      return p.wrapConn(c, true), err
   }

   // the fourth create new connections given back to pool
   p.Lock()
   current = atomic.LoadInt32(&p.current)
   if current < int32(p.opt.MaxActive) && nextRef > current*int32(p.opt.MaxConcurrentStreams) {
      // 2 times the incremental or the remain incremental  ##janrs.com
      increment := current
      if current+increment > int32(p.opt.MaxActive) {
         increment = int32(p.opt.MaxActive) - current
      }
      var i int32
      var err error
      for i = 0; i < increment; i++ {
         c, er := p.opt.Dial(p.address)
         if er != nil {
            err = er
            break
         }
         p.reset(int(current + i))
         p.conns[current+i] = p.wrapConn(c, false)
      }
	  // 本文博客來源:janrs.com
      current += i
      log.Printf("#janrs.com#grow pool: %d ---> %d, increment: %d, maxActive: %d\n",
         p.current, current, increment, p.opt.MaxActive)
      atomic.StoreInt32(&p.current, current)
      if err != nil {
         p.Unlock()
         return nil, err
      }
   }
   p.Unlock()
   next := atomic.AddUint32(&p.index, 1) % uint32(current)
   return p.conns[next], nil
}

Get 代碼邏輯

  • 先增加連接的引用計數,如果在設定 current*int32(p.opt.MaxConcurrentStreams) 範圍內,那麼直接取連接進行使用即可。
  • 若當前的連接數達到了最大活躍的連接數,那麼就看我們新建池子的時候傳遞的 option 中的 reuse 參數是否是 true,若是復用,則隨機取出連接池中的任意連接提供使用,如果不復用,則新建一個連接。
  • 其餘的情況,就需要我們進行 2 倍或者 1 倍的數量對連接池進行擴容了。

也可以在 Get 的實現上進行縮容,具體的縮容策略可以根據實際情況來定,例如當引用計數 nextRef 只有當前活躍連接數的 10% 的時候(這隻是一個例子),就可以考慮縮容了。


性能測試

有關鏈接池的創建以及性能測試

https://mycodesmells.com/post/pooling-grpc-connections


轉載請註明來源:https://janrs.com/2023/03/創建grpc鏈接池/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近演唱會還挺多的,都是大家喜歡的那些知名歌手,所以特地出一手教程給大家助力(主要是 表弟想追女神,所以教他自己搶票) 知識點 selenium 淘寶滑塊處理 搶購邏輯實現 必備環境 python 3.8 pycharm 專業版 谷歌瀏覽器+谷歌驅動+selenium3.141.0 stealth. ...
  • 一、寫在前面 又有很久沒更文了,真的是被催婚搞的整個人情緒特別不好,如果硬要形容的話,那就是沒法跟人正常溝通,一點就著,做什麼都沒耐心,看什麼都煩,簡直沒救了... 也是偶然發現的,自己居然沒寫關於Playwright的元素定位,這不是自動化測試的重中之重,怎麼可以忘,馬上安排! 二、元素定位 主要 ...
  • ​ 遞歸函數 在函數內部調用自身本身 計算階乘: def fact(n): if n == 1: return 1 return n * fact(n - 1) 註意:使用遞歸函數需要防止棧溢出。 在電腦中,函數調用是通過棧(stack)實現,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回, ...
  • docker實戰筆記 一、安裝docker 下麵以ubuntu系統舉例: 卸載已有的舊版本docker $ sudo apt-get remove docker \ docker-engine \ docker.io 使用apt安裝最新版docker $ sudo apt-get update $ ...
  • 實現Controller的三種方式分析 每種實現的方式對應的HanderAdapter都不同。 實現Controller介面 該介面對應的HanderAdapter為SimpleControllerHandlerAdapter。 使用案列: public class LeController imp ...
  • 用GoRoutines高性能同時進行多個Api調用 轉載請註明來源:https://janrs.com/2023/03/%E7%94%A8goroutines%E5%90%8C%E6%97%B6%E8%BF%9B%E8%A1%8C%E5%A4%9A%E4%B8%AAapi%E8%B0%83%E7%9 ...
  • 前言 今天看到一個程式,用到了智能指針, virtual tmp<volScalarField> rho() const; 藉此機會把有關智能指針的知識體系重新梳理一遍 智能指針autoPtr的由來: 首先要說明智能指針本質上是模板類,是對原有指針的改進,相比更安全, of對autoPtr的描述如下 ...
  • hello 大家好呀,我是小樓,這是系列文《Go底層原理剖析》的第三篇,依舊分析 Http 模塊。我們今天來看 Go內置的 RPC。說起 RPC 大家想到的一般是框架,Go 作為編程語言竟然還內置了 RPC,著實讓我有些吃鯨。 從一個 Demo 入手 為了快速進入狀態,我們先搞一個 Demo,當然這 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...