成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何在Go語言中使用Redis連接池

開發 前端 Redis
我們就有了 keepalive 的機制,不會出現 timeout 的連接了,從 redis 連接池里面取出的連接都是可用的連接了。看似簡單的代碼,卻完美的解決了連接池里面超時連接的問題。同時,就算 Redis server 重啟等情況,也能保證連接自動重連。

一、關于連接池

一個數據庫服務器只擁有有限的資源,并且如果你沒有充分使用這些資源,你可以通過使用更多的連接來提高吞吐量。一旦所有的資源都在使用,那么你就不 能通過增加更多的連接來提高吞吐量。事實上,吞吐量在連接負載較大時就開始下降了。通常可以通過限制與可用的資源相匹配的數據庫連接的數量來提高延遲和吞 吐量。

 

如果不使用連接池,那么,每次傳輸數據,我們都需要進行創建連接,收發數據,關閉連接。在并發量不高的場景,基本上不會有什么問題,一旦并發量上去了,那么,一般就會遇到下面幾個常見問題:

  • 性能普遍上不去

  • CPU 大量資源被系統消耗

  • 網絡一旦抖動,會有大量 TIME_WAIT 產生,不得不定期重啟服務或定期重啟機器

  • 服務器工作不穩定,QPS 忽高忽低

要想解決這些問題,我們就要用到連接池了。連接池的思路很簡單,在初始化時,創建一定數量的連接,先把所有長連接存起來,然后,誰需要使用,從這里取走,干完活立馬放回來。 如果請求數超出連接池容量,那么就排隊等待、退化成短連接或者直接丟棄掉。

二、使用連接池遇到的坑

最近在一個項目中,需要實現一個簡單的 Web Server 提供 Redis 的 HTTP interface,提供 JSON 形式的返回結果。考慮用 Go 來實現。

首先,去看一下 Redis 官方推薦的 Go Redis driver。官方 Star 的項目有兩個:Radix.v2 和 Redigo。經過簡單的比較后,選擇了更加輕量級和實現更加優雅的 Radix.v2。

Radix.v2 包是根據功能劃分成一個個的 sub package,每一個 sub package 在一個獨立的子目錄中,結構非常清晰。我的項目中會用到的 sub package 有 redis 和 pool。

由于我想讓這種被 fork 的進程***簡單點,做的事情單一一些,所以,在沒有深入去看 Radix.v2 的 pool 的實現之前,我選擇了自己實現一個 Redis pool。(這里,就不貼代碼了。后來發現自己實現的 Redis pool 與 Radix.v2 實現的 Redis pool 的原理是一樣的,都是基于 channel 實現的, 遇到的問題也是一樣的。)

不過在測試過程中,發現了一個詭異的問題。在請求過程中經常會報 EOF 錯誤。而且是概率性出現,一會有問題,一會又好了。通過反復的測試,發現 bug 是有規律的,當程序空閑一會后,再進行連續請求,會發生3次失敗,然后之后的請求都能成功,而我的連接池大小設置的是3。再進一步分析,程序空閑300秒 后,再請求就會失敗,發現我的 Redis server 配置了 timeout 300,至此,問題就清楚了。是連接超時 Redis server 主動斷開了連接。客戶端這邊從一個超時的連接請求就會得到 EOF 錯誤。

然后我看了一下 Radix.v2 的 pool 包的源碼,發現這個庫本身并沒有檢測壞的連接,并替換為新的連接的機制。也就是說我每次從連接池里面 Get 的連接有可能是壞的連接。所以,我當時臨時的解決方案是通過增加失敗后自動重試來解決了。不過,這樣的處理方案,連接池的作用好像就沒有了。技術債能早點 還的還是早點還上。

三、使用連接池的正確姿勢

想到我們的 ngx_lua 項目里面也大量使用 redis 連接池,他們怎么沒有遇到這個問題呢。只能去看看源碼了。

