当前位置:网站首页>Three ways of extending ribbon to support Nacos weight

Three ways of extending ribbon to support Nacos weight

2022-06-22 20:56:00 A rookie is a great God

Nacos Support weight configuration , This is a practical function , for example :

  • Set the weight of the machine with poor performance low , The weight of the machine with good performance is set high , Give priority to requests to high-performance machines ;
  • When an exception occurs in an instance , Set the weight low , Troubleshoot problems , After troubleshooting, restore the weight ;
  • When you want to offline an instance , You can set the weight of this instance to 0, In this way, the traffic will not reach the instance —— Then shut down the instance , In this way, you can achieve elegant offline . Of course this is for Nacos Customized elegant offline solution ——Spring Cloud in , There are many postures to achieve elegant offline , See :《 Practical skills :Spring Cloud in , How to gracefully get off line microservice ?》 , In it, the author summarizes four elegant offline methods .

However, the test found that ,Nacos Weight configuration pair Spring Cloud Alibaba Invalid . in other words , No matter in Nacos How to configure on the console , When calling, the weight setting is ignored .

Spring Cloud Alibaba Through integration Ribbon The way , Load balancing is realized . The load balancing rules used are  ZoneAvoidanceRule .

This section discusses how to extend Ribbon, Let it support Nacos Weight configuration , The author summarizes three schemes .

programme 1: Implement load balancing rules by yourself

Ideas

First of all Ribbon Load balancing rules are OK .

  • Weight configuration , Can be obtained from the instance information .
  • Configure based on weight , Calculate an example .

Code

@Slf4j
public class NacosWeightRandomV1Rule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object key) {
        List<Server> servers = this.getLoadBalancer().getReachableServers();

        List<InstanceWithWeight> instanceWithWeights = servers.stream()
                .map(server -> {
                    //  The registry uses only Nacos, Not using other registries at the same time ( for example Eureka), Theoretically, it will not be realized 
                    if (!(server instanceof NacosServer)) {
                        log.error(" invalid parameter ,server = {}", server);
                        throw new IllegalArgumentException(" invalid parameter , No NacosServer example !");
                    }

                    NacosServer nacosServer = (NacosServer) server;
                    Instance instance = nacosServer.getInstance();
                    double weight = instance.getWeight();
                    return new InstanceWithWeight(
                            server,
                            Double.valueOf(weight).intValue()
                    );
                })
                .collect(Collectors.toList());

        Server server = this.weightRandom(instanceWithWeights);

        log.info(" Selected server = {}", server);
        return server;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private class InstanceWithWeight {
        private Server server;
        private Integer weight;
    }

    /**
     *  Random according to weight 
     *  Algorithm reference  https://blog.csdn.net/u011627980/article/details/79401026
     *
     * @param list  The instance list 
     * @return  Random results 
     */
    private Server weightRandom(List<InstanceWithWeight> list) {
        List<Server> instances = Lists.newArrayList();
        for (InstanceWithWeight instanceWithWeight : list) {
            int weight = instanceWithWeight.getWeight();
            for (int i = 0; i <= weight; i++) {
                instances.add(instanceWithWeight.getServer());
            }
        }
        int i = new Random().nextInt(instances.size());
        return instances.get(i);
    }
}

WARNING

There is room for optimization in this code , Just to demonstrate the process of thinking , Not recommended for production , If you intend to use this scheme to realize , Please refer to the following two optimizations

  • Simplicity , I'll go straight to double Weight of type (weight), Into the integer To calculate the , There is a loss of precision .
  • InstanceWithWeight Is too heavy , stay  weightRandom  Two more floors for loop , It's quite memory intensive , Suggest Baidu other weight random algorithm optimization . However, there are generally only three or five instances of a microservice in an actual project , So the memory consumption can be tolerated . No optimization is a big problem .

programme 2: utilize Nacos Client The ability of [ recommend ]

Ideas

Reading code Nacos In the process of source code , Find out Nacos Client It provides the ability of load balancing , And load balancing algorithm This is exactly what we want to select instances based on weights

Code in  com.alibaba.nacos.api.naming.NamingService#selectOneHealthyInstance , Just find a way to call this line of code , We can realize the functions we want !

Code

@Slf4j
public class NacosWeightRandomV2Rule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Override
    public Server choose(Object key) {
        DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
        String name = loadBalancer.getName();
        try {
            Instance instance = discoveryProperties.namingServiceInstance()
                    .selectOneHealthyInstance(name);

            log.info(" Selected instance = {}", instance);

            /*
             * instance turn server The logical reference of is from :
             * org.springframework.cloud.alibaba.nacos.ribbon.NacosServerList.instancesToServerList
             */
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error(" Something goes wrong ", e);
            return null;
        }
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }
}

