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

    遇到的问题,总结一下

  • 相关阅读:
    linux 软件各文件安装位置
    c dup 函数
    c sigaction信号处理
    vtun 信号处理
    vtun 虚拟网卡的读写非阻塞研究
    vtun 守护进程详解
    vtun fork函数
    vtun 中的__io_canceled变量和相关函数
    android 之 AIDL
    android 显示电池电量
  • 原文地址:https://www.cnblogs.com/dhName/p/15640540.html
Copyright © 2020-2023  润新知