## Springboot-WebSocket ### **问题**:java websocket服务器发生EOFException异常,断开和客户端的连接 #### **原因**:使用了 Nginx 反向代理后,默认超过60S没有数据传输的连接会自动断开。 #### **解决方案一:** nginx配置: ```Shell location /finance/ws/ { proxy_pass http://192.168.3.47:56667/finance/ws/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } ``` 可以更新配置 /etc/nginx/nginx.conf ```Shell # 在http模块中添加 map $http_upgrade $connection_upgrade { default upgrade; '' close; } ``` map指令的作用:该作用主要是根据客户端请求中的值,来构造改变connection_upgrade的值,即根据变量的值创建新的变量connection_upgrade, 创建的规则就是{}里面的东西。其中的规则没有做匹配,因此使用默认的,即 http_upgrade为空字符串的话,那么值就是 close。 则代理配置更新为: ```Shell location /finance/ws/ { proxy_pass http://192.168.3.47:56667/finance/ws/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "$connection_upgrade"; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } ``` 如上配置其实无法解决nginx默认60s没有任何传输请求断开的问题,可以适当延长**proxy_read_timeout的时间** #### 解决方案二:后台判断有连接客户端的情况,通过心跳机制每隔30-50秒和客户端主动建立连接。** 1.创建定时任务 ```Java @Slf4j //Component 注入到Spring容器 @Component public class HeartbeatWebSocket { //使用@cheduled定义一个方法为计划任务,fixedDelay属性表示表示上一次任务执行完成后多久再执行,参数类型long,单位:ms @Scheduled(fixedDelay = 50000) private void heartbeat() { //这个Map存储了所有连接的会话 ConcurrentMap sessionPool = MonthEndCarryForwardServer.getSessionPool(); if (!sessionPool.isEmpty()){ //循环会话池 对每个会话发送ping指令 sessionPool.forEach((k,v)->{ try { //发送指令 v.getSession().getBasicRemote().sendText("ping"); } catch (IOException e) { e.printStackTrace(); log.error("心跳发送出错:",e); } }); } } } ``` 注解`@Scheduled`属性含义: ```Java cron表达式:指定任务在特定时间执行 fixedDelay:表示上一次任务执行完成后多久再执行,参数类型long,单位:ms fixedDelayString:与fixedDelay一样,只是参数类型是String fixedRate:表示按一定的频率执行任务,参数类型long,单位:ms 如: fixedRate(5000),表示这个定时器任务每5秒执行一次 fixedRateString:与fixedRate一样,只是参数类型变为String initialDelay:表示延迟多久再第一次执行任务,参数类型为long ,单位:ms initialDelayString:与initialDelay一样,只是参数类型String ``` 2.在Springboot Application启动类上添加`@EnableScheduling`注解启用 Spring 的计划任务执行功能 ```Java @EnableScheduling //启用 Spring 的计划任务执行功能 注:不添加此注解无法开启计划任务 @SpringBootApplication(scanBasePackages = "com.guadou.erp") public class FinanceApplication { public static void main(String[] args) throws UnknownHostException { ConfigurableApplicationContext context = SpringApplication.run(FinanceApplication.class, args); AppConfig appConfig = new AppConfig(context); appConfig.setAppName("Finance APP"); } } ``` ### **问题**:测试方法无法启动 Error creating bean with name 'serverEndpointExporter' defined in class path resource #### 解决方案:设置@SpringBootTest的属性为SpringBootTest.WebEnvironment.RANDOM_PORT ```java @RunWith(SpringRunner.class) @SpringBootTest(classes = AppTest.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) ```