开心一刻我找了个女朋友 , 挺丑的那一种 , 她也知道自己丑 , 平常都不好意思和我一块出门
昨晚 , 我带她逛超市 , 听到有两个人在我们背后小声嘀咕:“看咱前面 , 想不到这么丑都有人要 。”
女朋友听后 , 羞的满脸通红 , 我想女朋友虽然丑但是对我很好 , 我不会嫌弃她的
后面两个人继续嘀咕:“是啊 , 那男人真丑!”
卧槽 , 小丑竟是我自己!

文章插图
写在前面Redis 客户端除了 Redis 自己提供的命令行工具:redis-cli , 还有各种针对不同编程语言的客户端:Clients
Java 语言的 Redis 客户端有很多 , 推荐使用的有:Jedis、lettuce、Redisson , 而 Redisson 就是本文的主角之一
环境准备Redis 版本:3.2.8
Redisson 版本:3.13.6
下文都是基于这两个版本来进行讲解的;不同的版本 , 功能、特性还是有所不同的 , 这点还是需要注意的
Redis 的发布/订阅官方文档:Redis Pub/Sub
什么是发布/订阅Redis 提供了基于 “发布 / 订阅” 模式的消息机制 , 此种模式下 , 消息发布者和订阅者不进行直接通信 , 发布者向指定的频道发布消息 , 订阅该频道的每个客户端都可以收到该消息
发布订阅模型如下:

文章插图
四个角色:发布者(Pub)、订阅者(Sub)、对两者解耦的中间方(Channel)、消息(Message)
Sub 订阅 Channel , Pub 向 Channel 发布消息(Message) , Sub 就能收到 Pub 发布的消息了
以公众号为例 , 我们(Sub)订阅某个公众号(Channel) , 公众号作者(Pub)在公众号每发表一篇文章(Message) , 就会向我们推送这篇文章 , 我们就可以浏览这篇文章了
当我们取消订阅了 , 它就不会再向我们推送这篇文章了;只要这个公众号一直在运行 , 就会一直有人订阅它或者取消订阅
可以将发布/订阅理解成分布式版的观察者模式 , 关于观察者模式 , 大家可以查看:设计模式之观察者模式 → 事件机制的底层原理
很多的 MQ 产品中都存在发布/订阅模式 , 只是各自的实现有细微差别
Redis 中发布/订阅相关的命令只有 6 个 , 我们在 redis-cli 下一个一个来看
SUBSCRIBE通过该命令 , 客户端可以订阅一个或多个频道
基本语法: subscribe channel [channel ...]
假设我们订阅频道:channel:1 , 可以如下操作

文章插图
关于订阅命令(subscribe、psubscribe)有两点需要注意:
1、客户端在执行订阅命令后进入了订阅状态 , 只能接收 subscribe、psubscribe、unsubscribe、punsubscribe 这四个命令
在 redis-cli 下更是表现为阻塞状态 , 只能接收消息 , 不能输入任何命令
但是我们要明白 , redis 客户端除了 redis-cli , 还很多针对不同编程语言的客户端
实际应用中 , redis-cli 用的非常少 , 用的多的还是各种编程语言的 Redis 客户端
2、新开启的订阅客户端 , 无法接收到该频道之前的消息 , 因为 Redis 不会持久化发布的消息
PUBLISH通过该命令 , 客户端可以向某个频道发布一条消息
基本语法: publish channel message
假设我们向频道:channel:1 发布消息 , 可以如下操作

文章插图
返回值: (integer) 1 表示有 1 个订阅者收到了消息
我们再看看之前的订阅客户端 , 收到了发布的消息

文章插图
UNSUBSCRIBE通过此命令 , 客户端可以取消对指定频道的订阅 , 取消成功后不再接收该频道发布的消息
基本语法: unsubscribe [channel [channel ...]]
我们取消对频道:channel:1 的订阅 , 可以如下操作

文章插图
PSUBSCRIBE按照模式订阅 , 可以理解成正则匹配订阅
subscribe 只能订阅一个或多个具体的频道 , 不能按正则匹配订阅 , 而此命令正好弥补这个空缺
基本语法: psubscribe pattern [pattern ...]
我们订阅以 channel:u 开头的所有频道 , 可以如下操作

文章插图
此时 , 我们向频道:channel:user 发布消息 , 那么此客户端也能收到消息

文章插图
PUNSUBSCRIBE按照模式取消订阅 , 可以理解成正则匹配取消订阅
unsubscribe 只能对一个或多个具体的频道取消订阅 , 不能按正则匹配来取消订阅 , 而此命令正好弥补这个空缺
基本语法: punsubscribe [pattern [pattern ...]]
我们对 channel:r 开头的所有频道取消订阅 , 可以如下操作

文章插图
我们可以将 psubscribe、punsubscribe 与 subscribe、unsubscribe 进行类比 , 便于理解
PUBSUB该命令用于查看订阅与发布系统状态 , 它由数个不同格式的子命令组成
基本语法: pubsub subcommand [argument [argument ...]]
该命令用法比较灵活 , 常用的功能有如下几个
1、查看活跃的频道
活跃的频道指的是当前频道至少有一个订阅者
基本语法: pubsub channels [pattern] , 其中 [pattern] 是可以指定具体的模式
查看所有活跃的频道 , 可以如下操作

