boot3+JDK17+spring-cloud-gateway:4.0.0+spring-cloud:2022.0.0.0+Nacos2.2.1配置动态路由的网关

发布时间 2023-11-20 17:49:49作者: 白嫖老郭

项目依赖

image

配置

# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
# Nacos认证信息
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos
spring.cloud.nacos.config.contextPath=/nacos
# 设置配置中心服务端地址
spring.cloud.nacos.config.server-addr=localhost:8848
# Nacos 配置中心的namespace。需要注意,如果使用 public 的 namcespace ,请不要填写这个值,直接留空即可
spring.cloud.nacos.config.namespace=1c5ae1c6-cac5-40ef-b090-b4e9677ea2aa
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=zuul-client
spring.cloud.util.enabled=false
spring.profiles.active=dev
spring.cloud.nacos.discovery.group=cloud-test
spring.cloud.nacos.config.group=cloud-test
spring.cloud.nacos.config.file-extension=properties
# Nacos认证信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=1c5ae1c6-cac5-40ef-b090-b4e9677ea2aa
#dataId 的完整格式如下:prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置
# ${prefix}-${spring.profiles.active}.${file-extension}
# 数据库连接信息{单数据源}

路由配置

sidecar:
  # 异构微服务的IP
  ip: 127.0.0.1
  # 异构微服务的端口
  port: 8060
  # 异构微服务的健康检查URL
  health-check-url: http://localhost:8060/health.json

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #开启通过服务中心的自动根据 serviceId 创建路由的功能
      routes:
        - id: ldapsso-client
          uri: lb://ldapsso-client
          predicates:
            - Path=/ldapsso/**
          filters:
            - StripPrefix=1
        - id: service-b
          uri: lb://service-b
          predicates:
            - Path=/service-b/test-service-b/**
          filters:
            - StripPrefix=1
    inetutils:
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    nacos:
      discovery:
        server-addr: localhost:8848
  application:
    name: zuul-client

management:
  endpoint:
    health:
      show-details: always
server:
  port: 28083

启动器

@SpringBootApplication
@EnableDiscoveryClient
public class ZuulApplication {

    public static void main(String[] args) {
        try {
            SpringApplication.run(ZuulApplication.class, args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }


    @Bean
    public InetIPv6Utils inetIPv6Utils() {
        InetUtilsProperties properties = new InetUtilsProperties();
        return new InetIPv6Utils(properties);
    }

    @Bean
    public InetUtils inetUtils() {
        InetUtilsProperties properties = new InetUtilsProperties();
        return new InetUtils(properties);
    }


}

测试类

@RestController
public class IndexController {


    @RequestMapping("/index")
    public String getIndex(){
        return "Zuul";
    }
}

路由更新业务处理

public interface RouteService {

    /**
     * 更新路由配置
     *
     * @param routeDefinition
     */
    void update(RouteDefinition routeDefinition);

    /**
     * 添加路由配置
     *
     * @param routeDefinition
     */
    void add(RouteDefinition routeDefinition);

}

@Slf4j
@Service
public class RouteServiceImpl implements RouteService, ApplicationEventPublisherAware {

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    /**
     * 事件发布者
     */
    private ApplicationEventPublisher publisher;

    @Override
    public void update(RouteDefinition routeDefinition) {
        log.info("更新路由配置项:{}", routeDefinition);
        this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public void add(RouteDefinition routeDefinition) {
        log.info("新增路由配置项:{}", routeDefinition);
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }
}

动态路由加载

package com.example.zuul.listner;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.annotation.NacosConfigListener;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.utils.UuidUtils;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.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.stereotype.Component;

import java.net.URI;
import java.util.*;
import java.util.concurrent.Executor;

@Component
@Slf4j
public class NacosRefreshConfig {


    @Autowired
    private RouteService routeService;

    @Value("${spring.cloud.nacos.config.server-addr}")
    private String nacosAddr;
    @Value("${spring.cloud.nacos.config.namespace}")
    private String nameSpace;

    @Value("${spring.cloud.nacos.config.username}")
    private String username;

    @Value("${spring.cloud.nacos.config.password}")
    private String password;

    @PostConstruct
    public void initBindListner() throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosAddr);
        properties.put(PropertyKeyConst.NAMESPACE, nameSpace);
        properties.put(PropertyKeyConst.USERNAME, username);
        properties.put(PropertyKeyConst.PASSWORD, password);
        ConfigService configService = NacosFactory.createConfigService(properties);
        configService.addListener(Constants.CONFIG_FILE_NAME, Constants.CONFIG_FILE_GROUP, new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            @Override
            public void receiveConfigInfo(String configInfo) {
                // 刷新路由
                List<RouteDefinition> extracted = extracted(configInfo);
                for (RouteDefinition routeDefinition : extracted) {
                    routeService.update(routeDefinition);
                }
            }
        });
    }

    private static List<RouteDefinition> extracted(String configInfo) {
        List<RouteDefinition> list = new ArrayList<>();
        JSONArray jsonObjectArr = JSONArray.parseArray(configInfo);
        for (int i = 0; i < jsonObjectArr.size(); i++) {
            RouteDefinition routeDefinition = new RouteDefinition();
            JSONObject jsonObject = jsonObjectArr.getJSONObject(i);
            routeDefinition.setId(StringUtils.defaultIfEmpty(jsonObject.getString("id"), UuidUtils.generateUuid()));
            routeDefinition.setUri(URI.create(jsonObject.getString("uri")));
            List<PredicateDefinition> predicates = routeDefinition.getPredicates();
            JSONArray predicates1 = jsonObject.getJSONArray("predicates");
            if (predicates1 != null && predicates1.size() > 1) {
                for (int ii = 0; ii < predicates1.size(); ii++) {
                    Set<Map.Entry<String, Object>> entries = predicates1.getJSONObject(ii).entrySet();
                    for (Map.Entry<String, Object> entry : entries) {
                        PredicateDefinition predicateDefinition = new PredicateDefinition();
                        predicateDefinition.setName(entry.getKey());
                        Map<String, String> args = predicateDefinition.getArgs();
                        args.put(entry.getKey(), entry.getValue().toString());
                        predicates.add(predicateDefinition);
                    }
                }
            } else {
                Set<Map.Entry<String, Object>> entries = predicates1.getJSONObject(0).entrySet();
                for (Map.Entry<String, Object> entry : entries) {
                    PredicateDefinition predicateDefinition = new PredicateDefinition();
                    predicateDefinition.setName(entry.getKey());
                    Map<String, String> args = predicateDefinition.getArgs();
                    args.put(entry.getKey(), entry.getValue().toString());
                    predicates.add(predicateDefinition);
                }
            }

            List<FilterDefinition> filters = routeDefinition.getFilters();
            JSONArray var6 = jsonObject.getJSONArray("filters");
            if (var6 != null && var6.size() > 1) {
                for (int ij = 0; ij < var6.size(); ij++) {
                    Set<Map.Entry<String, Object>> entries = var6.getJSONObject(ij).entrySet();
                    for (Map.Entry<String, Object> entry : entries) {
                        FilterDefinition filterDefinition = new FilterDefinition();
                        filterDefinition.setName(entry.getKey());
                        Map<String, String> args = filterDefinition.getArgs();
                        args.put(entry.getKey(), entry.getValue().toString());
                        filters.add(filterDefinition);
                    }
                }

            } else {
                Set<Map.Entry<String, Object>> entries = var6.getJSONObject(0).entrySet();
                for (Map.Entry<String, Object> entry : entries) {
                    FilterDefinition filterDefinition = new FilterDefinition();
                    filterDefinition.setName(entry.getKey());
                    Map<String, String> args = filterDefinition.getArgs();
                    args.put(entry.getKey(), entry.getValue().toString());
                    filters.add(filterDefinition);
                }
            }
            list.add(routeDefinition);
        }
        return list;
    }

    @NacosConfigListener(dataId = Constants.CONFIG_FILE_NAME)
    public void refresh(String msg) {
        log.error("-----------------------");
        JSONObject jsonObject = JSONObject.parseObject(msg);
        log.error(jsonObject.toJSONString());
    }
}


public interface Constants {
    String CONFIG_FILE_NAME = "zuul-client";
    String CONFIG_FILE_GROUP = "cloud-test";
}

Nacos配置

[
    {"id":"ldapsso-client","uri":"lb://ldapsso-client","predicates":[{"Path":"/ldapsso/**"}],"filters":[{"StripPrefix":1}]},
     {"id":"baidu","uri":"https://www.baidu.com","predicates":[{"Path":"/baidu/**"}],"filters":[{"StripPrefix":1}]},
     {"id":"client","uri":"lb://ldapsso-client","predicates":[{"Path":"/client/**"}],"filters":[{"StripPrefix":1}]}
]

image

测试动态路由生效

image

完结====