Springboot中统一启动多个socketIO

news/2024/10/8 17:38:23

前言

这篇随笔属实没想到一个好名字,起因是在项目中遇到了一个springboot服务会发出多个socket服务的场景,而且我们使用的是socketIO服务,为了减少调试工作和重复的开发工作,让开发在项目中专注于业务编写,因此封装了一个在启动springboot服务时,自动创建socketIONamespace的逻辑

依赖

在使用此依赖时,我的项目版本为:Static Badge Static Badge
因为是要跟公司其他团队的架构保持一致,所以我们的socketIo的版本偏低,使用的是 Static Badge

<dependency>  <groupId>com.corundumstudio.socketio</groupId>  <artifactId>netty-socketio</artifactId>  <version>1.7.19</version>  
</dependency>  
<dependency>  <groupId>io.socket</groupId>  <artifactId>socket.io-client</artifactId>  <version>1.0.0</version>  
</dependency>

逻辑

首先需要添加一个socketIo的配置类

import com.corundumstudio.socketio.HandshakeData;
import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Slf4j
@Configuration
public class SocketIoConfig {@Value("${socketio.port}")  private Integer port;  @Value("${socketio.bossCount}")  private int bossCount;  @Value("${socketio.workCount}")  private int workCount;  @Value("${socketio.allowCustomRequests}")  private boolean allowCustomRequests;  @Value("${socketio.upgradeTimeout}")  private int upgradeTimeout;  @Value("${socketio.pingTimeout}")  private int pingTimeout;  @Value("${socketio.pingInterval}")  private int pingInterval;  @Bean  public SocketIOServer socketIoServer() {  SocketConfig socketConfig = new SocketConfig();  socketConfig.setTcpNoDelay(true);  socketConfig.setSoLinger(0);// 因为使用了Spring的com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();  config.setSocketConfig(socketConfig);  
        // 设置授权监听器
        config.setAuthorizationListener(new AuthorizationListener() {
            @Override
            public boolean isAuthorized(HandshakeData data) {
                // 在这里添加你的授权逻辑
                // 例如,检查握手数据中的 token
                String token = data.getSingleUrlParam("token");
                return "valid_token".equals(token);
            }
        });config.setPort(port);config.setBossThreads(bossCount);config.setWorkerThreads(workCount);config.setAllowCustomRequests(allowCustomRequests);config.setUpgradeTimeout(upgradeTimeout);config.setPingTimeout(pingTimeout);config.setPingInterval(pingInterval);return new SocketIOServer(config);}
}

这些类的配置,我是使用的 application.yml 进行管理,在 application.yml 中添加了相应的配置.
对于授权监听器这块的逻辑,我一直没有真正的使用起来。我对于token的处理是放在了建立socket链接后进行处理

需要添加一个策略接口 SocketIoStrategy和一个初始化加载的类 SocketInitHandle

