0


Spring Cloud Gateway网关转发websocket服务配置

Spring Cloud Gateway网关是所有微服务的统一入口。

1、Spring Cloud Gateway关键术语

  • Route:路由,网关配置的基本组成模块。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。
  • Predicate:断言,可以使用它来匹配来自 HTTP 请求的任何内容。
  • Filter:过滤器,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器org.springframework.cloud.gateway.filter.GatewayFilter类的实例。

2、Spring Cloud Gateway处理流程

客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
在这里插入图片描述

3、在Spring Cloud Gateway网关中,在yml配置文件中通过如下配置对websocket请求进行转发:

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:- id: websocket1
  5. uri: lb:ws://serviceName #使用方式2:websocket配置,通过nacos注册中心调用serviceName
  6. predicates:-Path=/websocket

当websocket服务为基于netty的socketio,netty需要单独开端口访问,上面方式要直接指定websocket服务的端口,多个websocket服务时,可以配置多个相同的路由规则,每个指定一个socketio服务,然后通权重实现负载均衡:

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:- id: websocket1
  5. uri: ws://127.0.0.1:8081
  6. predicates:-Path=/socket
  7. -Weight=group1,50- id: websocket2
  8. uri: ws://127.0.0.1:8082
  9. predicates:-Path=/socket
  10. -Weight=group1,50

4、在Spring Cloud Gateway网关中转发websocket请求时,连上websocket后出现立马断开问题,报错java.lang.UnsupportedOperationException,详细如下:

  1. 2023-10-2410:05:23.433ERROR12636---[ctor-http-nio-6]o.s.w.s.adapter.HttpWebHandlerAdapter:[6726d297-6]Error[java.lang.UnsupportedOperationException]forHTTPGET"/socket/?EIO=3&transport=websocket", but ServerHttpResponse already committed (200OK)2023-10-2410:05:23.433ERROR12636---[ctor-http-nio-6]r.n.http.server.HttpServerOperations:[6726d297-1,L:/192.168.20.5:9099-R:/192.168.20.5:9099]Error finishing response. Closing connection
  2. java.lang.UnsupportedOperationException:null
  3. at org.springframework.http.ReadOnlyHttpHeaders.put(ReadOnlyHttpHeaders.java:126)~[spring-web-5.3.20.jar:5.3.20]Suppressed:reactor.core.publisher.FluxOnAssembly$OnAssemblyException:Error has been observed at the following site(s):*__checkpoint org.springframework.web.cors.reactive.CorsWebFilter[DefaultWebFilterChain]*__checkpoint org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter[DefaultWebFilterChain]*__checkpoint HTTPGET"/socket/?EIO=3&transport=websocket"[ExceptionHandlingWebHandler]OriginalStackTrace:
  4. at org.springframework.http.ReadOnlyHttpHeaders.put(ReadOnlyHttpHeaders.java:126)~[spring-web-5.3.20.jar:5.3.20]

通过分析发现是Gateway处理跨域时使用的是如下方式:

  1. // 跨域配置源UrlBasedCorsConfigurationSource source =newUrlBasedCorsConfigurationSource();//设置跨域的配置信息CorsConfiguration corsConfiguration =newCorsConfiguration();// 允许所有请求来源进行跨域//corsConfiguration.addAllowedOrigin("*");...

需改成reactor响应式方式,如下:

  1. return(ServerWebExchange ctx,WebFilterChain chain)->{ServerHttpRequest request = ctx.getRequest();// 使用SpringMvc自带的跨域检测工具类判断当前请求是否跨域if(!CorsUtils.isCorsRequest(request)){return chain.filter(ctx);}HttpHeaders requestHeaders = request.getHeaders();// 获取请求头ServerHttpResponse response = ctx.getResponse();// 获取响应对象HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();// 获取请求方式对象HttpHeaders headers = response.getHeaders();// 获取响应头
  2. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());// 把请求头中的请求源(协议+ip+端口)添加到响应头中(相当于yml中的allowedOrigins)
  3. headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());if(requestMethod !=null){
  4. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());// 允许被响应的方法(GET/POST等,相当于yml中的allowedMethods)}
  5. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS,"true");// 允许在请求中携带cookie(相当于yml中的allowCredentials)
  6. headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS,"*");// 允许在请求中携带的头信息(相当于yml中的allowedHeaders)
  7. headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE,"18000L");// 本次跨域检测的有效期(单位毫秒,相当于yml中的maxAge)if(request.getMethod()==HttpMethod.OPTIONS){// 直接给option请求反回结果
  8. response.setStatusCode(HttpStatus.OK);returnMono.empty();}return chain.filter(ctx);// 不是option请求则放行};

5、Spring Cloud Gateway处理跨域,可以通过yml配置方式实现,如:

  1. gateway:
  2. # 全局的跨域配置
  3. globalcors:
  4. # 解决options请求被拦截问题
  5. add-to-simple-url-handler-mapping:true
  6. cors-configurations:
  7. # 拦截的请求
  8. '[/**]':
  9. # 允许跨域的请求
  10. #allowedOrigins:"*" # spring boot2.4以前的配置
  11. allowedOriginPatterns:"*" # spring boot2.4以后的配置
  12. # 允许请求中携带的头信息
  13. allowedHeaders:"*"
  14. # 运行跨域的请求方式
  15. allowedMethods:"*"
  16. # 是否允许携带cookie
  17. allowCredentials:true
  18. # 跨域检测的有效期,单位s
  19. maxAge:3600

也可以通过编码的方式定义跨域配置类,如:

  1. @ConfigurationpublicclassCorsConfig{@BeanpublicWebFiltercorsFilter(){return(ServerWebExchange ctx,WebFilterChain chain)->{ServerHttpRequest request = ctx.getRequest();// 使用SpringMvc自带的跨域检测工具类判断当前请求是否跨域if(!CorsUtils.isCorsRequest(request)){return chain.filter(ctx);}HttpHeaders requestHeaders = request.getHeaders();// 获取请求头ServerHttpResponse response = ctx.getResponse();// 获取响应对象HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();// 获取请求方式对象HttpHeaders headers = response.getHeaders();// 获取响应头
  2. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());// 把请求头中的请求源(协议+ip+端口)添加到响应头中(相当于yml中的allowedOrigins)
  3. headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());if(requestMethod !=null){
  4. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());// 允许被响应的方法(GET/POST等,相当于yml中的allowedMethods)}
  5. headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS,"true");// 允许在请求中携带cookie(相当于yml中的allowCredentials)
  6. headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS,"*");// 允许在请求中携带的头信息(相当于yml中的allowedHeaders)
  7. headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE,"18000L");// 本次跨域检测的有效期(单位毫秒,相当于yml中的maxAge)if(request.getMethod()==HttpMethod.OPTIONS){// 直接给option请求反回结果
  8. response.setStatusCode(HttpStatus.OK);returnMono.empty();}return chain.filter(ctx);// 不是option请求则放行};}}

本文转载自: https://blog.csdn.net/lengjunguan/article/details/134005654
版权归原作者 在荒野的梦想 所有, 如有侵权,请联系我们删除。

“Spring Cloud Gateway网关转发websocket服务配置”的评论:

还没有评论