国内中间件websocket解决方案

1.前言

  • 目前国内主流的中间件(金蝶、东方通、中创)对spring提供的websocket服务都支持得不好,程序在运行过程中都会让websocket不同程度上的死掉。导致websocket功能无法正常使用,严重情况下,会让自身的中间件卡住(或者死掉)。导致整个应用不能访问。
  • 为了解决这类问题,我们必须找到更加完美,更加贴合我们应用的websocket组件。org.java_websocket就是其中的解决方案之一,以下针对org.java_websocket组件实现聊天和消息提醒的替换方案的阐述。

2.java_websocket方案图

在这里插入图片描述

  • java_websocket:用于创建websocket服务,及websocket的onOpen/onClose/onMessage/onError等方法的维护,对应JavaWebSocketServer和WebSocketConfig类。
  • ws集中处理及业务分发:对websocket的onOpen/onClose/onMessage/onError等方法的集中处理及websocket业务分类,实现WebSocketService接口。
  • 聊天业务处理:对聊天websocket的session管理及相关业务实现,实现WebSocketBusinessService接口。
  • 消息提醒业务处理:对消息提醒websocket的session管理及相关业务实现,实现WebSocketBusinessService接口。

3.java_websocket组件使用

3.1 maven

<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.37</version>
</dependency>

3.2 JavaWebSocketServer实现

    JavaWebSocketServer需要继承java_websocket组件的WebSocketServer类。如下代码。
    注意:
    - 代码中必须要使用线程将接收到的WebSocket进行分发处理,否则很容易让websocket服务自动挂掉。
    - 调用WebSocketService接口,将WebSocket进行集中处理。
package com.lylp.sys.service;

import com.lylp.common.utils.SpringContextUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * WebSocket服务
 *
 * @author
 * @date 2021/7/1 10:53
 */
public class JavaWebSocketServer extends WebSocketServer {
   
    private static Logger logger = LoggerFactory.getLogger(JavaWebSocketServer.class);

    /**
 * 定义线程池<br>
 * websocket的onOpen/onClose/onMessage/onError接收消息后,<br>
 * 需要使用线程去处理相关业务逻辑,<br>
 * 如果自身处理的业务逻辑复杂,处理时间稍久一点,websocket服务就会自动关闭。
 */
 private ExecutorService executorService = new ThreadPoolExecutor(50, 200, 30, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            new BasicThreadFactory.Builder().namingPattern("thread-pool-%d").daemon(true).build());

    public JavaWebSocketServer(Integer port) {
   
        super(new InetSocketAddress(port));
    }

    @Override
 public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
   
        executorService.execute(() -> {
   
            WebSocketService bean = SpringContextUtils.getBean(WebSocketService.class);
            bean.onOpen(webSocket, clientHandshake.getResourceDescriptor());
        });

        logger.debug("JavaWebSocketServer online " + this.connections().size());
    }

    @Override
 public void onClose(WebSocket webSocket, int i, String s, boolean b) {
   
        if (webSocket == null) {
   
            return;
        }
        executorService.execute(() -> {
   
            WebSocketService bean = SpringContextUtils.getBean(WebSocketService.class);
            bean.onClose(webSocket, i, s, b);
        });
    }

    @Override
 public void onMessage(WebSocket webSocket, String s) {
   
        executorService.execute(() -> {
   
            WebSocketService bean = SpringContextUtils.getBean(WebSocketService.class);
            bean.onMessage(webSocket, s);
        });
    }

    @Override
 public void onError(WebSocket webSocket, Exception e) {
   
        if (webSocket == null) {
   
            logger.info("JavaWebSocketServer on error. websocket is null.");
            logger.error(e.getMessage(), e);
            return;
        }
        executorService.execute(() -> {
   
            WebSocketService bean = SpringContextUtils.getBean(WebSocketService.class);
            bean.onError(webSocket, e);
        });
    }

    @Override
 public void onStart() {
   
        logger.info("JavaWebSocketServer start");
    }

}

3.3WebSocketConfig类

  WebSocketConfig对websocket端口号、websocket服务器启动进行配置,代码如下
  注意:java_websocket组件需要独立占用一个端口号。
package com.lylp.sys.config;

import com.lylp.common.exception.LYLPException;
import com.lylp.sys.service.JavaWebSocketServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebSocketConfig {
   

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${core.configuration.websocket.port}")
    private Integer websocketPort;

    @Bean
 public JavaWebSocketServer javaWebSocketServer() {
   
        if (websocketPort == null) {
   
            logger.error("javaWebSocketServer start failed, reason: core.configuration.websocket.port is null");
            throw new LYLPException("javaWebSocketServer start failed");
        }
        JavaWebSocketServer javaWebSocketServer = new JavaWebSocketServer(websocketPort);
        javaWebSocketServer.start();
        return javaWebSocketServer;
    }

}

4.ws集中处理及业务分发

WebSocketService接口用于集中处理WebSocket信息,包括WebSocket消息的分类,token处理,获取当前用户信息等处理。
注意:java_websocket是通过url进行业务分类的。
接口代码如下:
package com.lylp.sys.service;

import org.java_websocket.WebSocket;

/**
* websocket处理
*
* @author 
* @date 2021/7/1 10:55
*/
public interface WebSocketService {
   

   /**
* onOpen
*
* @param webSocket session
* @param url url
*/
void onOpen(WebSocket webSocket, String url);

   /**
* onClose
*
* @param webSocket session
* @param i
* @param message 消息
* @param b
*/
void onClose(WebSocket webSocket, int i, String message, boolean b);

   /**
* 接收消息
*
* @param webSocket session
* @param message 消息
*/
void onMessage(WebSocket webSocket, String message);

   /**
* onError
*
* @param webSocket session
* @param e 异常
*/
void onError(WebSocket webSocket, Exception e);
}
实现代码如下:
注意:
-java_websocket的WebSocket类中有一个attachment属性,可以拥有存储附件信息。这里用于存储用户信息。attachment属性会在WebSocket生命周期中都会存在。比如在onOpen设置了此属性,则在onMessage/onClose/onError方法的WebSocket参数会自动携带attachment信息。
-在onOpen/onMessage/onClose/OnError根据解析的WebSocket类型,自动查找相应业务(即WebSocketBusinessService接口对应的实现类),对相关业务进行处理。
package com.lylp.sys.service.impl;

import com.lylp.common.exception.LYLPException;
import com.lylp.common.utils.HttpUtils;
import com.lylp.common.utils.ResponseResult;
import com.lylp.common.utils.SpringContextUtils;
import com.lylp.sys.dao.dto.SysUserAccountDto;
import com.lylp.sys.dao.dto.WsUserDto;
import com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值