当前位置:   article > 正文

springboot实现websocket客户端,含重连机制_spring-boot-starter-websocket 配置超时

spring-boot-starter-websocket 配置超时

一、简介

  • 因为用前端实现的客户端,比方说小程序,网络不稳定,会经常断,所以考虑用java实现客户端,稳定。
  • java版的重连机制确实花费了好多时间才正好。
  • 重连的时候刚开始没有加同步,导致定时器发心跳频繁的时候上次还没有完全创建完就又创建了一个客户端,加同步避免了。
  • sendMsg的时候之前没有加超时,可能有同时存在多个建立连接占用资源的隐患,加了超时。额 此处限制被我在生产环境去掉了,因为这个时间不好控制,短了的话会一直连不上。。。暂时再考虑这块有没有必要处理。
  • 还有websocket比较恶心的是 每次error的时候都开一个新线程去通知,如果你一直失败,就等着cpu爆炸吧。
  • WebSocket有五种状态:NOT_YET_CONNECTED、CONNECTING、OPEN、CLOSING、CLOSED, 只有not_yet_connected的时候才可以connect, 一旦connect之后,状态改变了,就无法再connect了。

  • 代码的git地址为:https://github.com/1956025812/websocketdemo

  • 找时间要自己写个服务端,含踢人和一段时间没收到响应就踢人的效果;

二、实现

2.1 pom依赖

  1. <!-- websocket -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-websocket</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.java-websocket</groupId>
  8. <artifactId>Java-WebSocket</artifactId>
  9. <version>1.3.5</version>
  10. </dependency>
  11. <!-- websocket -->
  12. <dependency>
  13. <groupId>org.apache.commons</groupId>
  14. <artifactId>commons-lang3</artifactId>
  15. <version>3.3.2</version>
  16. </dependency>

2.2 启动类

  1. package com.example.websocketdemo;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.ApplicationArguments;
  5. import org.springframework.boot.ApplicationRunner;
  6. import org.springframework.boot.SpringApplication;
  7. import org.springframework.boot.autoconfigure.SpringBootApplication;
  8. import org.springframework.scheduling.annotation.EnableScheduling;
  9. @Slf4j
  10. @SpringBootApplication
  11. @EnableScheduling
  12. public class WebsocketdemoApplication implements ApplicationRunner {
  13. @Autowired
  14. private WebSocketClientFactory webSocketClientFactory;
  15. @Override
  16. public void run(ApplicationArguments args) {
  17. // 项目启动的时候打开websocket连接
  18. webSocketClientFactory.retryOutCallWebSocketClient();
  19. }
  20. public static void main(String[] args) {
  21. SpringApplication.run(WebsocketdemoApplication.class, args);
  22. }
  23. }

2.3 WebSocketClientFactory

 

  1. package com.example.websocketdemo;
  2. import lombok.Data;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.java_websocket.WebSocket;
  5. import org.java_websocket.client.WebSocketClient;
  6. import org.java_websocket.handshake.ServerHandshake;
  7. import org.springframework.scheduling.annotation.Async;
  8. import org.springframework.scheduling.annotation.Scheduled;
  9. import org.springframework.stereotype.Component;
  10. import java.net.URI;
  11. import java.net.URISyntaxException;
  12. @Component
  13. @Slf4j
  14. @Data
  15. public class WebSocketClientFactory {
  16. public static final String outCallWebSockertUrl = "ws://IP:端口";
  17. private WebSocketClient outCallWebSocketClientHolder;
  18. /**
  19. * 创建websocket对象
  20. *
  21. * @return WebSocketClient
  22. * @throws URISyntaxException
  23. */
  24. private WebSocketClient createNewWebSocketClient() throws URISyntaxException {
  25. WebSocketClient webSocketClient = new WebSocketClient(new URI(outCallWebSockertUrl)) {
  26. @Override
  27. public void onOpen(ServerHandshake serverHandshake) {
  28. }
  29. @Override
  30. public void onMessage(String msg) {
  31. log.info("接收信息为:{}", msg);
  32. }
  33. @Override
  34. public void onClose(int i, String s, boolean b) {
  35. log.info("关闭连接");
  36. retryOutCallWebSocketClient();
  37. }
  38. @Override
  39. public void onError(Exception e) {
  40. log.error("连接异常");
  41. retryOutCallWebSocketClient();
  42. }
  43. };
  44. webSocketClient.connect();
  45. return webSocketClient;
  46. }
  47. /**
  48. * 项目启动或连接失败的时候打开新链接,进行连接认证
  49. * 需要加同步,不然会创建多个连接
  50. */
  51. public synchronized WebSocketClient retryOutCallWebSocketClient() {
  52. try {
  53. // 关闭旧的websocket连接, 避免占用资源
  54. WebSocketClient oldOutCallWebSocketClientHolder = this.getOutCallWebSocketClientHolder();
  55. if (null != oldOutCallWebSocketClientHolder) {
  56. log.info("关闭旧的websocket连接");
  57. oldOutCallWebSocketClientHolder.close();
  58. }
  59. log.info("打开新的websocket连接,并进行认证");
  60. WebSocketClient webSocketClient = this.createNewWebSocketClient();
  61. String sendOpenJsonStr = "{\"event\":\"connect\",\"sid\":\"1ae4e3167b3b49c7bfc6b79awww691562914214595\",\"token\":\"df59eba89\"}";
  62. this.sendMsg(webSocketClient, sendOpenJsonStr);
  63. // 每次创建新的就放进去
  64. this.setOutCallWebSocketClientHolder(webSocketClient);
  65. return webSocketClient;
  66. } catch (URISyntaxException e) {
  67. e.printStackTrace();
  68. log.error(e.getMessage());
  69. }
  70. return null;
  71. }
  72. /**
  73. * 发送消息
  74. * 注意: 要加超时设置,避免很多个都在同时超时占用资源
  75. *
  76. * @param webSocketClient 指定的webSocketClient
  77. * @param message 消息
  78. */
  79. public void sendMsg(WebSocketClient webSocketClient, String message) {
  80. log.info("websocket向服务端发送消息,消息为:{}", message);
  81. long startOpenTimeMillis = System.currentTimeMillis();
  82. while (!webSocketClient.getReadyState().equals(WebSocket.READYSTATE.OPEN)) {
  83. log.debug("正在建立通道,请稍等");
  84. long currentTimeMillis = System.currentTimeMillis();
  85. if(currentTimeMillis - startOpenTimeMillis >= 5000) {
  86. log.error("超过5秒钟还未打开连接,超时,不再等待");
  87. return;
  88. }
  89. }
  90. webSocketClient.send(message);
  91. }
  92. @Async
  93. @Scheduled(fixedRate = 10000)
  94. public void sendHeartBeat() {
  95. log.info("定时发送websocket心跳");
  96. try {
  97. WebSocketClient outCallWebSocketClientHolder = this.getOutCallWebSocketClientHolder();
  98. if (null == outCallWebSocketClientHolder) {
  99. log.info("当前连接还未建立,暂不发送心跳消息");
  100. return;
  101. }
  102. // 心跳的请求串,根据服务端来定
  103. String heartBeatMsg = "{\"event\":\"heartbeat\",\"sid\":\"1ae4e3167b3b49c7bfc6b79a74f2296915222214595\"}";
  104. this.sendMsg(outCallWebSocketClientHolder, heartBeatMsg);
  105. } catch (Exception e) {
  106. e.printStackTrace();
  107. log.error("发送心跳异常");
  108. retryOutCallWebSocketClient();
  109. }
  110. }
  111. }

 

