最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go創(chuàng)建Grpc鏈接池實(shí)現(xiàn)過(guò)程詳解

 更新時(shí)間:2023年03月03日 14:53:20   作者:janrs_com  
這篇文章主要為大家介紹了Go創(chuàng)建Grpc鏈接池實(shí)現(xiàn)過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

常規(guī)用法

gRPC 四種基本使用

  • 請(qǐng)求響應(yīng)模式
  • 客戶(hù)端數(shù)據(jù)流模式
  • 服務(wù)端數(shù)據(jù)流模式
  • 雙向流模式

常見(jiàn)的gRPC調(diào)用寫(xiě)法

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

存在的問(wèn)題:面臨高并發(fā)的情況,性能問(wèn)題很容易就會(huì)出現(xiàn),例如我們?cè)谧鲂阅軠y(cè)試的時(shí)候,就會(huì)發(fā)現(xiàn),打一會(huì)性能測(cè)試,客戶(hù)端請(qǐng)求服務(wù)端的時(shí)候就會(huì)報(bào)錯(cuò):

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

實(shí)際去查看問(wèn)題的時(shí)候,很明顯,這是 gRPC 的連接數(shù)被打滿(mǎn)了,很多連接都還未完全釋放。[#本文來(lái)源:janrs.com#]

gRPC 的通信本質(zhì)上也是 TCP 的連接,那么一次連接就需要三次握手,和四次揮手,每一次建立連接和釋放連接的時(shí)候,都需要走這么一個(gè)過(guò)程,如果我們頻繁的建立和釋放連接,這對(duì)于資源和性能其實(shí)都是一個(gè)大大的浪費(fèi)。

在服務(wù)端,gRPC 服務(wù)端的鏈接管理不用我們操心,但是 gRPC 客戶(hù)端的鏈接管理非常有必要關(guān)心,要實(shí)現(xiàn)復(fù)用客戶(hù)端的連接。

創(chuàng)建鏈接池

創(chuàng)建鏈接池需要考慮的問(wèn)題:

  • 連接池是否支持?jǐn)U縮容
  • 空閑的連接是否支持超時(shí)自行關(guān)閉,是否支持?;?/li>
  • 池子滿(mǎn)的時(shí)候,處理的策略是什么樣的

創(chuàng)建鏈接池接口

type Pool interface {
	// 獲取一個(gè)新的連接 , 當(dāng)關(guān)閉連接的時(shí)候,會(huì)將該連接放入到池子中
   Get() (Conn, error)
	// 關(guān)閉連接池,自然連接池子中的連接也不再可用
   Close() error
	//[#本文來(lái)源:janrs.com#]
   Status() string
}

實(shí)現(xiàn)鏈接池接口

創(chuàng)建鏈接池代碼

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
}

關(guān)于以上的代碼,需要特別注意每一個(gè)連接的建立也是在 New 里面完成的,[#本文來(lái)源:janrs.com#]只要有 1 個(gè)連接未建立成功,那么咱們的連接池就算是建立失敗,咱們會(huì)調(diào)用 p.Close() 將之前建立好的連接全部釋放掉。

關(guān)閉鏈接池代碼

// 關(guān)閉連接池
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
}

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

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

銷(xiāo)毀具體的鏈接代碼

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

關(guān)閉鏈接

代碼

func (c *conn) reset() error {
   cc := c.cc
   c.cc = nil
   c.once = false
   // 本文博客來(lái)源: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
}

在使用連接池通過(guò) pool.Get() 拿到具體的連接句柄 conn 之后,會(huì)使用 conn.Close()關(guān)閉連接,實(shí)際上也是會(huì)走到上述的 Close() 實(shí)現(xiàn)的位置,但是并未指定當(dāng)然也沒(méi)有權(quán)限顯示的指定將 once 置位為 false ,也就是對(duì)于調(diào)用者來(lái)說(shuō),是關(guān)閉了連接,對(duì)于連接池來(lái)說(shuō),實(shí)際上是將連接歸還到連接池中。

擴(kuò)縮容

關(guān)鍵代碼

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
   }
   // 本文博客來(lái)源: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)
      }
	  // 本文博客來(lái)源: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 代碼邏輯

  • 先增加連接的引用計(jì)數(shù),如果在設(shè)定 current*int32(p.opt.MaxConcurrentStreams) 范圍內(nèi),那么直接取連接進(jìn)行使用即可。
  • 若當(dāng)前的連接數(shù)達(dá)到了最大活躍的連接數(shù),那么就看我們新建池子的時(shí)候傳遞的 option 中的 reuse 參數(shù)是否是 true,若是復(fù)用,則隨機(jī)取出連接池中的任意連接提供使用,如果不復(fù)用,則新建一個(gè)連接。
  • 其余的情況,就需要我們進(jìn)行 2 倍或者 1 倍的數(shù)量對(duì)連接池進(jìn)行擴(kuò)容了。

