美文网首页
实现Ribbon规则

实现Ribbon规则

作者: zianL | 来源:发表于2022-08-08 17:19 被阅读0次

自定义获取节点路由规则,可满足发布时不影响客户正常使用处理方式如下

新代码定义版本号2,存量旧代码版本号1,
1、先执行ng lua 脚本实现功能是针对公司内部的请求IP获取到最新的版本号,并将这个版本号放在请求上下文中传递;
2、将新版本2 代码更新一个节点;
3、产品在内部网络验证,因为是内部网络所以ng 处获取到的是最新的版本,当验证时最新的版本号随上下文传递,所以ribbon 获取节点规则中会优先命中新代码,即可验证新代码逻辑是否符合业务需要;
4、验证成功后将ng 版本号全部更新为最新,然后将所有服务节点同步新代码;即可完成灰度发布全部过程;
如果像手机开发版这种需要更多的客户参与验证,可将ng 版本 跟客户id 或者 区域IP段映射。即可满足多客户验证;

@Configuration
@ConditionalOnNacosDiscoveryEnabled
public class NacosAutoConfiguration {

    @Bean
    public RouteProperties routeProperties() {
        return new RouteProperties();
    }

    @Bean
    public NacosNamingService nacosNamingService() {
        return new NacosNamingService();
    }

    @Bean
    @Scope("prototype")
    public IRule getRibbonRule() {
        return new NacosWeightLoadBalancerRule();
    }

}

@Slf4j
@Configuration
@ConditionalOnRouteEnabled
public class RouteAutoConfiguration {

    @Autowired(required = false)
    private RouteProperties routeProperties;

    @Value("${spring.cloud.nacos.discovery.namespace:}")
    private String namespace;

    @PostConstruct
    public void init() {
        NacosExecutorService.setBaseUrl(routeProperties.getAddress());
        NacosPullInstanceWorker worker = new NacosPullInstanceWorker();
        //保存路由信息交由线程池执行
        worker.run();
        int period = routeProperties.getPeriod();
        NacosExecutorService nacosExecutorService = new NacosExecutorService("nacos-pull-instance");
        nacosExecutorService.execute(period, worker);
    }

}
//创建一个调度线程执行拉取服务列表
public class NacosExecutorService {
    final ScheduledExecutorService executorService;

    private static String baseUrl;

    public static String getBaseUrl() {
        return baseUrl;
    }

    public static void setBaseUrl(String baseUrl) {
        NacosExecutorService.baseUrl = baseUrl;
    }

    public void execute(int period, Runnable task) {
        executorService.scheduleWithFixedDelay(task, 3, period, TimeUnit.SECONDS);
    }

    public NacosExecutorService(String name) {
        executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                t.setName(name);
                t.setDaemon(true);
                return t;
            }
        });
    }
}
//拉取服务线程实例
@Slf4j
public class NacosPullInstanceWorker implements Runnable {
      @Override
    public void run() {
        HashMap<Object, Object> paramMap = new HashMap<>();
        paramMap.put("md5", md5);


        RestTemplate restTemplate = new RestTemplate();
        TypeReference<List<SmartInstance>> forObject = restTemplate.getForObject(NacosExecutorService.getBaseUrl(), new TypeReference<List<SmartInstance>>() {
        }.getClass(), paramMap);
        List<SmartInstance> instances = (List<SmartInstance>) forObject.getType();
        if (instances == null) {
            log.warn("Nacos列表为空!");
            return;
        }
        log.debug("Nacos列表数量:{}", instances.size());
        NacosNamingService.setINSTANCE(instances);

    }
}
//实际实现策略方法
@Slf4j
public class NacosNamingService {
    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Autowired
    private RouteProperties routeProperties;
    public Instance getInstance(String serviceName, String groupName) throws NacosException {
        Boolean routeEnable = routeProperties.isEnable();
        if (!routeEnable) {
            return discoveryProperties.namingServiceInstance().selectOneHealthyInstance(serviceName, groupName);
        }

        GlobalContext globalContext = ContextUtil.getCurrentContext();
        if (globalContext == null) {
            globalContext = new GlobalContext();
        }
        String env = globalContext.getEnv();
        if (env == null) {
            ApplicationContext context = SpringContextUtil.getContext();
            if (context != null) {
                Environment environment = context.getEnvironment();
                env = environment.getProperty("spring.cloud.nacos.discovery.namespace", "");
            } else {
                env = "";
            }
        }
        String group = globalContext.getGroup();
        if (group == null) {
            ApplicationContext context = SpringContextUtil.getContext();
            if (context != null) {
                Environment environment = context.getEnvironment();
                group = environment.getProperty("spring.cloud.nacos.discovery.group", "");
            } else {
                group = CommonConstant.DEFAULT_GROUP;
            }
        }
        return getInstance(serviceName, env, group, globalContext.getClientIp(),globalContext.getVersion());
    }

