本文将为您提供关于使用Redis-Cli了解延迟的详细介绍,我们还将为您解释redis延迟的相关知识,同时,我们还将为您提供关于(Redis基础教程之六)如何使用Redis中的List、Golang中
本文将为您提供关于使用Redis-Cli了解延迟的详细介绍,我们还将为您解释redis 延迟的相关知识,同时,我们还将为您提供关于(Redis基础教程之六)如何使用Redis中的List、Golang中如何使用redis实现延迟队列。、Go实战--golang中使用redis(redigo和go-redis/redis)、keepalived+redis 高可用redis主从解决方案的实用信息。
本文目录一览:- 使用Redis-Cli了解延迟(redis 延迟)
- (Redis基础教程之六)如何使用Redis中的List
- Golang中如何使用redis实现延迟队列。
- Go实战--golang中使用redis(redigo和go-redis/redis)
- keepalived+redis 高可用redis主从解决方案
使用Redis-Cli了解延迟(redis 延迟)
我正在使用该redis-cli
工具观察Redis服务器的延迟。这是一个例子:
ubuntu:~$ redis-cli --latency -h 127.0.0.1 -p 6379min: 0, max: 15, avg: 0.12 (2839 samples)
问题是,这些值实际上意味着什么?我正在努力寻找有关此工具的文档,而该文档无法通过该工具自己的帮助文档获得。
答案1
小编典典该redis-cli --latency -h-p
命令是一个工具,可以帮助您解决和了解Redis可能遇到的延迟问题。通过测量Redis服务器响应Redis PING命令的时间(以毫秒为单位)来实现。
在这种情况下,延迟是客户端发出命令的时间与客户端收到对命令的回复之间的最大延迟。通常,Redis的处理时间非常短,在亚微秒的范围内,但是在某些情况下会导致更高的等待时间。
- Redis的延迟问题的故障排除
因此,当我们运行命令redis-cli --latency -h 127.0.0.1 -p6379
Redis时,它将进入一种特殊模式,在该模式下,Redis会连续采样延迟(通过运行PING)。
现在让我们细分返回的数据: min: 0, max: 15, avg: 0.12 (2839 samples)
什么(2839 samples)
啊 这是redis-cli
记录的发出PING命令并接收响应的次数。换句话说,这是您的样本数据。在我们的示例中,我们记录了2839个请求和响应。
什么min: 0
啊
该min
值表示CLI发出PING
的时间与接收到回复的时间之间的最小延迟。换句话说,这是我们采样数据中绝对最佳的响应时间。
什么max: 15
啊
该max
值与的相反min
。它表示CLI发出PING
的时间与收到对命令的答复之间的最大延迟。这是我们采样数据中最长的响应时间。在我们的2839个样本示例中,交易时间最长15ms
。
什么avg: 0.12
啊
该avg
值是所有采样数据的平均响应时间(以毫秒为单位)。因此,平均而言,我们从2839个样本中获取了响应时间0.12ms
。
对于基本上,较高的数字min
,max
以及avg
是一件坏事。
有关如何使用此数据的一些很好的后续材料:
- Redis延迟问题疑难解答
- Redis延迟监控框架
- Redis有多快?
- Redis性能思想
(Redis基础教程之六)如何使用Redis中的List
- 如何在ubuntu18.04上安装和保护redis
- 如何连接到Redis数据库
- 如何管理Redis数据库和Keys
- 如何在Redis中管理副本和客户端
- 如何在Redis中管理字符串
- 如何在Redis中管理list
- 如何在Redis中管理Hashes
- 如何在Redis中管理Sets
- 如何在Redis中管理Sorted Sets
- 如何在Redis中运行事务
- 如何使Redis中的Key失效
- 如何解决Redis中的问题
- 如何从命令行更改Redis的配置
- Redis数据类型简介
介绍
Redis是一个开源的内存中键值数据存储。在Redis中,列表是按插入顺序排序的字符串的集合,类似于链接列表。本教程介绍了如何在Redis列表中创建和使用元素。
如何使用本指南
本指南以备有完整示例的备忘单形式编写。我们鼓励您跳至与您要完成的任务相关的任何部分。
本指南中显示的命令已在运行Redis版本4.0.9的Ubuntu 18.04服务器上进行了测试。要设置类似的环境,您可以按照我们的指南如何在Ubuntu 18.04上安装和保护Redis的步骤1进行操作。我们将通过使用Redis命令行界面运行它们来演示这些命令的行为。请注意,如果您使用其他Redis界面(例如Redli),则某些命令的确切输出可能会有所不同。[](https://zthinker.com/archives...redis-cli
[](https://github.com/IBM-Cloud/...
另外,您可以提供一个托管的Redis数据库实例来测试这些命令,但是请注意,根据数据库提供者所允许的控制级别,本指南中的某些命令可能无法按所述方式工作。要配置DigitalOcean托管数据库,请遵循我们的托管数据库产品文档。然后,您必须 安装Redli 或 设置TLS隧道才能通过TLS连接到托管数据库。
Creating Lists
一个键只能容纳一个列表,尽管任何列表都可以容纳40亿个元素。Redis从左到右读取列表,您可以使用命令将新列表元素添加到列表的开头(“左”端),lpush
也可以使用尾部(“右”端)添加新元素rpush
。您还可以使用lpush
或rpush
创建新列表:
lpush key value
这两个命令都将输出一个整数,以显示列表中有多少个元素。为了说明,请运行以下命令以创建包含“我认为是我”的格言的列表:
lpush key_philosophy1 "therefore"
lpush key_philosophy1 "think"
rpush key_philosophy1 "I"
lpush key_philosophy1 "I"
rpush key_philosophy1 "am"
最后一条命令的输出将显示为:
(integer) 5
请注意,您可以使用单个lpush
或rpush
语句添加多个列表元素:
rpush key_philosophy1 "-" "Rene" "Decartes"
该lpushx
和rpushx
命令也用于元素添加到列表中,但如果给定的名单已经存在只会工作。如果任何一个命令失败,它将返回(integer) 0
:
rpushx key_philosophy2 "Happiness" "is" "the" "highest" "good" "–" "Aristotle"
(integer) 0
要更改列表中的现有元素,请运行lset
命令,然后输入键名,要更改的元素的索引和新值:
lset key_philosophy1 5 "sayeth"
如果尝试将列表元素添加到不包含列表的现有键中,则会导致数据类型冲突并返回错误。例如,以下set
命令创建一个保存字符串的键,因此以下尝试向其中添加列表元素的尝试lpush
将失败:
set key_philosophy3 "What is love?"
lpush key_philosophy3 "Baby don''t hurt me"
(error) WRONGTYPE Operation against a key holding the wrong kind of value
无法将Redis密钥从一种数据类型转换为另一种数据类型,因此要变成key_philosophy3
列表,您需要删除该密钥并使用lpush
or rpush
命令重新开始。
从列表中检索元素(Retrieving Elements from a List)
要检索列表中的项目范围,请使用lrange
命令,后跟起始偏移量)和终止偏移量。每个偏移量都是从零开始的索引,0
表示代表列表中的第一个元素,1
代表下一个,依此类推。
以下命令将从上一节创建的示例列表中返回所有元素:
lrange key_philosophy1 0 7
1) "I"
2) "think"
3) "therefore"
4) "I"
5) "am"
6) "sayeth"
7) "Rene"
8) "Decartes"
传递给的偏移量lrange
也可以为负数。在这种情况下使用时,-1
代表列表中的最后一个元素,-2
代表列表中的倒数第二个元素,依此类推。以下示例返回保存在列表中的最后三个元素key_philosophy1
:
lrange key_philosophy1 -3 -1
1) "I"
2) "am"
3) "sayeth"
要从列表中检索单个元素,可以使用lindex
命令。但是,此命令要求您提供元素的索引作为参数。与一样lrange
,索引是从零开始的,这意味着第一个元素在index 0
,第二个元素在index 1
,依此类推:
lindex key_philosophy1 4
"am"
要查找给定列表中有多少个元素,请使用以下llen
命令,该命令是“ l ist len gth”的缩写:
llen key_philosophy1
(integer) 8
如果存储在给定键上的值不存在,llen
将返回错误。
从列表中删除元素
该lrem
命令将删除与给定值匹配的已定义次数的第一个。要对此进行试验,请创建以下列表:
rpush key_Bond "Never" "Say" "Never" "Again" "You" "Only" "Live" "Twice" "Live" "and" "Let" "Die" "Tomorrow" "Never" "Dies"
以下lrem
示例将删除该值的第一次出现"Live"
:
lrem key_Bond 1 "Live"
此命令将输出从列表中删除的元素数量:
(integer) 1
传递给lrem
命令的数字也可以为负数。以下示例将删除该值的最后两个出现"Never"
:
lrem key_Bond -2 "Never"
(integer) 2
该lpop
命令从列表中删除并返回第一个或“最左边”的元素:
lpop key_Bond
"Never"
同样,要从列表中删除并返回最后或“最右边”的元素,请使用rpop
:
rpop key_Bond
"Dies"
Redis还包括rpoplpush
命令,该命令从列表中删除最后一个元素并将其推到另一个列表的开头:
rpoplpush key_Bond key_AfterToday
"Tomorrow"
如果传递给rpoplpush
命令的源键和目标键相同,则它将实质上旋转列表中的元素。
结论
本指南详细介绍了可用于在Redis中创建和管理列表的许多命令。如果您想在本指南中概述其他相关的命令,参数或过程,请在下面的评论中提出疑问或提出建议。
有关Redis命令的更多信息,请参阅关于如何管理Redis数据库的系列教程。
作者:分布式编程
出处:https://zthinker.com/
如果你喜欢本文,请长按二维码,关注 分布式编程
.
Golang中如何使用redis实现延迟队列。
延迟队列是一种非常实用的消息处理方式,它将消息延迟一段时间再进行处理,一般用于实现任务调度、定时任务等功能。在实际开发中,Redis是一款非常常用的缓存数据库,它提供了类似消息队列的功能,因此我们可以利用Redis来实现延迟队列。本文将介绍如何使用Golang和Redis实现延迟队列。
- Redis的ZSET
Redis提供了sorted sets(有序集合)的数据结构,我们可以使用它来实现一个延迟队列。在sorted sets中,每个元素都有一个score属性,用来表示元素的权重。sorted sets是按照score从小到大的顺序来存储元素的,score相同的元素会根据它们的成员来排序。我们可以将每个任务封装成一个元素,并将任务需要执行的时间作为该元素的score。
- 延迟队列的实现
具体来说,我们可以使用Redis的ZADD命令将任务添加到延迟队列。例如:
//添加任务到延迟队列 func AddTaskToDelayQueue(taskId string, delayTime int64) error { _, err := redisClient.ZAdd("DelayedQueue", redis.Z{ Score: float64(time.Now().Unix() + delayTime), Member: taskId, }).Result() if err != nil { return err } return nil }
在上述代码中,我们使用了Redis的ZADD命令将一个任务添加到名为“DelayedQueue”的sorted set中。其中,delayTime表示任务需要推迟的时间,Score为当前时间加上延迟时间,即任务需要执行的时间戳。
立即学习“go语言免费学习笔记(深入)”;
在实际的业务场景中,我们可以在任务执行前获取延迟队列中score最小的元素,即最近需要执行的任务:
//获取延迟任务队列中最近需要执行的任务id func GetNextTaskFromDelayQueue() (string, error) { now := time.Now().Unix() items, err := redisClient.ZRangeByScore("DelayedQueue", redis.ZRangeBy{ Min: "-inf", Max: strconv.FormatInt(now, 10), Offset: 0, Count: 1, }).Result() if err != nil { return "", err } if len(items) == 0 { return "", nil } return items[0], nil }
在上述代码中,我们使用了Redis的ZRangeByScore命令获取延迟队列中score小于等于当前时间戳的元素,然后取列表中的第一个元素作为下一个需要执行的任务。
- 任务执行后的处理
当我们从延迟队列中获取到需要执行的任务后,我们可以将任务从待执行列表中移动到已执行列表中,以便于我们统计任务的执行情况。
//将已经执行的任务移除 func RemoveTaskFromDelayQueue(taskId string) error { _, err := redisClient.ZRem("DelayedQueue", taskId).Result() if err != nil { return err } return nil }
- 完整代码示例
我们将上述代码整合在一起,并加入一些错误处理和日志信息,得到了完整的代码示例:
package delayqueue import ( "strconv" "time" "github.com/go-redis/redis" ) var redisClient *redis.Client //初始化redis连接 func InitRedis(redisAddr string, redisPassword string) error { redisClient = redis.NewClient(&redis.Options{ Addr: redisAddr, Password: redisPassword, DB: 0, }) _, err := redisClient.Ping().Result() if err != nil { return err } return nil } //添加任务到延迟队列 func AddTaskToDelayQueue(taskId string, delayTime int64) error { _, err := redisClient.ZAdd("DelayedQueue", redis.Z{ Score: float64(time.Now().Unix() + delayTime), Member: taskId, }).Result() if err != nil { return err } return nil } //获取延迟任务队列中最近需要执行的任务id func GetNextTaskFromDelayQueue() (string, error) { now := time.Now().Unix() items, err := redisClient.ZRangeByScore("DelayedQueue", redis.ZRangeBy{ Min: "-inf", Max: strconv.FormatInt(now, 10), Offset: 0, Count: 1, }).Result() if err != nil { return "", err } if len(items) == 0 { return "", nil } return items[0], nil } //将已经执行的任务移除 func RemoveTaskFromDelayQueue(taskId string) error { _, err := redisClient.ZRem("DelayedQueue", taskId).Result() if err != nil { return err } return nil }
- 总结
本文介绍了如何使用Golang和Redis来实现延迟队列。通过使用ZSET数据结构,我们可以轻松地实现一个延迟队列,在实际开发中非常实用。除了延迟队列,Redis还提供了很多其他的数据结构和功能,非常值得我们去探索和使用。
以上就是Golang中如何使用
Go实战--golang中使用redis(redigo和go-redis/redis)
生命不止,继续 go go go !!!
以前介绍过golang中如何使用sqlite3:
《Go实战–go语言操作sqlite数据库(The way to go)》
今天跟大家分享的是如何在golang中使用redis数据库。
何为redis
官网:
https://redis.io/
Redis is an in-memory database open-source software project implementing a networked,in-memory key-value store with optional durability.
Redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。
Redis 优势
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings,Lists,Hashes,Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe,通知,key 过期等等特性。
Redis与其他key-value存储有什么不同?
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
windows安装redis
这里只简单介绍一下Windows上redis的安装:
在官网中可以看到这样的描述:
Windows
The Redis project does not officially support Windows. However,the Microsoft Open Tech group develops and maintains this Windows port targeting Win64
下载
直接去github上下载就好了:
https://github.com/MicrosoftArchive/redis/releases
这里可以下载msi文件和zip文件,两者区别就是msi文件是安装文件,安装的过程中会帮助我们配好环境变量,所以推荐。
这里需要提示一下安装过程中,如果修改了端口号,一定要记住:
图1
启动服务端
安装成功后,通过终端键入如下命令:
redis-server.exe redis.windows.conf
一切没问题会出现下面的提示:
[11368] 13 Jul 10:10:31.487 # Creating Server TCP listening socket 127.0.0.1:6379: bind: No error
启动客户端
新打开一个终端,启动redis客户端,键入命令:
redis-cli.exe -h 127.0.0.1 -p 6379
测试:
127.0.0.1:6379> set mykey abc OK 127.0.0.1:6379> get mykey "abc"
开源库redigo的使用
github地址:
https://github.com/garyburd/redigo
文档地址:
http://godoc.org/github.com/garyburd/redigo/redis
获取:
go get github.com/garyburd/redigo/redis
连接redis
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c,err := redis.Dial("tcp","127.0.0.1:6379") if err != nil { fmt.Println("Connect to redis error",err) return } defer c.Close() }
读写
这里写入的值永远不会过期
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c,err) return } defer c.Close() _,err = c.Do("SET","mykey","superWang") if err != nil { fmt.Println("redis set Failed:",err) } username,err := redis.String(c.Do("GET","mykey")) if err != nil { fmt.Println("redis get Failed:",err) } else { fmt.Printf("Get mykey: %v \n",username) } }
如何设置过期呢,可以使用SET的附加参数:
package main import ( "fmt" "time" "github.com/garyburd/redigo/redis" ) func main() { c,"superWang","EX","5") if err != nil { fmt.Println("redis set Failed:",username) } time.Sleep(8 * time.Second) username,err = redis.String(c.Do("GET",username) } }
输出:
Get mykey: superWang
redis get Failed: redigo: nil returned
批量写入读取
MGET key [key …]
MSET key value [key value …]
批量写入读取对象(Hashtable)
HMSET key field value [field value …]
HMGET key field [field …]
检测值是否存在
EXISTS key
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c,err) } is_key_exit,err := redis.Bool(c.Do("EXISTS","mykey1")) if err != nil { fmt.Println("error:",err) } else { fmt.Printf("exists or not: %v \n",is_key_exit) } }
输出:
exists or not: false
删除
DEL key [key …]
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c,username) } _,err = c.Do("DEL","mykey") if err != nil { fmt.Println("redis delelte Failed:",username) } }
输出:
Get mykey: superWang
redis get Failed: redigo: nil returned
读写json到redis
package main import ( "encoding/json" "fmt" "github.com/garyburd/redigo/redis" ) func main() { c,err) return } defer c.Close() key := "profile" imap := map[string]string{"username": "666","phonenumber": "888"} value,_ := json.Marshal(imap) n,err := c.Do("SETNX",key,value) if err != nil { fmt.Println(err) } if n == int64(1) { fmt.Println("success") } var imapGet map[string]string valueGet,err := redis.Bytes(c.Do("GET",key)) if err != nil { fmt.Println(err) } errShal := json.Unmarshal(valueGet,&imapGet) if errShal != nil { fmt.Println(err) } fmt.Println(imapGet["username"]) fmt.Println(imapGet["phonenumber"]) }
设置过期时间
EXPIRE key seconds
// 设置过期时间为24小时 n,_ := rs.Do("EXPIRE", 24*3600) if n == int64(1) { fmt.Println("success") }
列表操作
命令:
redis 127.0.0.1:6379> LPUSH runoobkey redis (integer) 1 redis 127.0.0.1:6379> LPUSH runoobkey mongodb (integer) 2 redis 127.0.0.1:6379> LPUSH runoobkey MysqL (integer) 3 redis 127.0.0.1:6379> LRANGE runoobkey 0 10 1) "MysqL" 2) "mongodb" 3) "redis"
代码实现:
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c,err = c.Do("lpush","runoobkey","redis") if err != nil { fmt.Println("redis set Failed:",err) } _,"mongodb") if err != nil { fmt.Println("redis set Failed:",err) } _,"MysqL") if err != nil { fmt.Println("redis set Failed:",err) } values,_ := redis.Values(c.Do("lrange","0","100")) for _,v := range values { fmt.Println(string(v.([]byte))) } }
输出:
MysqL
mongodb
redis
管道
请求/响应服务可以实现持续处理新请求,即使客户端没有准备好读取旧响应。这样客户端可以发送多个命令到服务器而无需等待响应,最后在一次读取多个响应。这就是管道化(pipelining),这个技术在多年就被广泛使用了。距离,很多POP3协议实现已经支持此特性,显著加速了从服务器下载新邮件的过程。
Redis很早就支持管道化,所以无论你使用任何版本,你都可以使用管道化技术
连接支持使用Send(),Flush(),Receive()方法支持管道化操作
Send(commandName string,args ...interface{}) error Flush() error Receive() (reply interface{},err error)
Send向连接的输出缓冲中写入命令。Flush将连接的输出缓冲清空并写入服务器端。Recevie按照FIFO顺序依次读取服务器的响应。下例展示了一个简单的管道:
c.Send("SET","foo","bar") c.Send("GET","foo") c.Flush() c.Receive() // reply from SET v,err = c.Receive() // reply from GET
Do方法组合了Send,Flush和 Receive方法。Do方法先写入命令,然后清空输出buffer,最后接收全部挂起响应包括Do方发出的命令的结果。如果任何响应中包含一个错误,Do返回错误。如果没有错误,Do方法返回最后一个响应。
开源库go-redis/redis的使用
github地址:
https://github.com/go-redis/redis
文档地址:
https://godoc.org/github.com/go-redis/redis
获取:
go get -u github.com/go-redis/redis
应用:
package main import ( "fmt" "github.com/go-redis/redis" ) func main() { client := redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379",Password: "",// no password set DB: 0,// use default DB }) pong,err := client.Ping().Result() fmt.Println(pong,err) err = client.Set("key","value", 0).Err() if err != nil { panic(err) } val,err := client.Get("key").Result() if err != nil { panic(err) } fmt.Println("key",val) val2,err := client.Get("key2").Result() if err == redis.Nil { fmt.Println("key2 does not exists") } else if err != nil { panic(err) } else { fmt.Println("key2",val2) } }
输出:
PONG
key value
key2 does not exists
keepalived+redis 高可用redis主从解决方案
keepalived+redis 高可用redis主从解决方案 博客分类: 缓存keepalived+redis 高可用redis主从解决方案
背景介绍:
目前,Redis还没有一个类似于MySQL Proxy或Oracle RAC的官方HA方案。
#Redis 2.8版开始正式提供名为Sentinel的主从切换方案(后面附上,未测试)
因此,如何在出现故障时自动转移是一个需要解决的问题。
通过对网上一些资料的搜索,有建议采用HAProxy或Keepalived来实现的,事实上如果是做Failover而非负载均衡的话,Keepalived的效率肯定是超过HAProxy的,所以我决定采用Keepalived的方案。
环境介绍:
Master: 192.168.0.100
Slave: 192.168.0.101
Virtural IP Address (VIP): 192.168.0.200
设计思路:
当 Master 与 Slave 均运作正常时, Master负责服务,Slave负责Standby;
当 Master 挂掉,Slave 正常时, Slave接管服务,同时关闭主从复制功能;
当 Master 恢复正常,则从Slave同步数据,同步数据之后关闭主从复制功能,恢复Master身份,于此同时Slave等待Master同步数据完成之后,恢复Slave身份。
然后依次循环。
需要注意的是,这样做需要在Master与Slave上都开启本地化策略,否则在互相自动切换的过程中,未开启本地化的一方会将另一方的数据清空,造成数据完全丢失。
下面,是具体的实施步骤:
在Master和Slave上安装Keepalived
$ yum install keepalived
默认安装完成keepalived有默认的配置文件,因此我们重写覆盖它:
首先,在Master上创建如下配置文件:
$ vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
router_id redis100
}
vrrp_script chk_redis
{
script "/etc/keepalived/scripts/redis_check.sh 127.0.0.1 6379"
interval 2
timeout 2
fall 3
}
vrrp_instance redis {
state MASTER # master set to SLAVE also
interface eth0
virtual_router_id 50
priority 150
nopreempt # no seize,must add
advert_int 1
authentication { #all node must same
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.0.200/24
}
track_script {
chk_redis
}
notify_master "/etc/keepalived/scripts/redis_master.sh 127.0.0.1 192.168.0.101 6379"
notify_backup "/etc/keepalived/scripts/redis_backup.sh 127.0.0.1 192.168.0.101 6379"
notify_fault /etc/keepalived/scripts/redis_fault.sh
notify_stop /etc/keepalived/scripts/redis_stop.sh
}
然后,在Slave上创建如下配置文件:
! Configuration File for keepalived
global_defs {
router_id redis101
}
vrrp_script chk_redis
{
script "/etc/keepalived/scripts/redis_check.sh 127.0.0.1 6379"
interval 2
timeout 2
fall 3
}
vrrp_instance redis {
state BACKUP
interface eth0
virtual_router_id 50
priority 100
advert_int 1
authentication { #all node must same
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.0.200/24
}
track_script {
chk_redis
}
notify_master "/etc/keepalived/scripts/redis_master.sh 127.0.0.1 192.168.0.100 6379"
notify_backup "/etc/keepalived/scripts/redis_backup.sh 127.0.0.1 192.168.0.100 6379"
notify_fault /etc/keepalived/scripts/redis_fault.sh
notify_stop /etc/keepalived/scripts/redis_stop.sh
}
在Master和Slave上创建监控Redis的脚本
$ mkdir /etc/keepalived/scripts
$ vim /etc/keepalived/scripts/redis_check.sh
#!/bin/bash
ALIVE=`/usr/redis/redis-cli -h $1 -p $2 PING`
LOGFILE="/var/log/keepalived-redis-check.log"
echo "[CHECK]" >> $LOGFILE
date >> $LOGFILE
if [ $ALIVE == "PONG" ]; then :
echo "Success: redis-cli -h $1 -p $2 PING $ALIVE" >> $LOGFILE 2>&1
exit 0
else
echo "Failed:redis-cli -h $1 -p $2 PING $ALIVE " >> $LOGFILE 2>&1
exit 1
fi
编写以下负责运作的关键脚本:
notify_master /etc/keepalived/scripts/redis_master.sh
notify_backup /etc/keepalived/scripts/redis_backup.sh
notify_fault /etc/keepalived/scripts/redis_fault.sh
notify_stop /etc/keepalived/scripts/redis_stop.sh
因为Keepalived在转换状态时会依照状态来呼叫:
当进入Master状态时会呼叫notify_master
当进入Backup状态时会呼叫notify_backup
当发现异常情况时进入Fault状态呼叫notify_fault
当Keepalived程序终止时则呼叫notify_stop
首先,在Redis Master上创建notity_master与notify_backup脚本:
$ vim /etc/keepalived/scripts/redis_master.sh
#!/bin/bash
REDISCLI="/usr/redis/redis-cli -h $1 -p $3"
LOGFILE="/var/log/keepalived-redis-state.log"
echo "[master]" >> $LOGFILE
date >> $LOGFILE
echo "Being master...." >> $LOGFILE 2>&1
echo "Run MASTER cmd ..." >> $LOGFILE 2>&1
$REDISCLI SLAVEOF $2 $3 >> $LOGFILE
sleep 10 #delay 10 s wait data async cancel sync
echo "Run SLAVEOF NO ONE cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF NO ONE >> $LOGFILE 2>&1
$ sudo vim /etc/keepalived/scripts/redis_backup.sh
#!/bin/bash
REDISCLI="/usr/redis/redis-cli"
LOGFILE="/var/log/keepalived-redis-state.log"
echo "[backup]" >> $LOGFILE
date >> $LOGFILE
echo "Run SLAVEOF cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF $2 $3 >> $LOGFILE 2>&1
# echo "Being slave...." >> $LOGFILE 2>&1
sleep 15 #delay 15 s wait data sync exchange role
接着,在Redis Slave上创建notity_master与notify_backup脚本:
$ vim /etc/keepalived/scripts/redis_master.sh
#!/bin/bash
REDISCLI="/usr/redis/redis-cli -h $1 -p $3"
LOGFILE="/var/log/keepalived-redis-state.log"
echo "[master]" >> $LOGFILE
date >> $LOGFILE
echo "Being master...." >> $LOGFILE 2>&1
echo "Run SLAVEOF cmd ... " >> $LOGFILE
$REDISCLI SLAVEOF $2 $3 >> $LOGFILE 2>&1
#echo "SLAVEOF $2 cmd can''t excute ... " >> $LOGFILE
sleep 10 ##delay 15 s wait data sync exchange role
echo "Run SLAVEOF NO ONE cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF NO ONE >> $LOGFILE 2>&1
$ vim /etc/keepalived/scripts/redis_backup.sh
#!/bin/bash
REDISCLI="/usr/redis/redis-cli"
LOGFILE="/var/log/keepalived-redis-state.log"
echo "[BACKUP]" >> $LOGFILE
date >> $LOGFILE
echo "Being slave...." >> $LOGFILE 2>&1
echo "Run SLAVEOF cmd ..." >> $LOGFILE 2>&1
$REDISCLI SLAVEOF $2 $3 >> $LOGFILE
sleep 100 #delay 10 s wait data async cancel sync
exit(0)
然后在Master与Slave创建如下相同的脚本:
$ vim /etc/keepalived/scripts/redis_fault.sh
#!/bin/bash
LOGFILE=/var/log/keepalived-redis-state.log
echo "[fault]" >> $LOGFILE
date >> $LOGFILE
$ sudo vim /etc/keepalived/scripts/redis_stop.sh
#!/bin/bash
LOGFILE=/var/log/keepalived-redis-state.log
echo "[stop]" >> $LOGFILE
date >> $LOGFILE
给脚本都加上可执行权限:
(这点很重要,最开始由于这不没做,运行后一直报错 "VRRP_Instance(redis) Now in FAULT state")
$ sudo chmod +x /etc/keepalived/scripts/*.sh
脚本创建完成以后,我们开始按照如下流程进行测试:
1.启动Master上的Redis
$ /etc/init.d/redis start
2.启动Slave上的Redis
$ /etc/init.d/redis start
3.启动Master上的Keepalived
$ /etc/init.d/keepalived start
4.启动Slave上的Keepalived
$ /etc/init.d/keepalived start
5.尝试通过VIP连接Redis:
$ redis-cli -h 10.6.1.200 INFO
连接成功,Slave也连接上来了。
role:master
slave0:10.6.1.144,6379,online
6.尝试插入一些数据:
$ redis-cli -h 10.6.1.200 SET Hello Redis
OK
从VIP读取数据
$ redis-cli -h 10.6.1.200 GET Hello
"Redis"
从Master读取数据
$ redis-cli -h 10.6.1.143 GET Hello
"Redis"
从Slave读取数据
$ redis-cli -h 10.6.1.144 GET Hello
"Redis"
下面,模拟故障产生:
将Master上的Redis停了
$ service redis_6379 stop
查看Master上的Keepalived日志
$ tailf /var/log/keepalived-redis-state.log
[fault]
Thu Sep 27 08:29:01 CST 2012
同时Slave上的日志显示:
$ tailf /var/log/keepalived-redis-state.log
[master]
Fri Sep 28 14:14:09 CST 2012
Being master....
Run SLAVEOF cmd ...
OK
Run SLAVEOF NO ONE cmd ...
OK
然后我们可以发现,Slave已经接管服务,并且担任Master的角色了。
$ redis-cli -h 192.168.0.200 INFO
role:master
然后我们恢复Master的Redis进程
$ service redis_6379 start
查看Master上的Keepalived日志
$ tailf /var/log/keepalived-redis-state.log
[master]
Thu Sep 27 08:31:33 CST 2012
Being master....
Run SLAVEOF cmd ...
OK
Run SLAVEOF NO ONE cmd ...
OK
同时Slave上的日志显示:
$ tailf /var/log/keepalived-redis-state.log
[backup]
Fri Sep 28 14:16:37 CST 2012
Being slave....
Run SLAVEOF cmd ...
OK
可以发现目前的Master已经再次恢复了Master的角色,故障切换以及自动恢复都成功了。
主从用到的脚本及keepalived.conf 可以从这下载 http://download.csdn.net/detail/huwei2003/8252221
注意事项:主从的redis都要开启本地备份
附:
Redis Sentinel的主从切换方案
Redis 2.8版开始正式提供名为Sentinel的主从切换方案,Sentinel用于管理多个Redis服务器实例,主要负责三个方面的任务:
1. 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
2. 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
3. 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
Redis Sentinel 是一个分布式系统, 你可以在一个架构中运行多个 Sentinel 进程(progress), 这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线的信息, 并使用投票协议(agreement protocols)来决定是否执行自动故障迁移, 以及选择哪个从服务器作为新的主服务器。
启动Sentinel
使用--sentinel参数启动,并必须指定一个对应的配置文件,系统会使用配置文件来保存 Sentinel 的当前状态, 并在 Sentinel 重启时通过载入配置文件来进行状态还原。
redis-server /path/to/sentinel.conf --sentinel
使用TCP端口26379,可以使用redis-cli或其他任何客户端与其通讯。
如果启动 Sentinel 时没有指定相应的配置文件, 或者指定的配置文件不可写(not writable), 那么 Sentinel 会拒绝启动。
配置Sentinel
以下是一段配置文件的示例:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
第一行配置指示 Sentinel 去监视一个名为 mymaster 的主服务器, 这个主服务器的 IP 地址为 127.0.0.1 , 端口号为 6379 , 而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意 (只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行)。
不过需要注意的是,无论你设置要多少个 Sentinel 同意才能判断一个服务器失效,一个 Sentinel 都需要获得系统中多数(majority) Sentinel 的支持,才能发起一次自动故障迁移,并预留一个给定的配置纪元 (Configuration Epoch ,一个配置纪元就是一个新主服务器配置的版本号)。也就是说,如果只有少数(minority)Sentinel 进程正常运作的情况下,是不能执行自动故障迁移的。
down-after-milliseconds 选项指定了 Sentinel 认为服务器已经断线所需的毫秒数(判定为主观下线SDOWN)。
parallel-syncs 选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长,但越大就意味着越多的从服务器因为复制而不可用。可以通过将这个值设为 1 来保证每次只有一个从服务器处于不能处理命令请求的状态。
主观下线和客观下线
1. 主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断。
2. 客观下线(Objectively Down, 简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。
客观下线条件只适用于主服务器: 对于任何其他类型的 Redis 实例, Sentinel 在将它们判断为下线前不需要进行协商, 所以从服务器或者其他 Sentinel 永远不会达到客观下线条件。
只要一个 Sentinel 发现某个主服务器进入了客观下线状态, 这个 Sentinel 就可能会被其他 Sentinel 推选出, 并对失效的主服务器执行自动故障迁移操作。
每个Sentinel实例都执行的定时任务
1. 每个 Sentinel 以每秒钟一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个 PING 命令。
2. 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 那么这个实例会被 Sentinel 标记为主观下线。 一个有效回复可以是: +PONG 、 -LOADING 或者 -MASTERDOWN 。
3. 如果一个主服务器被标记为主观下线, 那么正在监视这个主服务器的所有 Sentinel 要以每秒一次的频率确认主服务器的确进入了主观下线状态。
4. 如果一个主服务器被标记为主观下线, 并且有足够数量的 Sentinel (至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断, 那么这个主服务器被标记为客观下线。
5. 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有主服务器和从服务器发送 INFO 命令。 当一个主服务器被 Sentinel 标记为客观下线时, Sentinel 向下线主服务器的所有从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
6. 当没有足够数量的 Sentinel 同意主服务器已经下线, 主服务器的客观下线状态就会被移除。 当主服务器重新向 Sentinel 的 PING 命令返回有效回复时, 主服务器的主管下线状态就会被移除。
Sentinel API
有两种方式可以与Sentinel进行通讯:指令、发布与订阅。
Sentinel命令
PING :返回 PONG 。
SENTINEL masters :列出所有被监视的主服务器,以及这些主服务器的当前状态;
SENTINEL slaves <master name> :列出给定主服务器的所有从服务器,以及这些从服务器的当前状态;
SENTINEL get-master-addr-by-name <master name> : 返回给定名字的主服务器的 IP 地址和端口号。 如果这个主服务器正在执行故障转移操作, 或者针对这个主服务器的故障转移操作已经完成, 那么这个 命令返回新的主服务器的 IP 地址和端口号;
SENTINEL reset <pattern> : 重置所有名字和给定模式 pattern 相匹配的主服务器。 pattern 参数是一个 Glob 风格的模式。 重置操作清楚主服务器目前的所有状态, 包括正在执行中的故障转移, 并移除目前已经发现和关联的, 主服务器的所有从服务器和 Sentinel ;
SENTINEL failover <master name> : 当主服务器失效时, 在不询问其他 Sentinel 意见的情况下, 强制开始一次自动故障迁移。
客户端可以通过SENTINEL get-master-addr-by-name <master name>获取当前的主服务器IP地址和端口号,以及SENTINEL slaves <master name>获取所有的Slaves信息
发布与订阅信息
客户端可以将 Sentinel 看作是一个只提供了订阅功能的 Redis 服务器: 你不可以使用 PUBLISH 命令向这个服务器发送信息, 但你可以用 SUBSCRIBE 命令或者 PSUBSCRIBE 命令, 通过订阅给定的频道来获取相应的事件提醒。
一个频道能够接收和这个频道的名字相同的事件。 比如说, 名为 +sdown 的频道就可以接收所有实例进入主观下线(SDOWN)状态的事件。
通过执行 PSUBSCRIBE * 命令可以接收所有事件信息。
+switch-master <master name> <oldip> <oldport> <newip> <newport> :配置变更,主服务器的 IP 和地址已经改变。 这是绝大多数外部用户都关心的信息。
可以看出,我们使用Sentinel命令和发布订阅两种机制就能很好的实现和客户端的集成整合:
使用get-master-addr-by-name和slaves指令可以获取当前的Master和Slaves的地址和信息;而当发生故障转移时,即Master发生切换,可以通过订阅的+switch-master事件获得最新的Master信息。
*PS:更多Sentinel的可订阅事件参见官方文档。
sentinel.conf中的notification-script
在sentinel.conf中可以配置多个sentinel notification-script <master name> <shell script-path>, 如sentinel notification-script mymaster ./check.sh
这个是在群集failover时会触发执行指定的脚本。脚本的执行结果若为1,即稍后重试(最大重试次数为10);若为2,则执行结束。并且脚本最大执行时间为60秒,超时会被终止执行。
PS:目前会存在该脚本被执行多次的问题,查找资料有人解释是:
脚本分为两个级别, SENTINEL_LEADER 和 SENTINEL_OBSERVER ,前者仅由领头 Sentinel 执行(一个 Sentinel),而后者由监视同一个 master 的所有 Sentinel 执行(多个 Sentinel)。
http://www.linuxidc.com/Linux/2014-07/104552.htm
我们今天的关于使用Redis-Cli了解延迟和redis 延迟的分享已经告一段落,感谢您的关注,如果您想了解更多关于(Redis基础教程之六)如何使用Redis中的List、Golang中如何使用redis实现延迟队列。、Go实战--golang中使用redis(redigo和go-redis/redis)、keepalived+redis 高可用redis主从解决方案的相关信息,请在本站查询。
本文标签: