Spring Cloud-Ribbon负载均衡&Feign声明式服务调用

目录

一、Ribbon负载均衡

        1.1 什么是负载均衡

         1.2 自定义实现负载均衡

       1.3 Ribbon介绍

        1.4 基于ribbon实现负载均衡

       1.5 Ribbon的问题

二、声明式服务调用Feign

        2.1 背景

        2.2 Feign概述

         2.3 Feign入门程序

         2.4 feign原理概述

        2.5 feign接口传参的三种方式

         2.6  OpenFeign性能优化

          1、日志增强

         2、http连接池

         3、gzip压缩

         4、feign超时处理


一、Ribbon负载均衡

        1.1 什么是负载均衡

        通俗的讲,负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。

         1.2 自定义实现负载均衡

          1.2.1 创建两个服务提供者provider

           

           1.2.2 配置文件application.yml设置不同的端口号

#ribbon_provider_1
server:
  port: 9090
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.57.132 #nacos服务的地址
  application:
    name: ribbon-provider #向注册中心注册的名字

#ribbon_provider_2
server:
  port: 9091
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.57.132 #nacos服务的地址
  application:
    name: ribbon-provider #向注册中心注册的名字

         1.2.3 创建服务消费者consumer

        1.2.4  配置文件application.yml

server:
  port: 80
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.57.132 #nacos服务的地址
  application:
    name: ribbon-consumer #向注册中心注册的名字

           1.2.5 controller     

@RestController
@RequestMapping(value = "/consumer")
public class ConsumerController {
	
	//发送Rest请求的工具类
	@Autowired
	private RestTemplate restTemplate;
	//发现服务的工具类
	@Autowired
	private DiscoveryClient discoveryClient;

	private int index;

	@RequestMapping("/getService/{id}")
	public User getServic(@PathVariable Integer id){
		//获取所有服务
		List<String> serviceList = discoveryClient.getServices();
		//随机方式获得服务
		//int currentIndex = new Random().nextInt(serviceList.size());
		//轮询方式获得服务
		index=index + 1;
		int currentIndex = (index) % serviceList.size();

		ServiceInstance serviceInstance = discoveryClient.getInstances("ribbon-provider").get(currentIndex);
		String url = "http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/provider/getUserById/"+id;
		return restTemplate.getForObject(url, User.class);

	}
}

//RestTemplate工具类
@Configuration
public class ConfigBean {
	@Bean
	public RestTemplate restTemplate(){
		return new RestTemplate();
	}
}

        1.2.6 测试: 分别使用轮询和随机策略调用服务提供者

       1.3 Ribbon介绍

        1.3.1 什么是Ribbon

        Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。

        我们不需要去引入ribbon的依赖,因为在nacos里面已经集成了ribbon的依赖  

        Ribbon默认提供 很多种负载均衡算法,例如轮询、随机 等等。

        1.3.2 负载均衡策略

        负载均衡接口:com.netflix.loadbalancer.IRule

        1.3.2 随机策略 

Ribbon的默认内置了7种负载均衡策略:

1、RoundRobinRule

  轮询策略,Rabbon默认采用的策略,若经过一轮轮询没有找到可用的provider(提供者),其最多轮询10轮,若最终

      没有找到,则返回NULL。

      举例:当前有3个提供者A,B,C,先挨个轮询1遍,A,B,C都不访问(1轮),在A,B,C访问一遍(2轮次),一共试10轮

                如果还不能访问,则返回NULL。

2、RandomRule

  随机策略,从所有可用的provider(提供者)中选择一个。

3、RetryRule

  重试策略,先按照RoundRobinRule策略获取provider(策略者)能获取到直接返回,若获取失败,则在指定的时限内重试,

       默认的时限为500毫秒。【RoundRobinRule轮询策略,默认是10轮,而RetryRule我给你500毫秒,你可以一直重试,直到找到为止】

4、BestAvailableRule

  最可用策略。选择并发量最小的provider(提供者), 即连接的消费者数量最少的provider 。

