WebSocket开发(客服对话双向绑定)功能

401 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情

前言

在上一篇中完成了WebSocket开发(客服对话)功能区分角色连接指定一对多的场景,在最后了存在的问题,本章通过双向绑定消息同步来解决。

  1. 区分角色在连接建立时区分用户跟客服的客户端角色
    1. 客服角色客户端id固定
    2. 用户角色客户端id可变
  2. 连接指定客户端无需选择指定客户端,系统自动匹配客服客户端
  3. 一对多一个客服是对应多个用户的
  4. 双向绑定一个客户跟一个客服建立消息连接后重新进入尽量分配给此客服
  5. 消息同步一个客户重新进入连接后并且更换客服后历史消息同步

1. 双向绑定

在上面第二节的最后提出了问题:一个客户端的消息发了好几个客服,每个客服的消息不完整。在上面给出了两个解决方案

  • 一个客户端只跟一个客服聊天
  • 消息记录跟客服端不绑定

第一个一个客户端只跟一个客服聊天就跟是本节的一个思路,双向绑定,当一个客户端与一个客服端发送消息后尽量将消息发送给此客服端。

  • 一是为了熟悉之前需求便于解决
  • 二是如果有缓存消息也不用重新加载历史消息

当然如果绑定的客服不在线或者设置了最大连接数已满无法建立连接那也是要漂移连接到其他客服的。如果与其他客服建立连接就需要一个历史消息补偿的操作了。这个在下一节消息同步捋捋。

1.1 绑定客服端

设置一个Map<String,String>结构,存储用户端对应的客服端,如果没有绑定的客服端就按上面的走随机分配。

定义一个HashMap存储绑定信息:

    private static HashMap<String,String> bindKfClients = new HashMap<>();

发送消息判断是否有绑定客服端,如果没有走正常随机匹配并写入,有则与绑定客户端进行通讯。

代码如下:

    private void toCSucceed(UserMessageModel userMessageModel){
        HashMap<String,WebSocketClient> hashMap = webSocketClientMap.get(WebSocketTypeEnum.getAcceptType(this.type));
        WebSocketClient webSocketClient;
        if (StringUtils.isEmpty(bindKfClients.get(this.clientId))){
            Random generator = new Random();
            Object[] values = hashMap.values().toArray();
            webSocketClient = (WebSocketClient) values[generator.nextInt(values.length)];
            bindKfClients.put(clientId,webSocketClient.clientId);
        }else{
            webSocketClient = hashMap.get(bindKfClients.get(this.clientId));
        }

        BaseResponseMessage infoMsg = BaseResponseMessage.success(userMessageModel);
        /**
         * 持久化
         */
        baseWebSocketService.saveCTOCMsg(this.clientId,webSocketClient.clientId,JSONObject.toJSONString(infoMsg),new Date(),new Date());
        /**
         * 发送消息
         */
        webSocketClient.sendMessage(infoMsg);
        this.sendMessage(infoMsg);
        log.info("客户端:{} 发送到客户端:{},消息内容:{}",clientId,webSocketClient.clientId,userMessageModel.getMessage());
    }

1.2 验证

如果是之前用户端发送消息会随机发送到多个客服端,现在加入绑定信息应该是发送多个消息只会让第一次接受的客服端接受

日志验证:

在这里插入图片描述 现在用户端发送给客服端的信息就会发送到同一客服端了

再开个新用户端进行验证

在这里插入图片描述 这个新用户端的消息也一直发送到1661497033459这个客服端了,这样用户端的消息就绑定到同一客服端了

2. 消息同步

消息同步的操作就当做历史消息的补偿,之前在消息落地里会把每一个客户端发送的数据放到Mysql中存储起来,在同步这一块就可以使用了。

2.1 代码调整

步骤:用户端发送消息->接收消息->判断没有绑定客服端->查询同步数据->添加同步数据->发送客服端消息

代码如下:

    @OnMessage
    public void onMessage(String message, Session session,@PathParam("clientId") String clientId){
        /**
         * 持久化
         */
        baseWebSocketService.saveClientSendMsg(clientId,message,new Date());
        /**
         * 处理消息
         */
        UserMessageModel userMessageModel = JSONObject.parseObject(message, UserMessageModel.class);
        userMessageModel.setSendId(clientId);
        if (userMessageModel == null){
            this.sendMessage(BaseResponseMessage.error(null,"传递参数结构异常"));
        }
        HashMap<String,WebSocketClient> hashMap = webSocketClientMap.get(WebSocketTypeEnum.getAcceptType(this.type));
        if (!CollectionUtils.isEmpty(hashMap)){
            if (StringUtils.isEmpty(bindKfClients.get(this.clientId))){
                List<UserMessageModel> list = new ArrayList();
                list.addAll(baseWebSocketService.queryClientSendMsg(clientId));
                list.forEach(model-> {
                    this.toCSucceed(model);
                });
            }else{
                this.toCSucceed(userMessageModel);
            }
        }else{
            baseWebSocketService.saveClientCompensateMsg(userMessageModel.getAcceptId(),message,(byte) 0);
            log.info("客户端:{} 发送消息到接受端:{} 不在线,放置到代发送列表,当前待发送列表:{}条",clientId,userMessageModel.getAcceptId());
            this.sendMessage(BaseResponseMessage.error(null,"接收端不在线"));
        }
    }

之前代码:

        if (!CollectionUtils.isEmpty(hashMap)){
			this.toCSucceed(userMessageModel);
        }else{
            baseWebSocketService.saveClientCompensateMsg(userMessageModel.getAcceptId(),message,(byte) 0);
            log.info("客户端:{} 发送消息到接受端:{} 不在线,放置到代发送列表,当前待发送列表:{}条",clientId,userMessageModel.getAcceptId());
            this.sendMessage(BaseResponseMessage.error(null,"接收端不在线"));
        }

现在代码:

        if (!CollectionUtils.isEmpty(hashMap)){
            if (StringUtils.isEmpty(bindKfClients.get(this.clientId))){
                List<UserMessageModel> list = new ArrayList();
                list.addAll(baseWebSocketService.queryClientSendMsg(clientId));
                list.forEach(model-> {
                    this.toCSucceed(model);
                });
            }else{
                this.toCSucceed(userMessageModel);
            }
        }else{
            baseWebSocketService.saveClientCompensateMsg(userMessageModel.getAcceptId(),message,(byte) 0);
            log.info("客户端:{} 发送消息到接受端:{} 不在线,放置到代发送列表,当前待发送列表:{}条",clientId,userMessageModel.getAcceptId());
            this.sendMessage(BaseResponseMessage.error(null,"接收端不在线"));
        }

2.2 验证

先找一下目前客户端落地的发送记录

在这里插入图片描述 在上图客户端1661497490016有8条发送的落地数据,在前端客户端ID固定为1661497490016,发生消息内容为:消息补偿看看

2.2.1 页面验证

用户端: 在这里插入图片描述 客服端: 在这里插入图片描述

2.2.2 日志验证

在这里插入图片描述

从日志上可以看到,客户端发一条消息的同时会将之前的消息也补偿同步上。如果已有建立双向连接的客服则不会补偿信息,避免信息重复