Go进阶(1): Golang + Goland 研究Redis的基本操作与函数接口
1. 開發環境搭建
-
GOROOT變量值是安裝的go路徑
-
PATH環境變量就是%GOROOT%\bin路徑
-
GOPATH環境變量是工作目錄,就是寫代碼的目錄,編譯源代碼等生成的文件都會放到這個目錄下
Note:需要在工作目錄下新建三個文件夾,分別是bin、pkg、src;src 目錄存放的是我們的go源代碼,不同工程項目的代碼以包名區分;pkg 中存放編譯后生成的文件(比如:.a);bin文件夾存放go install命名生成的可執行文件; 建議把GOPATH/bin加到系統的環境變量path里面,這樣就可以直接在終端里使用我們go開發生成的程序。
2. 初步配置Redisgo并調試/windows
- 先安裝Git,國內比較快的網址鏈接https://github.com/waylau/git-for-win
- 建議在%GOPATH%src中先創建Go工程,如redisTesting
- 然后在main.go中直接 import?redis "github.com/gomodule/redigo/redis";此時,Goland會自動提示安裝包的拉取,Redis包就在%GOPATH%pkg目錄中。
- 作為備選,可以在Goland終端中執行?go get -v github.com/gomodule/redigo
在%GOPATH%src的redisTesting工程的mian.go函數中輸入測試代碼:
package mainimport ("fmt""github.com/gomodule/redigo/redis" )func main() {// 使用redis封裝的Dial進行tcp連接rds,err := redis.Dial("tcp","localhost:32771")errCheck(err)defer rds.Close()//對本次連接進行set操作_,setErr := rds.Do("set","url","xxbandy.github.io")errCheck(setErr)//使用redis的string類型獲取set的k/v信息r,getErr := redis.String(rds.Do("get","url"))errCheck(getErr)fmt.Println(r)}func errCheck(err error) {if err != nil {fmt.Println("sorry,has some error:",err)return} }進一步:
- 終端執行 go build main.go 組建
- 終端執行 go run main.go 運行
3. Redis的Golang接口函數
- 鏈接功能
Conn接口是redis操作過程中比較重要的接口。應用一般通過調用Dial、DialWithTimeout、NewConn函數來創建一個鏈接。Note:?應用必須在操作redis結束之后去調用該連接的Close方法來關閉連接,以防止資源消耗以及其他問題。
Conn接口的相關方法如下:
type Conn interference{// 關閉鏈接Close() error// 當連接不可用的情況下,返回非空值Err() error// Do方法是指向redis服務器發送命令 并 返回接收到的命令Do(commandName string, args ...interface{}) (reply interface{}, err error)// Send將相關的命令寫入到客戶端的buffer中Send(commandName string, arg ...interface{}) error// Flush將客戶端的輸出緩沖內容刷新到redis服務器端Flush() error// Receive從redis服務端接受單個回復Receive() (reply interface{}, err error) }常用的創建Conn的方式:
// 使用Dial函數 // func Dial(network, address string, options ...DialOption) (Conn, error) /*可選的參數 func DialConnectTimeout(d time.Duration) DialOption func DialDatabase(db int) DialOption func DialKeepAlive(d time.Duration) DialOption func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption func DialPassword(password string) DialOption func DialReadTimeout(d time.Duration) DialOption func DialWriteTimeout(d time.Duration) DialOption */ rds,err := redis.Dial("tcp","localhost:32771") defer rds.Close()// 使用DialTimeout函數(默認增加了連接、讀寫超時時間) // func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error)// 使用NewConn函數(將一個網絡連接轉換成redis連接) // func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn- Redis命令的執行
一般采用Do()方法執行redis命令操作。
package main // Redis鏈接+執行import ("fmt""time"redis "github.com/gomodule/redigo/redis" ) func main() {// 使用Dial進行tcp連接.設置長連接時間為1s,連接超時時間為5s,讀寫超時均為1s,并設置密碼連接// 由于redis服務器設置了密碼,如果密碼錯誤會報異常: ERR invalid passwordrds,err := redis.Dial("tcp","localhost:32771",redis.DialKeepAlive(1*time.Second),redis.DialPassword("ziheng"),redis.DialConnectTimeout(5*time.Second),redis.DialReadTimeout(1*time.Second),redis.DialWriteTimeout(1*time.Second))fmt.Println(err)defer rds.Close()// 使用Conn的Do方法來操作redis命令// Do(commandName string, args ...interface{}) (reply interface{}, err error)// 使用redis的string類型獲取set的k/v信息r,getErr := redis.String(rds.Do("get","url"))fmt.Println(getErr)fmt.Println(r) }- 使用Pipelining操作redis
Note:建議使用redis自身包含的命令進行批量操作而不是使用pipelining,比如mset、mget、hmset、hmget等等。原子性可能會更好一些。Conn會使用Send、Flush、Receive來支持pipeline操作。
Send會寫入命令到連接的輸出緩沖區里。
Flush會將輸出緩沖區中的數據刷新到服務端。
Receive回去服務器端讀取單個響應。
rds.Send("SET", "foo", "bar") rds.Flush() v, err = rds.Receive()其實,在Do()方法中包含了Send、Flush、Receive等方法。例如,使用Send()和Do()方法來實現pipeline:
- 并發處理
一般并發訪問redis,社區中大部分專家建議使用安全線程池去獲取連接,從一個goroutine中使用和釋放一個鏈接。正如前面提到的,從線程池中獲取的連接會有并發限制。
- 發布/訂閱模式(Publish and Subscribe)
使用Send、Flush、Receive等方法來實現Pub/Sub訂閱,例如:
rds.Send("SUBSCRIBE","ANSIBLE-KEY") rds.Flush() for {reply,err := rds.Receive()if err != nil {return err}//process pushed message }PubSubConn類型用convenience方法包裝了一個Conn實現了訂閱,Subscribe PSubscribe Unsubscribe PUnsubscribe方法會send并且flush一個訂閱管理命令。接收方法會將push的消息轉換成一個可以進行switch的合適類型。例如:
psc := redis.PubSubConn{Conn: c}psc.Subscribe("example") for {switch v := psc.Receive().(type) {case redis.Message:fmt.Printf("%s: message: %s\n", v.Channel, v.Data)case redis.Subscription:fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)case error:return v} }3.?Redigo操作redis的應用場景
- 使用String和Int獲取字符串類型的輸出
就是set?get mset mget之類的命令。 Note:當獲取數字類型的key時需要使用redis.Int()獲取數字類型的輸出結果。例如,我想獲取string類型的name, Int類型的id:
func main() {c,connErr := redisConn("localhost","32771","ziheng")errCheck("connErr",connErr)getV,_ := redis.String(c.Do("get","name"))getV2,_ := redis.Int(c.Do("get","id"))fmt.Println(getV,getV2) //output1 = "shenziheng"; output2 = "0001" // mget name id 同理 }- 使用redis的安全鏈連接池
使用連接池可以高效的管理redis的連接,并可以方便地控制redis的并發性能。相關結構體/方法/實例如下:
// 相關的結構體和方法 type Pool struct {//創建一個tcp鏈接的匿名函數Dial func() (Conn, error)//可選的函數,用來對之前使用過的空閑鏈接進行安全檢查TestOnBorrow func(c Conn, t time.Time) error//最大可用鏈接數MaxIdle int//在給定的時間,最大可分配的連接數。為0則不限制MaxActive int//空閑鏈接的關閉時間,如果為空,空閑鏈接不會被關閉IdleTimeout time.Duration//如果Wait為true并且進行MaxActive限制了,Get()將會等待鏈接被返回Wait bool//關閉老鏈接的時間區間。如果為空則不會在生命周期內關閉鏈接MaxConnLifetime time.Duration }//創建一個Pool結構體 func NewPool(newFn func() (Conn, error), maxIdle int) *Pool // 獲取pool的連接數,包含空閑鏈接和使用連接 func (p *Pool) ActiveCount() int//關閉并釋放pool中的資源 func (p *Pool) Close() error //從pool中獲取一個連接 func (p *Pool) Get() Conn // 獲取空閑鏈接數 func (p *Pool) IdleCount() int // 獲取連接池的狀態信息 func (p *Pool) Stats() PoolStats type PoolStats struct {ActiveCount intIdleCount int } package main import ("fmt""time""os""github.com/gomodule/redigo/redis" ) //構造一個鏈接函數,如果沒有密碼,passwd為空字符串 func redisConn(ip,port,passwd string) (redis.Conn, error) {rds,err := redis.Dial("tcp",ip+":"+port,redis.DialConnectTimeout(5*time.Second),redis.DialReadTimeout(1*time.Second),redis.DialWriteTimeout(1*time.Second),redis.DialPassword(passwd),redis.DialKeepAlive(1*time.Second),)return rds,err }//構造一個錯誤檢查函數 func errCheck(tp string,err error) {if err != nil {fmt.Printf("sorry,has some error for %s.\r\n",tp,err)os.Exit(-1)} }//構造一個連接池 //url為包裝了redis的連接參數ip,port,passwd func newPool(ip,port,passwd string) *redis.Pool {return &redis.Pool{MaxIdle: 5, //定義redis連接池中最大的空閑鏈接為3MaxActive: 18, //在給定時間已分配的最大連接數(限制并發數)IdleTimeout: 240 * time.Second,MaxConnLifetime: 300 * time.Second,Dial: func() (redis.Conn,error) { return redisConn(ip,port,passwd) },} }func main() {//使用newPool構建一個redis連接池pool := newPool("localhost","32771","123qweasd")defer pool.Close()for i := 0;i <= 4;i++ {go func() {//從pool里面獲取一個可用的redis連接c := pool.Get()defer c.Close()//mset mgetfmt.Printf("ActiveCount:%d IdleCount:%d\r\n",pool.Stats().ActiveCount,pool.Stats().IdleCount)_,setErr := c.Do("mset","name","biaoge","url","http://xxbandy.github.io")errCheck("setErr",setErr)if r,mgetErr := redis.Strings(c.Do("mget","name","url")); mgetErr == nil {for _,v := range r {fmt.Println("mget ",v)}}}()}time.Sleep(1*time.Second) }?
總結
以上是生活随笔為你收集整理的Go进阶(1): Golang + Goland 研究Redis的基本操作与函数接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开放源码发展史
- 下一篇: Fedora 10 的主要功能特性已经冻