5、AvailabilityFilteringRule

  可用过滤算法。该算法规则是:过滤掉处于熔断状态的provider与已经超过连接极限的provider,对剩余provider采用轮询策略。

6、ZoneAvoidanceRule

  zone回避策略。根据provider所在zone及provider的可用性,对provider进行选择。

7、WeightedResponseTimeRule

  “权重响应时间”策略。根据每个provider的平均响应时间计算其权重,响应时间越快权重越大,被选中的机率就越高。 

     在刚启动时采用轮询策略。后面就会根据权重进行选择了。

        1.4 基于ribbon实现负载均衡

         1.4.1 ConfigBean

@Configuration
public class ConfigBean {

	@Bean
	/**
	 * 添加了@LoadBalanced注解之后,Ribbon会给restTemplate请求添加一个拦截器,在拦截器中获取
	 * 注册中心的服务列表,并使用Ribbon内置的负载均衡算法从服务列表里选中一个服务,通过获取到的服务信息        * (ip,port)替换 serviceId 实现负载请求。
	 */
	@LoadBalanced //开启负载均衡
	public RestTemplate getRestTemplate(){
		return new RestTemplate();
	}

	//随机策略
	@Bean
	public IRule iRule() {
		return new RandomRule();
	}
}

        1.4.2 Controller

@RestController
@RequestMapping(value = "/consumer")
public class ConsumerController {
	
	//发送Rest请求的工具类
	@Autowired
	private RestTemplate restTemplate;

    @RequestMapping("/get/{id}")
	public User getUser(@PathVariable Integer id){
        //ribbon-provider为服务要访问的名字即可,Ribbon会把服务名替换成ip和端口号
		String url = "http://ribbon-provider/provider/getUserById/"+id;
		return restTemplate.getForObject(url,User.class);
	}
}

        1.4.3 测试 

        分别使用轮询和随机策略调用服务提供者

       1.5 Ribbon的问题

         ①拼接url和参数比较麻烦

         ②默认不能剔除死亡的服务(如果其中的一台服务宕机了,浏览器会访问失败)

二、声明式服务调用Feign

        2.1 背景

        当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻

那么有没有更好的解决方案呢?答案是确定的有,Netflix已经为我们提供了一个框架:Feign。

        2.2 Feign概述

        Feign是Spring Cloud提供的声明式、模板化的HTTP客户端, 它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。

        Spring Cloud集成Feign并对其进行了增强,使Feign支持了Spring MVC注解;Feign默认集成了Ribbon,所以Fegin默认就实现了负载均衡的效果。

         2.3 Feign入门程序

          2.3.1 创建服务提供者

           application.yml

server:
  port: 9090
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.57.132:8848 #nacos服务的地址
  application:
    name: feign-provider #向注册中心注册的名字

           创建controller和service

//controller
@RestController
@RequestMapping("/provider")
public class ProviderController {
    @Autowired
    private UserService userService;

    @RequestMapping("/getUserById")
    public User getUserById(@RequestParam("id") Integer id){
        return userService.getUserById(id);
    }
}
//servic接口
public interface UserService {
    User getUserById(Integer id);
}
//UserService 实现类
@Service
public class UserServiceImpl implements UserService {

    @Override
    public User getUserById(Integer id) {
        return new User(id,"彭于晏-1",23);
    }
}

        2.3.2 创建feign接口

         pom.xml依赖

    <dependencies>
        <!--Spring Cloud OpenFeign Starter -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>springcloud_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

        创建接口

