JAVA面试题分享一百七十六:Nacos如何实现服务的动态路由?

一、前言

网关中有两个重要的概念,那就是路由配置和路由规则,路由配置是指配置某请求路径路由到指定的目的地址。而路由规则是指匹配到路由配置之后,再根据路由规则进行转发处理。 Spring Cloud Gateway作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,尽量避免重启,需要实现Spring Cloud Gateway动态路由配置。前面章节介绍了Spring Cloud Gateway提供的两种方法去配置路由规则,但都是在Spring Cloud Gateway启动时候,就将路由配置和规则加载到内存里,无法做到不重启网关就可以动态的对应路由的配置和规则进行增加,修改和删除。本文是基于Spring Cloud Gateway的动态路由实现 基础之上编写,通过Nacos配置服务下发路由配置实现动态路由。

二、Spring Cloud Gateway简单的动态路由实现

Spring Cloud Gateway的官方文档并没有讲如何动态配置,查看 Spring Cloud Gateway的源码,发现在org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint类中提供了动态配置的Rest接口,但是需要开启Gateway的端点,而且提供的功能不是很强大。通过参考和GatewayControllerEndpoint相关的代码,可以自己编码实际动态路由配置。 下面通过案例的方式去讲解怎么通Nacos实现Spring Cloud Gateway的动态路由。案例工程如spring-cloud-gateway-nacos所示。

三、简单动态路由的实现

1、新建Maven工程sc-gateway-server

配置主要的核心依赖如代码清单所示: 代码清单: spring-cloud-gateway-nacos/sc-gateway-server/pom.xml


 <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
        <version>0.4.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.47</version>
    </dependency>

2、根据Spring Cloud Gateway的路由模型定义数据传输模型

分别创建GatewayRouteDefinition.java, GatewayPredicateDefinition.java, GatewayFilterDefinition.java这三个类。

(1) 创建路由定义模型

public class GatewayRouteDefinition {
    //路由的Id
    private String id;
    //路由断言集合配置
    private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
    //路由过滤器集合配置
    private List<GatewayFilterDefinition> filters = new ArrayList<>();
    //路由规则转发的目标uri
    private String uri;
    //路由执行的顺序
    private int order = 0;
    //此处省略get和set方法
}

(2)创建过滤器定义模型

public class GatewayFilterDefinition {
    //Filter Name
    private String name;
    //对应的路由规则
    private Map<String, String> args = new LinkedHashMap<>();
    //此处省略Get和Set方法
}

(3)创建路由断言定义模型

public class GatewayPredicateDefinition {
    //断言对应的Name
    private String name;
    //配置的断言规则
    private Map<String, String> args = new LinkedHashMap<>();
    //此处省略Get和Set方法
}

3、编写动态路由实现类

编写DynamicRouteServiceImpl并实现ApplicationEventPublisherAware接口,代码如下所示

@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;
    private ApplicationEventPublisher publisher;
    //增加路由
    public String add(RouteDefinition definition) {
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "success";
    }
    //更新路由
    public String update(RouteDefinition definition) {
        try {
            this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
        } catch (Exception e) {
            return "update fail,not find route  routeId: "+definition.getId();
        }
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "success";
        } catch (Exception e) {
            return "update route  fail";
        }
    }
    //删除路由
    public Mono<ResponseEntity<Object>> delete(String id) {
        return this.routeDefinitionWriter.delete(Mono.just(id))
                .then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
                .onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
    }
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }
}

4、编写Nacos监听接收下发的路由配置

4.1 使用Nacos监听下发的配置

监听Nacos Config Server下发配置的代码如下所示:

@Component
public class DynamicRouteServiceImplByNacos {

    @Autowired
    private DynamicRouteServiceImpl dynamicRouteService;

    public DynamicRouteServiceImplByNacos() {

        dynamicRouteByNacosListener("sc-gateway","xujin_test");
    }

    /**
     * 监听Nacos Server下发的动态路由配置
     * @param dataId
     * @param group
     */
    public void dynamicRouteByNacosListener (String dataId, String group){
        try {
            ConfigService configService=NacosFactory.createConfigService("127.0.0.1:8848");
            String content = configService.getConfig(dataId, group, 5000);
            System.out.println(content);
            configService.addListener(dataId, group, new Listener()  {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    RouteDefinition definition= JSON.parseObject(configInfo,RouteDefinition.class);
                    dynamicRouteService.update(definition);
                }
                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException e) {
            //todo 提醒:异常自行处理此处省略
        }
    }

}
4.2 两种方式创建ConfigService

使用两种方式创建com.alibaba.nacos.api.config.ConfigService

  • 1.构建Properties创建

使用createConfigService(Properties properties),代码如下所示:

Properties properties = new Properties();
            properties.put("nacos.server-addr", "");
            properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
            ConfigService configService=NacosFactory.createConfigService(properties);

注意:PropertyKeyConst是com.alibaba.nacos.api.PropertyKeyConst

  • 2.只传递Nacos Config Server的地址
 ConfigService configService=NacosFactory.createConfigService("127.0.0.1:8848");

四、总结

Nacos通过以下方式实现服务的动态路由:

  1. 服务注册与发现:在Nacos中,服务提供者将自己的网络地址、服务名称等信息注册到Nacos注册中心。服务消费者通过Nacos发现感兴趣的服务,并获取可用的服务实例列表。这个过程是动态的,当服务提供者增加或减少时,Nacos会自动更新服务实例列表。
  2. 权重路由:Nacos支持基于权重的路由策略。服务提供者可以为不同的服务实例设置权重,权重越高,被访问的概率越大。当服务消费者的请求到达Nacos时,Nacos会根据权重策略选择一个合适的服务实例进行路由。
  3. 元数据及标签路由:Nacos还支持基于元数据和标签的路由策略。服务提供者可以为服务实例添加元数据和标签,这些元数据和标签可以用于描述服务实例的特性、版本等信息。服务消费者可以根据这些元数据和标签进行路由,实现更灵活的访问控制和服务调用。
  4. 动态配置更新:Nacos允许动态更新服务的配置信息,包括路由策略、权重等。当配置信息发生变化时,Nacos会实时推送给相关的服务消费者,确保服务消费者总是使用最新的路由策略进行访问。
  5. 健康检查与负载均衡:Nacos会对服务实例进行健康检查,确保服务实例的可用性。同时,Nacos还支持多种负载均衡策略,如随机、轮询等,可以根据实际需求进行配置。

通过以上方式,Nacos实现了服务的动态路由,提高了服务的可用性和灵活性。请注意,具体实现方式可能因版本更新有所不同。建议查阅Nacos的官方文档或联系相关技术支持人员以获取最准确的信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值