背景

有时候,在微服务时代,我们需要对全部的机器节点进行通知。在常规情况下,一个请求经过负载均衡只有一个机器可以收到。那么,如何能让全部的机器都收到同样的请求呢?需要借助消息队列的监听机制,让每个节点都监听一个队列,让消息发送到所有的队列中。

rabbit MQ的fanout交换机可以实现这种功能。

那么,如果想用redis去实现这个功能,有没有什么好的选择呢?毕竟仅仅为了一个全部节点的推送,就引入另外一个中间件,不是一个很经济选择。

那么Redis的Stream结构就是一个可以选择的了。

实现

对于Redis的Stream结构,诞生之初就是为了用作消息队列的。具体用法如下:

  • 发送消息
public void sendConfigMessage() {
MapRecord<String, String, String> entries = StreamRecords.mapBacked(Collections.singletonMap("msg", "plsGet")).withStreamKey(RedisConfig.stream);
// 将消息添加至消息队列中
redisTemplate.opsForStream().add(entries);
}
  • 建立监听
    @Bean
public Subscription subscription(RedisConnectionFactory factory) {
Set<String> keys = redisTemplate.keys(streamPattern);
if (keys != null && keys.size() != 0) {
keys = keys.stream().filter(key -> !key.equals(stream)).collect(Collectors.toSet());
if (keys.size() != 0) {
redisTemplate.delete(keys);
}
} group = UUID.randomUUID().toString();
if (Boolean.FALSE.equals(redisTemplate.hasKey(stream))) {
StringRecord stringRecord = StreamRecords.string(Collections.singletonMap("msg", "init")).withStreamKey(RedisConfig.stream);
redisTemplate.opsForStream().add(stringRecord);
} redisTemplate.opsForStream().createGroup(stream, group);
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String, String, String>> options = StreamMessageListenerContainer
.StreamMessageListenerContainerOptions
.builder()
.pollTimeout(Duration.ofSeconds(1))
.build();
StreamMessageListenerContainer<String, MapRecord<String, String, String>> listenerContainer = StreamMessageListenerContainer.create(factory, options);
Subscription subscription = listenerContainer.receiveAutoAck(Consumer.from(group, "consumer1"), StreamOffset.create(configStream, ReadOffset.lastConsumed()), configStreamListener);
listenerContainer.start();
return subscription;
}

对于每一台机器,都让它关联唯一的消费组,而这个功能关联唯一的stream key。Redis的stream机制在于,每一条给stream key发送的消息都会推送给所有的消费组,这样所有的机器都会收到这条消息。

一些问题

Redis的连接和MQ还是有一些区别。当Redis连接超时之后,之前建立的监听就不能用了,因为之前的长连接断开了。

一个解决档案就是手动维持一个Netty的心跳机制,不停轮训判断当前的订阅是否还处于活跃状态。一旦不处于活跃状态就要重新建立长连接:

    @Autowired
Subscription subscription; @Autowired
RedisConnectionFactory factory; @Autowired
StreamListener streamListener; @Autowired
RedisTemplate<String, Object> redisTemplate; //当Redis连接超时,自动重置stream队列。否则监听失效
@Bean
public ClientResources clientResources(){ NettyCustomizer nettyCustomizer = new NettyCustomizer() { @Override
public void afterChannelInitialized(Channel channel) {
channel.pipeline().addLast(
new IdleStateHandler(0, 0, 10)); channel.pipeline().addLast(new ChannelDuplexHandler() {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (subscription != null && !subscription.isActive()) {
synchronized ("resetStreamLock") {
if (!subscription.isActive()) {
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String, String, String>> options = StreamMessageListenerContainer
.StreamMessageListenerContainerOptions
.builder()
.pollTimeout(Duration.ofSeconds(1))
.build();
StreamMessageListenerContainer<String, MapRecord<String, String, String>> listenerContainer0 = StreamMessageListenerContainer.create(factory, options);
Subscription subscription0 = listenerContainer0.receiveAutoAck(Consumer.from(RedisConfig.group, "consumer1"), StreamOffset.create(RedisConfig.stream, ReadOffset.lastConsumed()), streamListener);
listenerContainer0.start();
subscription = subscription0;
log.info("reset getStream!");
}
}
}
if (evt instanceof IdleStateEvent) {
ctx.disconnect();
}
}
});
} @Override
public void afterBootstrapInitialized(Bootstrap bootstrap) { } }; return ClientResources.builder().nettyCustomizer(nettyCustomizer ).build();
}

