最近在项目中遇到了页面实时告警提示的需求。而页面要不停地显示最新的告警信息,就必须不停地刷新页面,或者用一段js代码每隔几秒钟发消息询问服务器数据。但这么做不仅会让你抓头,还会被项目经理打爆你的狗头。
而使用WebSocket技术之后,当服务器有了新的数据,会主动通知浏览器。 在本业务中就是,当有了报警信息之后,服务器会主动给浏览器发起告警的通知。
使用WebSocket的优点
1. 节约带宽。 不停地轮询服务端数据这种方式,使用的是http协议,head信息很大,有效数据占比低, 而使用WebSocket方式,头信息很小,有效数据占比高。
2. 无浪费。 轮询方式有可能轮询10次,才碰到服务端数据更新,那么前9次都白轮询了,因为没有拿到变化的数据。 而WebSocket是由服务器主动回发,来的都是新数据。
3. 实时性,考虑到服务器压力,使用轮询方式不可能很短的时间间隔,否则服务器压力太多,所以轮询时间间隔都比较长,好几秒,设置十几秒。 而WebSocket是由服务器主动推送过来,实时性是最高的
springboot整合websocket实战
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2. 编写配置类
@Configuration
public class WebSocketConfig {
/**
* 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3. 编写websocket模块
/**
* @author Lsc
*/
@Component
@ServerEndpoint(value = "/socket/alarm/{userId}/{token}")
@Slf4j
public class AlarmServer {
/**
* 用来记录当前在线连接数
*/
private static AtomicInteger online = new AtomicInteger();
/**
* 存放每个客户端对应的连接对象
*/
private static Map<Long, Session> sessionPools = new ConcurrentHashMap<>();
/**
* 发送消息方法
*
* @param session 客户端与socket建立的会话
* @param message 消息
* @throws IOException
*/
public void sendMessage(Session session, String message) throws IOException {
if (session != null) {
session.getBasicRemote().sendText(message);
}
}
public void sendMessageById(Long userId, String message) throws IOException {
Session session = sessionPools.get(userId);
if (session != null) {
session.getBasicRemote().sendText(message);
}
}
/**
* 连接建立成功调用
*
* @param session 客户端与socket建立的会话
* @param userId 客户端的userId
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "userId") Long userId, @PathParam(value = "token")String token) {
Map<String, Object> jwtMap = JwtUtil.parseJwt(token);
if(MapUtils.isNotEmpty(jwtMap)){
return;
}
sessionPools.put(userId, session);
addOnlineCount();
log.info(userId + "加入webSocket!当前人数为" + online);
}
/**
* 关闭连接时调用
*
* @param userId 关闭连接的客户端的userId
*/
@OnClose
public void onClose(@PathParam(value = "userId") long userId) {
sessionPools.remove(userId);
subOnlineCount();
log.info(userId + "断开webSocket连接!当前人数为" + online);
}
/**
* 收到客户端消息时触发(群发)
*
* @param message
* @throws IOException
*/
@OnMessage
public void onMessage(@PathParam(value = "userId") long userId, String message) throws IOException {
//TODO 客户端向服务端发送消息
//sendToUser(userId, null);
}
/**
* 发生错误时候
*
* @param session
* @param throwable
*/
@OnError
public void onError(Session session, Throwable throwable) {
log.error("websocket发生错误");
throwable.printStackTrace();
}
/**
* 全部发送
*
* @param message
*/
public void sendAll(String message) {
for (Session session : sessionPools.values()) {
try {
sendMessage(session, message);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
/**
* 增加在线数量
*/
private static void addOnlineCount() {
online.incrementAndGet();
}
/**
* 减少在线数量
*/
private static void subOnlineCount() {
online.decrementAndGet();
}
}
在实际开发过程中,根据实际场景修改相关方法即可。
在使用时,调用相关的方法就可以实现主动发送消息。