1.在創(chuàng)建連接池之后,起一個(gè) go routine,每隔一段 idleTime 發(fā)送一個(gè) PING 到 Redis server。其中,idleTime 略小于 Redis server 的 timeout 配置。

成都創(chuàng)新互聯(lián)專注于射洪企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,成都做商城網(wǎng)站。射洪網(wǎng)站建設(shè)公司,為射洪等地區(qū)提供建站服務(wù)。全流程專業(yè)公司,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
2.連接池初始化部分代碼如下:
p, err := pool.New("tcp", u.Host, concurrency) errHndlr(err) go func() { for { p.Cmd("PING") time.Sleep(idelTime * time.Second) } }()
3.使用 redis 傳輸數(shù)據(jù)部分代碼如下:
func redisDo(p *pool.Pool, cmd string, args ...interface{}) (reply *redis.Resp, err error) { reply = p.Cmd(cmd, args...) if err = reply.Err; err != nil { if err != io.EOF { Fatal.Println("redis", cmd, args, "err is", err) } } return }
4.其中,Radix.v2 連接池內(nèi)部進(jìn)行了連接池內(nèi)連接的獲取和放回,代碼如下:
// Cmd automatically gets one client from the pool, executes the given command // (returning its result), and puts the client back in the pool func (p *Pool) Cmd(cmd string, args ...interface{}) *redis.Resp { c, err := p.Get() if err != nil { return redis.NewResp(err) } defer p.Put(c) return c.Cmd(cmd, args...) }
這樣,就有了系統(tǒng) keep alive 的機(jī)制,不會(huì)出現(xiàn) time out 的連接了,從 redis 連接池里面取出的連接都是可用的連接了。看似簡(jiǎn)單的代碼,卻完美的解決了連接池里面超時(shí)連接的問(wèn)題。同時(shí),就算 Redis server 重啟等情況,也能保證連接自動(dòng)重連。
一、關(guān)于連接池
一個(gè)數(shù)據(jù)庫(kù)服務(wù)器只擁有有限的資源,并且如果你沒(méi)有充分使用這些資源,你可以通過(guò)使用更多的連接來(lái)提高吞吐量。一旦所有的資源都在使用,那么你就不 能通過(guò)增加更多的連接來(lái)提高吞吐量。事實(shí)上,吞吐量在連接負(fù)載較大時(shí)就開(kāi)始下降了。通常可以通過(guò)限制與可用的資源相匹配的數(shù)據(jù)庫(kù)連接的數(shù)量來(lái)提高延遲和吞 吐量。
如何在Go語(yǔ)言中使用Redis連接池
如果不使用連接池,那么,每次傳輸數(shù)據(jù),我們都需要進(jìn)行創(chuàng)建連接,收發(fā)數(shù)據(jù),關(guān)閉連接。在并發(fā)量不高的場(chǎng)景,基本上不會(huì)有什么問(wèn)題,一旦并發(fā)量上去了,那么,一般就會(huì)遇到下面幾個(gè)常見(jiàn)問(wèn)題:
性能普遍上不去
CPU 大量資源被系統(tǒng)消耗
網(wǎng)絡(luò)一旦抖動(dòng),會(huì)有大量 TIME_WAIT 產(chǎn)生,不得不定期重啟服務(wù)或定期重啟機(jī)器
服務(wù)器工作不穩(wěn)定,QPS 忽高忽低
要想解決這些問(wèn)題,我們就要用到連接池了。連接池的思路很簡(jiǎn)單,在初始化時(shí),創(chuàng)建一定數(shù)量的連接,先把所有長(zhǎng)連接存起來(lái),然后,誰(shuí)需要使用,從這里取走,干完活立馬放回來(lái)。 如果請(qǐng)求數(shù)超出連接池容量,那么就排隊(duì)等待、退化成短連接或者直接丟棄掉。
二、使用連接池遇到的坑
最近在一個(gè)項(xiàng)目中,需要實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 Web Server 提供 Redis 的 HTTP interface,提供 JSON 形式的返回結(jié)果。考慮用 Go 來(lái)實(shí)現(xiàn)。
首先,去看一下 Redis 官方推薦的 Go Redis driver。官方 Star 的項(xiàng)目有兩個(gè):Radix.v2 和 Redigo。經(jīng)過(guò)簡(jiǎn)單的比較后,選擇了更加輕量級(jí)和實(shí)現(xiàn)更加優(yōu)雅的 Radix.v2。
Radix.v2 包是根據(jù)功能劃分成一個(gè)個(gè)的 sub package,每一個(gè) sub package 在一個(gè)獨(dú)立的子目錄中,結(jié)構(gòu)非常清晰。我的項(xiàng)目中會(huì)用到的 sub package 有 redis 和 pool。
由于我想讓這種被 fork 的進(jìn)程最好簡(jiǎn)單點(diǎn),做的事情單一一些,所以,在沒(méi)有深入去看 Radix.v2 的 pool 的實(shí)現(xiàn)之前,我選擇了自己實(shí)現(xiàn)一個(gè) Redis pool。(這里,就不貼代碼了。后來(lái)發(fā)現(xiàn)自己實(shí)現(xiàn)的 Redis pool 與 Radix.v2 實(shí)現(xiàn)的 Redis pool 的原理是一樣的,都是基于 channel 實(shí)現(xiàn)的, 遇到的問(wèn)題也是一樣的。)
不過(guò)在測(cè)試過(guò)程中,發(fā)現(xiàn)了一個(gè)詭異的問(wèn)題。在請(qǐng)求過(guò)程中經(jīng)常會(huì)報(bào) EOF 錯(cuò)誤。而且是概率性出現(xiàn),一會(huì)有問(wèn)題,一會(huì)又好了。通過(guò)反復(fù)的測(cè)試,發(fā)現(xiàn) bug 是有規(guī)律的,當(dāng)程序空閑一會(huì)后,再進(jìn)行連續(xù)請(qǐng)求,會(huì)發(fā)生3次失敗,然后之后的請(qǐng)求都能成功,而我的連接池大小設(shè)置的是3。再進(jìn)一步分析,程序空閑300秒 后,再請(qǐng)求就會(huì)失敗,發(fā)現(xiàn)我的 Redis server 配置了 timeout 300,至此,問(wèn)題就清楚了。是連接超時(shí) Redis server 主動(dòng)斷開(kāi)了連接。客戶端這邊從一個(gè)超時(shí)的連接請(qǐng)求就會(huì)得到 EOF 錯(cuò)誤。
然后我看了一下 Radix.v2 的 pool 包的源碼,發(fā)現(xiàn)這個(gè)庫(kù)本身并沒(méi)有檢測(cè)壞的連接,并替換為新server{location/pool{content_by_lua_block{localredis=require"resty.redis"localred=redis:new()localok,err=red:connect("127.0.0.1",6379)ifnotokthenngx.say("failedtoconnect:",err)returnendok,err=red:set("hello","world")ifnotokthenreturnendred:set_keepalive(10000,100)}}}
發(fā)現(xiàn)有個(gè) set_keepalive 的方法,查了一下官方文檔,方法的原型是 syntax: ok, err = red:set_keepalive(max_idle_timeout, pool_size) 貌似 max_idle_timeout 這個(gè)參數(shù),就是我們所缺少的東西,然后進(jìn)一步跟蹤源碼,看看里面是怎么保證連接有效的。
function_M.set_keepalive(self,...)localsock=self.sockifnotsockthenreturnnil,"notinitialized"endifself.subscribedthenreturnnil,"subscribedstate"endreturnsock:setkeepalive(...)end
至此,已經(jīng)清楚了,使用了 tcp 的 keepalive 心跳機(jī)制。
于是,通過(guò)與 Radix.v2 的作者一些討論,選擇自己在 redis 這層使用心跳機(jī)制,來(lái)解決這個(gè)問(wèn)題。
四、最后的解決方案
在創(chuàng)建連接池之后,起一個(gè) goroutine,每隔一段 idleTime 發(fā)送一個(gè) PING 到 Redis server。其中,idleTime 略小于 Redis server 的 timeout 配置。連接池初始化部分代碼如下:
p,err:=pool.New("tcp",u.Host,concurrency)errHndlr(err)gofunc(){for{p.Cmd("PING")time.Sleep(idelTime*time.Second)}}()
使用 redis 傳輸數(shù)據(jù)部分代碼如下:
funcredisDo(p*pool.Pool,cmdstring,args...interface{})(reply*redis.Resp,errerror){reply=p.Cmd(cmd,args...)iferr=reply.Err;err!=nil{iferr!=io.EOF{Fatal.Println("redis",cmd,args,"erris",err)}}return}
其中,Radix.v2 連接池內(nèi)部進(jìn)行了連接池內(nèi)連接的獲取和放回,代碼如下:
//Cmdautomaticallygetsoneclientfromthepool,executesthegivencommand//(returningitsresult),andputstheclientbackinthepoolfunc(p*Pool)Cmd(cmdstring,args...interface{})*redis.Resp{c,err:=p.Get()iferr!=nil{returnredis.NewResp(err)}deferp.Put(c)returnc.Cmd(cmd,args...)}
這樣,我們就有了 keepalive 的機(jī)制,不會(huì)出現(xiàn) timeout 的連接了,從 redis 連接池里面取出的連接都是可用的連接了。看似簡(jiǎn)單的代碼,卻完美的解決了連接池里面超時(shí)連接的問(wèn)題。同時(shí),就算 Redis server 重啟等情況,也能保證連接自動(dòng)重連。
Go 語(yǔ)言被設(shè)計(jì)成一門應(yīng)用于搭載 Web 服務(wù)器,存儲(chǔ)集群或類似用途的巨型中央服務(wù)器的系統(tǒng)編程語(yǔ)言。對(duì)于高性能分布式系統(tǒng)領(lǐng)域而言,Go 語(yǔ)言無(wú)疑比大多數(shù)其它語(yǔ)言有著更高的開(kāi)發(fā)效率。學(xué)習(xí)Go語(yǔ)言,可以說(shuō)是很簡(jiǎn)單的,入門快,想學(xué)習(xí)Go語(yǔ)言,可以到黑馬程序員看看,有新出的教程。
可以參考: windows環(huán)境下redis的安裝
啟動(dòng)redis服務(wù)器:redis-server.exe redis.windows.conf
獲取包:
導(dǎo)入包
訪問(wèn):
1、服務(wù)器編程:以前你如果使用C或者C++做的那些事情,用Go來(lái)做很合適,例如處理日志、數(shù)據(jù)打包、虛擬機(jī)處理、文件系統(tǒng)等。
2、分布式系統(tǒng)、數(shù)據(jù)庫(kù)代理器、中間件:例如Etcd。
3、網(wǎng)絡(luò)編程:這一塊目前應(yīng)用最廣,包括Web應(yīng)用、API應(yīng)用、下載應(yīng)用,而且Go內(nèi)置的net/http包基本上把我們平常用到的網(wǎng)絡(luò)功能都實(shí)現(xiàn)了。
4、開(kāi)發(fā)云平臺(tái):目前國(guó)外很多云平臺(tái)在采用Go開(kāi)發(fā),我們所熟知的七牛云、華為云等等都有使用Go進(jìn)行開(kāi)發(fā)并且開(kāi)源的成型的產(chǎn)品。
5、區(qū)塊鏈:目前有一種說(shuō)法,技術(shù)從業(yè)人員把Go語(yǔ)言稱作為區(qū)塊鏈行業(yè)的開(kāi)發(fā)語(yǔ)言。如果大家學(xué)習(xí)區(qū)塊鏈技術(shù)的話,就會(huì)發(fā)現(xiàn)現(xiàn)在有很多很多的區(qū)塊鏈的系統(tǒng)和應(yīng)用都是采用Go進(jìn)行開(kāi)發(fā)的,比如ehtereum是目前知名度最大的公鏈,再比如fabric是目前最知名的聯(lián)盟鏈,兩者都有g(shù)o語(yǔ)言的版本,且go-ehtereum還是以太坊官方推薦的版本。
自1.0版發(fā)布以來(lái),go語(yǔ)言引起了眾多開(kāi)發(fā)者的關(guān)注,并得到了廣泛的應(yīng)用。go語(yǔ)言簡(jiǎn)單、高效、并發(fā)的特點(diǎn)吸引了許多傳統(tǒng)的語(yǔ)言開(kāi)發(fā)人員,其數(shù)量也在不斷增加。
使用 Go 語(yǔ)言開(kāi)發(fā)的開(kāi)源項(xiàng)目非常多。早期的 Go 語(yǔ)言開(kāi)源項(xiàng)目只是通過(guò) Go 語(yǔ)言與傳統(tǒng)項(xiàng)目進(jìn)行C語(yǔ)言庫(kù)綁定實(shí)現(xiàn),例如 Qt、Sqlite 等。
后期的很多項(xiàng)目都使用 Go 語(yǔ)言進(jìn)行重新原生實(shí)現(xiàn),這個(gè)過(guò)程相對(duì)于其他語(yǔ)言要簡(jiǎn)單一些,這也促成了大量使用 Go 語(yǔ)言原生開(kāi)發(fā)項(xiàng)目的出現(xiàn)。
首選,如果之前使用過(guò)redis容器,我們需要先remove掉之前的容器
然后創(chuàng)建redis容器,并運(yùn)行
進(jìn)入redis容器中
接著我們通過(guò) redis-cli 連接測(cè)試使用 redis 服務(wù)
setex指令 可以設(shè)置數(shù)據(jù)存在的時(shí)間, setex key second value
MSET 一次設(shè)置多個(gè)key-value
MGET一次獲取多個(gè)key-value
HGET
HGETALL
Hlen和hexist
Lpush 和 Lrange
Lpop和Rpop 從鏈表取出并移走數(shù)據(jù)
刪除鏈表所有數(shù)據(jù) DEL
字符串無(wú)序 不能重復(fù)
從連接池中Get出一個(gè)conn連接
網(wǎng)站標(biāo)題:go語(yǔ)言緩存redis,go語(yǔ)言內(nèi)存不斷升高
網(wǎng)站路徑:http://chinadenli.net/article40/dsgieho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、微信公眾號(hào)、網(wǎng)站制作、ChatGPT、網(wǎng)站收錄、品牌網(wǎng)站設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
營(yíng)銷型網(wǎng)站建設(shè)知識(shí)