@FeignClient(value = "feign-provider")//value属性写服务名字
@RequestMapping("/provider")//对应提供者的requestmapping
public interface UserFeign {
    @RequestMapping("/getUserById")
    User getUserById(@RequestParam("id") Integer id);
}

         2.3.3 创建消费者

 <dependencies>
        <!--nacos客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入feign接口-->
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>feign_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
</dependencies>

        在启动器上加入@EnableFeignClients注解扫描feign接口

        在controller调用feign接口 

 

         测试

         2.4 feign原理概述

          1、将feign接口的代理类扫描到Spring容器中:
        @EnableFeignClients开启feign注解扫描:FeignClientsRegistrar.registerFeignClients()扫描被 @FeignClient标识的接口生成代理类,
        并把接口和代理类交给Spring的容器管理。
          2、为接口的方法创建RequestTemplate
        当consumer调用feign代理类时,代理类会调用SynchronousMethodHandler.invoke()创建RequestTemplate(url,参数)
          3、发出请求
        代理类会通过RequestTemplate创建Request,然后client(URLConnetct、HttpClient、OkHttp)使用Request发送请求

        2.5 feign接口传参的三种方式

        1、路径拼接?传参,在参数前加入@RequestParam("id")注解:

        2、restful传参,在参数前加入@PathVariable("id")注解:

        3、pojo,在参数前加入@RequestBody注解:

         2.6  OpenFeign性能优化

          1、日志增强

        OpenFeign 虽然提供了日志增强功能,但是默认是不显示任何日志的,不过开发者在调试阶段可以自己配置日志的级别。

        OpenFeign 的日志级别如下:

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

        1.1、配置文件application.yml 文件中设置日志的开启:

#com.bjpowernode.feign包下接口的日志级别
logging:
  level:
    com.bjpowernode.feign: debug


#OpenFeign 的日志级别
feign:
  client:
    config:
      default:#此处写服务名字或default默认的
        loggerLevel: full

         控制台打印的日志效果

         1.2、配置类中配置 OpenFeign 日志级别

        自定义一个配置类,在其中设置日志级别,这样是全局的配置


@Configuration
public class FeignConfiguration {
    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

         局部配置,在客户端接口指定此配置

configuration = FeignConfiguration.class

@FeignClient(value = "feign-provider", fallbackFactory = RemoteUserFallbackFactory.class, configuration = FeignConfiguration.class)
public interface UserFeign{
 
    @RequestMapping("/getUserById")
    User getUserById(@RequestParam("id") Integer id);

}

         2、http连接池

        两台服务器建立HTTP连接的过程涉及到多个数据包的交换,很消耗时间。采用HTTP连接池可以节约大量的时间提示吞吐量。

Feign的HTTP客户端支持3种框架:HttpURLConnectionHttpClientOkHttp

        观察源码可以发现,Feign 默认是采用java.net.HttpURLConnection 的,每次请求都会建立、关闭连接。

        2.1、在feign接口工程添加依赖

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

         2.2、 配置文件中开启

feign:
  # 开启 httpclient
  httpclient:
    enabled: true

        2.3、如何验证是否开启了呢

        在feign.SynchronousMethodHandler#executeAndDecode()这个方法中可以清楚的看出调用哪个 client。
        没开启之前是这样的:

        开启之后:

         3、gzip压缩

        介绍

        gzip 是一种数据格式,采用用 deflate 算法压缩 data;gzip 是一种流行的文件 压缩算法,应用十分广泛,尤其是在 Linux 平台。

        能力

        当 Gzip 压缩到一个纯文本文件时,效果是非常明显的,大约可以减少 70% 以上的文件大小。 

        作用: 

        网络数据经过压缩后实际上降低了网络传输的字节数,可以加快网页加载的速度,改善用户体验

        在消费者配置文件中开启gzip压缩

server:
  compression:
    enabled: true #开启gzip压缩

 

         4、feign超时处理

         在访问网页的时候,经常会出现网络不好导致访问超时。如果后台没有设置网络超时处理,则超时一秒并且没有设置日志输出,后台就会抛出这样的异常:

         在consumer的配置文件中可以这样设置:

1、方式一:全局的处理
ribbon:
  ConnectTimeout: 5000 #请求连接的超时时间
  ReadTimeout: 5000 #请求处理的超时时间

2、方式二:
  feign:
    client:
      config:
        feign-provider:#这里可以写服务名,或者default默认的,全局的处理
          ConnectTimeout: 5000 #请求连接的超时时间
          ReadTimeout: 5000 #请求处理的超时时间

        通过以上设置,就可以进行超时处理了!!!

猜你喜欢

转载自blog.csdn.net/m0_71560190/article/details/126352625