您的位置:首页 > 数据库 > Redis

Go进阶(1): Golang + Goland 研究Redis的基本操作与函数接口

2019-08-14 02:19 525 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/shenziheng1/article/details/99370922

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函数中输入测试代码:

[code]package main

import (
"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接口的相关方法如下:

[code]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的方式:

[code]//	 使用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命令操作。

[code]package main
// Redis链接+执行

import (
"fmt"
"time"
redis "github.com/gomodule/redigo/redis"
)
func main() {
// 使用Dial进行tcp连接.设置长连接时间为1s,连接超时时间为5s,读写超时均为1s,并设置密码连接
// 由于redis服务器设置了密码,如果密码错误会报异常: ERR invalid password
rds,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,比如

msetmgethmsethmget
等等。原子性可能会更好一些。
Conn
会使用
SendFlushReceive
来支持pipeline操作。

Send
会写入命令到连接的输出缓冲区里。

Flush
会将输出缓冲区中的数据刷新到服务端。

Receive
回去服务器端读取单个响应。

[code]rds.Send("SET", "foo", "bar")
rds.Flush()
v, err = rds.Receive()

其实,在

Do()
方法中包含了
SendFlushReceive
等方法。例如,使用
Send()
Do()
方法来实现pipeline:

  • 并发处理

一般并发访问redis,社区中大部分专家建议使用安全线程池去获取连接,从一个goroutine中使用和释放一个链接。正如前面提到的,从线程池中获取的连接会有并发限制。

  • 发布/订阅模式(Publish and Subscribe)

使用

SendFlushReceive
等方法来实现Pub/Sub订阅,例如:

[code]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
的合适类型。例如:

[code]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:

[code]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的并发性能。相关结构体/方法/实例如下:

[code]// 相关的结构体和方法
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 int
IdleCount   int
}
[code]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连接池中最大的空闲链接为3
MaxActive:          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 mget
fmt.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)
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: