1、webSocket webSocket长连接是一种在单个tcp连接上进行全双工通信的协议,允许双向数据推送。一般微服务提供的restful API只是对前端请求做出相应。使用webSocket可以实现后端主动向前端推送消息。 2、springboot使用webSocket 1、pom文件添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> 2、添加webSocket配置 @Configuration @EnableWebSocket public class WebSocketAutoConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // webSocket通道 // 指定处理器和路径 registry.addHandler(new WebSocketHandler(), "/websocket") // 指定自定义拦截器 .addInterceptors(new WebSocketInterceptor()) // 允许跨域 .setAllowedOrigins("*"); // sockJs通道 registry.addHandler(new WebSocketHandler(), "/sock-js") .addInterceptors(new WebSocketInterceptor()) .setAllowedOrigins("*") // 开启sockJs支持 .withSockJS(); } } 处理器代码: import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.reflect.FieldUtils; import org.springframework.stereotype.Component; import org.springframework.web.socket.*; import org.springframework.web.socket.handler.AbstractWebSocketHandler; import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession; @Component public class WebSocketHandler extends AbstractWebSocketHandler { /** * 存储sessionId和webSocketSession * 需要注意的是,webSocketSession没有提供无参构造,不能进行序列化,也就不能通过redis存储 * 在分布式系统中,要想别的办法实现webSocketSession共享 */ private static Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>(); private static Map<String, String> userMap = new ConcurrentHashMap<>(); /** * 获取sessionId */ private String getSessionId(WebSocketSession session) { if (session instanceof WebSocketServerSockJsSession) { // sock js 连接 try { return ((WebSocketSession) FieldUtils.readField(session, "webSocketSession", true)).getId(); } catch (IllegalAccessException e) { throw new RuntimeException("get sessionId error"); } } return session.getId(); } /** * webSocket连接创建后调用 */ @Override public void afterConnectionEstablished(WebSocketSession session) { // 获取参数 String user = String.valueOf(session.getAttributes().get("user")); String sessionId = getSessionId(session); userMap.put(user, getSessionId(session)); sessionMap.put(sessionId, session); } /** * 接收到消息会调用 */ @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { if (message instanceof TextMessage) { } else if (message instanceof BinaryMessage) { } else if (message instanceof PongMessage) { } else { System.out.println("Unexpected WebSocket message type: " + message); } } /** * 连接出错会调用 */ @Override public void handleTransportError(WebSocketSession session, Throwable exception) { sessionMap.remove(getSessionId(session)); } /** * 连接关闭会调用 */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { sessionMap.remove(getSessionId(session)); } @Override public boolean supportsPartialMessages() { return false; } /** * 后端发送消息 */ public void sendMessage(String user, String message) { String sessionId = userMap.get(user); WebSocketSession session = sessionMap.get(sessionId); try { session.sendMessage(new TextMessage(message)); } catch (IOException e) { e.printStackTrace(); } } } 自定义拦截器: public class WebSocketInterceptor implements HandshakeInterceptor { /** * handler处理前调用,attributes属性最终在WebSocketSession里,可能通过webSocketSession.getAttributes().get(key值)获得 */ @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) { if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request; // 获取请求路径携带的参数 String user = serverHttpRequest.getServletRequest().getParameter("user"); attributes.put("user", user); return true; } else { return false; } } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } } 2、前端连接webSocket WebSocket对象是H5新增的对象,低版本浏览器可能不支持,可以使用sockJs的方式连接 webSocket连接 // 打开一个 web socket var ws = new WebSocket("ws://localhost:8080/websocket?user=Tony"); ws.onopen = function() { // Web Socket 已连接上,使用 send() 方法发送数据 ws.send("测试发送"); console.log('open'); }; ws.onmessage = function (e) { console.log('message', e.data); }; ws.onclose = function() { console.log('close'); }; sockJs连接 <script src="//cdn.jsdelivr.net/sockjs/1.0.0/sockjs.min.js"></script> var sock = new SockJS('http://localhost:8080/sock-js?user=Tony'); sock.onopen = function() { sock.send("测试发送"); console.log('open'); }; sock.onmessage = function(e) { console.log('message', e.data); }; sock.onclose = function() { console.log('close'); }; 3、springCloud使用zuul转发webSocket连接 zuul网关还需要做额外的配置,添加自定义过滤器: @Component public class WebSocketFilter extends ZuulFilter { @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String upgradeHeader = request.getHeader("Upgrade"); if (null == upgradeHeader) { upgradeHeader = request.getHeader("upgrade"); } if (null != upgradeHeader && "websocket".equalsIgnoreCase(upgradeHeader)) { context.addZuulRequestHeader("connection", "Upgrade"); } return null; } } 配置文件添加配置: hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 60000 #设置API网关中路由转发请求的HystrixCommand执行超时时间 ribbon: ConnectTimeout: 3000 #设置路由转发请求的时候,创建请求连接的超时时间 ReadTimeout: 60000 #用来设置路由转发请求的超时时间 4、网关使用springCloud Gateway无需做任何处理 5、若使用了Nginx,需要添加配置 配置示例: map $http_upgrade $connection_upgrade { default upgrade; '' close; } # 网关只有一个,写一个网关地址就行了 upstream [服务器组名] { server [IP地址]:[端口号]; server [IP地址]:[端口号]; ... } server { listen 80; location / { proxy_pass http://[服务器组名]; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } 前端连接示例: sockJs连接 <script src="//cdn.jsdelivr.net/sockjs/1.0.0/sockjs.min.js"></script> var sock = new SockJS('http://(网关路径)/(服务名称)/sock-js?user=Tony'); sock.onopen = function() { sock.send("测试发送"); console.log('open'); }; sock.onmessage = function(e) { console.log('message', e.data); }; sock.onclose = function() { console.log('close'); }; webSocket连接 // 打开一个 web socket var ws = new WebSocket("ws://(网关路径)/(服务名称)/websocket?user=Tony"); ws.onopen = function() { // Web Socket 已连接上,使用 send() 方法发送数据 ws.send("测试发送"); console.log('open'); }; ws.onmessage = function (e) { console.log('message', e.data); }; ws.onclose = function() { console.log('close'); };