Nacos动态路由配置(一)-相关源码解析

1:配置相关nacos信息

其他信息已省略

项目中gateway.yml

nacos:
  server-addr:xx.xx.xx.xx:8848
  namespace: dev

#省略其他代码。。。。。。。。。

#动态路由相关配置
dynamic:
  route:
    data-id: routes-${spring.application.name}.yaml
    group: ROUTE_GROUP
    server-addr: xx.xx.xx.xx:8848
    namespace: dev

 

2:创建相关实体类

过滤器

package com.carry.www.entity;

/**
 * 过滤器实体类
 *
 */
import lombok.Data;

import java.util.LinkedHashMap;
import java.util.Map;

@Data
public class FilterEntity {

  // 过滤器对应的Name
  private String name;

  // 路由规则
  private Map<String, String> args = new LinkedHashMap<>();

}

断言:

package com.carry.www.entity;

import lombok.Data;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 路由断言实体类
 *
 */
@Data
public class PredicateEntity {
  // 断言对应的Name
  private String name;

  // 断言规则
  private Map<String, String> args = new LinkedHashMap<>();

}

路由类

package com.carry.www.entity;

import lombok.Data;

import java.util.ArrayList;
import java.util.List;

/**
 * 路由实体类
 *
 */
@Data
public class RouteEntity {
  // 路由id
  private String id;

  // 路由断言集合
  private List<PredicateEntity> predicates = new ArrayList<>();

  // 路由过滤器集合
  private List<FilterEntity> filters = new ArrayList<>();

  // 路由转发的目标uri
  private String uri;

  // 路由执行的顺序
  private int order = 0;
}

3:添加监听

监听nacos配置信息

package com.carry.www.config;

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.listener.Listener;
import com.carry.www.entity.FilterEntity;
import com.carry.www.entity.PredicateEntity;
import com.carry.www.entity.RouteEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
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.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 从Nacos获取动态路由
 * 实现ApplicationEventPublisherAware发布接口来发布路由更新事件
 */
@Configuration
@Slf4j
public class DynamicRoutingConfig implements ApplicationEventPublisherAware {

    @Value("${dynamic.route.server-addr}")
    private String serverAddr;

    @Value("${nacos.namespace}")
    private String namespace;

    @Value("${dynamic.route.data-id}")
    private String dataId;

    @Value("${dynamic.route.group}")
    private String groupId;

    // 保存、删除路由服务
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

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

    /**
     * @return
     * @Author carryer
     * @Description 获取nacos配置服务
     * @Date
     * @Param
     **/
    public ConfigService getNacosConfigInfo() throws Exception {
        Properties properties = new Properties();
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddr);
//        properties.setProperty(PropertyKeyConst.USERNAME, username);
//        properties.setProperty(PropertyKeyConst.PASSWORD, password);
        properties.setProperty(PropertyKeyConst.NAMESPACE, namespace);
        ConfigService configService = NacosFactory.createConfigService(properties);

