foreword
The Demo function was completed in the previous article 客服对话
, but the current connection is 无限制
a long-term connection without functions such as 心跳
, 失活
, 超时断连
and so on. There are many ways to implement heartbeat, and WebSocket
it provides ping/pong
types of messages.
There are two ways to trigger the heartbeat:
- Client triggers:
如果是前端发送心跳,后端需要返回心跳,也就是ping pong的过程会有两次数据传递。
- Server-side trigger:
后端来发送心跳的话,就只需要发送ping,前端不需要回应。
These two subsequent processing methods also have their own advantages and disadvantages.
- Client triggers:
- advantage
灵活控制
无需设置主动超时
逻辑清晰
服务端简单
- shortcoming
两次消息传递
消息内容容易篡改
- advantage
- Server-side trigger:
- advantage
节省宽带
服务端控制频率
消息体固定
- shortcoming
处理逻辑复杂
需要添加定时任务
考虑稳定性
- advantage
The two methods have their own advantages and disadvantages. The heartbeat method is the best depending on the specific
应用场景
choice. Here, the clientDemo
is used to trigger the heartbeat for experiments. After fixing the message, return the corresponding message.
1. WebSocket Heartbeat
OnMessage
If the client triggers the heartbeat , it intercepts and processes the event on the server . If the parameter is accepted String
, then the judgment logic is added on top of the previous logic 健康检查
. The function is very simple. The client sends a special message and directly returns the corresponding message. Can.
1.1 String messages
WebSocket
Heartbeat has been designed, that is Ping/Pong
, this function can reach and detect whether the link is available, but if you want to carry data, you still need to implement it with the message type of 字符串
.对象
code show as below:
@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);
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 = 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 the client sends the content HEALTH
, it will reply the corresponding message, I replied hereSUCCESS
But there is a problem with this. After the user sends HEALTH
this string, the server will process the message as a health check instead of a message, which affects the use of the client.
Do you still remember that a send type field was reserved before sendType
? At this time, this type will work. If you want to do a health check operation, sendType
set this to HEALTH
, and the server sendType
will judge the business processing according to the field, and modify the code:
/**
* 健康检查
*/
if (WebSocketHealthEnum.HEALTH.msg.equals(userMessageModel.getSendType())){
this.sendText(WebSocketHealthEnum.HEALTH.result);
return;
}
1.2 Ping/Pong message
- The protocol header of Ping is 0x9, and the protocol header of Pong is 0xA
- The maximum payload of the control frame is 125bytes and cannot be split
The server can actively generate Ping/Pong messages. In the previous article, I wrote about WebSocket
sending messages 四种类型
. Here, I will change the above sending Text
text type to sending Ping
type messages. Of course, Pong type messages can also be sent.
code show as below:
if (WebSocketHealthEnum.HEALTH.msg.equals(userMessageModel.getSendType())){
try {
session.getBasicRemote().sendPing(ByteBuffer.wrap("SUCCESS".getBytes()));
} catch (IOException e) {
throw new RuntimeException(e);
}
return;
}
The Ping message will not be received by our OnMessage event, so no special processing is required. If the Pong message is received by the service, it is possible.
code show as below:
@OnMessage
public void onPong(PongMessage pongMessage) {
ByteBuffer byteBuffer = pongMessage.getApplicationData();
}
Specific business can be processed twice
2. Service Heartbeat
The above heartbeat is the heartbeat monitoring of each client, and the heartbeat of the service must also be done. The heartbeat of the service is simple, and the front end regularly requests the HTTP/HTTPS
protocol interface.
code show as below:
@Slf4j
@RestController
public class CheckHealthController {
@GetMapping("/health")
public ResponeApi health() {
log.info("健康检查chatroom-IM --> 检查成功!");
return ResponeApi.success(ResponeCodeEnum.SUCCESS,"SUCCESS");
}
}
The effect is as follows: