一、Ribbon简介

  1. RibbonNetflix发布的负载均衡器,它有助于控制HTTPTCP的客户端的行为。为Ribbon配置服务提供者地址后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多负载均衡算法,例如轮询、随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。<br /> Ribbon是一套**客户端负载均衡**工具,供一系列的完善的配置,如超时,重试等。通过Load Balancer获取到服务提供的所有机器实例, Ribbon会自动基于某种规则(轮询,随机)去调用这些服务。<br /> **客户端负载均衡**<br />![e18910c7a8d74c278917b96d58bfc5fd.png](https://cdn.nlark.com/yuque/0/2023/png/35893726/1687748994385-015df002-046a-417f-a041-aa2b42f82998.png#averageHue=%23f5ece2&clientId=ua64ef278-9c36-4&from=paste&height=610&id=ue848de11&originHeight=610&originWidth=871&originalType=binary&ratio=1&rotation=0&showTitle=false&size=191457&status=done&style=none&taskId=ua0cd7dbc-9418-4ace-bdfc-f81800f1115&title=&width=871)<br /> **服务端负载均衡**<br />![b8beaaa44f3a44a088bea7ec6441cd5a.png](https://cdn.nlark.com/yuque/0/2023/png/35893726/1687749008286-f76c00e1-791a-4d6e-8b58-80d6ac04dbf4.png#averageHue=%23f6e6d7&clientId=ua64ef278-9c36-4&from=paste&height=548&id=u07f26170&originHeight=548&originWidth=924&originalType=binary&ratio=1&rotation=0&showTitle=false&size=161841&status=done&style=none&taskId=ueb083392-3d5c-4873-b232-7a3f329908e&title=&width=924)

二、Ribbon与SpringCloud整合

  1. 由上面可以知道,Ribbon是用于客户端负载均衡的工具。Ribbon只负责负载均衡,那么发送请求,还需要Spring提供的[RestTemplate](https://so.csdn.net/so/search?q=RestTemplate&spm=1001.2101.3001.7020)。只不过,对RestTemplate进行一下加强,控制其把请求发送到哪个服务器上。

1、RestTemplate简介

  1. 发送 http 请求,估计很多人用过 httpclient okhttp,确实挺好用的,而 Spring web 中的 RestTemplate 和这俩的功能类似,也是用来发送 http 请求的,不过用法上面比前面的 2 位要容易很多。<br /> spring 框架提供的 RestTemplate 类可用于在应用中调用 rest 服务,它简化了与 http 服务的通信方式,统一了 RESTful 的标准,封装了 http 链接, 我们只需要传入 url 及返回值类型即可。相较于之前常用的 HttpClientRestTemplate 是一种更优雅的调用 RESTful 服务的方式。<br /> RestTemplate详细用法参考: [一文吃透接口调用神器RestTemplate](https://blog.csdn.net/likun557/article/details/121072832?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167851613916782428653627%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=167851613916782428653627&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-121072832-null-null.article_score_rank_blog&utm_term=%E4%B8%80%E6%96%87%E5%90%83%E9%80%8F%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E7%A5%9E%E5%99%A8RestTemplate&spm=1018.2226.3001.4450)<br /> **RestTemplate发送get请求**<br />Controller接口
  1. @GetMapping("/test/get")
  2. @ResponseBody
  3. public BookDto get() {
  4. return new BookDto(1, "SpringMVC系列");
  5. }

使用 RestTemplate 调用上面这个接口,通常有 2 种写法,如下

  1. @Test
  2. public void test1() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/get";
  5. // getForObject方法,获取响应体,将其转换为第二个参数指定的类型
  6. BookDto bookDto = restTemplate.getForObject(url, BookDto.class);
  7. System.out.println(bookDto);
  8. }
  9. @Test
  10. public void test2() {
  11. RestTemplate restTemplate = new RestTemplate();
  12. String url = "http://localhost:8080/chat16/test/get";
  13. /**
  14. getForEntity方法,返回值为ResponseEntity类型
  15. ResponseEntity中包含了响应结果中的所有信息,比如头、状态、body
  16. */
  17. ResponseEntity<BookDto> responseEntity = restTemplate.getForEntity(url, BookDto.class);
  18. //状态码
  19. System.out.println(responseEntity.getStatusCode());
  20. //获取头
  21. System.out.println("头:" + responseEntity.getHeaders());
  22. //获取body
  23. BookDto bookDto = responseEntity.getBody();
  24. System.out.println(bookDto);
  25. }

2、引入Ribbon

Spring Cloud Alibaba Nacos 中已经内置了 Ribbon 框架,所以无需再单独引入。

  1. <!--Ribbon的依赖-->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
  5. </dependency>

默认情况下,使用RestTemplate是无需进行配置的,Spring会自动注入。但是如果配合Ribbon使用时,就需要手动声明了。

  1. @Configuration
  2. public class RestConfig {
  3. @Bean
  4. @LoadBalanced // 使RestTemplate自动支持Ribbon负载均衡
  5. public RestTemplate restTemplate() {
  6. return new RestTemplate();
  7. }
  8. }

3、服务调用

  1. 使用添加@LoadBalanced注解后的RestTemplate调用服务提供者的接口时,可以使用**虚拟IP**替代**真实IP**地址。所谓的虚拟IP就是服务提供者在application.propertiesyml文件中配置的spring.application.name属性的值。
  1. @RestController
  2. public class ConfigClientController {
  3. @Autowired
  4. private RestTemplate restTemplate;
  5. // 客户端注册到Nacos的服务名称
  6. private static final String client_name = "client";
  7. @GetMapping("/server")
  8. public String getConfigInfo1() {
  9. return restTemplate.getForObject("http://" + client_name + "/ribbon", String.class);
  10. }
  11. }

4、搭建环境

  • 服务端:Server 端口9090
  • 客户端1:Client 端口9080
  • 客户端2:Client 端口9081

实现效果: 服务端Server调用客户端,轮训调用客户端1、客户端2,达到负载均衡的效果。

4.1 客户端

客户端整合Nacos,并且两个客户端的application.name相同,为了后续实现负载均衡的效果。
image-20230311144839079.png
image-20230311145046536.png

4.2 服务端

image-20230311145607222.png

4.3 启动服务

image-20230311150419819.png

4.4 访问接口

  1. 访问服务端接口: [http://localhost:9090/server](http://localhost:9090/server) , 返回结果: Client-01-9080 、Client-02-9081 实现了负载均衡效果。

5、Ribbon组件IRule

Ribbon默认的是RoundBobinRule(轮询)
20190802100110952.png

注意:Ribbon的自定义配置类不可以放在@ComponentScan所扫描的当前包下以及子包下,否则这个自定义配置类就会被所有的Ribbon客户端共享,达不到为指定的Ribbon定制配置,而@SpringBootApplication注解里就有@ComponentScan注解,所以不可以放在主启动类所在的包下。(因为Ribbon是客户端(消费者)这边的,所以Ribbon的自定义配置类是在客户端(消费者)添加,不需要在提供者或注册中心添加)

  1. @Configuration
  2. public class RestConfig {
  3. /**
  4. * 使RestTemplate自动支持Ribbon负载均衡
  5. */
  6. @Bean
  7. @LoadBalanced
  8. public RestTemplate restTemplate() {
  9. return new RestTemplate();
  10. }
  11. /**
  12. * 将Ribbon负载均衡机制改为 随机 (默认轮训)
  13. */
  14. @Bean
  15. public IRule myRule(){
  16. return new RandomRule();
  17. }
  18. }

6、自定义负载均衡算法

  1. @Configuration
  2. public class MySelfRule
  3. {
  4. @Bean
  5. public IRule myRule()
  6. {
  7. return new RandomRule_ZY(); // 我自定义为每台机器5次,5次之后在轮询到下一个
  8. }
  9. }

springboot主程序

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. //在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效
  4. @RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
  5. public class DeptConsumer80_App
  6. {
  7. public static void main(String[] args)
  8. {
  9. SpringApplication.run(DeptConsumer80_App.class, args);
  10. }
  11. }

自定义LoadBalance

  1. public class RandomRule_ZY extends AbstractLoadBalancerRule{
  2. // total = 0 // 当total==5以后,我们指针才能往下走,
  3. // index = 0 // 当前对外提供服务的服务器地址,
  4. // total需要重新置为零,但是已经达到过一个5次,我们的index = 1
  5. // 分析:我们5次,但是微服务只有8001 8002 8003 三台,OK?
  6. private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
  7. private int currentIndex = 0; // 当前提供服务的机器号
  8. public Server choose(ILoadBalancer lb, Object key){
  9. if (lb == null) {
  10. return null;
  11. }
  12. Server server = null;
  13. while (server == null) {
  14. if (Thread.interrupted()) {
  15. return null;
  16. }
  17. List<Server> upList = lb.getReachableServers(); //激活可用的服务
  18. List<Server> allList = lb.getAllServers(); //所有的服务
  19. int serverCount = allList.size();
  20. if (serverCount == 0) {
  21. return null;
  22. }
  23. if(total < 5){
  24. server = upList.get(currentIndex);
  25. total++;
  26. }else {
  27. total = 0;
  28. currentIndex++;
  29. if(currentIndex >= upList.size()){
  30. currentIndex = 0;
  31. }
  32. }
  33. if (server == null) {
  34. Thread.yield();
  35. continue;
  36. }
  37. if (server.isAlive()) {
  38. return (server);
  39. }
  40. // Shouldn't actually happen.. but must be transient or a bug.
  41. server = null;
  42. Thread.yield();
  43. }
  44. return server;
  45. }
  46. @Override
  47. public Server choose(Object key){
  48. return choose(getLoadBalancer(), key);
  49. }
  50. @Override
  51. public void initWithNiwsConfig(IClientConfig clientConfig){
  52. }
  53. }

三、OpenFeign简介

OpenFeign官网文档:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeign

1、Feign简介

Feign也是一个狠角色,Feign旨在使得Java Http客户端变得更容易。
Feign集成了Ribbon、RestTemplate实现了负载均衡的执行Http调用,只不过对原有的方式(Ribbon+RestTemplate)进行了封装,开发者不必手动使用RestTemplate调服务,而是定义一个接口,在这个接口中标注一个注解即可完成服务调用,这样更加符合面向接口编程的宗旨,简化了开发。
但遗憾的是Feign现在停止迭代了。

2、OpenFeign简介

  1. OpenFeignspringcloudFeign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign@FeignClient可以解析SpringMVC@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

3、Feign和OpenFeign的区别

Feign OpenFiegn
Feign是SpringCloud组件中一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服 OpenFeign 是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等。OpenFeign 的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

四、OpenFeign与SpringCloud整合

1、引入OpenFeign

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-openfeign</artifactId>
  4. </dependency>

启动会报错 No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?

原因是SpringCloud Feign在Hoxton.M2 RELEASED版本之后不再使用ribbon,而是使用spring-cloud-loadbalancer,所以在不引入spring-cloud-loadbalancer情况下会报错,需要单独引入loadbalancer

Spring Cloud LoadBalancer是Spring Cloud官方自己提供的客户端负载均衡器,抽象和实现,用来替代Ribbon。(Spring Cloud LoadBalancer源码解析)

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-loadbalancer</artifactId>
  4. </dependency>

完整的POM

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.6.11</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <modelVersion>4.0.0</modelVersion>
  12. <artifactId>Producer-OpenFeign-9090</artifactId>
  13. <properties>
  14. <java.version>1.8</java.version>
  15. <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
  16. <spring-cloud.version>2021.0.4</spring-cloud.version>
  17. </properties>
  18. <dependencies>
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-web</artifactId>
  22. </dependency>
  23. <dependency>
  24. <groupId>com.alibaba.cloud</groupId>
  25. <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>com.alibaba.cloud</groupId>
  29. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.cloud</groupId>
  33. <artifactId>spring-cloud-starter-openfeign</artifactId>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.springframework.cloud</groupId>
  37. <artifactId>spring-cloud-loadbalancer</artifactId>
  38. </dependency>
  39. <dependency>
  40. <groupId>org.springframework.cloud</groupId>
  41. <artifactId>spring-cloud-starter-bootstrap</artifactId>
  42. <version>3.1.1</version>
  43. </dependency>
  44. <dependency>
  45. <groupId>org.springframework.boot</groupId>
  46. <artifactId>spring-boot-starter-test</artifactId>
  47. <scope>test</scope>
  48. <exclusions>
  49. <exclusion>
  50. <groupId>org.junit.vintage</groupId>
  51. <artifactId>junit-vintage-engine</artifactId>
  52. </exclusion>
  53. </exclusions>
  54. </dependency>
  55. </dependencies>
  56. <dependencyManagement>
  57. <dependencies>
  58. <dependency>
  59. <groupId>org.springframework.cloud</groupId>
  60. <artifactId>spring-cloud-dependencies</artifactId>
  61. <version>${spring-cloud.version}</version>
  62. <type>pom</type>
  63. <scope>import</scope>
  64. </dependency>
  65. <dependency>
  66. <groupId>com.alibaba.cloud</groupId>
  67. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  68. <version>${spring-cloud-alibaba.version}</version>
  69. <type>pom</type>
  70. <scope>import</scope>
  71. </dependency>
  72. </dependencies>
  73. </dependencyManagement>
  74. <build>
  75. <plugins>
  76. <plugin>
  77. <groupId>org.apache.maven.plugins</groupId>
  78. <artifactId>maven-compiler-plugin</artifactId>
  79. <version>3.8.1</version>
  80. <configuration>
  81. <source>1.8</source>
  82. <target>1.8</target>
  83. <encoding>UTF-8</encoding>
  84. </configuration>
  85. </plugin>
  86. <plugin>
  87. <groupId>org.springframework.boot</groupId>
  88. <artifactId>spring-boot-maven-plugin</artifactId>
  89. </plugin>
  90. </plugins>
  91. </build>
  92. </project>

2、服务调用

2.1、启动类添加注解

  1. package com.example.demo;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  5. import org.springframework.cloud.openfeign.EnableFeignClients;
  6. @SpringBootApplication
  7. @EnableDiscoveryClient // 通过注解 开启nacos服务注册
  8. @EnableFeignClients // 整合openFeign
  9. public class ProducerOpenFeign9090Application {
  10. public static void main(String[] args) {
  11. SpringApplication.run(ProducerOpenFeign9090Application.class, args);
  12. }
  13. }

2.2、编写接口

  1. @Component
  2. @FeignClient(value = "consumer") // 被调用服务注册到Nacos的服务名称
  3. public interface ConsumerService {
  4. // 该接口和被调用者的Controller方法名、参数一致
  5. @GetMapping(value = "/test/{string}")
  6. public String getConfigInfo(@PathVariable(value = "string") String string);
  7. }

被调用服务的Controller

  1. @RestController
  2. public class Controller {
  3. @GetMapping("/test/{string}")
  4. public String getConfigInfo(@PathVariable(value = "string") String string) {
  5. return "Client-02-9080 " + string;
  6. }
  7. }

3、服务搭建

  • 生产者producer 端口9090
  • 消费者consumer 端口9080
  • 消费者consumer 端口9081

生产者—调用—消费者

3.1 生产者

image-20230313214037348.png

3.2 消费者

image-20230313213724360.png
image-20230313213832169.png

3.3 启动服务

image-20230313214407702.png

3.4 访问接口

  1. 访问服务端接口: [http://localhost:9090/server](http://localhost:9090/server) , 返回结果: 'Client-02-9080 OpenFeign测试' 、'Client-02-9081 OpenFeign测试' 实现了负载均衡效果。

4、OpenFeign传参方式

4.1 传递JSON数据

  1. @RestController
  2. @RequestMapping("/openfeign/provider")
  3. public class OpenFeignProviderController {
  4. @PostMapping("/order2")
  5. public Order createOrder2(@RequestBody Order order){
  6. return order;
  7. }
  8. }
  1. @FeignClient(value = "openFeign-provider")
  2. public interface OpenFeignService {
  3. /**
  4. * 参数默认是@RequestBody标注的,这里的@RequestBody可以不填
  5. * 方法名称任意
  6. */
  7. @PostMapping("/openfeign/provider/order2")
  8. Order createOrder2(@RequestBody Order order);
  9. }

4.2 POJO表单传参

openFeign提供了一个注解@SpringQueryMap完美解决POJO表单传参。

  1. @RestController
  2. @RequestMapping("/openfeign/provider")
  3. public class OpenFeignProviderController {
  4. @PostMapping("/order1")
  5. public Order createOrder1(Order order){
  6. return order;
  7. }
  8. }
  1. @FeignClient(value = "openFeign-provider")
  2. public interface OpenFeignService {
  3. /**
  4. * 参数默认是@RequestBody标注的,如果通过POJO表单传参的,使用@SpringQueryMap标注
  5. */
  6. @PostMapping("/openfeign/provider/order1")
  7. Order createOrder1(@SpringQueryMap Order order);
  8. }

4.3 URL中携带参数

  1. @RestController
  2. @RequestMapping("/openfeign/provider")
  3. public class OpenFeignProviderController {
  4. @GetMapping("/test/{id}")
  5. public String test(@PathVariable("id")Integer id){
  6. return "accept one msg id="+id;
  7. }
  1. @FeignClient(value = "openFeign-provider")
  2. public interface OpenFeignService {
  3. @GetMapping("/openfeign/provider/test/{id}")
  4. String get(@PathVariable("id")Integer id);
  5. }

4.4 普通表单参数

  1. @RestController
  2. @RequestMapping("/openfeign/provider")
  3. public class OpenFeignProviderController {
  4. @PostMapping("/test2")
  5. public String test2(String id,String name){
  6. return MessageFormat.format("accept on msg id={0},name={1}",id,name);
  7. }
  8. }
  1. @FeignClient(value = "openFeign-provider")
  2. public interface OpenFeignService {
  3. /**
  4. * 必须要@RequestParam注解标注,且value属性必须填上参数名
  5. * 方法参数名可以任意,但是@RequestParam注解中的value属性必须和provider中的参数名相同
  6. */
  7. @PostMapping("/openfeign/provider/test2")
  8. String test(@RequestParam("id") String arg1,@RequestParam("name") String arg2);
  9. }

五、OpenFeign超时处理

openFeign其实是有默认的超时时间的,默认分别是连接超时时间10秒、读超时时间60秒
查看源码: 利用Idea按Ctrl+N 搜索 feign.Request.Options#Options()

  1. public Options() {
  2. this(10L, TimeUnit.SECONDS, 60L, TimeUnit.SECONDS, true);
  3. }

SpringCloud Feign在Hoxton.M2 RELEASED版本之前,集成Ribbon
使用Ribbon时,Ribbon的默认超时连接时间、读超时时间都是是1秒。源码在org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute()方法中,如果openFeign没有设置对应得超时时间,那么将会采用Ribbon的默认超时时间。
SpringCloud Feign在Hoxton.M2 RELEASED版本之后,集成spring-cloud-loadbalancer
SpringCloud Feign在Hoxton.M2 RELEASED版本之后不再使用ribbon,而使用spring-cloud-loadbalancer,spring-cloud-loadbalancer默认是没有超时时间的,所以会使用OpenFeign的60秒超时时间。

  1. 消费者端设置75秒的睡眠时间,生产者进行调用,在第60s后会报错 java.net.SocketTimeoutException: Read timed out

1、设置openFeign的超时时间

  1. feign:
  2. client:
  3. config:
  4. default: # 设置的全局超时时间,指定服务名称可以设置单个服务的超时时间
  5. connectTimeout: 5000 # 指的是建立链接后从服务器读取可用资源所用的时间
  6. readTimeout: 5000 # 指的是建立链接所用的时间,适用于网络状况正常的情况下, 两端链接所用的时间

一般正常的业务逻辑中可能涉及到多个openFeign接口的调用,可单独设置超时时间。

  1. feign:
  2. client:
  3. config:
  4. default:
  5. connectTimeout: 5000
  6. readTimeout: 5000
  7. consumer9080: #注册到nacos的服务名称
  8. connectTimeout: 7000
  9. readTimeout: 7000

六、OpenFeign开启增强日志

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

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

    1.1 配置类中配置日志级别

    1. @Configuration
    2. public class OpenFeignConfig {
    3. @Bean
    4. Logger.Level feignLoggerLever() {
    5. return Logger.Level.FULL;
    6. }
    7. }

    1.2 yaml文件中设置接口日志级别

    1. logging:
    2. level:
    3. com.example.demo.service: debug

    这里的com.example.demo.service是openFeign接口所在的包名,当然也可以配置一个特定的openFeign接口。

    1.3 演示效果

    image-20230322141234461.png

    七、OpenFeign如何替换默认的httpclient

    Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。
    在生产环境中,通常不使用默认的http client,通常有如下两种选择:

  • 使用ApacheHttpClient

  • 使用OkHttp

    1.1、添加ApacheHttpClient依赖

    1. <!-- 使用Apache HttpClient替换Feign原生httpclient-->
    2. <dependency>
    3. <groupId>org.apache.httpcomponents</groupId>
    4. <artifactId>httpclient</artifactId>
    5. </dependency>
    6. <!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-httpclient -->
    7. <dependency>
    8. <groupId>io.github.openfeign</groupId>
    9. <artifactId>feign-httpclient</artifactId>
    10. <version>11.8</version>
    11. </dependency>
    为什么要添加上面的依赖呢?从源码中不难看出,请看org.springframework.cloud.openfeign.FeignAutoConfiguration.HttpClientFeignConfiguration这个类,代码如下:
    image-20230322144932152.png
    上述红色框中的生成条件,其中的@ConditionalOnClass(ApacheHttpClient.class),必须要有ApacheHttpClient这个类才会生效,并且feign.httpclient.enabled这个配置要设置为true

    1.2、配置文件中开启

    1. feign:
    2. client:
    3. config:
    4. # 替换默认的httpclient,开启 Http Client
    5. httpclient:
    6. enabled: true # 开启 Http Client
    7. max-connections: 200 # 最大连接数,默认:200
    8. max-connections-per-route: 50 # 最大路由,默认:50
    9. connection-timeout: 2000 # 连接超时,默认:2000/毫秒
    10. time-to-live: 900 # 生存时间,默认:900L
    11. timeToLiveUnit: SECONDS # 响应超时的时间单位,默认:TimeUnit.SECONDS

    1.3、如何验证已经替换成功

    feign.SynchronousMethodHandler#executeAndDecode()这个方法中可以清楚的看出调用哪个client,如下图:
    image-20230322144415437.png

    八、OpenFeign如何通讯优化

    在讲如何优化之前先来看一下GZIP 压缩算法,概念如下:
    gzip是一种数据格式,采用用deflate算法压缩数据;gzip是一种流行的数据压缩算法,应用十分广泛,尤其是在Linux平台。
    当GZIP压缩到一个纯文本数据时,效果是非常明显的,大约可以减少70%以上的数据大小。
    网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是GZIP与搜索引擎的抓取工具有着更好的关系。例如 Google就可以通过直接读取GZIP文件来比普通手工抓取更快地检索网页。
    GZIP压缩传输的原理如下图:

b76eb5c36e514f8da0878b558a790afe.png

按照上图拆解出的步骤如下:
客户端向服务器请求头中带有:Accept-Encoding:gzip,deflate 字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。
服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式压缩过的。
客户端接收到响应之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。
openFeign支持请求/响应开启GZIP压缩,整体的流程如下图:
d8f1dbb1684e5e850f72cd9fcaf568d6.png
上图中涉及到GZIP传输的只有两块,分别是Application client -> Application Service、 Application Service->Application client。
注意:openFeign支持的GZIP仅仅是在openFeign接口的请求和响应,即是openFeign消费者调用服务提供者的接口。
openFeign开启GZIP步骤也是很简单,只需要在配置文件中开启如下配置:

  1. feign:
  2. ## 开启压缩
  3. compression:
  4. request:
  5. enabled: true
  6. ## 开启压缩的阈值,单位字节,默认2048,即是2k,这里为了演示效果设置成10字节
  7. min-request-size: 10
  8. mime-types: text/xml,application/xml,application/json
  9. response:
  10. enabled: true

上述配置完成之后,发出请求,可以清楚看到请求头中已经携带了GZIP压缩,如下图
image-20230322151459355.png