作爲一個phper,第一次聽到連接池還有點蒙圈,轉golang開發後連接池的概念會經常使用。
一、連接池是什麼
連接池是什麼?一個服務端資源的連接數量都是有限的,每次初始化時他建一定數量的連接,先把所有連接存起來,誰要用則從裏面取,用完後放回去。如果超出連接池容量,要是排隊等着或麼直接丟棄。
比如我們做開發中常用的mysq,redis,php-fpm的配置
1,redis服務端設置
maxclients 最大連接數設置
2, mysql服務端設置
max_connections 最大連接數
3,PHP-FPM 服務端設置
max_children 最大子進程數
start_servers 起始進程數
我們golang開發時連接redis用到自己設計的連接池概念,想要達到的效果是什麼?
1,最大的連接數,最大是爲了控制整個系統連接redis服務端的總數。超過個數量不再與reids進行連接。
2,最大的空閒連接數,最大空閒是想在沒有redis操作時還可以保持的連接個數,這些連接會一直保持與redis服務端的連接(當然如果redis服務端設置了timeout非0,或是你在代碼中手動設置了空閒連接超時間除外)。
二、如何使用Redis連接池
關於go的redis包github裏常用的有兩個:
github.com/gomodule/redigo/redis
github.com/go-redis/redis
“github.com/go-redis/redis” 這個包裏的連接池使用有些模糊,作爲一個golang新手不太建議使用這個包來做連接池的使用。
例子1:go-redis/redis 這個包的簡單使用
package main
import (
"fmt"
"github.com/go-redis/redis"
"time"
)
func main() {
var addr = "127.0.0.1:6379"
var password = ""
c := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
})
p, err := c.Ping().Result()
if err != nil {
fmt.Println("redis kill")
}
fmt.Println(p)
c.Do("SET", "key", "duzhenxun")
rs := c.Do("GET", "key").Val()
fmt.Println(rs)
c.Close()
}
例子2: “github.com/gomodule/redigo/redis” 這個包中有連接池的Api,非常好用
package main
import (
"fmt"
redigo "github.com/gomodule/redigo/redis"
"time"
)
func main() {
var addr = "127.0.0.1:6379"
var password = ""
pool := PoolInitRedis(addr, password)
c1 := pool.Get()
c2:=pool.Get()
c3:=pool.Get()
c4:=pool.Get()
c5:=pool.Get()
fmt.Println(c,c2,c3,c4,c5)
time.Sleep(time.Second * 5)//redis一共有多少個連接??
c1.Close()
c2.Close()
c3.Close()
c4.Close()
c5.Close()
time.Sleep(time.Second*5) //redis一共有多少個連接??
//下次是怎麼取出來的??
b1:=pool.Get()
b2:=pool.Get()
b3:=pool.Get()
fmt.Println(b1,b2,b3)
time.Sleep(time.Second*5)
b1.Close()
b2.Close()
b3.Close()
//redis目前一共有多少個連接??
for{
fmt.Println("主程序運行中....")
time.Sleep(time.Second*1)
}
}
// redis pool
func PoolInitRedis(server string, password string) *redigo.Pool {
return &redigo.Pool{
MaxIdle: 2,//空閒數
IdleTimeout: 240 * time.Second,
MaxActive: 3,//最大數
Dial: func() (redigo.Conn, error) {
c, err := redigo.Dial("tcp", server)
if err != nil {
return nil, err
}
if password != "" {
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
return nil, err
}
}
return c, err
},
TestOnBorrow: func(c redigo.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
}
}
三、參數配置說明
MaxIdle:最大空閒連接數,沒有redis操作進依然可以保持這個連接數量
MaxActive:最大連接數。同一時間最多有這麼多的連接
一般go程序運行時選設置redis連接池的初始化。假如我們設置MaxIdle:2,MaxActive:3時。
連接時:調用pool.Get()時,先從MaxIdle中取出可用連接,如果失敗,則看當前設置的MaxActive是否有超出最大數,沒有超出則創建一個新的連接。
斷開時:調用c.Close() 後,看當前連接數,如果比MaxIdle設置的數量大,則關閉redis連接。反之將連接放回到連接池中。大體如下流程圖所示:
四、分析與實驗
例子2中,程序啓動時休息的5秒裏,我們先看一下redis中的當前的連接數,當前連接數只有1個,這個是當前cli的連接。說明go還沒有和redis進行連接。我們可以通過info clients 或client list來查看當前redis連接情況。
redis-cli
//當前客戶端連接數
127.0.0.1:6379> info clients
# Clients
connected_clients:1
我們get了5次,想是取出5個連接,但我們所設置的MaxActive是3,那redis最大連接數只有3個:
c1 :=pool.Get()
c2:=pool.Get()
c3:=pool.Get()
c4:=pool.Get()
c5:=pool.Get()
可以在redis中使用 info clients 命令查看
127.0.0.1:6379> info clients
# Clients
connected_clients:4 (這裏顯示4個是因爲本身自己的cli連接redis時會有一個連接)
調用關閉接口,發現雖然是關閉了5次。但最終還有2個連接沒有關閉。因爲我們設置的MaxIdle是2
c1.Close()
c2.Close()
c3.Close()
c4.Close()
c5.Close()
可以在redis中使用 info clients 命令查看
127.0.0.1:6379> info clients
# Clients
connected_clients:3 (這裏顯示3個是因爲本身自己的cli連接redis時會有一個連接)