[WebSocket]第⼆章:WebSocket集分布式改造——实现多⼈室
前⾔
书接上⽂,我们开始对我们的⼩⼩聊天室进⾏集化改造。
上⽂地址:
本⽂内容摘要:
为何要改造为分布式集
如何改造为分布式集
⽤户在聊天室集如何发消息
⽤户在聊天室集如何接收消息
补充知识点:STOMP 简介
功能⼀:向聊天室集中的全体⽤户发消息——Redis的订阅/发布
功能⼆:集集⽤户上下线通知——Redis订阅发布
功能三:集⽤户信息维护——Redis集合
WebSocket集还有哪些可能性
本⽂源码:(妈妈再也不⽤担⼼我⽆法复现⽂章代码啦)
github/qqxx6661/springboot-websocket-demo/releases/tag/%E9%9B%86%E7%BE%A4%E7%89%88
如果您觉得这个教程对您有⽤,请关注我的技术:Rude3Knife,不定时更新技术点滴。
正⽂
WebSocket集/分布式改造:实现多⼈室
为何要改造为分布式集
分布式就是为了解决单点故障问题,想象⼀下,如果⼀个服务器承载了1000个⼤佬同时聊天,服务器突然挂了,1000个⼤佬瞬间全部掉线,⼤概明天你就被⼤佬们吊起来打了。
当聊天室改为集后,就算服务器A挂了,服务器B上聊天的⼤佬们还可以愉快的聊天,并且在前端还能通过代码,让连接A的⼤佬们快速重连⾄存活的服务器B,继续和⼤家愉快的聊天,岂不美哉!
总结⼀下:实现了分布式WebSocket后,我们可以将流量负载均衡到不同的服务器上并提供⼀种通信机制让各个服务器能进⾏消息同步(不然⽤户A连上服务器A,⽤户B脸上服务器B,它们发消息的时候对⽅都没法收到)。
如何改造为分布式集
当我们要实现分布式的时候,我们则需要在各个机器上共享这些信息,所以我们需要⼀个Publish/Subscribe的中间件。我们现在使⽤Redis作为我们的解决⽅案。
1. ⽤户在聊天室集如何发消息
假设我们的聊天室集有服务器A和B,⽤户Alice连接在A上,Bob连接在B上、
Alice向聊天室的服务器A发送消息,A服务器必须要将收到的消息转发到Redis,才能保证聊天室集的所有服务器(也就是A和B)能够拿到消息。否则,只有Alice在的服务器A能够读到消息,⽤户Bob在的服务器B并不能收到消息,A和B也就⽆法聊天了。
2. ⽤户在聊天室集如何接收消息
说完了发送消息,那么如何保证Alice发的消息,其他所有⼈都能收到呢,前⾯我们知道了Alice发送的消息已经被传到了Redis的频道,那么所有服务器都必须订阅这个Redis频道,然后把这个频道的消息转发到⾃⼰的⽤户那⾥,这样⾃⼰服务器所管辖的⽤户就能收到消息。补充知识点: 简介
上期我们搭建了个websocket聊天室demo,并且使⽤了STOMP协议,但是我并没有介绍到底什么是STOMP协议,同学们会有疑惑,这⾥对于STOMP有很好地总结:
当直接使⽤WebSocket时(或SockJS)就很类似于使⽤TCP套接字来编写Web应⽤。因为没有⾼层级的线路协议(wire
protocol),因此就需要我们定义应⽤之间所发送消息的语义,还需要确保连接的两端都能遵循这些语义。
就像HTTP在TCP套接字之上添加了请求-响应模型层⼀样,STOMP在WebSocket之上提供了⼀个基于帧的线路格式(frame-based wire format)层,⽤来定义消息的语义。
与HTTP请求和响应类似,STOMP帧由命令、⼀个或多个头信息以及负载所组成。例如,如下就是发送数据的⼀个STOMP帧:好了,介绍完了概念,让我们开始动⼿改造!
功能⼀:向聊天室集中的全体⽤户发消息——Redis 的订阅/发布
如果你不熟悉Redis的sub/pub(订阅/发布)功能,请看这⾥进⾏简单了解它的⽤法,很简单:
adthedocs.io/en/latest/feature/pubsub.html
在我们上篇⽂章的Demo基础上,我们进⾏集改造。上⼀篇⽂章的源码见下⽅:
github/qqxx6661/springboot-websocket-demo/releases/tag/%E5%8D%95%E6%9C%BA%E7%89%88
1. 添加Redis 依赖pom
2. application.properties 新增redis 配置
当然⾸先要确保你安装了Redis,windows下安装redis⽐较⿇烦,你可以搜索redis-for-windows下载安装。>>> SEND transaction:tx-0destination:/app/marco content-length:20{"message":"Marco!"}
1
2
3
4
56<!-- redis --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
1
2
3
4
5
3. 在application.properties 添加频道名定义
4. 新建redis/RedisListenerBean # redis 连接配置dis.dis.host=127.0.dis.dis.dis.ssl=false # 空闲连接最⼤数dis.jedis.pool.max-idle=10# 获取连接最⼤等待时间(dis.jedis.pool.max-wait=60000
1
2
3
4
5
6
7
8
9
10# Redis 定义redis.channel.msgToAll = websocket.msgToAll
1
2
可以看到,我们在代码⾥监听了redis频道msgToAll,这个是在application.properties定义的,当然如果你懒得定义,这⾥可以写死。
5. 聊天室集:发消息改造
我们单机聊天室的发送消息Controller是这样的:dis;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import t.annotation.Bean;import org.tion.RedisConnectionFactory;import org.dis.listener.PatternTopic;import org.dis.listener.RedisMessageListenerContainer;import org.dis.listener.adapter.MessageListenerAdapter;import org.springframework.stereotype.Component;import java.Inet4Address;import java.InetAddress;/** * Redis 订阅频道属性类 * @author yangzhendong01 */@Component public class RedisListenerBean {    private static final Logger LOGGER = Logger(RedisListenerBean.class);    @Value("${server.port}")    private String serverPort;    @Value("${redis.channel.msgToAll}")    private String msgToAll;    /**    * redis 消息容器    * 可以添加多个监听不同话题的redis ,只需要把消息和相应的消息订阅处理器绑定,该消息    * 通过反射技术调⽤消息订阅处理器的相关⽅法进⾏⼀些业务处理    * @param con
nectionFactory      * @param listenerAdapter      * @return      */    @Bean    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {        RedisMessageListenerContainer container = new RedisMessageListenerContainer();        container.setConnectionFactory(connectionFactory);        // 监听msgToAll        container.addMessageListener(listenerAdapter, new PatternTopic(msgToAll));        LOGGER.info("Subscribed Redis channel: " + msgToAll);        return container;    }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
前端websocket怎么用34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49@MessageMapping("/chat.sendMessage")@SendTo("/topic/public")    public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {        return chatMessage;
1
2
3
4
我们前端发给我们消息后,直接给/topic/public转发这个消息,让其他⽤户收到。
在集中,我们需要把消息转发给Redis,并且不转发给前端,⽽是让服务端监听Redis消息,在进⾏消息发送。
将Controller改为:
你会发现我们在代码中使⽤了JsonUtil将实体类ChatMessage转为了Json发送给了Redis,这个Json⼯具类需要使⽤到FaskJson依赖:
1. pom添加FastJson依赖
2. 添加Json解析⼯具类JsonUtil,提供对象转Json,Json转对象的能⼒@Value("${redis.channel.msgToAll}")private String msgToAll;@Autowired private RedisTemplate<String, String> redisTemplate;    @MessageMapping("/chat.sendMessage")    public void sendMessage(@Payload ChatMessage chatMessage) {        try {            vertAn
dSend(msgToAll, JsonUtil.parseObjToJson(chatMessage));        } catch (Exception e) {            (e.getMessage(), e);        }    }
12
3
4
5
6
7
8
9
10
11
12
13
14<!-- json --><dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson</artifactId>    <version>1.2.58</version></dependency>
1
2
3
4
5
6

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。