programme 3: The most violent way to play

Ideas

In the process of reading the source code , Find the following code :

//  come from :org.springframework.cloud.alibaba.nacos.ribbon.NacosServerList#getServers
private List<NacosServer> getServers() {
  try {
    List<Instance> instances = discoveryProperties.namingServiceInstance()
      .selectInstances(serviceId, true);
    return instancesToServerList(instances);
  }
  catch (Exception e) {
    throw new IllegalStateException(
      "Can not get service instances from nacos, serviceId=" + serviceId,
      e);
  }
}

This NacosServerList  Just give Ribbon To do load balancing ” data source ”! If you change the code here to  com.alibaba.nacos.api.naming.NamingService#selectOneHealthyInstance  It can also realize the functions we want ?

in other words , hand Ribbon Of List Always only 1 An example ! So no matter Ribbon What kind of load balancing , It's all up to him .

Code

1 Reference resources NacosServerList Code for , rewrite NacosRibbonServerList

/**
 *  Reference resources org.springframework.cloud.alibaba.nacos.ribbon.NacosServerList
 */
@Slf4j
public class NacosRibbonServerList extends AbstractServerList<NacosServer> {

    private NacosDiscoveryProperties discoveryProperties;

    private String serviceId;

    public NacosRibbonServerList(NacosDiscoveryProperties discoveryProperties) {
        this.discoveryProperties = discoveryProperties;
    }

    @Override
    public List<NacosServer> getInitialListOfServers() {
        return getServers();
    }

    @Override
    public List<NacosServer> getUpdatedListOfServers() {
        return getServers();
    }

    private List<NacosServer> getServers() {
        try {
            Instance instance = discoveryProperties.namingServiceInstance()
                    .selectOneHealthyInstance(serviceId, true);
            log.debug(" Select the instance = {}", instance);
            return instancesToServerList(
                    Lists.newArrayList(instance)
            );
        } catch (Exception e) {
            throw new IllegalStateException(
                    "Can not get service instances from nacos, serviceId=" + serviceId,
                    e);
        }
    }

    private List<NacosServer> instancesToServerList(List<Instance> instances) {
        List<NacosServer> result = new ArrayList<>();
        if (null == instances) {
            return result;
        }
        for (Instance instance : instances) {
            result.add(new NacosServer(instance));
        }
        return result;
    }

    public String getServiceId() {
        return serviceId;
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        this.serviceId = iClientConfig.getClientName();
    }
}

2 Write configuration classes

/**
 *  Reference resources :org.springframework.cloud.alibaba.nacos.ribbon.NacosRibbonClientConfiguration
 */
@Configuration
public class NacosRibbonClientExtendConfiguration {
    @Bean
    public ServerList<?> ribbonServerList(IClientConfig config, NacosDiscoveryProperties nacosDiscoveryProperties) {
        NacosRibbonServerList serverList = new NacosRibbonServerList(nacosDiscoveryProperties);
        serverList.initWithNiwsConfig(config);
        return serverList;
    }
}

3 adding annotations , Let's go to the top NacosRibbonClientExtendConfiguration Become Ribbon Default configuration .

// ... Other notes 
@RibbonClients(defaultConfiguration = NacosRibbonClientExtendConfiguration.class)
public class ConsumerMovieApplication {
  public static void main(String[] args) {
    SpringApplication.run(ConsumerMovieApplication.class, args);
  }
}

Be careful  :

Be sure to pay attention to , take  NacosRibbonClientExtendConfiguration  Put it in ComponentScan Context ( The default is the package of the startup class and its child packages ) outside !!!

Summary and contrast

  • programme 1: Is the easiest way to think of playing .
  • programme 2: It is my favorite plan at present . First, it's simple , And they are all reused Nacos/Ribbon Existing code —— and Ribbon/Nacos They all come from the production environment of large companies , After rigorous production tests .
  • programme 3: It's too violent , hold Ribbon Overhead . This scenario , Throw to Ribbon When making load balancing choices ,List Only 1 Elements , No matter what algorithm is used to calculate , This element is always returned in the end !

reflection

since Nacos Client Already have the ability of load balancing ,Spring Cloud Alibaba Why should we integrate Ribbon Well ?

Personally think that , This is mainly to comply with Spring Cloud standard .Spring Cloud Commons There are subprojects  spring-cloud-loadbalancer , The project has developed standards , Used to adapt various client load balancers ( Although the current implementation only Ribbon, but Hoxton There will be alternative implementations ).

Spring Cloud Alibaba This standard has been followed , So integrated Ribbon, Instead of using Nacos Client Load balancing capabilities provided .

原网站

版权声明
本文为[A rookie is a great God]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/173/202206221924216308.html