Java architecture of the road - and the basic use internal algorithms (Micro Service Focus) ribbon of self-realization

Last return:

  The last time we mainly say, we use a registry nacos, as we namespace, grouping, cluster, version, etc. is how to use, and if so what? We now have three customer service and three service orders, how we should distribute it to those requests? All requests to a service? polling? Weights? This is how we solve these problems we would look at.

The main elements:

  How this is our main ribbon of use, there is a ribbon configuration of each distribution policy, then there how can we achieve our own distribution policy themselves.

Client load balancing:

  nginx everyone is familiar with, mentioned load balancing, our first thought must be nginx, his reverse proxy is very powerful, can achieve internal polling, weight, IP hash, URL hashing algorithm to distribute several our request, this is what we are familiar with load balancing, also known as server load balancing. So what is the client load balancing it? For small chestnut. *** We went to senior clubs, called a massage, in fact, this is a supervisor to make arrangements for a technician to you, not to your own choice, by the competent arrangements, we can understand the server load balancing.

  So if we went to *** senior clubs (due frequented, which are familiar to the technician force), also known as a massage, feeling good the last number by 89, dexterity, we directly select our 89 No. technician, not by superior assigned, is one of our independent choice, we have chosen the right person to give massages in our hearts, this operation, we can understand the client load balancing.

ribbon usage:

  ribbon in the middle to give us a choice of a role, but also eliminates the need for (compared to the previous blog code is) a lot of code, let's look at a simple example code.

  First, the easiest way to implement

  ①. Join our ribbon dependencies, placed directly under my parent project was.

<!-- 加入ribbon依赖包 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

  ②. Config configuration change user services (caller). @LoadBalanced

package com.user.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public  class ClientConfig {

    @LoadBalanced
    @Bean
    public Residual Template rest template () {
         return  new Rest Template ();
    }
}

  ③. Change the service user (the caller) calls the way, I have just mentioned will be much simpler code look simple in the end how much code

package com.user.clintOrder;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class UserController {
    @Autowired
    private Rest Template rest template;

    @GetMapping("/getOrder")
    public String getOrderData(){
        String forObject = restTemplate.getForObject("http://ribbon-order/getOrderData", String.class);
        System.out.println("forObject = " + forObject);
        return forObject;
    }
}

  ④. Start order service (the caller), start the service user (the caller)

   OK, the simplest example is complete.

  Second, the internal algorithm

  Just now I said the ribbon can do load balancing the client, and now we only have one service reflects not come out ah, we get more than a few are called end. Many small partners might not IDEA start multiple services, I am here by the way, will please ignore. Let's start a service order, then click the drop-down options for the upper left corner, is that you run the program, Debug program, where the end of the program, and then click Edit Configurations ..., after opening the Check Allow parenel run on it, remember port of the program, will not be a port conflict. Some IDEA version is checked to share.

   This time we start three orders for service, and then look at the visits. First visit, call our order service service port 8082, and the second is our 8083, 8081 is our third time ...... you can see this is a poll rules to call .

   严格意义来说也不算是轮询的,如果你是亚马逊的服务器,你就不会体验的轮询了,我们接下来看一下都有什么算法。

   我知道的有这7中算法。

  ①.RoundRobinRule 轮询选择, 轮询index,选择index对应位置的Server。轮询

  ②.ZoneAvoidanceRule(默认是这个),复合判断Server所在Zone的性能和Server的可用性选择Server,在没有Zone的情况下类是轮询。你在国内可以理解为轮询。轮询

  ③.RandomRule,随机选择一个Server)。随机

  ④.RetryRule,对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功, 则一直尝试使用subRule的方式选择一个可用的server. 

  ⑤.AvailabilityFilteringRule,过滤掉一直连接失败的被标记为circuit tripped的后端Server,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个Server的运行状态

  ⑥.BestAvailableRule,选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过。并发

  ⑦.WeightedResponseTimeRule,根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低。响应时间长短

  那么这么多的算法,我们来如何配置呢?我们来改一下我们的config配置

