基于spring @EnableWebSocket 实现socket通信业务处理优化

本篇文章针对基于spring @EnableWebSocket 实现socket通信业务处理的处理

在socket通信默认情况下是线程不安全的,当多个线程访问同一个socket实体是将会发生错误,具体看源码当socket发送信息是改变自身状态,当另一个线程发送时会检查状态,当状态不为初始值是将抛出异常,

本人解决思路是将每个socket客户端的信息根据放到单独队列去处理,以实现单线程操作

不涉及socket存储处理以及发送的逻辑代码

首先定义消息实体类封装收到的消息

public class MessageCheckBean {
    /*
     1 open 2 message 3 close 4 error
     */
    private Integer msgType;
    private WebSocketSession session;
    private String msg;
    private CloseReason closeReason;
    private Throwable throwable;
    public Integer getMsgType() {
        return msgType;
    }

    public void setMsgType(Integer msgType) {
        this.msgType = msgType;
    }

    public WebSocketSession getSession() {
        return session;
    }

    public void setSession(WebSocketSession session) {
        this.session = session;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public CloseReason getCloseReason() {
        return closeReason;
    }

    public void setCloseReason(CloseReason closeReason) {
        this.closeReason = closeReason;
    }

    public Throwable getThrowable() {
        return throwable;
    }

    public void setThrowable(Throwable throwable) {
        this.throwable = throwable;
    }

然后首先消息队列以及线程池管理逻辑,讲接收消息和发送消息分开

/**
 * 消息队列以及线程池管理
 */
public class MsgCheckSessionQueueManager {

    private static boolean openAll = false;
    private static boolean closeAllThread = false;
    /*
    原始数据队列
     */
    private static LinkedBlockingQueue<MessageCheckBean> rowBlockingQueue = new LinkedBlockingQueue();
    private static ArrayList<LinkedBlockingQueue<UserMsg>> needSendMsgArray = new ArrayList<>();
    static {
        needSendMsgArray.add(new LinkedBlockingQueue<>());
        needSendMsgArray.add(new LinkedBlockingQueue<>());
        needSendMsgArray.add(new LinkedBlockingQueue<>());
    }
    //实际生产请使用其他线程池
    private static ExecutorService sendMsgCacheThreadPool = new ThreadPoolExecutor(needSendMsgArray.size(), needSendMsgArray.size(),
            20L, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>());
    private static ExecutorService rawMsgDealCacheThreadPool = new ThreadPoolExecutor(needSendMsgArray.size(), needSendMsgArray.size(),
            20L, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>());

    /**
     * 处理client发送来的消息
     * @param msg
     */
    public static void addRawMsg(MessageCheckBean msg){
        try {
            rowBlockingQueue.put(msg);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理发送给client的消息
     * @param userMsg
     */
    public static void addUserMsg(UserMsg userMsg){
        try {
            needSendMsgArray.get(根据业务处理消息到不同队列).put(userMsg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*
     开启线程调度
     */
    public static void beginDealMsg(){
        if(!openAll){
            dealMessage();
            openAll = true;
        }
    }

    public static void main(String[] args) {
        beginDealMsg();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        shutDownTheadPool();
    }

    private static void dealMessage(){
        Thread threadRowDeal = new Thread(new Runnable() {
            @Override
            public void run() {
                rawMsgDealCacheThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            while (!closeAllThread){
                                MessageCheckBean msgBewan = rowBlockingQueue.take();
                                //处理来自socket客户端端的原始数据
                                dealRawMsgToSendQueue(msgBewan);
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        });
       threadRowDeal.setDaemon(true);
       threadRowDeal.start();

       //开启线程池处理消息队列
       Thread openRowDealThread = new Thread(new Runnable() {
           @Override
           public void run() {
               for(int i = 0; i< needSendMsgArray.size(); i++){
                   final int j = i;
                   sendMsgCacheThreadPool.execute(new Runnable() {
                       @Override
                       public void run() {
                           LinkedBlockingQueue<UserMsg> linkedBlockingQueue = needSendMsgArray.get(j);
                           while (!closeAllThread){
                               try {
                                   UserMsg userMsg = linkedBlockingQueue.take();
                                   //发送消息
                                   MsgClientDealCheckSeesionManager.sendMsgToClient(userMsg);
                               } catch (InterruptedException e) {
                                   e.printStackTrace();
                               }
                               //处理来自socket客户端端的原始数据
                           }
                       }
                   });
               }
           }
       });
       openRowDealThread.setDaemon(true);
       openRowDealThread.start();
    }

    /**
     * 更具业务处理来自socket客户端的原始数据 并将消息插入到needSendMsgArray
     * 每个用户插入到单独队列
     */
    private static void dealRawMsgToSendQueue(MessageCheckBean msgBean) throws InterruptedException {
        UserMsg userMsg = MsgClientDealCheckSeesionManager.dealRawMsgToSendQueue(msgBean);
        if(userMsg != null){
            addUserMsg(userMsg);
        }
    }
    /*
        关闭线程池  添加消息防止堵塞队列抛出异常
     */
    public static void shutDownTheadPool(){
        closeAllThread = true;
        openAll = false;
        MessageCheckBean messageBean = new MessageCheckBean();
        messageBean.setMsgType(5);
        MsgCheckSessionQueueManager.addRawMsg(messageBean);
        for (int i = 0; i < needSendMsgArray.size(); i++) {
            try {
                UserMsg userMsg = new UserMsg();
                userMsg.setDeviceId(String.valueOf(i));
                needSendMsgArray.get(i).put(userMsg);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        rawMsgDealCacheThreadPool.shutdown();
        sendMsgCacheThreadPool.shutdown();
    }
}

封装消息接收消息处理类实现收到信息的业务处理

/**
 * 消息处理类
 *    1 open 2 message 3 close 4 error
 */
public class MsgCheckSeesionHandler {

    public static void openSeesion(WebSocketSession session){
        MessageCheckBean messageBean = new MessageCheckBean();
        messageBean.setMsgType(1);
        messageBean.setSession(session);
        MsgCheckSessionQueueManager.addRawMsg(messageBean);
    }

    public static void onMessage(WebSocketSession session, String msg){
        MessageCheckBean messageBean = new MessageCheckBean();
        messageBean.setMsgType(2);
        messageBean.setSession(session);
        messageBean.setMsg(msg);
        MsgCheckSessionQueueManager.addRawMsg(messageBean);
    }

    public static void onClose(WebSocketSession session) {
        MessageCheckBean messageBean = new MessageCheckBean();
        messageBean.setMsgType(3);
        messageBean.setSession(session);
        MsgCheckSessionQueueManager.addRawMsg(messageBean);
    }

    public static void onError(WebSocketSession session, Throwable throwable) {
        MessageCheckBean messageBean = new MessageCheckBean();
        messageBean.setMsgType(4);
        messageBean.setSession(session);
        messageBean.setThrowable(throwable);
        MsgCheckSessionQueueManager.addRawMsg(messageBean);
    }
}

处理封装的消息的具体实现类更具业务处理消息

/**
 * 原始数据处理类
 */
public class MsgClientDealCheckSeesionManager {

    /*
    根据userMsg直接发送消息
     */
    public static void sendMsgToClient(UserMsg userMsg) {

        System.out.println("send to client" + userMsg.getDeviceId());
    }

    /*
    处理客户端发送来的消息
     */
    public static UserMsg dealRawMsgToSendQueue(MessageCheckBean msgBean) {
        UserMsg uerMsg = new UserMsg();
        if(msgBean.getMsgType() == 1){
            //openSession
            uerMsg = SeesionOpenDealManager.openSeesion(msgBean.getSession());
        } else if(msgBean.getMsgType() == 2){
            //onMessage
            System.out.println("message");
            uerMsg.setDeviceId(msgBean.getSession().getAttributes().get("deviceId").toString());
            uerMsg = SessionMessageDealManager.onMessag(msgBean.getSession(),msgBean.getMsg());
        } else if(msgBean.getMsgType() == 3){
            //onClose
            uerMsg.setDeviceId(msgBean.getSession().getAttributes().get("deviceId").toString());
            System.out.println("close");
        } else if(msgBean.getMsgType() == 4){
            //onError
            uerMsg.setDeviceId(msgBean.getSession().getAttributes().get("deviceId").toString());
            System.out.println("error");
        } else {

        }
        return uerMsg;
    }
}

重新上篇文章长得消息接口实现类替换消息接收处理,将消息直接存储到消息队列中

@RestController
public class WebSocketControllerBack extends TextWebSocketHandler {

    private final Logger logger = LoggerFactory.getLogger(WebSocketController.class);

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        logger.info("WebSocket服务端连接: "+session.getAttributes().get("token")+"===>"+session.getAttributes().get("deviceId"));
        MsgCheckSeesionHandler.openSeesion(session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        logger.info("WebSocket服务端关闭: 关闭连接状态: "+status);
        MsgCheckSeesionHandler.onClose(session);
    }

    @Override
    public void handleMessage(WebSocketSession wsSession, WebSocketMessage<?> message) throws Exception {
        logger.info("WebSocket服务端接受:接受来自客户端发送的信息: "+message.getPayload().toString());
       MsgCheckSeesionHandler.onMessage(wsSession, message.getPayload().toString());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        logger.info("WebSocket服务端异常:连接异常信息: " + exception.getMessage());
        MsgCheckSeesionHandler.onError(session, exception);
    }

    /*
     * 是否支持消息拆分发送:如果接收的数据量比较大,最好打开(true), 否则可能会导致接收失败。
     * 如果出现WebSocket连接接收一次数据后就自动断开,应检查是否是这里的问题。
     */
    @Override
    public boolean supportsPartialMessages() {
        return true;
    }

}

到此整体思路实现完成,此版本针对对数据安全不高的通信,若不能丢数据或者其他业务需求,可尝试将消息存储到其他消息框架中

更具业务优化发送消息

   public static void sendMsgToClient(UserMsg userMsg) {

        System.out.println("send to client" +);
    }

猜你喜欢

转载自blog.csdn.net/H291850336/article/details/89377467