gateway是netty开发的天生支持websocket。zuul不支持,但是好像可以配置
注意: 如果网关中有登录验证,可能需要对websocket做放行
**
- gateway无任何配置的情况下 websocket (一个websocket服务下,多个服务没有测试)一切正常
- 设置跨域可能会出现websocket连接不上
```yaml
globalcors:
  corsConfigurations:
'[/**]':allowedHeaders: "*"allowedOrigins: "*"allowedMethods:- GETPOSTDELETEPUTOPTION
 
 - 设置跨域可能会出现websocket连接不上
```yaml
globalcors:
  corsConfigurations:
 
1. 把 allowedMethods 改成 allowedMethods: "*" 就好了1. gateway配置文件```yamlserver:port: 9005spring:application:name: test-gatewaycloud:nacos:discovery:server-addr: 127.0.0.1:8848 # 注册中心group: websockt#路由配置,user-service是我的服务名gateway:routes:# ws表示websocket的转发- id: test-websocketuri: ws://127.0.0.1:9006predicates:- Path=/ws/**filters:- StripPrefix=1# ws表示websocket的转发- id: nocas-websocketuri: ws://test-websocketpredicates:- Path=/ser/ws/**filters:- StripPrefix=2- id: api-websocketuri: lb://test-websocketpredicates:- Path=/api/**filters:- StripPrefix=1# ws表示websocket的转发 -- 可以走 无均衡- id: test-websocket2uri: ws://127.0.0.1:9007predicates:- Path=/ws2/**filters:- StripPrefix=1# ws表示websocket的转发 - 这是错误的写法- id: nocas-websocket2uri: ws://test-websocket2predicates:- Path=/ser2/ws/**filters:- StripPrefix=2# ws表示websocket的转发 -- 可以走 有均衡 (还没测试均衡怎么玩)- id: nocas2-websocket2uri: lb:ws://test-websocket2predicates:- Path=/serlb/ws/**filters:- StripPrefix=2# websocket项目中的正常接口地址- id: api-websocket2uri: lb://test-websocket2predicates:- Path=/api2/**filters:- StripPrefix=1discovery:locator:enabled: truelower-case-service-id: true
b. websocket spring单机版的配置就行了
<!-- 我的websocket单机版直接使用的依赖 --><dependency><groupId>com.databstech</groupId><artifactId>util-websocket</artifactId><version>1.0.0</version></dependency><!-- 如果是微服务记得加这个o 注册中心 要不然gateway 配置lb:服务名 是访问不到的 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><!-- 不放进jar包里 用户自己单独加入 --><!--<scope>provided</scope>--></dependency>
//util-websocket 配置过程/**------------------------最重要的注册类-------------------------------*/package com.databstech.utils.websocket.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;/**** 往 spring 容器中注入ServerEndpointExporter实例** @ClassName : WebSocketConfig //类名* @Description : socket配置 //描述* @Author : tn //作者* @Date: 2020-07-08 12:33 //时间*/@Configurationpublic class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}}/**------------------------开始连接,断开连接,发送消息等-------------------------------*/package com.databstech.utils.websocket.service;import org.springframework.stereotype.Component;import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.concurrent.atomic.AtomicInteger;/**** 包含接收消息,推送消息等接口** @ClassName : WebSocketServer* @Description : webSocker服务端* @Author : tn* @Date: 2020-07-08 12:36*/@Component@ServerEndpoint(value = "/socket/{name}")public class WebSocketServer {//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。private static AtomicInteger online = new AtomicInteger();//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。//不允许多端登陆// private static Map<String, Session> sessionPools = new HashMap<>();//允许多端登陆private static Map<String, List<Session>> sessionPoolsS = new HashMap<>();/*** 发送消息方法* @param session 客户端与socket建立的会话* @param message 消息* @throws IOException 抛出异常*/public void sendMessage(Session session, String message) throws IOException {if(session != null){session.getBasicRemote().sendText(message);}}/*** 连接建立成功调用* @param session 客户端与socket建立的会话* @param userName 客户端的userName*/@OnOpenpublic void onOpen(Session session, @PathParam(value = "name") String userName){List<Session> sessionsArray = new ArrayList<>();//获取sessionList<Session> sessions = sessionPoolsS.get(userName);//存在sessionif(sessions==null){sessionsArray.add(session);}else {sessionsArray.addAll(sessions);sessionsArray.add(session);}sessionPoolsS.put(userName, sessionsArray);addOnlineCount();System.out.println(userName + "加入webSocket!当前人数为" + online);try {sendMessage(session, "欢迎" + userName + "加入连接!");} catch (IOException e) {e.printStackTrace();}}/*** 关闭连接时调用* @param userName 关闭连接的客户端的姓名*/@OnClosepublic void onClose(@PathParam(value = "name") String userName, Session session){//获取sessionList<Session> sessions = sessionPoolsS.get(userName);if(session==null){sessionPoolsS.remove(userName);}else {sessions.remove(session);sessionPoolsS.put(userName, sessions);}subOnlineCount();System.out.println(userName + "断开webSocket连接!当前人数为" + online);}/*** 收到客户端消息时触发(群发)* @param message*/@OnMessagepublic void onMessage(String message){for (List<Session> session: sessionPoolsS.values()) {try {session.forEach(sessionf -> {try {sendMessage(sessionf, message);} catch (IOException e) {e.printStackTrace();}});} catch(Exception e){e.printStackTrace();continue;}}}/*** 发生错误时候* @param session session* @param throwable throwable*/@OnErrorpublic void onError(Session session, Throwable throwable){System.out.println("发生错误");throwable.printStackTrace();}/*** 给指定用户发送消息* @param userName 用户名* @param message 消息*/public void sendInfo(String userName, String message){List<Session> session = sessionPoolsS.get(userName);if(session!=null){try {session.forEach(sessionf -> {try {sendMessage(sessionf, message);} catch (IOException e) {e.printStackTrace();}});}catch (Exception e){e.printStackTrace();}}}public static void addOnlineCount(){online.incrementAndGet();}public static void subOnlineCount() {online.decrementAndGet();}}/**------------------------内部默认的接口-----------------------------------------------*/package com.databstech.utils.websocket.controller;import com.databstech.utils.websocket.service.WebSocketServer;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiImplicitParams;import io.swagger.annotations.ApiOperation;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;/*** @ClassName : SocketController 接口* @Description : socket //描述* @Author : tn //作者* @Date: 2020-07-08 12:40 //时间*/public interface SocketController {/*** 给指定用户推送消息* @param userName 用户名* @param message 消息*/@RequestMapping(value = "/only", method = RequestMethod.GET)@ApiOperation("给指定用户推送消息")@ApiImplicitParams({@ApiImplicitParam(name="userName", value="用户名", dataType="String", required=true),@ApiImplicitParam(name="message", value="消息", dataType="String", required=true)})default void onlyUserSocket(@RequestParam String userName, @RequestParam String message, WebSocketServer webSocketServer){webSocketServer.sendInfo(userName, message);}/*** 给所有用户推送消息* @param message 消息*/@ApiOperation("给所有用户推送消息")@ApiImplicitParams({@ApiImplicitParam(name="message", value="消息", dataType="String", required=true)})@RequestMapping(value = "/all", method = RequestMethod.GET)default void allUserSocket(@RequestParam String message, WebSocketServer webSocketServer){webSocketServer.onMessage(message);}}// 依赖<!--引入websocket依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>io.swagger</groupId><artifactId>swagger-annotations</artifactId><version>1.5.22</version><scope>provided</scope></dependency>
本来运行的也好好的,但是用了 gateway 聚合了swagger之后从网关访问就是一个接着一个的错误!
错误如下
java.lang.ClassCastException: org.apache.catalina.connector.ResponseFacade cannot be cast to reactor.netty.http.server.HttpServerResponseat org.springframework.web.reactive.socket.server.upgrade.ReactorNettyRequestUpgradeStrategy.getNativeResponse(ReactorNettyRequestUpgradeStrategy.java:124) ~[spring-webflux-5.2.4.RELEASE.jar:5.2.4.RELEASE]Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:Error has been observed at the following site(s):|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]|_ checkpoint ⇢ HTTP GET "/ws/socket/tn" [ExceptionHandlingWebHandler]
层层测试
- gateway 原生配置 - 正常转发了
 - gateway 结合 nacos 配置动态路由 - 也正常转发了
 - gateway 配置全局异常 - 正常转发
 - gateway 配置了swagger后 - 也正常转发
 - 配置了简单的 GatewayFilter(全局拦截)- 也正常 ```java package com.tn.gateway3.filter;
 