Redis Stream实现全部节点机器推送消息的更多相关文章

  1. wp推送消息笔记

    最近想给应用添加推送消息,主要是toast消息,所以就打算去了解一下wp消息推送机制以及实现方法,过程中,查了许多资料,也遇到过一些问题,做完后,自己就做个小笔记,总结一下,好记性不如烂笔头嘛,以后可 ...

  2. 使用极光推送(www.jpush.cn)向安卓手机推送消息【服务端向客户端主送推送】C#语言

    在VisualStudio2010中新建网站JPushAndroid.添加引用json帮助类库Newtonsoft.Json.dll. 在web.config增加appkey和mastersecret ...

  3. HTML5服务器推送消息的各种解决办法

    摘要 在各种BS架构的应用程序中,往往都希望服务端能够主动地向客户端推送各种消息,以达到类似于邮件.消息.待办事项等通知. 往BS架构本身存在的问题就是,服务器一直采用的是一问一答的机制.这就意味着如 ...

  4. python 全栈开发,Day131(向app推送消息,玩具端消息推送)

    先下载github代码,下面的操作,都是基于这个版本来的! https://github.com/987334176/Intelligent_toy/archive/v1.4.zip 注意:由于涉及到 ...

  5. Laravel 中使用 swoole 项目实战开发案例二 (后端主动分场景给界面推送消息)

    推荐阅读:Laravel 中使用 swoole 项目实战开发案例一 (建立 swoole 和前端通信)​ 需求分析 我们假设有一个需求,我在后端点击按钮 1,首页弹出 “后端触发了按钮 1”.后端点了 ...

  6. 使用PushSharp给iOS应用推送消息

    PushSharp是一个C#编写的服务端类库,用于推送消息到各种客户端,支持iOS(iPhone/iPad).Android.Windows Phone.Windows 8.Amazo.Blackbe ...

  7. android不需要Socket的跨进程推送消息AIDL!

    上篇介绍了跨进程实时通讯http://www.cnblogs.com/xiaoxiaing/p/5818161.html 但是他有个缺点就是服务端无法推送消息给客户端,今天这篇文章主要说的就是服务器推 ...

  8. 用JPUSH极光推送实现服务端向安装了APP应用的手机推送消息(C#服务端接口)

    这次公司要我们做一个功能,就是当用户成功注册以后,他登录以后要收到消息,当然这个消息是安装了我们的手机APP应用的手机咯. 极光推送的网站的网址是:https://www.jpush.cn/ 极光推送 ...

  9. iOS点击推送消息跳到应用指定页面

    现在的推送用的越来越频繁,几乎每个应用都开始用到了.其实又有几个用户会去看推送消息呢?没办法,产品经理最大啊,只是苦了我们这一帮程序员啊!闲话少说,进入正题.兄弟我用的是极光推送,自然是以极光推送为例 ...

  10. signalr推送消息

    参考:Tutorial: Getting Started with SignalR 2 and MVC 5 环境:vs2013,webapi2,entity framework6.0 实现效果:当用户 ...

随机推荐

  1. SQL时间函数学习

    笔记来自如鹏网杨中科老师所著的 <程序员的SQL金典> 一.日期时间函数: 日期时间类型分为 日期.时间.日期时间.时间戳     日期:"年-月-日" 没有时间分秒 ...

  2. Linux df -h 显示磁盘空间满,但实际未占用满——问题分析

    问题现象: 遇到一个问题,在系统上,告警提示磁盘空间不够,如下图所示: 问题分析: 1.首先使用 :du  -h  / --max-depth=1 命令查看各个目录的占用空间,试图找到占用较多空间的目 ...

  3. EXE项目和DLL项目远程调试、Props设置说明

    通用宏设置 exe项目根据平台.配置设置输出路径: dll项目根据平台.配置设置输出路径,dll文件.lib文件分别输出到自己的路径: 中间文件根据平台.配置.项目设置输出路径: 设置远程调试命令.远 ...

  4. 批处理命令for循环(cmd命令)

    记录一下: https://www.cnblogs.com/Braveliu/p/5081087.html

  5. C语言学习记录(三)

    C语言学习记录(三) 一.知识要点(数据类型.运算符与表达式) 1.C语言的数据类型 C语言数据结构有:基本类型.指针类型.构造类型和空类型.其中基本类型:查看书籍的22页表3-1. C语言的基本数据 ...

  6. springdata(jpa)的基础使用

    jpa与mybatis-plus类似,都属于数据库相关的框架 jpa需要扫描,这里在启动类指定路径 jpa的实体类需要绑定@Entity, 与mysql表映射@Table(@name="数据 ...

  7. jmeter压测dubbo接口,参数为dto时如何写传参及有错误时的分析思路

    一.传参 1. 无论dubbo接口传参是否为dto,所有参数都是在args的tab传进去的. 2. 如果dto中有自定义对象,paramType为自定义dto名,paramValue为其他参数组成的j ...

  8. ERROR 1862 (HY000): Your password has expired. To log in you must change it using a .....

    navcat 登录本地失败 https://blog.csdn.net/fenniang16/article/details/81216602?spm=1001.2101.3001.6661.1&am ...

  9. EXCEL函数总结

    ------------------截取"号"之前的字符 =MID(A45,1,FIND("号",A45,1)-1)

  10. python基础 补漏

    输出 -----------定义变量类型 类型 集合 mongodb的用法和redis差不多一样的 NoSQL 数据库之一 https://www.runoob.com/python3/python- ...