    public Instance getInstance(String serviceName, String envName, String groupName, String clientIp,String version) throws NacosException {
        log.debug("getInstance serviceName:{}, envName:{}, groupName:{}, clientIp:{}", serviceName, envName, groupName, clientIp);
        List<SmartInstance> envAndGroupInstanceList = new ArrayList<>();
        List<SmartInstance> envDefaultGroupInstanceList = new ArrayList<>();
        List<SmartInstance> defaultInstanceList = new ArrayList<>();

        List<SmartInstance> instanceList = INSTANCE_MAP.get(serviceName);
        //非空校验
        if (instanceList == null || instanceList.size() == 0) {
            log.warn("Nacos服务列表为空!");
            return null;
        }
        //遍历搜索
        for (SmartInstance instance : instanceList) {
            //服务信息获取

            //优先本机匹配
            if (clientIp == null) {
                clientIp = LocalIpUtil.getLocalIp();
            }
            if (clientIp.equals(instance.getIp())) {
                return instance;
            }
            //如果ng 传进来的版本等于最新版本的代码就优先选择,每次发布需要更新ng的验证版本,验证通过后在全面更新所有节点
            if(eq(version,instance.getVersion())){
                defaultInstanceList.add(instance);
                continue;
            }
            //按照下列规则路由: (环境,组) -> (环境,默认组) -> (默认环境,默认组)
            if (eq(instance.getNamespace(), envName) && eq(instance.getGroupName(),groupName)) {
                envAndGroupInstanceList.add(instance);
                continue;
            }
            if (eq(instance.getNamespace(),envName) && eq(instance.getGroupName(),CommonConstant.DEFAULT_GROUP)) {
                envDefaultGroupInstanceList.add(instance);
                continue;
            }
            if (eq(instance.getNamespace(),CommonConstant.Env.MASTER) && eq(instance.getGroupName(),CommonConstant.DEFAULT_GROUP)) {
                defaultInstanceList.add(instance);
            }
        }
        //输出路由结果
        log.debug("getInstance envAndGroupInstanceList:{} envDefaultGroupInstanceList:{} defaultInstanceList:{}",
                envAndGroupInstanceList, envDefaultGroupInstanceList, defaultInstanceList);

        //获取最合适实例
        SmartInstance instance = getSuitableInstance(envAndGroupInstanceList);
        if (instance != null) {
            return instance;
        }
        instance = getSuitableInstance(envDefaultGroupInstanceList);
        if (instance != null) {
            return instance;
        }
        return getSuitableInstance(defaultInstanceList);
    }

    /**
     * 获取包含测试网段的实例
     * @param instanceList 实例列表
     * @return
     */
    private SmartInstance getSuitableInstance(List<SmartInstance> instanceList) {
        if (instanceList.size() > 0) {
            SmartInstance instance = instanceList.get(0);
            for (SmartInstance instanceItem : instanceList) {
                if(instanceItem.getWeight() > instance.getWeight()){
                    instance = instanceItem;
                }
            }
            return instance;
        }
        return null;
    }

    static boolean eq(String s1, String s2){
        return StringUtils.equals(s1, s2);
    }

    public static void setINSTANCE(List<SmartInstance> instances) {
        if(instances != null){
            Map<String, List<SmartInstance>> map = instances.stream()
                    .collect(Collectors.groupingBy(SmartInstance::getName));
            if(map != null){
                INSTANCE_MAP = map;
            }
        }
    }
    private static Map<String, List<SmartInstance>> INSTANCE_MAP = new HashMap<>();
}
//重写获取服务的方法
@Slf4j
public class NacosWeightLoadBalancerRule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Autowired
    private NacosNamingService nacosNamingService;

    @Autowired
    private ConfigurableApplicationContext applicationContext;
    @Override
    public Server choose(Object o) {
        String groupName = applicationContext.getEnvironment().getProperty("spring.cloud.nacos.config.group");
        if (groupName == null) {
            groupName = "DEFAULT_GROUP";
        }
        DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
        String name = loadBalancer.getName();
        try {
            Instance instance = nacosNamingService.getInstance(name, groupName);
            log.info("请求服务:{}", instance != null ? instance.getInstanceId() : null);
            return new NacosServer(instance);
        } catch (NacosException ee) {
            log.error("请求服务异常!异常信息:{}", ee);
        } catch (Exception e) {
            log.error("请求服务异常!异常信息:{}", e);
        }
        return null;
    }
}
public class SmartInstance extends Instance {

    String groupName;

    String name;

    String namespace;

    String version;
    }
}

相关文章

网友评论

      本文标题:实现Ribbon规则

      本文链接:https://www.haomeiwen.com/subject/remnwrtx.html