前言
在之前的文章中完成了
客服对话
的Demo功能,但是现在的连接是
无限制
的长时间连接没有做
心跳
、
失活
、
超时断连
等功能,心跳的实现方法有很多种,并且
WebSocket
就提供了
ping/pong
类型的消息。
心跳的触发方式也分两种:
- 客户端触发:
如果是前端发送心跳,后端需要返回心跳,也就是ping pong的过程会有两次数据传递。
- 服务端触发:
后端来发送心跳的话,就只需要发送ping,前端不需要回应。
这两种后续的处理方式也有各自优缺点。
- 客户端触发:- 优点-
灵活控制
-无需设置主动超时
-逻辑清晰
-服务端简单
- 缺点-两次消息传递
-消息内容容易篡改
- 服务端触发:- 优点-
节省宽带
-服务端控制频率
-消息体固定
- 缺点-处理逻辑复杂
-需要添加定时任务
-考虑稳定性
两种方式各有利弊,看具体的
应用场景
选择心跳方式是最好的,这里使用客户端触发心跳进行
Demo
实验,前端变更比较容易,服务端也不需要写定时等处理复杂的业务,只需要在收到固定消息后返回对应消息即可。
1. WebSocket心跳
客户端触发心跳的话就是在服务端的
OnMessage
事件里进行截获处理,如果是接受参数为
String
,就在之前的逻辑之上加上判断
健康检查
的逻辑,功能很简单,客户端发送了特点消息直接返回对应的消息即可。
1.1 字符串消息
WebSocket
已经设计了心跳,也就是
Ping/Pong
,这个功能可以到达检测链接是否可用,但是如果要携带数据还是需要自己用
字符串
、
对象
的消息类型进行实现。
代码如下:
@OnMessagepublicvoidonMessage(String message,Session session,@PathParam("clientId")String clientId){/**
* 持久化
*/
baseWebSocketService.saveClientSendMsg(clientId,message,newDate());/**
* 处理消息
*/UserMessageModel userMessageModel =JSONObject.parseObject(message,UserMessageModel.class);if(userMessageModel ==null){this.sendMessage(BaseResponseMessage.error(null,"传递参数结构异常"));}
userMessageModel.setSendId(clientId);/**
* 健康检查
*/if("HEALTH".equals(userMessageModel.getMessage())){this.sendText(WebSocketHealthEnum.HEALTH.result);return;}/**
* 发送消息
*/HashMap<String,WebSocketClient> hashMap = webSocketClientMap.get(WebSocketTypeEnum.getAcceptType(this.type));if(!CollectionUtils.isEmpty(hashMap)){if(StringUtils.isEmpty(bindKfClients.get(this.clientId))){List<UserMessageModel> list =newArrayList();
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,"接收端不在线"));}}
如果客户端发送了内容
HEALTH
则回复对应消息,我这里回复了
SUCCESS
但是这样有个问题,用户发送了
HEALTH
这个字符串服务端会将这个消息当作健康检查进行处理,而不是消息,这样影响了用户端的使用。
还记得之前预留了一个发送类型字段
sendType
吗,这时候这个类型就起作用了,如果要做健康检查的操作就将这个
sendType
设置为
HEALTH
,服务端根据
sendType
字段进行判断业务处理,修改一下代码:
/**
* 健康检查
*/if(WebSocketHealthEnum.HEALTH.msg.equals(userMessageModel.getSendType())){this.sendText(WebSocketHealthEnum.HEALTH.result);return;}
1.2 Ping/Pong消息
- Ping的协议头是0x9,Pong的协议头是0xA
- 控制帧最大载荷为125bytes且不能拆分
服务端可以主动发生Ping/Pong消息,之前文章中写过
WebSocket
发送消息的
四种类型
,这里将上面发送
Text
文本类型换成发送
Ping
类型的消息,当然也可以发送Pong类型的消息。
代码如下:
if(WebSocketHealthEnum.HEALTH.msg.equals(userMessageModel.getSendType())){try{
session.getBasicRemote().sendPing(ByteBuffer.wrap("SUCCESS".getBytes()));}catch(IOException e){thrownewRuntimeException(e);}return;}
Ping消息是不会被我们的OnMessage事件接收的,所以不需要特殊处理,如果是Pong消息在服务的接收是可以的。
代码如下:
@OnMessagepublicvoidonPong(PongMessage pongMessage){ByteBuffer byteBuffer = pongMessage.getApplicationData();}
具体的业务可以二次处理
2. 服务心跳
上面的心跳是对每个客户端的心跳监测,服务的心跳也要做,服务的心跳就简单了,前端定时请求
HTTP/HTTPS
协议接口。
代码如下:
@Slf4j@RestControllerpublicclassCheckHealthController{@GetMapping("/health")publicResponeApihealth(){
log.info("健康检查chatroom-IM --> 检查成功!");returnResponeApi.success(ResponeCodeEnum.SUCCESS,"SUCCESS");}}
效果如下:
版权归原作者 余生大大 所有, 如有侵权,请联系我们删除。