learning_record_doc/java/踩坑记录/Springboot-WebSocket.md
2022-10-21 17:02:22 +08:00

4.3 KiB
Raw Blame History

Springboot-WebSocket

问题java websocket服务器发生EOFException异常,断开和客户端的连接

原因:使用了 Nginx 反向代理后默认超过60S没有数据传输的连接会自动断开。

解决方案一:

nginx配置

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

# 在http模块中添加
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

map指令的作用该作用主要是根据客户端请求中的值来构造改变connection_upgrade的值即根据变量的值创建新的变量connection_upgrade 创建的规则就是{}里面的东西。其中的规则没有做匹配,因此使用默认的,即 http_upgrade为空字符串的话那么值就是 close。

则代理配置更新为:

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.创建定时任务

@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属性含义:

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 的计划任务执行功能

@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

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppTest.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)