package com.user.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ClientConfig {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    /**
     * 选择ribbon算法
     * @return
     */
    @Bean
    public IRule chooseAlgorithm(){
        return new RandomRule();
    }
}

  这样我们就更改了我们的默认算法,改为了随机选择的算法,我就不用上图给你们看测试结果了,我这测过了,好用~!我这是8081->8081->8081->8083->8082->8082,完全没有规律,是随机的。

  三、细粒度配置

  说到这还没完,我们来看一个需求,我们用户服务需要轮询调用积分服务,且需要随机调用订单服务,怎么来配置?我们下面来看一下如何细粒度的来配置。

  首先我们在用户服务下面添加两个类,注意不要被spring扫描到。

package com.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;public class IntegralConfig {

    /**
     * 选择积分服务算法
     * @return
     */public IRule chooseAlgorithm(){
        return new RoundRobinRule();
    }
}
package com.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
public class OrderConfig {

    /**
     * 选择订单服务算法
     * @return
     */public IRule chooseAlgorithm(){
        return new RandomRule();
    }
}

  然后在我们的主配置类的加入注解,就是说我们的遇到ribbon-order服务调用OrderConfig类下面的算法,我们的遇到ribbon-logistics服务调用LogisticsConfig类下面的算法,

package com.user.config;

import com.config.IntegralConfig;
import com.config.OrderConfig;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
@RibbonClients(value={
        @RibbonClient(name="ribbon-order",configuration = OrderConfig.class),
        @RibbonClient(name="ribbon-integral",configuration = IntegralConfig.class)
})
public class ClientConfig {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
}

  这是其中的一种配置,下面还有一种配置比较简单,在配置文件中加入算法就可以了,但是切记,不要两种同时使用,容易乱。

ribbon-order:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
ribbon-integral:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule

  我们先来实测一下我们的配置是否真的可用,先测试第一种注解+外部配置文件的,轮询积分服务,随机订单服务,我们先验证积分。第一次访问积分服务

第二次访问积分服务

第三次访问积分服务

第四次访问积分服务

积分服务是轮询的,说明积分服务算法配置正确,我们再来看一下订单服务。第一访问订单服务

 

第二次访问订单服务

第三次访问订单服务

第四和第五次访问订单服务

吓死我了,以为配置失败了呢,害得我第四次和第五次同时访问的,可以看出来12311这个顺序是一个随机的,我们再来验证一下我们的配置文件方式。

我们的订单服务的结果是:8083->8083->8083->8082->8082->8082->8082,可以看出来我们的订单是一个随机数。

我们的积分服务的结果是:9081->9082->9083->9081->9082->9083->9081,可以看出来我们的积分是一个轮询的算法。

   这里我说到了两种配置,再次强调一次,强烈不建议两种配置同时使用,你会乱的,建议使用配置文件的方式(不需要考虑文件是否被spring扫描到的问题),其次使用外部配置文件,不让spring扫描到的方式。

  四、ribbon参数解析

ribbon.MaxAutoRetries=1 #每一台服务器重试的次数,不包含首次调用的那一次 
ribbon.MaxAutoRetriesNextServer=2 #重试的服务器的个数,不包含首次调用的那一台实例 
ribbon.OkToRetryOnAllOperations=true #是否对所以的操作进行重试(True 的话,会对pos、 put操作进行重试,存在服务幂等问题) 没事别配置这个玩意,是个坑
ribbon.ConnectTimeout=3000 # 建立连接超时 
ribbon.ReadTimeout=3000 # 读取数据超时

  也可以为每个Ribbon客户端设置不同的超时时间, 通过服务名称进行指定:

  ribbon-order.ribbon.ConnectTimeout=2000

  ribbon-integral.ribbon.ReadTimeout=5000

  详细地址http://c.biancheng.net/view/5356.html

  开启饥饿加载,用来解决Ribbon第一次调用耗时高的配置

ribbon.eager-load.enabled=true #开启Ribbon的饥饿加载模式
ribbon.eager-load.clients=ribbon-order #指定需要饥饿加载的服务名

   五、自定义规则策略(权重)

   我们的ribbon并没有我们平时用的权重算法,所以我们还是需要自己来实现权重算法的,首先新建一个规则类,然后集成我们的AbstractLoadBalancerRule,重写我们的choose方法。

