当前位置:   article > 正文

Spring Gateway转发websocket原理

gateway转发websocket

Spring Cloud Gateway简称Spring Gateway,它可以转发请求到后端微服务。Spring Gateway除了转发HTTP请求,也支持websocket请求。我们看下它是怎么实现的吧。

配置支持websocket转发

支持websocket转发,需要用到spring-cloud-starter-gateway ,不要搞错成spring-cloud-starter-gateway-web 。引入maven配置:

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-gateway</artifactId>
  4. <version>4.1.4</version>
  5. </dependency>

然后注册需要路由的规则,可以通过yml配置。

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: ws1
  6. uri: ws://localhost:8080
  7. predicates:
  8. - Path=/ws

Java配置方式,与yml方式等效。

  1. @Bean
  2. public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
  3. return builder.routes()
  4. .route("ws1", r -> r.path("/ws")
  5. .uri("ws://localhost:8080"))
  6. .build();
  7. }

websocket转发原理

处理websocket协议转发的类是org.springframework.cloud.gateway.filter.WebsocketRoutingFilter。它的filter方法会过滤出ws和wss两类请求。

  1. @Override
  2. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  3. changeSchemeIfIsWebSocketUpgrade(exchange);
  4. URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
  5. String scheme = requestUrl.getScheme();
  6. if (isAlreadyRouted(exchange) || (!"ws".equals(scheme) && !"wss".equals(scheme))) {
  7. return chain.filter(exchange);
  8. }
  9. setAlreadyRouted(exchange);
  10. HttpHeaders headers = exchange.getRequest().getHeaders();
  11. HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);
  12. List<String> protocols = getProtocols(headers);
  13. return this.webSocketService.handleRequest(exchange,
  14. new ProxyWebSocketHandler(requestUrl, this.webSocketClient, filtered, protocols));
  15. }

可以看到方法的最后一行的handleRequest()方法,exchange是当前发给网关的ws握手请求,ProxyWebSocketHandler用来处理网关和客户端建立完websocket链接成功后的事件。重点看看ProxyWebSocketHandler的handle()方法。

  1. @Override
  2. public Mono<Void> handle(WebSocketSession session) {
  3. // pass headers along so custom headers can be sent through
  4. return client.execute(url, this.headers, new WebSocketHandler() {
  5. // 省略部分代码...
  6. });
  7. }

handle()方法里用client(WebSocketClient )给后端websocket地址发来一个握手请求。

到这里,网关握手的流程就清晰了。前端客户端 —[连接]—>网关—[连接]—>后端websocket服务,总共会产生2条websocket连接。

然后就是发消息和断开连接的方式,就在上面省略代码里。

  1. new WebSocketHandler() {
  2. @Override
  3. public Mono<Void> handle(WebSocketSession proxySession) {
  4. Mono<Void> serverClose = proxySession.closeStatus().filter(__ -> session.isOpen())
  5. .map(this::adaptCloseStatus).flatMap(session::close);
  6. Mono<Void> proxyClose = session.closeStatus().filter(__ -> proxySession.isOpen())
  7. .map(this::adaptCloseStatus).flatMap(proxySession::close);
  8. // Use retain() for Reactor Netty
  9. Mono<Void> proxySessionSend = proxySession
  10. .send(session.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
  11. if (log.isTraceEnabled()) {
  12. log.trace("proxySession(send from client): " + proxySession.getId()
  13. + ", corresponding session:" + session.getId() + ", packet: "
  14. + webSocketMessage.getPayloadAsText());
  15. }
  16. }));
  17. // .log("proxySessionSend", Level.FINE);
  18. Mono<Void> serverSessionSend = session.send(
  19. proxySession.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
  20. if (log.isTraceEnabled()) {
  21. log.trace("session(send from backend): " + session.getId()
  22. + ", corresponding proxySession:" + proxySession.getId() + " packet: "
  23. + webSocketMessage.getPayloadAsText());
  24. }
  25. }));
  26. // .log("sessionSend", Level.FINE);
  27. // Ensure closeStatus from one propagates to the other
  28. Mono.when(serverClose, proxyClose).subscribe();
  29. // Complete when both sessions are done
  30. return Mono.zip(proxySessionSend, serverSessionSend).then();
  31. }
  32. }

websocket连接关闭,是serverClose和proxyClose这两行代码,当后端的websocket连接断开时,就会把断开的转发设置到网关的websocket连接上;网关的websocket连接断开时,就会把断开的转发设置到后端的websocket连接上。这样,两个websocket连接的断开状态就一致了。

websocket发送消息,是proxySessionSend和serverSessionSend这两行代码,当网关收到客户端消息时,就会把消息发送给后端websocket服务;当网关收到后端websocket发来的消息时,就会把消息转发给客户端。

至此,网关在websocket连接、发消息、断开连接就和后端websocket服务保持一致了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/858212
推荐阅读
相关标签
  

闽ICP备14008679号