10.rabbion负载均衡和服务调用

spring cloud ribbon是基于NetflixRibbon实现的一套客户端(注意是客户端)负载均衡工具

LB负载均衡(Load Balance)是什么
    简单的说就是将用户的请求平摊到多个服务器上,从而达到系统的HA(高可用)
    常见的负载均衡有软件Nginx,LXS,硬件F5等
    
Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别
    1.Nginx是服务器负载均衡,客户所有请求都会交给Nginx,然后由Nginx实现转发请求。即负载均衡是由服务端实现的。
    2.Ribbon是本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册服务列表后缓存到JVM本地,从而在本地实现RPC远程服务调用技术!
    
集中式LB:
    即在服务的消费方换和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如Nginx),由该设施负责将访问请求通过某种策略转发到
    服务的提供方!

进程内LB:
    将LB逻辑集成到消费方,消费方从服务注册中心获知到有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器!
    Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它获取到服务提供方的地址!
Ribbon其实就是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例!
Ribbon在工作时分成两步:
    1.先选择EurekaServer,它优先选择在同一个区域内负载较少的server
    2.再根据用户指定的策略,在从server渠道的服务注册列表中选择一个地址
其中的ribbon提供了多种策略;比如轮询、随机和根据响应时间加权等等!

总结:重要
    ribbon:就是负载均衡+RestTemplate调用

RestTemplate的常用方法解析:

1.返回Object对象
2.getForEntity()返回的是ResponseEntity对象

Rabbion的核心组件IRule接口:
    IRule:根据特定算法从服务列表中选取一个要访问的服务(有以下7种算法)
        1.com.netflix.loadbalancer.RoundRobinRule:轮询
        2.com.netflix.loadbalancer.RandomRule:随机
        3.com.netflix.loadbalancer.RetryRule:先安装RoundRobinRule的策略获取服务,如果获取服务失败,则在指定时间内进行重试,获取可用服务
        4.WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的示例选择权重越大,月容易被选中
        5.BestAvailableRule:会先过滤掉多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
        6.AvailabilityFilteringRule:会先过滤掉故障实例,再选择并发较小的实例
        7.ZoneAvoidanceRule:默认规则,复合判断Server所在的区域的性能和server的可用性选择服务器


重点1:ribbon的负载均衡策略更换(ribbon的默认负载算法是轮询)
    pom文件的解释:
        因为在客户端引入了:
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
        spring-cloud-starter-netflix-eureka-client里包含有
            <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
              <version>2.2.1.RELEASE</version>
              <scope>compile</scope>
            </dependency>
    如图所示:无需再单独引入ribbon的包

1.注意点,ribbon选择自己的负载方法时,需要创建自己的配置类,该配置类不能在springboot的包扫描范围内!

1.在自定义配置类中代码如下:
    @Configuration
    public class MyRule {
        @Bean
        public RandomRule getRandomRule()
            //重点1:随机策略
            return new RandomRule();
        }
    }
    
2.在springboot启动类上加上该注解:@RibbonClient  1.value:表示调用的服务在eureka的注册名称,configuration:是指自定义的配置类位置
    @SpringBootApplication
    @EnableEurekaClient
    @RibbonClient(value = "CLOUD-PROVIDER",configuration = MyRule.class)
    public class OrderMain80 {
        public static void main(String[] args) {
                SpringApplication.run(OrderMain80.class, args);
            }
    }

 

如何编写自己的负载均衡

原理解析:
    1.主要用到的是:List<ServiceInstance> instances =discoveryClient.getInstances(serverName)根据服务注册中心中的注册服务名称获取服务实例
    2.根据下表获取到具体的ServiceInstance服务实例,String uri = instances.get(index).getUri().toString(),获取远程调用的uri即(http://192.168.137)
    3.需要考虑的是代表访问次数的数字线程安全问题,因为会有多个客户端调用更改该数字:这里涉及到了CAS和自旋解锁等问题,等后面学习了JUC会明白
    
具体的代码设计如下:设计自己的轮询算法
    1.负载均衡的接口
        //传入远程服务的名称,获取到具体服务实例的URI
        public interface MyLB {
            String getUri(String serverName);
        }
    2.接口实现如下:这就是简单的轮询算法,可以在此基础上加上自己的逻辑等等,
        但重点是:
            1.discoveryClient获取服务相关信息
            2.AtomicInteger:CAS和自旋解锁等JUC知识
        @Component
        @Slf4j
        public class MyLbImp implements MyLB{
            //重点1:使用discoveryClient获取服务相关信息
            @Resource
            private DiscoveryClient discoveryClient;
            //重点2:使用AtomicInteger线程安全的去自增数字
            private AtomicInteger atomicInteger;
        
            public MyLbImp() {
                this.atomicInteger = new AtomicInteger(0);
            }
        
            @Override
            public String getUri(String serverName) {
                List<ServiceInstance> instances = discoveryClient.getInstances(serverName);
                int size = instances.size();
                int current;
                int next;
                do {
                    current = atomicInteger.get();
                    next = current >= Integer.MAX_VALUE ? 0 : current + 1;
                } while (!atomicInteger.compareAndSet(current, next));
                log.info("current:" + current + " next:" + next);
                int index = next % size;
                String uri = instances.get(index).getUri().toString();
                log.info("访问URI:" + uri);
                return uri;
            }
        }
    3.控制层调用:
        @Resource
        private MyLB myLB;
        public CommResult queryUserById(@PathVariable("id") int id){
            log.info("客户端根据id查询用户,用户id:"+id);
            //重点1:根据服务名称获取对应的轮询访问地址!
            String uri=myLB.getUri("PRODUCER-USER-API") + "/producer/queryUserById/" + id;
            log.info("访问路径:"+uri);
            CommResult result = restTemplate.getForObject(uri, CommResult.class);
            return  result;
        }

 

7种负载均衡策略详解:https://blog.csdn.net/sufu1065/article/details/123492451

posted @ 2022-05-29 16:58  努力的达子  阅读(665)  评论(0编辑  收藏  举报