1、Redis 发布订阅

参考:https://www.runoob.com/redis/redis-pub-sub.html

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

2、案例:netty-socketio和redis实现发布/订阅功能

  本Demo实现:netty-socketio实现订阅(参考:https://www.cnblogs.com/xy-ouyang/p/10675904.html),redis实现推送消息。本demo保存地址:https://github.com/wenbinouyang/oy_java

  demo使用 springboot 2.1.3.RELEASE,项目总体结构:

  

  pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.oy</groupId>
<artifactId>nettysocketio007</artifactId>
<version>0.0.1</version>
<name>nettysocketio008</name>
<description>nettysocketio008 for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<!-- spring boot start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <!-- netty-socketio server -->
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.11</version>
</dependency> <!-- springboot data redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!-- exclusion lettuce,jedis -->
<exclusions>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency> <!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency> <!-- jedis pool dependency -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

  application.properties

logging.file=/home/wwwlogs/nettysocketio008/log.log

#redis
#spring.redis.host=127.0.0.1
spring.redis.host=47.244.48.230
spring.redis.port=
spring.redis.password=Redis0929 spring.redis.jedis.pool.maxActive=
spring.redis.jedis.pool.max-idle=
spring.redis.jedis.pool.min-idle=
spring.redis.timeout=

  Nettysocketio008Application类

package com.oy;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner; @SpringBootApplication
@Order(1)
public class Nettysocketio008Application implements CommandLineRunner {
private SocketIOServer server; public static void main(String[] args) {
SpringApplication.run(Nettysocketio008Application.class, args);
} @Bean
public SocketIOServer socketIOServer() {
Configuration config = new Configuration();
config.setHostname("localhost");
config.setPort(4001);
this.server = new SocketIOServer(config);
return server;
} @Bean
public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
return new SpringAnnotationScanner(socketServer);
} @Override
public void run(String... args) throws Exception {
server.start();
UtilFunctions.log.info("socket.io run success!"); // 向"channel_1" push数据
// Service.send(args);
}
}

  RedisConfig类

package com.oy;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @Configuration
public class RedisConfig extends CachingConfigurerSupport {
public static final Logger log = LoggerFactory.getLogger(RedisConfig.class); @Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer(); // RedisTemplate
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(redisSerializer);
template.setValueSerializer(redisSerializer);
template.setHashKeySerializer(redisSerializer);
template.setHashValueSerializer(redisSerializer); template.afterPropertiesSet();
return template;
} @Bean(destroyMethod = "destroy")
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory,
MessageListener redisMessageListener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory); ThreadPoolTaskScheduler taskExecutor = new ThreadPoolTaskScheduler();
taskExecutor.setPoolSize(10);
taskExecutor.initialize();
container.setTaskExecutor(taskExecutor); Map<MessageListener, Collection<? extends Topic>> listeners = new HashMap<>();
List<Topic> list = new ArrayList<>();
list.add(new ChannelTopic("cfd_md"));
listeners.put(redisMessageListener, list);
container.setMessageListeners(listeners); return container;
} }

  MessageEventHandler类