package com.user.myRule;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.beans.factory.annotation.Autowired;

public class WeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;


    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        //读取配置文件并且初始化,ribbon内部的 几乎用不上
    }

    @Override
    public Server choose(Object key) {
        try {
            BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            //获取微服务的名称
            String serviceName = baseLoadBalancer.getName();

            //获取Nocas服务发现的相关组件API
            NamingService namingService =  discoveryProperties.namingServiceInstance();

            //获取 一个基于nacos client 实现权重的负载均衡算法
            Instance instance = namingService.selectOneHealthyInstance(serviceName);

            //返回一个server
            return new NacosServer(instance);
        } catch (NacosException e) {
            System.out.println("自定义负载均衡算法错误");
            e.printStackTrace();
        }
        return null;
    }
}

  然后按照我们配置算法的方式来配置一下我们的自定义算法,首先写一个不会被spring扫描的类,在我们的配置中加入我们的注解,这次我们设置所有的服务都用我们的自定义算法

package com.config;

import com.netflix.loadbalancer.IRule;
import com.user.myRule.WeightedRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class WeightedConfig {

    /**
     * 选择权重算法
     * @return
     */public IRule chooseAlgorithm(){
        return new WeightedRule();
    }
}
@RibbonClients(defaultConfiguration = WeightedConfig.class)

启动我们的订单服务,这次我启动了两个订单服务,打开我们的nacos页面,设置权重

   从这个配置来看我们是几乎都是访问的8081这个服务的,我们来测试一下。我获取了10次,实际上也是有9次落到我们的8081的服务上

   六、自定义算法,同集群优先

  上次nacos我们留下了一个小问题,就是同一个集群的优先调用,需要我们自己来实现,上次没有说,今天说了ribbon,可以去说如何同集群优先调用了

package com.user.myRule;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class TheSameClusterPriorityRule extends AbstractLoadBalancerRule {

    public static final Logger log = LoggerFactory.getLogger(TheSameClusterPriorityRule.class);

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {

    }

    @Override
    public Server choose(Object key) {
        try {
            //第一步:获取当前服务所在的集群
            String currentClusterName = discoveryProperties.getClusterName();

            //第二步:获取一个负载均衡对象
            BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) getLoadBalancer();

            //第三步:获取当前调用的微服务的名称
            String invokedSerivceName = baseLoadBalancer.getName();

            //第四步:获取nacos clinet的服务注册发现组件的api
            NamingService namingService = discoveryProperties.namingServiceInstance();

            //第五步:获取所有的服务实例
            List<Instance> allInstance =  namingService.getAllInstances(invokedSerivceName);

            List<Instance> theSameClusterNameInstList = new ArrayList<>();

            //第六步:过滤筛选同集群下的所有实例
            for(Instance instance : allInstance) {
                if(StringUtils.endsWithIgnoreCase(instance.getClusterName(),currentClusterName)) {
                    theSameClusterNameInstList.add(instance);
                }
            }

            Instance toBeChooseInstance ;

            //第七步:选择合适的一个实例调用
            if(theSameClusterNameInstList.isEmpty()) {

                toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(allInstance);
                System.out.println("发生跨集群调用");
                log.info("发生跨集群调用--->当前微服务所在集群:{},被调用微服务所在集群:{},Host:{},Port:{}",
                        currentClusterName,toBeChooseInstance.getClusterName(),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
            }else {
                toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(theSameClusterNameInstList);
                System.out.println("同集群调用");
                log.info("同集群调用--->当前微服务所在集群:{},被调用微服务所在集群:{},Host:{},Port:{}",
                        currentClusterName,toBeChooseInstance.getClusterName(),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());

            }

            return new NacosServer(toBeChooseInstance);

        } catch (NacosException e) {
            log.error("同集群优先权重负载均衡算法选择异常:{}",e);
        }
        return null;
    }
}

总结:

  本次博客主要说了我们的ribbon的使用,以及我们的内部算法也简单说了一遍,后面的源码博客会具体去说内部的实现,我们还定义了我们自己的算法。

最进弄了一个公众号,小菜技术,欢迎大家的加入

Guess you like

Origin www.cnblogs.com/cxiaocai/p/12286041.html