【SpringCloud】6. 服务间通信方式(高级)

609 阅读5分钟

原始调用方式

虽然现在企业中也有一部分采用RestTemplate + Ribbon来解决服务间通信的方式,但是这样的方案造成了代码高耦合,因为要将URL集成在代码中。为了解决这样的问题,Spring Cloud提供了一个OpenFeign组件,该组件底层封装了RestTemplate和Ribbon,使用更简单的代码就可以进行服务之间的通信。

造成的问题:

  • 每次调用服务都需要写这些代码,存在大量的代码冗余。
  • 服务地址如果修改,维护成本增高。
  • 使用时不够灵活。

OpenFeign组件

1. 说明

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

2. 使用

  1. 调用方引入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>    
    </dependency>
    
  2. 调用方入口类加入注解开启OpenFeign支持

    @SpringBootApplication
    @EnableDiscoveryClient
    // 开启OpenFeign支持
    @EnableFeignClients
    public class UsersApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(UsersApplication.class, args);
        }
    
    }
    
  3. 调用方创建client包

  4. 调用方创建被调用方的Feign客户端

    // 注解中填写被调用方服务名
    @FeignClient("products")
    public interface ProductClient {
    
        /**
         * 接口必须和被调用接口一致
         * 方法名尽量和被调用方法名一致
         * @return 响应信息
         */
        @GetMapping("/product/showProducts")
        Map<String, Object> showProducts();
    
    }
    
  5. 调用方Controller中使用

    @Autowired
    private ProductClient productClient;
    
    /**
     * 在users微服务中通过服务注册中心负载均衡调用products微服务中某一服务器暴露出来的接口(OpenFeign)
     * @return 响应信息
     */
    @GetMapping("/user/showProducts")
    public Map<String, Object> showProducts() {
        Map<String, Object> result = productClient.showProducts();
        return result;
    }
    

    注意:OpenFeign组件的负载均衡调用算法和Ribbon一样,修改算法的方式也和Ribbon一样。

OpenFeign参数传递

1. GET参数传递

假设调用方的相应方法已创建,需要在被调用方和调用方中的Fengin客户端中的方法参数上加上@RequestParam注解进行参数传递

  1. 被调用方

    @GetMapping("/product/findOne")
    public Map<String, Object> findOne(@RequestParam("id") int id) {
        Map<String, Object> result = new HashMap<>();
        result.put("status", true);
        result.put("msg", "商品");
        result.put("id", id);
        return result;
    }
    
  2. 调用方Feign客户端

    /**
     * OpenFeign组件GET类型请求传递的参数需要使用@RequestParam,注解中value必须和被调用方函数参数的参数名一致
     * 方法参数名建议和被调用方法参数名一致
     * @param id 商品id
     * @return 响应信息
     */
    @GetMapping("/product/findOne")
    Map<String, Object> findOne(@RequestParam("id") int id);
    

2. GET(RESTful)参数传递

如果URL符合RESTful规范,同时使用GET方式进行参数传递,需要在被调用方和调用方中的Fengin客户端中的方法参数上加上@PathVariable注解进行参数传递

  1. 被调用方

    @GetMapping("/product/findOne/{id}")
    public Map<String, Object> findOne(@PathVariable("id") int id) {
        Map<String, Object> result = new HashMap<>();
        result.put("status", true);
        result.put("msg", "商品");
        result.put("id", id);
        return result;
    }
    
  2. 调用方Feign客户端

    @GetMapping("/product/findOne/{id}")
    Map<String, Object> findOne(@PathVariable("id") int id);
    

3. POST参数传递

如果POST方式传递的参数不是对象的话,可用和GET传递参数一样,使用@RequestParam解决。

但是通常POST方式传递的是Bean,这个时候解决方案为:调用方的Fengin客户端使用@RequestBody将对象转成JSON格式进行数据传递,被调用方也使用@RequestBody将接收到的JSON格式的数据转为Bean进行处理

  1. 被调用方

    @PostMapping("/product/updateOne")
    public Map<String, Object> updateOne(@RequestBody Product product) {
        Map<String, Object> result = new HashMap<>();
        result.put("status", true);
        result.put("msg", "修改商品");
        result.put("product", product);
        return result;
    }
    
  2. 调用方Feign客户端

    /**
     * OpenFeign组件POST类型请求传递对象参数
     * @param product 修改商品信息
     * @return 响应信息
     */
    @PostMapping("/product/updateOne")
    Map<String, Object> updateOne(@RequestBody Product product);
    

OpenFeign超时设置

1. 说明

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

2. 调用方配置

  1. 指定服务配置

    application.yml

    # 配置Feign指定服务超时等待
    feign:
      client:
        config:
          # 被调用服务名
          products:
            # 服务连接超时 单位(ms)
            connectTimeout: 5000
            # 服务等待超时 单位(ms)
            readTimeout: 5000
    
  2. 全局配置

    application.yml

    # 配置Feign所有服务超时等待
    feign:
      client:
        config:
          default:
            connectTimeout: 5000
            readTimeout: 5000
    

OpenFeign日志调试

1. 说明

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

我们可以为Feign客户端配置各自的loggerLevel对象,告诉Feign记录哪些日志。

关于SpringBoot日志级别:TARCE < DEBUG < INFO < WARN < ERROR < FATAL

SpringBoot默认日志级别为INFO,因此默认TARCE,DEBUG级别的日志是看不到的。

2. 日志展示类型

可用通过配置loggerLevel的值实现:

  • NONE:不记录任何日志。
  • BASIC:仅仅记录请求方法,URL,响应状态代码及执行时间。
  • HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
  • FULL:记录请求和响应的header,body和元数据。

3. 调用方开启日志展示

  1. 指定Feign客户端所在包(指定和全局都需要配置)

    # 指定Feign客户端所在包
    logging:
      level:
        # 包路径
        org:
          aydenbryan:
            users:
              # 必须是DEBUG级别
              clients: debug
    
  2. 指定服务日志展示

    # 配置Feign指定服务的日志展示
    feign:
      client:
        config:
          # 被调用服务名
          products:
          	# 展示类型
            loggerLevel: full
    
  3. 全局配置日志展示

    # 配置Feign所有服务的日志展示
    feign:
      client:
        config:
          default:
            loggerLevel: full