前言
服务端一般都是被动接收请求,不主动向客户端发送信息。在某些场景情况下,客户需要及时获取更新的数据,有两种方法解决,一是由前端轮询定时调用接口,二是采用websocket服务端与客户端保持实时通信。
集成说明
采用tomcat自带的websocket实现,其中内置tomcat和外置tomcat集成稍有差别,外置tomcat不需要注入ServerEndpointExporter,因为它由容器自己提供和管理,websocket接收类不需要加@Component注解。如果涉及用户权限的问题,需要定义websocket拦截器,本文不再赘述。
集成步骤
1、pom.xml引入依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
|
2、定义配置类注入ServerEndpointExporter
1 2 3 4 5 6 7 8 9
| @Configuration public class WebSocketConfig {
@Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
|
3、定义websocket接收类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| @ServerEndpoint("/websocket/{uuid}") public class WebSocketServer {
private WebSocketService webSocketService = SpringUtil.getBean(WebSocketService.class);
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
private static int onlineCount = 0;
private static ConcurrentHashMap<String, Session> webSocketMap = new ConcurrentHashMap<>();
@OnOpen public void onOpen(Session session, @PathParam("uuid") String uuid) throws InterruptedException { if (webSocketMap.containsKey(uuid)) { webSocketMap.remove(uuid); webSocketMap.put(uuid, session); } else { webSocketMap.put(uuid, session); addOnlineCount(); } log.info("用户连接:{},当前在线人数为:{}", uuid, getOnlineCount()); sendMessageToSingle(WebSocketResult.success("connect", "连接成功"), uuid); webSocketService.sendAllData(uuid); }
@OnClose public void onClose(@PathParam("uuid") String uuid) { if (webSocketMap.containsKey(uuid)) { webSocketMap.remove(uuid); subOnlineCount(); } log.info("用户退出:{},当前在线人数为:{}", uuid, getOnlineCount()); }
@OnMessage public void onMessage(String message, Session session, @PathParam("uuid") String uuid) { log.info("用户:{},发送消息:{}", uuid, message); if (StringUtils.isNotBlank(message)) { try { } catch (Exception e) { log.error(e.getMessage(), e); } } }
@OnError public void onError(Session session, Throwable error, @PathParam("uuid") String uuid) { log.info("用户:{}连接错误,错误原因{}", uuid, error.getMessage()); if(webSocketMap.containsKey(uuid)){ webSocketMap.remove(uuid); subOnlineCount(); } }
public static void sendMessageToSingle(WebSocketResult message, String uuid) { if (webSocketMap.containsKey(uuid)) { try { webSocketMap.get(uuid).getBasicRemote().sendText(JSON.toJSONString(message)); } catch (IOException e) { log.error("发送消息异常:{}", e.getMessage()); } } else { log.error("用户:{}不在线", uuid); } }
public static void sendMessageToAll(WebSocketResult message) { for (Map.Entry<String, Session> map : webSocketMap.entrySet()) { sendMessageToSingle(message, map.getKey()); } }
public static synchronized int getOnlineCount() { return onlineCount; }
public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; }
public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; } }
|
4、定义SpringUtil工具类,用于获取bean,因为WebSocketServer是多例对象,不能通过直接使用@Autowired注入bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Component public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtil.applicationContext = applicationContext; }
public ApplicationContext getApplicationContext(){ return applicationContext; }
public static Object getBean(String beanName){ return applicationContext.getBean(beanName); }
public static <T> T getBean(Class<T> clazz){ return (T)applicationContext.getBean(clazz); }
}
|
5、定义WebSocketResult结果集,转换成json返回给前端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Data @NoArgsConstructor @AllArgsConstructor public class WebSocketResult implements Serializable {
private int code;
private String type;
private Object data;
public static WebSocketResult success(String type, Object data) { return new WebSocketResult(200, type, data); }
public static WebSocketResult fail(String type, Object data) { return new WebSocketResult(500, type, data); }
}
|
格式为
1 2 3 4 5 6 7 8
| { "code": 200, "data": { "org_id": "30004", "uuid": "1ebf7693-dc68-4093-aa6f-3fe31260e3fd" }, "type": "KbsActualData" }
|
code为状态码,data为数据,type为类型标识