当前位置:网站首页>Go redis initialization connection
Go redis initialization connection
2022-07-06 09:09:00 【~Pompeii】
Catalog
go-redis Initialize the connection
install
If you are using Redis 6, Please install go-redis/ v8:
go get github.com/go-redis/redis/v8
If you are using Redis 7, Please install go-redis/ v9( It's in the testing phase ):
go get github.com/go-redis/redis/v9
Connect to Redis The server
To connect to Redis The server :
import "github.com/go-redis/redis/v8"
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
Another popular method is to use connection strings :
opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")
if err != nil {
panic(err)
}
rdb := redis.NewClient(opt)
Use TLS
To enable the TLS/SSL, You need to provide an empty tls.Config
. If you are using a private certificate , You need to Specify to open in a new window They are tls.Config
.
rdb := redis.NewClient(&redis.Options{
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
//Certificates: []tls.Certificate{cert}
},
})
If you get x509: cannot validate certificate for xxx.xxx.xxx.xxx because it doesn't contain any IP SANs
, Please try setting ServerName
Options :
rdb := redis.NewClient(&redis.Options{
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: "your.domain.com",
},
})
adopt SSH
adopt SSH Channel connection :
sshConfig := &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{
ssh.Password("password")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 15 * time.Second,
}
sshClient, err := ssh.Dial("tcp", "remoteIP:22", sshConfig)
if err != nil {
panic(err)
}
rdb := redis.NewClient(&redis.Options{
Addr: net.JoinHostPort("127.0.0.1", "6379"),
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
return sshClient.Dial(network, addr)
},
// Disable timeouts, because SSH does not support deadlines.
ReadTimeout: -1,
WriteTimeout: -1,
})
dial tcp: i/o timeout
When go-redis Can't connect to Redis Server time , You will receive dial tcp: i/o timeout
Error message , for example , When the server is down or the port is protected by a firewall . To check Redis Whether the server is listening on port , Please run go-redis Run on the client's host telnet command :
telnet localhost 6379
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
If you use Docker、Istio Or any other service grid /sidecar, Make sure the application starts after the container is fully available , for example , By configuring The health check opens in a new window Use Docker and holdApplicationUntilProxyStarts
Istio.
Context
Every Redis Commands accept a context , You can use it to set Overtime Or spread some information , for example Tracking context .
ctx := context.Background()
Carry out orders
Carry out orders :
val, err := rdb.Get(ctx, "key").Result()
fmt.Println(val)
perhaps , You can save the command and access the values and errors respectively later :
get := rdb.Get(ctx, "key")
fmt.Println(get.Val(), get.Err())
Execute unsupported commands
To perform any / Custom command :
val, err := rdb.Do(ctx, "get", "key").Result()
if err != nil {
if err == redis.Nil {
fmt.Println("key does not exists")
return
}
panic(err)
}
fmt.Println(val.(string))
Do
Return to one Cmd Open... In a new window There are a bunch of assistants who can handle interface{}
value :
// Text is a shortcut for get.Val().(string) with proper error handling.
val, err := rdb.Do(ctx, "get", "key").Text()
fmt.Println(val, err)
A complete list of assistants :
s, err := cmd.Text()
flag, err := cmd.Bool()
num, err := cmd.Int()
num, err := cmd.Int64()
num, err := cmd.Uint64()
num, err := cmd.Float32()
num, err := cmd.Float64()
ss, err := cmd.StringSlice()
ns, err := cmd.Int64Slice()
ns, err := cmd.Uint64Slice()
fs, err := cmd.Float32Slice()
fs, err := cmd.Float64Slice()
bs, err := cmd.BoolSlice()
redis.Nil
go-redis export redis.Nil
Error and in Redis The server returns it when it responds (nil)
. You can use redis-cli Check Redis Response returned .
In the following example , We use redis.Nil
To distinguish between empty string replies and nil reply (key non-existent ):
val, err := rdb.Get(ctx, "key").Result()
switch {
case err == redis.Nil:
fmt.Println("key does not exist")
case err != nil:
fmt.Println("Get failed", err)
case val == "":
fmt.Println("value is empty")
}
GET
for example , Not the only return nil Reply to the order ,BLPOP
also ZSCORE
Can return redis.Nil
.
Conn
Conn Represents a single Redis Connect , Instead of connection pooling . Unless there is a special need for continuous single Redis Connect , Otherwise, it is preferred to run the command from the client .
cn := rdb.Conn(ctx)
defer cn.Close()
if err := cn.ClientSetName(ctx, "myclient").Err(); err != nil {
panic(err)
}
name, err := cn.ClientGetName(ctx).Result()
if err != nil {
panic(err)
}
fmt.Println("client name", name)
redis colony
go-redis Bring their own Redis Cluster The client opens in a new window . below ,redis.ClusterClient
be used for redis.Client
Communicate with each node in the cluster . Every one of them redis.Client
Maintain a separate connection pool .
To connect to Redis colony :
import "github.com/go-redis/redis/v8"
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{
":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
// To route commands by latency or randomly, enable one of the following.
//RouteByLatency: true,
//RouteRandomly: true,
})
Iterative fragmentation :
err := rdb.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
return shard.Ping(ctx).Err()
})
if err != nil {
panic(err)
}
To iterate over the master node , Please use ForEachMaster
. To traverse the slave node , Please use ForEachSlave
.
To change the options for some tiles :
rdb := redis.NewClusterClient(&redis.ClusterOptions{
NewClient: func(opt *redis.Options) *redis.NewClient {
user, pass := userPassForAddr(opt.Addr)
opt.Username = user
opt.Password = pass
return redis.NewClient(opt)
},
})
redis sentry
Redis Server client
Connect to by Redis Sentinel Managed Redis The server opens in a new window :
import "github.com/go-redis/redis/v8"
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "master-name",
SentinelAddrs: []string{
":9126", ":9127", ":9128"},
})
from v8 Start , You can use experiments NewFailoverClusterClient
Route read-only commands to slave nodes :
import "github.com/go-redis/redis/v8"
rdb := redis.NewFailoverClusterClient(&redis.FailoverOptions{
MasterName: "master-name",
SentinelAddrs: []string{
":9126", ":9127", ":9128"},
// To route commands by latency or randomly, enable one of the following.
//RouteByLatency: true,
//RouteRandomly: true,
})
Redis Sentinel client
To connect to Redis Sentinel In itself :
import "github.com/go-redis/redis/v8"
sentinel := redis.NewSentinelClient(&redis.Options{
Addr: ":9126",
})
addr, err := sentinel.GetMasterAddrByName(ctx, "master-name").Result()
redis Ring
Introduce
Ring It's a Redis client , It uses consistent hashes in multiple Redis The server ( Fragmentation ) Distribute keys between . Multiple goroutine Concurrent use is safe .
Ring Monitor the status of each fragment and delete the dead fragment from the ring . When a slice goes online , It will be added back to the loop . This achieves maximum availability and partition fault tolerance , But there is no consistency between different partitions or even clients . Each client uses shards available to the client , And do not coordinate with other clients when the partition state changes .
When you need more Redis When the server caches and can tolerate the loss of data when one of the servers crashes , You should use Ring. Otherwise you should use Redis Cluster or Redis Server.
Quick start
Create a containing 3 Piece by piece Ring colony :
import "github.com/go-redis/redis/v8"
rdb := redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"shard1": ":7000",
"shard2": ":7001",
"shard3": ":7002",
},
})
Then the client can use it as usual :
if err := rdb.Set(ctx, "foo", "bar", 0).Err(); err != nil {
panic(err)
}
Iterative fragmentation :
err := rdb.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
return shard.Ping(ctx).Err()
})
if err != nil {
panic(err)
}
Each slice option
To change the slice connection options :
rdb := redis.NewRing(&redis.RingOptions{
NewClient: func(opt *redis.Options) *redis.NewClient {
user, pass := userPassForAddr(opt.Addr)
opt.Username = user
opt.Password = pass
return redis.NewClient(opt)
},
})
Key distribution
By default ,Ring Use Rendezvous Open... In a new window Hash to distribute keys on multiple slices . But you can change the default consistent hash implementation :
import "github.com/golang/groupcache/consistenthash"
ring := redis.NewRing(&redis.RingOptions{
NewConsistentHash: func() {
return consistenthash.New(100, crc32.ChecksumIEEE)
},
})
Redis Universal client
UniversalClient
Is an abstract client , Based on the options provided , representative a ClusterClient
、 aFailoverClient
or single-node Client
. This is useful for testing cluster specific applications locally or having different clients in different environments .
NewUniversalClient
Return a new multi client . The type of client returned depends on the following conditions :
- If this option is specified , Then return to
MasterName
Sentinel support .FailoverClient
- If the quantity
Addrs
by 2 Or more ,ClusterClient
Then return to a. - otherwise , Returns a single node
Client
.
// rdb is *redis.Client.
rdb := NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{
":6379"},
})
// rdb is *redis.ClusterClient.
rdb := NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{
":6379", ":6380"},
})
// rdb is *redis.FailoverClient.
rdb := NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{
":6379"},
MasterName: "mymaster",
})
Golang Redis The Conduit 、WATCH And transaction
Use pipes to accelerate Redis
Redis Pipelines allow you to use a single client - The server - The client executes multiple commands back and forth to improve performance . You can route commands in a pipeline , Then use a single write like a single command + Read operations execute queued commands , Instead of executing one by one 100 An order .
To use a single write + Read operation executes multiple commands :
pipe := rdb.Pipeline()
incr := pipe.Incr(ctx, "pipeline_counter")
pipe.Expire(ctx, "pipeline_counter", time.Hour)
cmds, err := pipe.Exec(ctx)
if err != nil {
panic(err)
}
// The value is available only after Exec is called.
fmt.Println(incr.Val())
perhaps , You can use Pipelined
which call :Exec
var incr *redis.IntCmd
cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
incr = pipe.Incr(ctx, "pipelined_counter")
pipe.Expire(ctx, "pipelined_counter", time.Hour)
return nil
})
if err != nil {
panic(err)
}
// The value is available only after the pipeline is executed.
fmt.Println(incr.Val())
The pipeline also returns the executed command , So you can traverse them to retrieve the results :
cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
for i := 0; i < 100; i++ {
pipe.Get(ctx, fmt.Sprintf("key%d", i))
}
return nil
})
if err != nil {
panic(err)
}
for _, cmd := range cmds {
fmt.Println(cmd.(*redis.StringCmd).Val())
}
Transactions and Watch
Use Redis The transaction opens in a new window , You can observe key changes and execute the pipeline only if the observed key has not been changed by another client . This method of conflict resolution is also known as Optimistic locking
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
TxPipelined
You can use and Package with MULTI and EXEC Command pipeline TxPipeline
, But it is not very useful in itself :
cmds, err := rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
for i := 0; i < 100; i++ {
pipe.Get(ctx, fmt.Sprintf("key%d", i))
}
return nil
})
if err != nil {
panic(err)
}
// MULTI
// GET key0
// GET key1
// ...
// GET key99
// EXEC
contrary , You should use Watch Transaction processing pipeline Open... In a new window , for example , We can correctly realize INCR Open... In a new window Use GET
,SET
and WATCH
The order of Please note how we use redis.TxFailedErr
To check whether the transaction failed .
// Redis transactions use optimistic locking.
const maxRetries = 1000
// Increment transactionally increments the key using GET and SET commands.
func increment(key string) error {
// Transactional function.
txf := func(tx *redis.Tx) error {
// Get the current value or zero.
n, err := tx.Get(ctx, key).Int()
if err != nil && err != redis.Nil {
return err
}
// Actual operation (local in optimistic lock).
n++
// Operation is commited only if the watched keys remain unchanged.
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.Set(ctx, key, n, 0)
return nil
})
return err
}
// Retry if the key has been changed.
for i := 0; i < maxRetries; i++ {
err := rdb.Watch(ctx, txf, key)
if err == nil {
// Success.
return nil
}
if err == redis.TxFailedErr {
// Optimistic lock lost. Retry.
continue
}
// Return any other error.
return err
}
return errors.New("increment reached maximum number of retries")
}
Redis Publish subscribe
go-redis Allow publishing messages and subscribing to channels . When a network error occurs , It will also automatically reconnect to Redis Server.
To publish a message :
err := rdb.Publish(ctx, "mychannel1", "payload").Err()
if err != nil {
panic(err)
}
Subscribed Channels :
// There is no error because go-redis automatically reconnects on error.
pubsub := rdb.Subscribe(ctx, "mychannel1")
// Close the subscription when we are done.
defer pubsub.Close()
To receive messages :
for {
msg, err := pubsub.ReceiveMessage(ctx)
if err != nil {
panic(err)
}
fmt.Println(msg.Channel, msg.Payload)
}
But the easiest way is to use the Go channel :
ch := pubsub.Channel()
for msg := range ch {
fmt.Println(msg.Channel, msg.Payload)
}
边栏推荐
- [text generation] recommended in the collection of papers - Stanford researchers introduce time control methods to make long text generation more smooth
- The carousel component of ant design calls prev and next methods in TS (typescript) environment
- MYSQL卸载方法与安装方法
- 在QWidget上实现窗口阻塞
- LeetCode:劍指 Offer 42. 連續子數組的最大和
- [OC-Foundation框架]-<字符串And日期与时间>
- LeetCode:162. Looking for peak
- LeetCode:26. 删除有序数组中的重复项
- LeetCode:162. 寻找峰值
- [today in history] February 13: the father of transistors was born The 20th anniversary of net; Agile software development manifesto was born
猜你喜欢
随机推荐
多元聚类分析
KDD 2022 paper collection (under continuous update)
[oc]- < getting started with UI> -- common controls uibutton
UML diagram memory skills
xargs命令的基本用法
什么是MySQL?MySql的学习之路是怎样的
Pytorch view tensor memory size
Esp8266-rtos IOT development
KDD 2022论文合集(持续更新中)
Cesium draw points, lines, and faces
【图的三大存储方式】只会用邻接矩阵就out了
Digital people anchor 618 sign language with goods, convenient for 27.8 million people with hearing impairment
Advanced Computer Network Review(4)——Congestion Control of MPTCP
Implement window blocking on QWidget
力扣每日一题(二)
Advance Computer Network Review(1)——FatTree
[OC-Foundation框架]--<Copy对象复制>
BN folding and its quantification
Compétences en mémoire des graphiques UML
Leetcode: Jianzhi offer 03 Duplicate numbers in array