        return configService;
    }

    /**
     * @return
     * @Author carryer
     * @Description 初始化路由
     * @Date
     * @Param
     **/
    @Bean
    public void initRouting() {
        try {
            ConfigService configService = this.getNacosConfigInfo();
            String configInfo = configService.getConfig(dataId, groupId, 5000);
            if (null != configInfo) {
                List<RouteEntity> list = JSONObject.parseArray(configInfo).toJavaList(RouteEntity.class);
                for (RouteEntity route : list) {
                    update(assembleRouteDefinition(route));
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        }
    }

    /**
     * @return
     * @Author carryer
     * @Description 刷新路由
     * @Date
     * @Param
     **/
    @Bean
    public void refreshRouting() {
        try {
            ConfigService configService = this.getNacosConfigInfo();
            //监听路由变化
            configService.addListener(
                    dataId,
                    groupId,
                    new Listener() {
                        @Override
                        public Executor getExecutor() {
                            return null;
                        }

                        @Override
                        public void receiveConfigInfo(String configInfo) {
                            try {
                                log.info(configInfo);
                                if (null != configInfo) {
                                    List<RouteEntity> list =
                                            JSONObject.parseArray(configInfo).toJavaList(RouteEntity.class);
                                    //更新路由表
                                    for (RouteEntity route : list) {
                                        update(assembleRouteDefinition(route));
                                    }
                                }
                            } catch (Exception e) {
                                log.error(e.getMessage(), e);
                                e.printStackTrace();
                            }
                        }
                    });
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        }
    }

    /**
     * @return
     * @Author carryer
     * @Description 路由更新
     * @Date
     * @Param
     **/
    private void update(RouteDefinition routeDefinition) throws Exception {
        //先删除路由
        routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
        //再保存路由
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        //发布事件 发布者是RefreshRoutesEvent 事件是刷新路由
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
     * @return
     * @Author carryer
     * @Description 实体信息解析
     * @Date
     * @Param
     **/
    private RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) {
        RouteDefinition definition = new RouteDefinition();
        // ID
        definition.setId(routeEntity.getId());

        // Predicates断言
        List<PredicateDefinition> pdList = new ArrayList<>();
        for (PredicateEntity predicateEntity : routeEntity.getPredicates()) {
            PredicateDefinition predicateDefinition = new PredicateDefinition();
            predicateDefinition.setArgs(predicateEntity.getArgs());
            predicateDefinition.setName(predicateEntity.getName());
            pdList.add(predicateDefinition);
        }
        definition.setPredicates(pdList);

        // Filters过滤器
        List<FilterDefinition> fdList = new ArrayList<>();
        for (FilterEntity filterEntity : routeEntity.getFilters()) {
            FilterDefinition filterDefinition = new FilterDefinition();
            filterDefinition.setArgs(filterEntity.getArgs());
            filterDefinition.setName(filterEntity.getName());
            fdList.add(filterDefinition);
        }
        definition.setFilters(fdList);

        // URI
        URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri();
        definition.setUri(uri);

        return definition;
    }
}

以上代码nacos即可动态添加路由,小伙伴们可以直接使用

=============================分割线=============================================

4:相关源码解析

上面的配置,首先加载api-gatway.yaml里的信息,然后加载routes-api-gatway.yaml里的配置信息,原理都是一样的,只不过监听的是routes-api-gatway.yaml(里面是json格式)
ApplicationEventPublisherAware是一个事件发布接口,包括事件、发布者、监听者。这里事件的发布者是网关自带的类,RefreshRoutesEvent,事件是刷新路由。


不管初始化路由还是刷新路由,第一步都需要获取nacos的相关信息,ConfigService由静态工厂类NacosFactory获取

进入ConfigFactory发现ConfigService是由反射机制获取的NacosConfigService,NacosConfigService实现了ConfigService

进入NacosConfigService,里面的构造方法进行了nacos的相关赋值,包括命名空间之类的,至此可以看做已经成功连接了nacos并且返回了nacos的配置类。

下面看第二句代码 String configInfo = configService.getConfig(dataId, groupId, 5000) 这句话的意思其实就是nacos配置类根据dataId和groupId获取配置文件信息,也就是你在nacos可视化界面里面配置的yaml内容。

下面看代码

直接看getConfigInner方法,这个方法优先获取本地的Nacos配置信息,如果没有,则获取服务器上的配置信息

String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);

获取nacos服务器上配置信息。

String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
Nacos的ClientWorker类进行赋值操作,调用getServerConfig方法获取配置返回配置数组

进入ClientWorker的getServerConfig

ClientWorker里面核心方法 agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout),点击进入ServerHttpAgent
ServerHttpAgent类进行http调用

ServerHttpAgent里又调用了HttpSimpleClient类,进行调用 Nacos Open API 向 Nacos Server 获取配置
第二次这里的url是:http://xx.xx.xx.xx:8848/nacos/v1/cs/configs?dataId=routes-api-gatway&group=ROUTE_GROUP&tenant=dev,你用postman也可以调通,其实返回的就是你在nacos中的api-gatway.yaml或routes-api-gatway.yaml的配置文件

 

 

nacos中的routes-api-gatway.yaml的配置文件

 

至此,前面2行的代码已经执行完毕。下面就是进行解析然后发布事件。

String configInfo = configService.getConfig(dataId, groupId, 5000)方法返回的路由字符串,解析成路由实体类集合后,循环解析路由赋值给RouteDefinition类,

RouteDefinition里进行路由、断言、url等设置

下面验证,新加一个id是service4的路由然后发布,会发现代码自动读取了最新配置并且进行了路由解析更新了路由

猜你喜欢

转载自blog.csdn.net/CarryBest/article/details/112985659