也可以在 Get 的實(shí)現(xiàn)上進(jìn)行縮容,具體的縮容策略可以根據(jù)實(shí)際情況來(lái)定,例如當(dāng)引用計(jì)數(shù) nextRef 只有當(dāng)前活躍連接數(shù)的 10% 的時(shí)候(這只是一個(gè)例子),就可以考慮縮容了。

性能測(cè)試

有關(guān)鏈接池的創(chuàng)建以及性能測(cè)試

mycodesmells.com/post/poolin…

以上就是Go創(chuàng)建Grpc鏈接池實(shí)現(xiàn)過(guò)程詳解的詳細(xì)內(nèi)容,更多關(guān)于Go創(chuàng)建Grpc鏈接池的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang String字符串類(lèi)型轉(zhuǎn)Json格式

    Golang String字符串類(lèi)型轉(zhuǎn)Json格式

    本文主要介紹了Golang String字符串類(lèi)型轉(zhuǎn)Json格式的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • Golang時(shí)間處理庫(kù)go-carbon?v2.2.13發(fā)布細(xì)則

    Golang時(shí)間處理庫(kù)go-carbon?v2.2.13發(fā)布細(xì)則

    這篇文章主要為大家介紹了Golang?時(shí)間處理庫(kù)go-carbon?v2.2.13發(fā)布細(xì)則,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • 詳解Go語(yǔ)言RESTful JSON API創(chuàng)建

    詳解Go語(yǔ)言RESTful JSON API創(chuàng)建

    這篇文章主要介紹了詳解Go語(yǔ)言RESTful JSON API創(chuàng)建,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • go-zero?組件布隆過(guò)濾器使用示例詳解

    go-zero?組件布隆過(guò)濾器使用示例詳解

    這篇文章主要為大家介紹了go-zero組件介紹之布隆過(guò)濾器使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • go-zero自定義中間件的幾種方式

    go-zero自定義中間件的幾種方式

    首先 go-zero 已經(jīng)為我們提供了很多的中間件的實(shí)現(xiàn),但有時(shí)難免有需求需要自定義,這里介紹幾種自定義的方法,文中通過(guò)代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2024-07-07
  • Golang內(nèi)存泄漏詳解之原因、檢測(cè)與修復(fù)過(guò)程

    Golang內(nèi)存泄漏詳解之原因、檢測(cè)與修復(fù)過(guò)程

    本文詳細(xì)介紹了Golang中的內(nèi)存泄漏問(wèn)題,包括內(nèi)存泄漏的定義、分類(lèi)、影響以及預(yù)防和修復(fù)方法,通過(guò)使用Golang自帶的性能分析工具和火焰圖工具,可以有效地檢測(cè)和定位內(nèi)存泄漏的代碼路徑,合理的代碼設(shè)計(jì)和定期的代碼審查也是預(yù)防內(nèi)存泄漏的關(guān)鍵
    2024-12-12
  • 使用go xorm來(lái)操作mysql的方法實(shí)例

    使用go xorm來(lái)操作mysql的方法實(shí)例

    今天小編就為大家分享一篇關(guān)于使用go xorm來(lái)操作mysql的方法實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-04-04
  • Go?gRPC服務(wù)客戶(hù)端流式RPC教程

    Go?gRPC服務(wù)客戶(hù)端流式RPC教程

    這篇文章主要為大家介紹了Go?gRPC服務(wù)客戶(hù)端流式RPC教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Golang反射模塊reflect使用方式示例詳解

    Golang反射模塊reflect使用方式示例詳解

    Golang的反射功能,在很多場(chǎng)景都會(huì)用到,最基礎(chǔ)的莫過(guò)于rpc、orm跟json的編解碼,更復(fù)雜的可能會(huì)到做另外一門(mén)語(yǔ)言的虛擬機(jī),這篇文章主要介紹了Golang反射模塊reflect使用方式探索,需要的朋友可以參考下
    2023-01-01
  • Go語(yǔ)言中的定時(shí)器原理與實(shí)戰(zhàn)應(yīng)用

    Go語(yǔ)言中的定時(shí)器原理與實(shí)戰(zhàn)應(yīng)用

    在Go語(yǔ)言中,Timer和Ticker是處理定時(shí)任務(wù)的重要工具,Timer用于一次性事件,而Ticker則用于周期性事件,本文詳細(xì)介紹了這兩種定時(shí)器的創(chuàng)建、使用和停止方法,并通過(guò)實(shí)際案例展示了它們?cè)诒O(jiān)控日志、檢查系統(tǒng)狀態(tài)等方面的應(yīng)用
    2024-10-10

最新評(píng)論

长葛市| 利津县| 河津市| 巍山| 济阳县| 云林县| 武汉市| 郓城县| 平和县| 祁连县| 鹿邑县| 佳木斯市| 绍兴市| 北辰区| 新乡县| 东乌| 叙永县| 高邮市| 肇庆市| 桓仁| 滦平县| 泸西县| 祁东县| 平昌县| 岳西县| 莱州市| 阿鲁科尔沁旗| 醴陵市| 潞西市| 兴海县| 彰化县| 霍州市| 定远县| 天祝| 江安县| 石渠县| 丹巴县| 响水县| 卢龙县| 江门市| 海安县|