WebSocket集群解决方案,不用MQ
首先不了解WebSocket的可以先看看这篇文章,以及传统的WebSocket方案是怎么做的,https://www.cnblogs.com/jeremylai7/p/16875115.html 这是用MQ解决的版本,那么这种方案存在什么问题呢。
第一:增加MQ,可能造成消息挤压、消息顺序的问题
第二:增加MQ,则还需要保证MQ的可用性
第三:每个socket服务都需要去消费消息,增加每个服务的压力(做无用功)
那么,基于以上问题,还有没有解决方案呢?
当然有!!!
首先我们理解一个逻辑,为什么WebSocket不能直接做集群,socket是一个长链接,当我们要给socket用户发送消息的时候,我们不知道用户是连接到哪一个服务上面的,这样就无法直接发送消息了
那么,我们能不能给每一个socket服务器增加一个标识,然后在用户连接的时候将用户与socket服务器的关系绑定起来,然后在使用的时候再去判断用户存在哪,再给指定的服务器发送消息不就解决问题了吗?
那么,我们来结合springcloud来完成这个工作,根据这个理论,其他方式也可以实现
首先,来看websocket服务,启动的时候主要注意的问题
@SpringBootApplication
public class WsApplication implements CommandLineRunner {
public static void main(String[] args) {
//动态服务名
System.setProperty("SpringApplicationName", "WS-" + IdUtil.simpleUUID());
SpringApplication.run(WsApplication.class, args);
}
@Override
public void run(String... args) {
System.out.println("项目启动完毕");
}
}
需要注意的是动态服务名这里,每个服务的名字都是不一样的,这样就保证了每个服务的一个唯一性
spring:
application:
#随机名字,做ws集群使用
name: ${SpringApplicationName}
# name: ws
redis:
host: 127.0.0.1
cloud:
nacos:
server-addr: 127.0.0.1
config:
file-extension: yaml
server:
port: 9090
这里用到了nacos与redis,使用的地方待会会有,其中SpringApplicationName是在启动的时候传入的
接下来看WebSocket链接时候需要注意的点
@Component
@ServerEndpoint("/ws/{userId}")
public class WebSocket {
/**
* 存放用户信息
*/
private static final ConcurrentHashMap<Long, WebSocket> WEB_SOCKET_MAP = new ConcurrentHashMap<>(16);
/**
* session
*/
private Session session;
private Long userId;
private String applicationName = System.getProperty("SpringApplicationName");
private StringRedisTemplate stringRedisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
/**
* 静态常量
*/
private static final String SOCKET_USER_SPRING_APPLICATION_NAME = "ws:socket:user:spring:application:name";
/**
* 当有新的WebSocket连接完成时
*
* @param session
* @param userId
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") Long userId) {
System.out.println("new connection");
System.out.println(userId);
this.session = session;
//根据token获取用户信息
this.userId = userId;
WEB_SOCKET_MAP.put(this.userId, this);
this.stringRedisTemplate.opsForHash().put(SOCKET_USER_SPRING_APPLICATION_NAME, userId + "", applicationName);
}
}
其中在链接的时候,将用户ID与socket服务的关系保存进了redis,这样我们在使用的时候就可以根据这个关系,找到对应的socket服务从而实现自己的业务逻辑
然后我们定义一个发送消息的接口
@RestController
@RequestMapping("push")
public class PushController {
@PostMapping("{userId}")
public void pushMessage(@PathVariable Long userId, @RequestBody JSONObject message) {
WebSocket.sendMessage(userId, message);
}
}
再单独封装一个接口,供使用方使用feign
@FeignClient(name = "pushFeign", configuration = DynamicRoutingConfig.class)
public interface PushFeign {
/**
* 推送消息
*
* @param serviceName 服务名
* @param userId 用户
* @param message 消息体
*/
@PostMapping(value = "//{serviceName}/push/{userId}")
void pushMessage(@PathVariable String serviceName, @PathVariable Long userId, @RequestBody JSONObject message);
}
再来个Service
@Service
public class PushService {
@Resource
private PushFeign pushFeign;
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 静态常量
*/
private static final String SOCKET_USER_SPRING_APPLICATION_NAME = "ws:socket:user:spring:application:name";
/**
* 发送消息
*
* @param userId
* @param message
*/
public void pushMessage(Long userId, JSONObject message) {
Object serviceName = this.stringRedisTemplate.opsForHash().get(SOCKET_USER_SPRING_APPLICATION_NAME, userId + "");
if (serviceName != null) {
this.pushFeign.pushMessage(serviceName.toString(), userId, message);
}
}
}
还有个feign的配置文件,将链接重写DynamicRoutingConfig
public class DynamicRoutingConfig {
@Bean
public RequestInterceptor cloudContextInterceptor() {
return template -> {
String url = template.url();
if (url.startsWith("//")) {
url = "http:" + url;
if (url.contains("?")) {
url = url.substring(0, url.indexOf("?"));
}
template.target(url);
template.uri("");
}
};
}
}
那么在使用的时候,我们可以直接调用PushService.pushMessage方法就可以直接给对应的用户发送消息了
那么可能又有人想问了,每个服务都不一样,那网关这些该怎么做,项目源码已经放在了码云上面, https://gitee.com/liupan1230/spring-cloud-websocket-cluster 大家可以参考,同时也有发送方调用示例
WebSocket集群解决方案,不用MQ的更多相关文章
- Websocket集群解决方案
最近在项目中在做一个消息推送的功能,比如客户下单之后通知给给对应的客户发送系统通知,这种消息推送需要使用到全双工的websocket推送消息. 所谓的全双工表示客户端和服务端都能向对方发送消息.不使用 ...
- Terrocotta - 基于JVM的Java应用集群解决方案
前言 越来越多的企业关键应用都必须采用集群技术,实现负载均衡(Load Balancing).容错(Fault Tolerance)和灾难恢复(Failover).以达到系统可用性(High Avai ...
- zookeeper、solrcloud、rediscluster集群解决方案
集群解决方案 课程目标 目标1:说出什么是集群以及与分布式的区别 目标2:能够搭建Zookeeper集群 目标3:能够搭建SolrCloud集群 目标4:能够搭建RedisCluster集群 ...
- 高可用性、负载均衡的mysql集群解决方案
高可用性.负载均衡的mysql集群解决方案 一.mysql的市场占有率 二.mysql为什么受到如此的欢迎 三.mysql数据库系统的优缺点 四.网络服务器的需求 五.什么是mysql的集群 六.什么 ...
- t-io 集群解决方案以及源码解析
t-io 集群解决方案以及源码解析 0x01 概要说明 本博客是基于老谭t-io showcase中的tio-websocket-showcase 示例来实现集群.看showcase 入门还是挺容易的 ...
- springBoot+websocket集群系列知识
WebSocket简介和spring boot集成简单消息代理 Spring Boot 集成 websocket,使用RabbitMQ做为消息代理 Spring Websocket实现向指定的用户发送 ...
- redis集群搭建 不用ruby
redis 从5开始 可以直接用redis-cli命令创建集群了,不用那么麻烦 安装ruby环境 redis配置文件需要修改的地方 port 7000 cluster-enabled yes clus ...
- spring websocket集群问题的简单记录
目录 前言 解决方案 代码示例 前言 最近公司里遇到一个问题,在集群中一些websocket的消息丢失了. 产生问题的原理很简单,发送消息的服务和接收者连接的服务不是同一个服务. 解决方案 用中间件( ...
- 关于websocket集群中不同服务器的用户间通讯问题
最近将应用部署到集群时遇到一个问题,即用户命中不同的服务器导致的用户间无法进行websocket通讯,在网上搜索到类似问题但都没有具体解决方案. 于是用redis的订阅发布功能解决了该问题,具体流程如 ...
- Redis 集群解决方案 Codis
(来源:开源中国社区 http://www.oschina.net/p/codis) Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生 ...
随机推荐
- Jenkins项目中的Performance Trend图表不显示
权限问题:chmod 777 /.../*.jtl 其中上述目录为jmeter生成jtl格式的结果报告的路径,也就是ant对应build.xml里配置好的路径.
- IE8兼容的零零碎碎
css部分 1 nth-of-type选择器 2 span:nth-of-type(1) 3 /*IE8兼容写法*/ 4 span:first-child /*选中第一个*/ 5 span:first ...
- mysql 增加自定义函数
查看mysql当前是否支持编写自定义 SHOW variables like '%fun%'; 开启自定义函数 set global log_bin_trust_function_creators=1 ...
- HDFS、Ceph、GFS、GPFS、Swift、Lustre……容器云选择哪种分布式存储更好?
HDFS.Ceph.GFS.GPFS.Swift.Lustre--容器云选择哪种分布式存储更好?-51CTO.COM 容器云在使用分布式存储时,HDFS.CEPH.GFS.GPFS.Swift等分布式 ...
- 英国延长 UKCA 标记截止日期
政府于 2022 年 11 月 14 日宣布,企业将有 2 年的时间来应用新的 UKCA 产品标记.在 2024 年 12 月 31 日之前,企业可以选择使用 UKCA 或 CE 标志,之后企业只能使 ...
- 5ppm高精度自动同步标准化考场时钟系统
自动同步标准化考场时钟系统------专业LED时钟厂家![点击进入] 学校考场医院车站GPS/NTP网络校时之组联网对时精度达5ppm原理剖析. 时间精度是根据各个用户所要求对LE ...
- C++11新特新-varitable template
C++11新特新-varitable template应用 可变参模板原理可以仔细阅读C++primer 第5版相关部分 应用1 一个万用的HashFun 通过不断调用可变模板函数进行参数包的运算,最 ...
- IT工具知识-13: 如何编辑SVG图像文件并转换为ICO图标文件
使用背景 最近做了个小软件, 但是桌面快捷方式图标不好看, 于是想着找个好看点的图标, 但是网上搜了一圈, 发现好看的几乎都要钱, 常用的话, 付费倒也不反感, 但是, 仅仅只用那么一两次, 为这个付 ...
- Collectors类的静态工厂方法-《Java 8实战》笔记
- windows消息机制_PostMessage和SendMessage
1.子线程中建立一个窗口 为了在后面比较这两个函数,先使用win32 windows程序中建立子线程,在子线程中建立一个窗口. (1)新建一个 win32 windows应用程序 (2)定义子窗口的窗 ...