首先不了解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的更多相关文章

  1. Websocket集群解决方案

    最近在项目中在做一个消息推送的功能,比如客户下单之后通知给给对应的客户发送系统通知,这种消息推送需要使用到全双工的websocket推送消息. 所谓的全双工表示客户端和服务端都能向对方发送消息.不使用 ...

  2. Terrocotta - 基于JVM的Java应用集群解决方案

    前言 越来越多的企业关键应用都必须采用集群技术,实现负载均衡(Load Balancing).容错(Fault Tolerance)和灾难恢复(Failover).以达到系统可用性(High Avai ...

  3. zookeeper、solrcloud、rediscluster集群解决方案

        集群解决方案 课程目标 目标1:说出什么是集群以及与分布式的区别 目标2:能够搭建Zookeeper集群 目标3:能够搭建SolrCloud集群 目标4:能够搭建RedisCluster集群 ...

  4. 高可用性、负载均衡的mysql集群解决方案

    高可用性.负载均衡的mysql集群解决方案 一.mysql的市场占有率 二.mysql为什么受到如此的欢迎 三.mysql数据库系统的优缺点 四.网络服务器的需求 五.什么是mysql的集群 六.什么 ...

  5. t-io 集群解决方案以及源码解析

    t-io 集群解决方案以及源码解析 0x01 概要说明 本博客是基于老谭t-io showcase中的tio-websocket-showcase 示例来实现集群.看showcase 入门还是挺容易的 ...

  6. springBoot+websocket集群系列知识

    WebSocket简介和spring boot集成简单消息代理 Spring Boot 集成 websocket,使用RabbitMQ做为消息代理 Spring Websocket实现向指定的用户发送 ...

  7. redis集群搭建 不用ruby

    redis 从5开始 可以直接用redis-cli命令创建集群了,不用那么麻烦 安装ruby环境 redis配置文件需要修改的地方 port 7000 cluster-enabled yes clus ...

  8. spring websocket集群问题的简单记录

    目录 前言 解决方案 代码示例 前言 最近公司里遇到一个问题,在集群中一些websocket的消息丢失了. 产生问题的原理很简单,发送消息的服务和接收者连接的服务不是同一个服务. 解决方案 用中间件( ...

  9. 关于websocket集群中不同服务器的用户间通讯问题

    最近将应用部署到集群时遇到一个问题,即用户命中不同的服务器导致的用户间无法进行websocket通讯,在网上搜索到类似问题但都没有具体解决方案. 于是用redis的订阅发布功能解决了该问题,具体流程如 ...

  10. Redis 集群解决方案 Codis

    (来源:开源中国社区 http://www.oschina.net/p/codis) Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生 ...

随机推荐

  1. elementUI 中日历自定义内容 配置具体内容

    效果图如下: 代码如下: <template> <div class="con-main"> <div class="con-list&qu ...

  2. 20200925--矩阵加法(奥赛一本通P93 6 多维数组)

    输入两个n行m列的矩阵A和B,输出它们的和A+B 输入: 第1行包含两个整数n和m(1<=n<=100,1<=m<=100),表示矩阵的行数和列数. 接下来n行,每行m个整数, ...

  3. 重写mybatis-plus的saveUpdate方法

    重写mybatis-plus的saveUpdate方法 1.问题出现 同步外部数据的时候,如果需要同步逻辑删除的数据,mybatis-plus的saveOrUpdate||saveOrUpdateBa ...

  4. mybatis-plus 3.4.3.1 设置数据库字段为NULL

    实体字段注解配置 @TableField(jdbcType = JdbcType.VARCHAR, updateStrategy = FieldStrategy.IGNORED) private St ...

  5. Linux 中最常用 150 个命令汇总

  6. 关于VScode里TS文件内引入插件没有提示内置属性和方法这件事

    前几天使用VScode + Vue + Vite + Ts开发项目 由于自己手残 把VScode设置文件的代码做了一些修改 导致TS文件引入的插件没有提示了!! 几经折腾下 终于靠自己解决了! 不多说 ...

  7. alia linux

    alias lrt='ls -lrt'

  8. VBA中的集合

    Sub testSet() Dim c As New Collection c.Add ("A") c.Add (2) c.Add ("A") For i = ...

  9. 微信小程序 真机调试白屏

    真机调试白屏,报define is not defined 解决:   更新小程序版本

  10. windows远程linux桌面

    TigerVNC是一个高性能.平台无关的虚拟网络计算(Virtual Network Computing,VNC)实现,是一个客户端/服务器应用程序,允许用户在远程机器上启动并与图形应用程序进行交互. ...