package com.oy;
import java.util.Set;
import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent; @Component
public class MessageEventHandler {
public static SocketIOServer socketIoServer; @Autowired
public MessageEventHandler(SocketIOServer server) {
MessageEventHandler.socketIoServer = server;
} @OnConnect
public void onConnect(SocketIOClient client) {
UUID socketSessionId = client.getSessionId();
String ip = client.getRemoteAddress().toString();
UtilFunctions.log.info("client connect, socketSessionId:{}, ip:{}", socketSessionId, ip);
} @OnEvent("sub")
public void sub(SocketIOClient client, AckRequest request, String channel) {
UUID socketSessionId = client.getSessionId();
String ip = client.getRemoteAddress().toString();
client.joinRoom(channel);
UtilFunctions.log.info("client sub, channel:{}, socketSessionId:{}, ip:{}", channel, socketSessionId, ip); Set<String> rooms = client.getAllRooms();
for (String room : rooms) {
UtilFunctions.log.info("after client connect, room:{}", room);
} // 客户端一订阅,就马上push一次
if ("channel_1".equals(channel)) {
sendAllEvent(Service.getMsg());
} else if ("redis_channel".equals(channel)) {
sendAllEvent(RedisSub.getMsg());
}
} // @OnEvent("unsub")
// public void unsub(SocketIOClient client, AckRequest request, String channel) {
// UUID socketSessionId = client.getSessionId();
// String ip = client.getRemoteAddress().toString();
// client.leaveRoom(channel);
// UtilFunctions.log.info("client unsub, channel:{}, socketSessionId:{}, ip:{}", channel, socketSessionId, ip);
// } @OnDisconnect
public void onDisconnect(SocketIOClient client) {
UUID socketSessionId = client.getSessionId();
String ip = client.getRemoteAddress().toString();
UtilFunctions.log.info("client disconnect, socketSessionId:{}, ip:{}", socketSessionId, ip); Set<String> rooms = client.getAllRooms();
for (String room : rooms) {
UtilFunctions.log.info("after client disconnect, room:{}", room);
}
} // broadcast to channel "channel_1"
public static void sendAllEvent(String data) {
socketIoServer.getRoomOperations("channel_1").sendEvent("channel_1", data);
} // broadcast to channel "redis_channel"
public static void sendAllEvent2(String data) {
socketIoServer.getRoomOperations("redis_channel").sendEvent("redis_channel", data);
}
}

  RedisSub类

package com.oy;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; @Service
public class RedisSub implements MessageListener {
private static String msg;
private String redisStr; @Autowired
private RedisTemplate<String, Object> redisTemplate; public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
} @Override
public void onMessage(Message message, byte[] pattern) {
String msg = (String) redisTemplate.getValueSerializer().deserialize(message.getBody());
String channel = (String) redisTemplate.getValueSerializer().deserialize(message.getChannel()); if (null != channel && !"".equals(channel) && null != msg && !"".equals(msg)) { // 相同数据不push
if(redisStr == null) {
UtilFunctions.log.info("===== redisStr == null =====");
redisStr = msg;
} else if (redisStr.equals(msg)) {
UtilFunctions.log.info("===== redisStr is same =====");
return ;
} else {
UtilFunctions.log.info("===== redisStr:{} =====", redisStr);
redisStr = msg;
} JSONObject data = new JSONObject();
JSONObject json = JSON.parseObject(msg);
data.put(json.getString("contract"), json); JSONObject jsonObj = new JSONObject();
jsonObj.put("channel", channel);
jsonObj.put("timestamp", new Date().getTime());
jsonObj.put("data", data); MessageEventHandler.sendAllEvent2(jsonObj.toJSONString());
RedisSub.msg = jsonObj.toJSONString();
UtilFunctions.log.info("message from channel:{}, msg:{} ", channel, jsonObj.toJSONString());
} } public static String getMsg() {
return msg;
} public static void setMsg(String msg) {
RedisSub.msg = msg;
}
}

  

  客户端html页面及测试参考:https://www.cnblogs.com/xy-ouyang/p/10675904.html

