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;
    }

遇到的问题,总结一下

原文地址:https://www.cnblogs.com/dhName/p/15640540.html