learning_record_doc/java/踩坑记录/Springboot-WebSocket.md

119 lines
4.3 KiB
Markdown
Raw Normal View History

2022-10-21 17:02:22 +08:00
## 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<Long, MonthEndCarryForwardServer> 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)
```