public interface SocketIoStrategy {  /**  * 定义命名空间的Url  *     * @return 命名空间url  */String defineNamespaceUrl();  /**  * 链接后方法  *  * @param client socket客户端信息  */  void connected(SocketIOClient client);/**  * 自定义监听器  *  * @param socketIoNamespace socketIo命名空间  */  void customListener(SocketIONamespace socketIoNamespace);/**  * 链接后方法  *  * @param client socket客户端信息  */  void disconnect(SocketIOClient client);}
import com.corundumstudio.socketio.SocketIONamespace;  
import com.corundumstudio.socketio.SocketIOServer;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.ApplicationListener;  
import org.springframework.context.event.ContextRefreshedEvent;  
import org.springframework.stereotype.Service;  import javax.annotation.PreDestroy;  
import javax.annotation.Resource;@Slf4j
@Service  
public class SocketInitHandle implements ApplicationListener<ContextRefreshedEvent> {  @Resourceprivate SocketIOServer socketIoServer;@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {  ApplicationContext applicationContext = event.getApplicationContext();  // 获取继承了ISocketIoService的beanapplicationContext.getBeansOfType(SocketIoStrategy.class).forEach((beanName, socketService) -> {log.info("{} socket io namespace:{}", beanName, socketService.defineNamespaceUrl());String namespaceUrl = socketService.defineNamespaceUrl();SocketIONamespace socketIoNamespace = socketIoServer.addNamespace(namespaceUrl);socketIoNamespace.addConnectListener(socketService::connected);socketIoNamespace.addDisconnectListener(socketService::disconnect);socketService.customListener(socketIoNamespace);});// 启动服务socketIoServer.start();}/**  * Spring IoC容器在销毁SocketIOServiceImpl Bean之前关闭,避免重启项目服务端口占用问题  */  @PreDestroy  private void autoStop() {  stop();}public void stop() {if (socketIoServer != null) {socketIoServer.stop();socketIoServer = null;}}
}

这样,在项目启动时,会自动识别到实现了SocketIoStrategy这个接口的类,下面是一个这个接口实现的样例


@Slf4j
@Service
public class DemoSocketIoStrategyImpl implements SocketIoStrategy {  /**  * 存放已连接的客户端  */  private static final Map<String, SocketUtil.SocketClientInfo> CLIENT_MAP = new ConcurrentHashMap<>();  @Override  public String defineNamespaceUrl() {  return "/demo";  }  @Override  public void connected(SocketIOClient client) {  SocketUtil.SocketClientInfo clientInfo = SocketUtil.formationSocketInfoWithClient(client);  bizLog.info("************ 客户端:{} 已连接 ************", clientInfo.getClientId());  // 自定义事件`connected` -> 与客户端通信  (也可以使用内置事件,如:Socket.EVENT_CONNECT)  client.sendEvent(Socket.EVENT_CONNECT, "成功连接");  CLIENT_MAP.put(clientInfo.getClientId(), clientInfo);  }  @Override  public void customListener(SocketIONamespace socketIoNamespace) {  socketIoNamespace.addEventListener("PUSH_DATA_EVENT", String.class, (client, data, ackSender) -> {  // 客户端推送`client_info_event`事件时,onData接受数据,这里是string类型的json数据,还可以为Byte[],object其他类型  String clientId = SocketUtil.getClientIdByClient(client);  bizLog.info("client{} push msg:{}", clientId, data);  });  }  @Override  public void disconnect(SocketIOClient client) {  String clientId = SocketUtil.getClientIdByClient(client);  bizLog.info("{} *********************** 客户端已断开连接", clientId);  if (clientId != null) {  CLIENT_MAP.remove(clientId);  client.disconnect();  }  }  public void pushBroadcastMessages(String eventType, String msgContent) {  CLIENT_MAP.forEach((clientId, clientInfo) -> {  bizLog.info("send fence msg to {}, content:{}", clientId, msgContent);  clientInfo.getClient().sendEvent(eventType, msgContent);  });  }  
}

这个类实现了socketIO策略,还提供了一个广播的实现方法,在服务中需要广播消息时,执行消息的类型和内容即可发送

前端逻辑

下面是一个基于react的使用逻辑


const mySocket = useRef<any>(null);const startSocket = () => {if (!mySocket) {// socketIoUrl是对应后端服务的域名,/demo是对应链接的链接路径,用于区分一个服务中的多个socket逻辑mySocket = io(`${socketIoUrl}/demo`, {reconnectionDelayMax: 10000,query: {// 一些在链接的时候需要携带的参数},});mySocket.on('connect', (ev: any) => {console.log('socket 连接成功', ev);// 在链接成功后,发送一个emitEventType类型的事件消息mySocket.emit('emitEventType',"emit data")});// 此处监听后端服务的eventType类型的事件的数据mySocket.on('eventType', (data: any) => {// 接收到数据后的逻辑});}
};useEffect(()=>{// 在加载的时候建立链接startSocket();return () => {//断开socket连接if (mySocket.current) {mySocket.current.closeSocket();}};
},[])

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/69072.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

软件工程第二次结对作业

软件工程 https://edu.cnblogs.com/campus/fzu/SE2024作业要求 https://edu.cnblogs.com/campus/fzu/SE2024/homework/13281作业目标 基于第一次结对作业项目程序的实现学号 102201129合作伙伴 102201127项目分工: 102201129周鑫: 前端开发: 设计和实现用户界面。 确保界面响…

CSP 模拟9

CSP 模拟9 我也不明白学校模拟赛什么命名逻辑,凑合着看吧最唐的一集 邻面合并 这个直接状压就做完了,赛时早早想到做法,但是因为自己太唐把 \(0\) 写成 \(1\),在优秀大样例的助攻下挂掉 \(50\)点击查看代码 #include<bits/stdc++.h> using namespace std; using llt=…

南沙C++信奥赛陈老师解一本通题 1297:公共子序列

​【题目描述】我们称序列Z=<z1,z2,...,zk>Z=<z1,z2,...,zk>是序列X=<x1,x2,...,xm>X=<x1,x2,...,xm>的子序列当且仅当存在严格上升的序列<i1,i2,...,ik><i1,i2,...,ik>,使得对j=1,2,...,k,有xij=zjxij=zj。比如Z=<a,b,f,c> 是X=&l…

语音生成公司 ElevenLabs 估值达 30 亿美元;OpenAI Realtime API 很好也很贵丨RTE 开发者日报

开发者朋友们大家好:这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文章 」、「有看点的 会议 」,但内容仅代表编…

万兆加速计算卡设计资料保存:372-基于XC7VX690T的万兆光纤、双FMC扩展的综合计算平台 RISCV 芯片验证平台

一、板卡概述 基于V7的高性能PCIe信号处理板,板卡选用Xilinx 公司Virtex7系列FPGA XC7VX690T-2FFG1761C为处理芯片,板卡提供两个标准FMC插槽,适用于高性能采集、回放以及相关处理。通过连接不同的FMC子卡的方式,可实现不同形式的数据采集、回放、处理的功能模块。板卡…

中电金信:源启数据建模平台:建模效率和管理精细度进一步提升

​源启数据建模平台是源启数据资产平台面向数据仓库等大型数据模型构建专门打造的模型设计工具。它以需求牵引模型动态演进,持续变更模型适应业务变化,并以Web和图形化方式,提供正向、逆向建模能力,高效复用模型资产和构建大型数据模型。同时,秉承“建模即治理”的思想,在…

是用python脚本清理reids连接

背景:测试环境的redis不知道咋回事突然无法连接,服务器登录查了一下发现连接数用完了。研发说雨女无瓜,测试环境删了没事,正事要紧赶紧恢复。得嘞!> info clients # Clients connected_clients:9997 # 连接中的数量 client_recent_max_input_buffer:54366 client_rece…

在Cucumber中应用 PicoContainer容器实现组件的实例化

通过 PicoContainer 这个轻量级的DI(Dependency Injection)组件容器进行组件的实例化, 相关介绍参考:http://picocontainer.com/introduction.html step1:定义一个ScenarioContext类 step2:添加jar依赖 implementation io.cucumber:cucumber-picocontainer:7.2.3 step3:…