http访问支持websocket
最近开发一个动态路由的网关,通过nginx配置网关支持网关的高可用后,由于网关配置的路由是根据nacos服务名进行动态路由刷新,使用lb负载均衡,代码如下:
package com.sinux.sitesupport.gw.schedule; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.sinux.sitesupport.gw.config.GateWayCustomConfig; import com.sinux.sitesupport.gw.service.DynamicUpdateRouteService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.http.ResponseEntity; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import javax.annotation.PostConstruct; import java.net.URI; import java.util.*; /** * @ClassName DynamicUpdateRouteSchedule * @Description * @Author dh * @Date 2021/10/25 19:33 * @Version 1.0 */ @Component @Slf4j public class DynamicUpdateRouteSchedule { @Value("${spring.cloud.nacos.discovery.server-addr}") private String nacosHost; @Value("${spring.application.name}") private String springServerName; @Autowired private RestTemplate restTemplate; @Autowired private DynamicUpdateRouteService dynamicUpdateRouteService; @Autowired private GateWayCustomConfig customConfig; @PostConstruct public void init() { log.info("gateway route init"); List<String> serviceNames = getNacosPublicServiceNames(); //对服务名称组装对应的路由规则 for (String serviceName : serviceNames) { RouteDefinition routeDefinitionHttp = generateRouteDefinitionHttp(serviceName); dynamicUpdateRouteService.addRoute(routeDefinitionHttp); if (customConfig.getWsNames().contains(serviceName)) { RouteDefinition routeDefinitionWs = generateRouteDefinitionWs(serviceName); dynamicUpdateRouteService.addRoute(routeDefinitionWs); } } } /** * 定时刷新路由 */ @Scheduled(cron = "${dynamic.update.route.cron}") public void updateRoute() { //获取所有服务列表 List<String> serviceNames = getNacosPublicServiceNames(); //清理内存中的所有路由 dynamicUpdateRouteService.clearRoute(); //对服务组装对应的路由规则 for (String serviceName : serviceNames) { RouteDefinition routeDefinitionHttp = generateRouteDefinitionHttp(serviceName); dynamicUpdateRouteService.addRoute(routeDefinitionHttp); if (customConfig.getWsNames().contains(serviceName)) { RouteDefinition routeDefinitionWs = generateRouteDefinitionWs(serviceName); dynamicUpdateRouteService.addRoute(routeDefinitionWs); } } dynamicUpdateRouteService.publish(); } /** * 获取nacos上public命令空间下所有的服务名 */ public List<String> getNacosPublicServiceNames() { List<String> serviceNames = new ArrayList<>(); try { String url = "http://" + nacosHost + "/nacos/v1/ns/catalog/services?" + "pageNo=1&hasIpCount=true&withInstances=false&pageSize=10000&serviceNameParam=&groupNameParam=&namespaceId="; ResponseEntity<String> rtn = restTemplate.getForEntity(url, String.class); String body = rtn.getBody(); if (StringUtils.isNotBlank(body)) { JSONObject jsonObject = JSON.parseObject(body); JSONArray serviceList = jsonObject.getJSONArray("serviceList"); for (Object o : serviceList) { JSONObject serviceItem = (JSONObject) o; String serviceName = serviceItem.getString("name"); if (serviceName.equals(springServerName)) { continue; } serviceNames.add(serviceName); } } } catch (Exception e) { e.printStackTrace(); } return serviceNames; } /** * 根据serviceName组装RouteDefinition 支持http * 格式见 gateway_config.bak */ public RouteDefinition generateRouteDefinitionHttp(String serviceName) { RouteDefinition routeDefinition = null; try { //http //设置路由唯一id routeDefinition = new RouteDefinition(); routeDefinition.setId(serviceName + "-http"); //设置路由的uri URI uri = UriComponentsBuilder.fromUriString("lb://" + serviceName).build().toUri(); routeDefinition.setUri(uri); //设置多个filter FilterDefinition stripPrefixFilter = new FilterDefinition(); stripPrefixFilter.setName("StripPrefix"); HashMap<String, String> stripPrefixFilterParams = new HashMap<>(); stripPrefixFilterParams.put("parts", "1"); stripPrefixFilter.setArgs(stripPrefixFilterParams); FilterDefinition addRequestHeaderFilter = new FilterDefinition(); addRequestHeaderFilter.setName("AddRequestHeader"); HashMap<String, String> addRequestHeaderFilterParams = new HashMap<>(); addRequestHeaderFilterParams.put("name", "serviceName"); addRequestHeaderFilterParams.put("value", serviceName); addRequestHeaderFilter.setArgs(addRequestHeaderFilterParams); routeDefinition.setFilters(Arrays.asList(stripPrefixFilter, addRequestHeaderFilter)); //设置多个断言 PredicateDefinition pathPredicate = new PredicateDefinition(); pathPredicate.setName("Path"); HashMap<String, String> pathPredicateParams = new HashMap<>(); pathPredicateParams.put("pattern", "/" + serviceName + "/**"); pathPredicate.setArgs(pathPredicateParams); routeDefinition.setPredicates(Arrays.asList(pathPredicate)); routeDefinition.setOrder(1); } catch (Exception e) { e.printStackTrace(); } return routeDefinition; } }
因此有两种策略。
1. 在nginx端配置:(参考:https://blog.csdn.net/weixin_37264997/article/details/80341911)
location / { proxy_pass http://127.0.0.1:8080/; // 代理转发地址 // 启用支持websocket连接 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
2. 在网关处增添websocket (ws)的路由
/** * 根据serviceName组装RouteDefinition 支持ws * 格式见 gateway_config.bak */ public RouteDefinition generateRouteDefinitionWs(String serviceName) { RouteDefinition routeDefinition = null; try { //http //设置路由唯一id routeDefinition = new RouteDefinition(); routeDefinition.setId(serviceName + "-ws"); //设置路由的uri URI uri = UriComponentsBuilder.fromUriString("lb:ws://" + serviceName).build().toUri(); routeDefinition.setUri(uri); //设置多个filter FilterDefinition stripPrefixFilter = new FilterDefinition(); stripPrefixFilter.setName("StripPrefix"); HashMap<String, String> stripPrefixFilterParams = new HashMap<>(); stripPrefixFilterParams.put("parts", "1"); stripPrefixFilter.setArgs(stripPrefixFilterParams); FilterDefinition addRequestHeaderFilter = new FilterDefinition(); addRequestHeaderFilter.setName("AddRequestHeader"); HashMap<String, String> addRequestHeaderFilterParams = new HashMap<>(); addRequestHeaderFilterParams.put("name", "serviceName"); addRequestHeaderFilterParams.put("value", serviceName); addRequestHeaderFilter.setArgs(addRequestHeaderFilterParams); routeDefinition.setFilters(Arrays.asList(stripPrefixFilter, addRequestHeaderFilter)); //设置多个断言 PredicateDefinition pathPredicate = new PredicateDefinition(); pathPredicate.setName("Path"); HashMap<String, String> pathPredicateParams = new HashMap<>(); pathPredicateParams.put("pattern", "/" + serviceName + "/**"); pathPredicate.setArgs(pathPredicateParams); routeDefinition.setPredicates(Arrays.asList(pathPredicate)); routeDefinition.setOrder(2); } catch (Exception e) { e.printStackTrace(); } return routeDefinition; }
遇到的问题,总结一下