import org.apache.commons.lang.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component;
/**
- @author fsl
 - @description: SwaggerHeaderFilter
 @date 2019-06-0310:47 */ @Component public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory { private static final String HEADER_NAME = “X-Forwarded-Prefix”;
private static final String URI = “/v2/api-docs”;
@Override public GatewayFilter apply(Object config) {
return (exchange, chain) -> {ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();if (!StringUtils.endsWithIgnoreCase(path,URI )) {return chain.filter(exchange);}return chain.filter(exchange);};
} }
1. 配置了复杂的 GatewayFilter(全局拦截)- 也正常2. 我将所有配置集成起来打成jar后,第三方使用websocket使用出现报错3. 我将自己配置 gateway-doc 的 jar 包 进行拆解1. 发现是我配置的 gateway-doc 里的登录拦截器用的 JWT包 发生的问题2. **fuck 原来是因为gateway是netty写的跟web犯冲,我JWT项目中引入了**```xml<dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>8.5.43</version></dependency>
因为我jwt中有个 gettoken的方法使用到了 HttpServletRequest ,现在我他这个方法改了改成了 ```java /**
- 获取token
 - @param request 请求头
 @return 返回token */ public static String getToken(ServerHttpRequest request) { final String tokenName = “token”; //从请求头获取token return Optional.ofNullable(request.getHeaders().get(tokenName))
.map(t -> t.get(0)).orElse(null);
}
依赖
nacos 上 websockt相关的 gateway.yaml配置```yamlspring:cloud:gateway:routes:# ws表示websocket的转发- id: test-websocketuri: ws://127.0.0.1:9006predicates:- Path=/ws/**- DocName=noSwaggerfilters:- StripPrefix=1# ws表示websocket的转发- id: nocas-websocketuri: ws://test-websocketpredicates:- Path=/ser/ws/**- DocName=noSwaggerfilters:- StripPrefix=2- id: api-websocketuri: lb://test-websocketpredicates:- Path=/api/**- DocName=websocketfilters:- StripPrefix=1# ws表示websocket的转发- id: test-websocket2uri: ws://127.0.0.1:9007predicates:- Path=/ws2/**- DocName=noSwaggerfilters:- StripPrefix=1# ws表示websocket的转发 - 这是错误的- id: nocas-websocket2uri: ws://test-websocket2predicates:- Path=/ser2/ws/**- DocName=noSwaggerfilters:- StripPrefix=2- id: nocas2-websocket2uri: lb:ws://test-websocket2predicates:- Path=/serlb/ws/**- DocName=noSwaggerfilters:- StripPrefix=2- id: api-websocket2uri: lb://test-websocket2predicates:- Path=/api2/**- DocName=noSwaggerfilters:- StripPrefix=1# 测试动态路由- id: dy-routesuri: lb://dy-routespredicates:- Path=/dy/**- DocName=noSwaggerfilters:- StripPrefix=1default-filters:# - StripPrefix=2# - RewritePath=/api/(?<segment>.*), /$\{segment}# - name: SwaggerHeaderFilter- name: AuthFilterargs:excludePatterns: #不做权限校验的路径- /serlb/ws/socket/**
