RestTemplate+ribbon方式 存在问题:

1.每次调用服务都需要写这些代码,存在大量的代码冗余 String restTemplateForObject = restTemplate.getForObject(“http://服务名/url?参数“ + name, String.class);

2.服务地址如果修改,维护成本增高

3.使用时不够灵活

1、Feign介绍

https://cloud.spring.io/spring-cloud-openfeign/reference/html/

Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,默认实现了负载均衡的效果并且springcloud为feign添加了springmvc注解的支持。

2、springcloud-consul-user-openfeign-6107

2.1 pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.5.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.morrow</groupId>
  12. <artifactId>springcloud-consul-user-openfeign-6107</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>springcloud-consul-user-openfeign-6107</name>
  15. <description>springcloud-consul-user-openfeign-6107 project for Spring Boot</description>
  16. <properties>
  17. <java.version>11</java.version>
  18. <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
  19. </properties>
  20. <dependencies>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-web</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.projectlombok</groupId>
  27. <artifactId>lombok</artifactId>
  28. <optional>true</optional>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-test</artifactId>
  33. <scope>test</scope>
  34. </dependency>
  35. <!--引入consul依赖-->
  36. <dependency>
  37. <groupId>org.springframework.cloud</groupId>
  38. <artifactId>spring-cloud-starter-consul-discovery</artifactId>
  39. </dependency>
  40. <!-- 健康度监控-->
  41. <dependency>
  42. <groupId>org.springframework.boot</groupId>
  43. <artifactId>spring-boot-starter-actuator</artifactId>
  44. </dependency>
  45. <!-- 引入 openfeign 依赖 -->
  46. <dependency>
  47. <groupId>org.springframework.cloud</groupId>
  48. <artifactId>spring-cloud-starter-openfeign</artifactId>
  49. </dependency>
  50. </dependencies>
  51. <!--全局管理springcloud版本,并不会引入具体依赖-->
  52. <dependencyManagement>
  53. <dependencies>
  54. <dependency>
  55. <groupId>org.springframework.cloud</groupId>
  56. <artifactId>spring-cloud-dependencies</artifactId>
  57. <version>${spring-cloud.version}</version>
  58. <type>pom</type>
  59. <scope>import</scope>
  60. </dependency>
  61. </dependencies>
  62. </dependencyManagement>
  63. <build>
  64. <plugins>
  65. <plugin>
  66. <groupId>org.springframework.boot</groupId>
  67. <artifactId>spring-boot-maven-plugin</artifactId>
  68. <configuration>
  69. <excludes>
  70. <exclude>
  71. <groupId>org.projectlombok</groupId>
  72. <artifactId>lombok</artifactId>
  73. </exclude>
  74. </excludes>
  75. </configuration>
  76. </plugin>
  77. </plugins>
  78. </build>
  79. </project>

2.2 yml 文件

  1. server:
  2. port: 6107
  3. spring:
  4. application:
  5. name: users
  6. cloud:
  7. consul:
  8. host: localhost #注册consul服务的主机
  9. port: 8500 #注册consul服务的端口号
  10. discovery:
  11. # register-health-check: true # 开启 关闭consul 服务的健康检查[不推荐]
  12. service-name: ${spring.application.name} #指定注册的服务名称 默认就是应用名

2.3 ProductClient 文件

  1. package com.morrow.springcloudconsuluseropenfeign6107.clients;
  2. import com.morrow.springcloudconsuluseropenfeign6107.entity.Product;
  3. import org.springframework.cloud.openfeign.FeignClient;
  4. import org.springframework.web.bind.annotation.GetMapping;
  5. import org.springframework.web.bind.annotation.PostMapping;
  6. import org.springframework.web.bind.annotation.RequestBody;
  7. import org.springframework.web.bind.annotation.RequestParam;
  8. import java.util.Map;
  9. //value属性用来指定:调用服务名称
  10. @FeignClient("products")
  11. public interface ProductClient {
  12. @GetMapping("/product/findAlls") //书写服务调用路径
  13. String findAlls();
  14. @GetMapping("/product/findOne")
  15. Map<String, Object> findOne(@RequestParam("productId") String productId);
  16. @PostMapping("/product/save")
  17. Map<String,Object> save(@RequestParam("name") String name);
  18. @PostMapping("/product/saveProduct")
  19. Map<String,Object> saveProduct(@RequestBody Product product);
  20. }

2.4 Product 文件

  1. @Data
  2. public class Product {
  3. private Integer id;
  4. private String name;
  5. }

2.5 TestFeignController文件

  1. package com.morrow.springcloudconsuluseropenfeign6107.controller;
  2. import com.morrow.springcloudconsuluseropenfeign6107.clients.ProductClient;
  3. import com.morrow.springcloudconsuluseropenfeign6107.entity.Product;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.PostMapping;
  8. import org.springframework.web.bind.annotation.RestController;
  9. import java.util.Map;
  10. @RestController
  11. @Slf4j
  12. public class TestFeignController {
  13. @Autowired
  14. ProductClient productClient;
  15. /**
  16. * Get 请求 无参数
  17. * @return
  18. */
  19. @GetMapping("testFindAll")
  20. public String findAll(){
  21. log.info("通过使用OpenFeign组件调用商品服务...");
  22. String result = productClient.findAlls();
  23. log.info("成功调用商品服务...:[{}]",result);
  24. return result;
  25. }
  26. /**
  27. * Get 请求 单个参数
  28. * @param id
  29. * @return
  30. */
  31. @GetMapping("/test1")
  32. public Map<String,Object> test1(String id){
  33. log.info("用来测试Openfiegn的GET方式参数传递");
  34. Map<String, Object> msg = productClient.findOne(id);
  35. log.info("调用返回信息:[{}]",msg);
  36. return msg;
  37. }
  38. /**
  39. * POST 请求 单个参数
  40. * @param name
  41. * @return
  42. */
  43. @PostMapping("/user/save")
  44. public Map<String,Object> save(String name){
  45. log.info("接收到的商品信息名称:[{}]",name);
  46. Map<String,Object> map = productClient.save(name);
  47. log.info("调用成功返回结果: "+map);
  48. return map;
  49. }
  50. /**
  51. * POST 请求 实体类
  52. * @param product
  53. * @return
  54. */
  55. @PostMapping("/user/saveProduct")
  56. public Map<String, Object> saveProduct(Product product){
  57. log.info("接收到的商品信息:[{}]",product);
  58. Map<String, Object> map = productClient.saveProduct(product);
  59. log.info("调用成功返回结果: [{}]",map);
  60. return map;
  61. }
  62. }

3、 springcloud-consul-products-6105

3.1 添加 ProductController2 文件

  1. @RestController
  2. @Slf4j
  3. public class ProductController2 {
  4. @Value("${server.port}")
  5. private int port;
  6. @GetMapping("/product/findAlls")
  7. public Map<String,Object> findAlls() throws InterruptedException {
  8. // Thread.sleep(2000);
  9. log.info("商品服务查询所有调用成功,当前服务端口:[{}]",port);
  10. Map<String, Object> map = new HashMap<String,Object>();
  11. map.put("msg","服务调用成功,服务提供端口为: "+port);
  12. map.put("status",true);
  13. return map;
  14. }
  15. @GetMapping("/product/findOne")
  16. public Map<String,Object> findOne(String productId){
  17. log.info("商品服务查询商品信息调用成功,当前服务端口:[{}]",port);
  18. log.info("当前接收商品信息的id:[{}]",productId);
  19. Map<String, Object> map = new HashMap<String,Object>();
  20. map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
  21. map.put("status",true);
  22. map.put("productId",productId);
  23. return map;
  24. }
  25. @PostMapping("/product/save")
  26. public Map<String,Object> save(String name){
  27. log.info("商品服务保存商品调用成功,当前服务端口:[{}]",port);
  28. log.info("当前接收商品名称:[{}]",name);
  29. Map<String, Object> map = new HashMap<String,Object>();
  30. map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
  31. map.put("status",true);
  32. map.put("name",name);
  33. return map;
  34. }
  35. @PostMapping("/product/saveProduct")
  36. public Map<String,Object> saveProduct(@RequestBody Product product){
  37. log.info("商品服务保存商品信息调用成功,当前服务端口:[{}]",port);
  38. log.info("当前接收商品名称:[{}]",product);
  39. Map<String, Object> map = new HashMap<String,Object>();
  40. map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
  41. map.put("status",true);
  42. map.put("product",product);
  43. return map;
  44. }
  45. }

4、重启项目 测试

5、OpenFeign超时设置

默认情况下,openFiegn在进行服务调用时,要求服务提供方处理业务逻辑时间必须在1S内返回,如果超过1S没有返回则OpenFeign会直接报错,不会等待服务执行,但是往往在处理复杂业务逻辑是可能会超过1S,因此需要修改OpenFeign的默认服务调用超时时间。

5.1 修改OpenFeign默认超时时间

  1. server:
  2. port: 6107
  3. spring:
  4. application:
  5. name: users
  6. cloud:
  7. consul:
  8. host: localhost #注册consul服务的主机
  9. port: 8500 #注册consul服务的端口号
  10. discovery:
  11. # register-health-check: true # 开启 关闭consul 服务的健康检查[不推荐]
  12. service-name: ${spring.application.name} #指定注册的服务名称 默认就是应用名
  13. feign:
  14. client:
  15. config:
  16. products:
  17. connectTimeout: 5000 #配置指定服务连接超时
  18. readTimeout: 5000 #配置指定服务等待超时
  19. # default:
  20. # connectTimeout: 5000 #配置所有服务连接超时
  21. # readTimeout: 5000 #配置所有服务等待超时

6、OpenFeign调用详细日志展示

往往在服务调用时我们需要详细展示feign的日志,默认feign在调用是并不是最详细日志输出,因此在调试程序时应该开启feign的详细日志展示。feign对日志的处理非常灵活可为每个feign客户端指定日志记录策略,每个客户端都会创建一个logger默认情况下logger的名称是feign的全限定名需要注意的是,feign日志的打印只会DEBUG级别做出响应。

我们可以为feign客户端配置各自的logger.level对象,告诉feign记录那些日志logger.lever有以下的几种值
NONE 不记录任何日志BASIC 仅仅记录请求方法,url,响应状态代码及执行时间
HEADERS 记录Basic级别的基础上,记录请求和响应的headerFULL 记录请求和响应的header,body和元数据

6.1 、开启日志展示

  1. server:
  2. port: 6107
  3. spring:
  4. application:
  5. name: users
  6. cloud:
  7. consul:
  8. host: localhost #注册consul服务的主机
  9. port: 8500 #注册consul服务的端口号
  10. discovery:
  11. # register-health-check: true # 开启 关闭consul 服务的健康检查[不推荐]
  12. service-name: ${spring.application.name} #指定注册的服务名称 默认就是应用名
  13. feign:
  14. client:
  15. config:
  16. products:
  17. connectTimeout: 5000 #配置指定服务连接超时
  18. readTimeout: 5000 #配置指定服务等待超时
  19. loggerLevel: full #开启指定服务日志展示
  20. # default:
  21. # connectTimeout: 5000 #配置所有服务连接超时
  22. # readTimeout: 5000 #配置所有服务等待超时
  23. # loggerLevel: full #全局开启服务日志展示
  24. logging:
  25. level:
  26. com:
  27. morrow:
  28. springcloudconsuluseropenfeign6107:
  29. clients: debug #指定feign调用客户端对象所在包,必须是debug级别

image.png