經過抽象分離, ngx_lua 里面使用 redis 連接池部分的代碼大致是這樣的:

 

  1. server { 
  2.     location /pool { 
  3.         content_by_lua_block { 
  4.             local redis = require "resty.redis" 
  5.             local red = redis:new() 
  6.  
  7.             local ok, err = red:connect("127.0.0.1"6379
  8.             if not ok then 
  9.                 ngx.say("failed to connect: ", err) 
  10.                 return 
  11.             end 
  12.  
  13.             ok, err = red:set("hello""world"
  14.             if not ok then 
  15.                 return 
  16.             end 
  17.  
  18.             red:set_keepalive(10000100
  19.         } 
  20.     } 

發現有個 set_keepalive 的方法,查了一下官方文檔,方法的原型是 syntax: ok, err = red:set_keepalive(max_idle_timeout, pool_size) 貌似 max_idle_timeout 這個參數,就是我們所缺少的東西,然后進一步跟蹤源碼,看看里面是怎么保證連接有效的。

 

  1. function _M.set_keepalive(self, ...) 
  2.     local sock = self.sock 
  3.     if not sock then 
  4.         return nil, "not initialized" 
  5.     end 
  6.  
  7.     if self.subscribed then 
  8.         return nil, "subscribed state" 
  9.     end 
  10.  
  11.     return sock:setkeepalive(...) 
  12. end 

至此,已經清楚了,使用了 tcp 的 keepalive 心跳機制。

于是,通過與 Radix.v2 的作者一些討論,選擇自己在 redis 這層使用心跳機制,來解決這個問題。

四、***的解決方案

在創建連接池之后,起一個 goroutine,每隔一段 idleTime 發送一個 PING 到 Redis server。其中,idleTime 略小于 Redis server 的 timeout 配置。
連接池初始化部分代碼如下:

  1. p, err := pool.New("tcp", u.Host, concurrency) 
  2. errHndlr(err) 
  3. go func() { 
  4.     for { 
  5.         p.Cmd("PING"
  6.         time.Sleep(idelTime * time.Second) 
  7.     } 
  8. }() 

使用 redis 傳輸數據部分代碼如下:

 

  1. func redisDo(p *pool.Pool, cmd string, args ...interface{}) (reply *redis.Resp, err error) { 
  2.     reply = p.Cmd(cmd, args...) 
  3.     if err = reply.Err; err != nil { 
  4.         if err != io.EOF { 
  5.             Fatal.Println("redis", cmd, args, "err is", err) 
  6.         } 
  7.     } 
  8.  
  9.     return 

其中,Radix.v2 連接池內部進行了連接池內連接的獲取和放回,代碼如下:

 

  1. // Cmd automatically gets one client from the pool, executes the given command 
  2. // (returning its result), and puts the client back in the pool 
  3. func (p *Pool) Cmd(cmd string, args ...interface{}) *redis.Resp { 
  4.     c, err := p.Get() 
  5.     if err != nil { 
  6.         return redis.NewResp(err) 
  7.     } 
  8.     defer p.Put(c) 
  9.  
  10.     return c.Cmd(cmd, args...) 

這樣,我們就有了 keepalive 的機制,不會出現 timeout 的連接了,從 redis 連接池里面取出的連接都是可用的連接了。看似簡單的代碼,卻***的解決了連接池里面超時連接的問題。同時,就算 Redis server 重啟等情況,也能保證連接自動重連。

責任編輯:王雪燕 來源: 極客頭條
相關推薦

2013-06-25 09:52:32

GoGo語言Go編程

2019-12-30 15:30:13

連接池請求PHP

2011-06-01 13:54:10

MySQL

2022-09-29 10:01:05

Go編程語言文本文件

2022-11-03 20:38:01

CMD命令Go

2014-04-09 09:32:24

Go并發

2009-06-15 13:46:00

netbeans設置數據庫連接池

2011-05-25 13:22:05

PHPJSON

2011-08-25 09:55:27

2024-04-01 00:02:56

Go語言代碼

2025-03-27 00:45:00

2010-03-11 19:16:32

Python語言

2024-05-10 08:36:40

Go語言對象

2023-10-09 07:14:42

panicGo語言

2025-02-13 09:02:04

2019-09-16 19:00:48

Linux變量

2014-07-02 09:47:06

SwiftCocoaPods

2020-11-30 11:55:07

Docker命令Linux

2024-09-06 11:34:15

RustAI語言

2020-04-09 10:18:51

Bash循環Linux
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 情侣酒店偷拍一区二区在线播放 | 日日操日日舔 | 99久久中文字幕三级久久日本 | 国产1区 | 久久久久国产 | 九九久久久 | 一级看片 | 亚洲网一区 | 亚洲成人av| 麻豆av电影网 | 国产免费观看久久黄av片涩av | 亚洲一区二区三区久久久 | 中文字幕在线精品 | 国产精品99久久久久久久久 | 精品欧美一区二区三区久久久 | 色婷婷影院| 九色porny自拍视频 | 粉嫩在线 | 在线免费观看成年人视频 | 国产在线观看一区二区三区 | 国产日产精品一区二区三区四区 | 国产二区视频 | 精品国产乱码久久久久久闺蜜 | 毛片软件 | 99成人| 亚洲精品一区二三区不卡 | 成人国产精品色哟哟 | 欧美日韩在线精品 | 免费国产网站 | 狠狠伊人 | 成人午夜毛片 | 欧美成人猛片aaaaaaa | 亚洲一区二区三区免费视频 | 日韩在线一区二区三区 | 国产激情偷乱视频一区二区三区 | 成人小视频在线观看 | 视频一区在线播放 | 国产一区二区在线免费播放 | 日韩中文一区二区三区 | 国产日韩欧美激情 | 日韩毛片 |