netty-socketio(二)整合redis实现发布订阅的更多相关文章

  1. (二)Redis 笔记——发布&订阅、事务、数据库操作

    1. Redis 发布订阅 1.1 概述 Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息. Redis 客户端可以订阅任意数量的频道. 下 ...

  2. redis(3)发布订阅

    一.发布/订阅模式 在软件工程里面,发布/订阅是一种消息模式,这种模式旨在将消息发送者和消息接收者解耦.发送者不需要关心将消息发送给谁,接收者也不需要知道消息的发送者是谁.发送者将消息发布以后就结束动 ...

  3. StackExchange.Redis 使用-发布订阅 (二)

    使用Redis的发布订阅功能 redis另一个常见的用途是发布订阅功能 . 它非常的简单 ,当连接失败时 ConnectionMultiplexer 会自动重新进行订阅 . ISubscriber s ...

  4. redis 实现发布订阅的功能

    redis 除了作为缓存的功能外还可以用作消息中间件的功能,这片博客主要是介绍一下 redis 整合spring 实现消息的发布和订阅功能: 1:redis依赖,依赖两个包,redis 包, spri ...

  5. Redis之发布订阅

    一 什么是发布订阅 发布订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知 Redis 发布订阅(pub/sub)是一种消息通信模式: ...

  6. [翻译] C# 8.0 新特性 Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南) 【由浅至深】redis 实现发布订阅的几种方式 .NET Core开发者的福音之玩转Redis的又一傻瓜式神器推荐

    [翻译] C# 8.0 新特性 2018-11-13 17:04 by Rwing, 1179 阅读, 24 评论, 收藏, 编辑 原文: Building C# 8.0[译注:原文主标题如此,但内容 ...

  7. 【spring boot】【redis】spring boot 集成redis的发布订阅机制

    一.简单介绍 1.redis的发布订阅功能,很简单. 消息发布者和消息订阅者互相不认得,也不关心对方有谁. 消息发布者,将消息发送给频道(channel). 然后是由 频道(channel)将消息发送 ...

  8. Springboot+Redis(发布订阅模式)跨多服务器实战

    一:redis中发布订阅功能(http://www.redis.cn/commands.html#pubsub) PSUBSCRIBE pattern [pattern -]:订阅一个或者多个符合pa ...

  9. redis的发布订阅模式

    概要 redis的每个server实例都维护着一个保存服务器状态的redisServer结构 struct redisServer {     /* Pubsub */     // 字典,键为频道, ...

随机推荐

  1. 一本值得你反复研读的Python佳作《Python编程从0到1》

    现在的Python入门书太多太多了,究竟如何选择呢? 当然选最好的最入门的讲解最清晰的,没有那么多废话的. 现在就推荐一本<Python编程从0到1>,还带视频的,到时候跟大家一起学习沟通 ...

  2. java中变量的线程安全性

    静态变量:线程非安全.静态变量即类变量,位于方法区,为所有对象共享,共享一份内存,一旦静态变量被修改,其他对象均对修改可见,故线程非安全.实例变量:单例模式(只有一个对象实例存在)线程非安全,非单例线 ...

  3. python 并发编程 非阻塞IO模型

    非阻塞IO(non-blocking IO) Linux下,可以通过设置socket使其变为non-blocking.当对一个non-blocking socket执行读操作时,流程是这个样子: 从图 ...

  4. Log的相关用法

    1.最好用静态final定义Log变量 private static final Log log = LogFactory.getLog(MyTest.class); 这样做的好处有三: 可以保证线程 ...

  5. 微服务理论之二:面向微服务架构与传统架构、SOA对比,以及云化对比

    一.Monolith 网上对Microservice进行介绍的文章常常以Monolith作为开头,我也不会例外.原因是,知道了Monolith的不便之后才能更容易地理解Microservice架构模式 ...

  6. 洛谷 P2023 维护序列 题解

    题面 注意一个细节,查询和更新都需要pushdown(); #include <bits/stdc++.h> #define int long long using namespace s ...

  7. 洛谷 P3386 二分图匹配 题解

    题面 这道题虽然是练习匈牙利算法的,但可以用网络流来切掉它: 我们可以建立一个超级源和一个超级汇,超级源连接左部分点,超级汇连接右部分点: 然后在该图上跑最大流就可以了: PS:我设的超级源是2001 ...

  8. PHP控制session时效(转)

    1.php session 有效期php的session有效期默认是1440秒(24分钟),如果客户端超过24分钟没有刷新,当前session会被回收,失效. 当用户关闭浏览器,会话结束,sessio ...

  9. flask中使用jsonify和json.dumps的区别

    一.实验 python的flask框架为用户提供了直接返回包含json格式数据响应的方法,即jsonify,在开发中会经常用到.如下一段简单的flask后端代码,服务端视图函数根据请求参数返回json ...

  10. .net core 2.2.0 SOAP踩坑

    首先确认下面几个程序集是最新版本: <PackageReference Include="System.ServiceModel.Http" Version="4. ...