SpringCloud(3)--服务调用(Ribbon、OpenFeign)

系列链接:


服务调用

Ribbon

Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端,主要功能是提供客户端的软件负载均衡算法和服务调用。

注:Ribbon虽然大规模运用,但目前也进入维护,基本上不准备更新了

负载均衡:将用户的请求平摊的分配到多个服务上,常见的负载均衡软件有Nginx等。

Ribbon本地负载均衡客户端与Nginx服务端负载均衡的区别:

  • Nginx是服务器负载均衡,客户端所有请求交给 Nginx,然后由 Nginx 实现转发请求。

  • Ribbon是本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到 JVM 本地,从而在本地实现 RPC 远程服务调用技术。

由上面的区别可以将负载均衡分为两种:

  • 集中式LB:在服务的消费方和提供方之间使用独立的LB设施(Nginx)
  • 进程内LB:将LB的逻辑继承到消费方,消费方从服务注册中心获知哪些提供方地址可用,然后自己进行选择(Ribbon)

Ribbon就是一个软负载均衡的客户端组件,就是负载均衡+RestTemplate调用,与Eureka结合的架构如下:
在这里插入图片描述


Ribbon的使用

  1. 我们之前实现了负载均衡就是基于 Ribbon,其实在使用eureka的新版本时,默认就集成了Ribbon。

    		<!--eureka client-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
    
  2. RestTemplate类

    • xxxForObject()方法:返回的是响应体中的数据
    • xxxForEntity()方法:返回的是entity对象,包含响应体数据、响应体信息(状态码等)

Ribbon常用负载均衡算法

Riboon使用 IRule 接口,根据特定算法从所有服务中,选择一个服务。而I Rule 接口有7个实现类。每个实现类代表一个负载均衡算法:

  • com.netflix.loadbalancer.RoundRobinRule:轮询
  • com.netflix.loadbalancer.RandomRule:随机
  • com.netflix.loadbalancer.RetryRule:先轮询,失败则在指定时间内进行重试
  • WeightedResponseTimeRule:对轮询的扩展,响应速度越快权重越大
  • BestAvailableRule:先过滤处于断路状态的服务,选择一个并发量最小的服务
  • AvailabilityFilteringRule:先过滤故障实例,再选择并发较小的实例
  • ZoneAvoidanceRule默认规则,根据Server所在区域的性能和可用性选择服务器。

修改Order模块

有两点需要注意:

  • 这里使用 Eureka 的相关服务

  • IRule 自定义配置类不能放在@ComponentScan所扫描的当前包以及子包下

    (有@SpringBootApplication注解的Main所在的包)

  1. 额外创建一个包
    image-20200711151134933

  2. 创建配置类,指定负载均衡算法

            @Configuration
            public class MySelfRule {
                @Bean
                public IRule myRule(){
                    return new RandomRule();//随机负载均衡算法
                }
            }
    
  3. 在主启动类上加@RibbonClient注解,表示访问CLOUD-PAYMENT-SERVICE服务时,使用我们自定义的负载均衡算法

            @SpringBootApplication
            @EnableEurekaClient
            @RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
            public class OrderMain80 {
                public static void main(String[] args){
                    SpringApplication.run(OrderMain80.class,args);
                }
            }
    

自定义负载均衡算法

ribbon的轮询算法原理

rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务器重启计数从1开始。

自定义负载均衡算法