三、测试

3.0 控制类

  1. package com.example.websocketdemo;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.bind.annotation.GetMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. @Slf4j
  7. @RestController
  8. public class OutCallWebSocketController {
  9. @Autowired
  10. private WebSocketClientFactory webSocketClientFactory;
  11. @GetMapping("/sendCall")
  12. public void sendCall() {
  13. String heartBeatMsg = "{\"event\":\"heartbeat\",\"sid\":\"1ae4e3167b3b49c7bfc6b79a74f229691562914214595\"}";
  14. webSocketClientFactory.sendMsg(webSocketClientFactory.getOutCallWebSocketClientHolder(), heartBeatMsg);
  15. }
  16. }

3.1 日志

3.1.1 启动的日志

  1. 2020-01-02 21:56:43.365 INFO 14800 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
  2. 2020-01-02 21:56:43.377 INFO 14800 --- [ scheduling-1] c.e.w.WebSocketClientFactory : 定时发送websocket心跳
  3. 2020-01-02 21:56:43.377 INFO 14800 --- [ scheduling-1] c.e.w.WebSocketClientFactory : 当前连接还未建立,暂不发送心跳消息
  4. 2020-01-02 21:56:43.391 INFO 14800 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8000 (http) with context path ''
  5. 2020-01-02 21:56:43.393 INFO 14800 --- [ main] c.e.w.WebsocketdemoApplication : Started WebsocketdemoApplication in 1.208 seconds (JVM running for 1.617)
  6. 2020-01-02 21:56:43.394 INFO 14800 --- [ main] c.e.w.WebSocketClientFactory : 打开新的websocket连接,并进行认证
  7. 2020-01-02 21:56:43.398 INFO 14800 --- [ main] c.e.w.WebSocketClientFactory : websocket向服务端发送消息,消息为:{"event":"connect","sid":"1ae4e3167b3b49c7bfc6b79awww691562914214595","token":"df59eba89"}
  8. 2020-01-02 21:56:43.410 INFO 14800 --- [ Thread-5] c.e.w.WebSocketClientFactory : 接收信息为:{"result":2,"reqBody":{"event":"connect","sid":"1ae4e3167b3b49c7bfc6b79awww691562914214595","token":"df59eba89"},"event":"connect","resultMessage":"企业用户不存在","sid":"1ae4e3167b3b49c7bfc6b79awww691562914214595"}
  9. 2020-01-02 21:56:53.377 INFO 14800 --- [ scheduling-1] c.e.w.WebSocketClientFactory : 定时发送websocket心跳
  10. 2020-01-02 21:56:53.377 INFO 14800 --- [ scheduling-1] c.e.w.WebSocketClientFactory : websocket向服务端发送消息,消息为:{"event":"heartbeat","sid":"1ae4e3167b3b49c7bfc6b79a74f2296915222214595"}
  11. Disconnected from the target VM, address: '127.0.0.1:55203', transport: 'socket'
  12. 2020-01-02 21:56:55.993 INFO 14800 --- [extShutdownHook] o.s.s.c.ThreadPoolTaskScheduler : Shutting down ExecutorService 'taskScheduler'
  13. 2020-01-02 21:56:55.993 INFO 14800 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'

3.1.2 测试日志 TODO

 

 

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号