文章插图
查看符合某种模式的活跃频道 , 可以如下操作

文章插图
2、查看频道订阅数
基本语法: pubsub numsub [channel ...]

文章插图
channel:1 频道的订阅数是 1 , channel:user 频道的订阅数也是 1
3、查看模式订阅数
基本语法: pubsub numpat
返回的不是订阅模式的客户端的数量 , 而是客户端订阅的所有模式的数量总和

文章插图
Redisson 发布/订阅上面讲了那么多 , 其实都是在 redis-cli 下自嗨 , 如何在实际项目中应用起来了 , 我们基于 Redisson 来实现个简单示例
【redisson官网 Redisson 分布式锁实现之前置篇 → Redis 的发布订阅 与 Lua】订阅端

文章插图
发布端

文章插图
完整代码:pubsub , 执行结果如下

文章插图
至此 , 相信大家对 Redis 的发布/订阅有了一定的了解了
Redis 的 Lua官方文档:Redis Lua scripting
关于 Lua , 本文不作详细介绍;语法比较简单 , 基本都能看懂 , 感兴趣的可以去看它的官方文档:Lua Documentation
Redis 提供了一系列的命令供我们使用:Redis Commands , 基本上能满足我们的绝大部分需求
但是 , 总有一些特殊的需求游离在三界之外 , 不在五行之中 , 不能通过其中的某个命令直接实现
有人可能就会说了:一个命令不行 , 那就多个命令组合实现嘛
但是 , 我们需要考虑到:多个命令组合能保证原子性吗 , 如果有逻辑处理又该怎么办?
Redis 早已替我们想好了解决办法 , 那就是:Lua 脚本
在 Redis 中执行 Lua 脚本有两种方法:eval 和 evalsha
eval基本语法: eval script numkeys key [key ...] arg [arg ...]
其中 script 表示 Lua 脚本 , numkeys 表示 key 个数
通过一个具体案例 , 我们就能理解了

文章插图
其中表示 .. 表示连接两个字符串
如果 Lua 脚本太长 , 还可以使用 redis-cli --eval 直接执行文件
基本语法: redis-cli --eval script key [key...] , arg [arg ...]
注意:key 与 arg 之间是 , , 英文逗号前后都有一个空格

文章插图
hello.lua 文件内容: return 'hello '..KEYS[1]..ARGV[1]
evalsha除了 eval , Redis 还提供了 evalsha 来执行 Lua 脚本
基本语法: evalsha sha1 numkeys key [key ...] arg [arg ...]
使用 evalsha 之前需要将 Lua 脚本加载到 Redis 服务端 , 得到该脚本的 SHA1 校验和 , 然后将 SHA1 作为 evalsha 的入参执行对应的 Lua 脚本
脚本会常驻 Redis 服务端 , 客户端执行脚本时不需要每次都传递脚本到服务端 , 使得脚本得以复用 , 降低了参数传递的开销
加载脚本基本语法: redis-cli script load script

文章插图
得到 SHA1: 5a8bcaa0ac71ab25ea5c504d61964859fffc20ce , 再执行 evalsha 命令

文章插图
Lua 的 Redis API
Lua 可以使用 redis.call 函数实现对 Redis 命令的调用 , 例如:

文章插图
另外还可以使用 redis.pcall 函数实现对 Redis 命令的调用
redis.call 和 redis.pcall 的区别在于 , 如果 redis.call 执行失败 , 那么脚本执行结束会直接返回错误 , 而 redis.pcall 会忽略错误继续执行脚本
Lua 带来的好处Lua 为 Redis 开发和运维人员带来了如下三个好处:
1、Lua 脚本在 Redis 中是原子执行的 , 执行过程中不会插入其他命令
2、通过 Lua 脚本 , 我可以创造出自己定制的命令 , 并可以将这些命令常驻在内存 , 实现复用
3、Lua 脚本可以将多条命令一次性打包 , 有效减少网络开销
Redisson Lua基于 Redisson , 我们来看看 Lua 的简单使用

文章插图
完整代码:LuaDemo , 执行结果如下:

文章插图
LuaDemo.java 中有个方法 distLockTest , 有兴趣的可以看看 , 对理解 Redisson 分布式锁的实现有帮助
细节疑问给大家留两个问题
1、客户端未主动取消订阅 , 而是直接断开连接 , Redis 服务端会如何处理该客户端订阅的那些频道
2、lua 脚本保证的是执行该脚本的过程中 , 不能有其他命令插入 , 但是如果脚本中的某个命令出错了 , Redis 会如何处理
总结1、Redis 发布订阅模式可以类比观察者模式 , 便于理解
涉及 4 个角色 , 理清楚它们各自的作用就好理解了
2、Lua 在 Redis 中非常灵活 , 相当于给我们留了一个自定义命令的接口
3、Redis 客户端有很多 , 我们不能只局限于 redis-cli
参考《Redis开发与运维》
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