实现轮询方式的负载均衡算法。

  1. 给pay模块(8001,8002)的controller方法添加一个方法,返回当前节点端口

            @GetMapping("/lb")
            public String getPaymentLB(){
                return serverPort;
            }
    
  2. 修改order模块,去掉ApplicationContextConfig中的 @LoadBalanced 注解,以及Main上的@RibbonClient注解

  3. 自定义接口

            public interface LoadBalancer {
                //从服务列表中选取一个服务
                ServiceInstance instances(List<ServiceInstance> serviceInstances);
            }
    
  4. 接口实现类

            @Component
            public class MyLB implements LoadBalancer {
    
                private AtomicInteger atomicInteger = new AtomicInteger(0);
    
                private final int getAndIncrement(){
                    int current;
                    int next;
                    do{
                        current = this.atomicInteger.get();
                        next = current >= Integer.MAX_VALUE ? 0 : current+1;
                    }while(!this.atomicInteger.compareAndSet(current,next));
                    System.out.println("****next: "+next);
                    return next;
                }
    
                @Override
                public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
                    int index = getAndIncrement() % serviceInstances.size();
                    return serviceInstances.get(index);
                }
            }
    
  5. 修改controller:

            @Resource
            private LoadBalancer loadBalancer;//自定义的
            @Resource
            private DiscoveryClient discoveryClient;
    
            @GetMapping("/payment/lb")
            public String getPaymentLB(){
                List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
                if(serviceInstances == null || serviceInstances.size() <= 0){
                    return null;
                }
                ServiceInstance instance = loadBalancer.instances(serviceInstances);
                URI url = instance.getUri();
                return restTemplate.getForObject(url+"/payment/lb",String.class);
            }
    
  6. 启动服务,测试接口
    image-20200711161024303

OpenFeign

Feign是一个声明式WebService客户端,服务调用只需在自己的接口上添加相应的注解。其简化了使用 Spring Cloud Ribbon 时,自动封装服务调用客户端的开发过程,其实 Feign 底层集成了 Ribbon 和 RestTemplate。

Feign与OpenFeign区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNbFY4C1-1594459297952)(https://hw.mokespace.cn/images/2020/07/11/242dd143bf8a766ead4941bef46bf3b0.png)]


OpenFeign的使用

  1. 新建一个order项目,用于feign测试,名称为 cloud-consumer-feign-order80

  2. pom文件,比原始的多一个openfeign依赖

    		<!-- openfeign -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
    
  3. 配置文件

            server:
              port: 80
    
            eureka:
              client:
                register-with-eureka: false
                service-url:
                  defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
    
  4. 主启动类

            @SpringBootApplication
            @EnableFeignClients //开启
            public class OrderFeginMain80 {
                public static void main(String[] args){
                    SpringApplication.run(OrderFeginMain80.class,args);
                }
            }
    
  5. fegin需要调用的其他的服务的接口

    @FeignClient(value = "CLOUD-PAYMENT-SERVICE")
    public interface PaymentService {
    
        @GetMapping("/payment/get/{id}")
        public CommonResult getPaymentById(@PathVariable("id") Long id);
    }
    
  6. controller

            @RestController
            @Slf4j
            @RequestMapping("/consumer")
            public class OrderFeginController {
    
                @Resource
                private PaymentService paymentService;
    
                @GetMapping("/payment/get/{id}")
                public CommonResult<Payment> getPyamentById(@PathVariable("id")Long id){
                    return paymentService.getPaymentById(id);
                }
            }
    
  7. 测试:启动两个erueka(7001,7002)、两个pay(8001,8002)、当前的order模块


OpenFeign超时机制

首先,OpenFeign 默认等待时间是1秒,超过1秒会直接报错,如下图:
image-20200711170507739

设置超时时间,修改配置文件

因为OpenFeign的底层是ribbon进行负载均衡,所以它的超时时间是由ribbon控制,所以在配置文件对 ribbon 进行配置:

        ribbon:
          # 建立连接后,在服务端读取数据的时间
          ReadTimeout: 5000
          # 建立连接所需的时间
          ConnetTimeout: 5000

OpenFeign日志

Feign提供了日志打印的功能,可以调整日志级别来了解接口的调用情况。

OpenFeign的日志级别有:

  • NOEE:默认,不显示日志
  • BASIC:仅记录请求方法、URL、响应状态码以及执行时间
  • HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息
  • FULL:除了 HEADERS 中定义的信息外,还有请求和响应的正文以及元数据

使用OpenFeign的日志:

  1. 实现在配置类中添加OpenFeign的日志类

            @Configuration
            public class FeignConfig {
                @Bean
                public Logger.Level feignLoggerLevel(){
                    return Logger.Level.FULL;//日志级别
                }
            }
    
  2. 为指定类设置日志级别,配置文件中

            logging:
              level:
                # feign 日志以什么级别监控哪个接口
                com.moke.springcloud.service.PaymentService: debug
    
  3. 启动服务即可
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/MOKEXFDGH/article/details/107287927