(4)声明式服务调用——OpenFeign

1、OpenFeign引言

Ribbon 提供了客户端的负载均衡问题,RestTemplate 封装了 HTTP 的通讯,简化了发送请求过程。两者相辅相成构建了服务间的高可用通信。

但在使用RestTemplate后发现,它对 HTTP 的封装,如URL、请求参数、请求头、请求体这些处理非常繁琐

Feign和OpenFeign

  • Netflix Feign:开源声明式 WebService 客户端,采用“接口+注解”的方式开发,屏蔽网络通信的细节,但使用舒适度一般。
  • Spring Cloud OpenFeign 是Netflix Feign 的基础上进行的封装,用于简化服务间通信,结合原有 Spring MVC 的注解,对 Spring Cloud 微服务通信提供了良好的支持,大幅简化了服务间高可用通信处理过程。
    在这里插入图片描述

2、OpenFeign的使用

2.1、OpenFeign的使用步骤

a. 导入依赖
<!-- 引入openFeign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
b. 在启动类上添加@EnableFeignClients,启动Feign
@SpringBootApplication
@EnableFeignClients//不可省略
public class xxxServiceApplication {
	public static void main(String[] args) {
		SpringApplication.run(xxxServiceApplication.class, args);
	}
}
c. 开发Fegin的服务远程调用客户端,该接口形似对应的controller
/*
value/name:指定提供者的微服务名称
url:直接指定请求的路径地址
decode404:是否应该编码或者抛出FeignException异常
configuration:配置feign.codec.Decoder、feign.codec.Encoder、feign.Contract
fallback:指定发送异常调用或者超时时应该调用那个类来执行备用方法
fallbackFactory:提供统一的异常熔断处理,避免重复代码的编写
path:当服务提供者使用了server.context.path时,对应提供者的项目路径。
contextId:用来定义spring bean的名称,给service层注入调用
 */
@FeignClient(value="user-service",contextId = "userClient")
@RequestMapping("users")
public interface UserClient {
    @GetMapping("{id}")
    User queryUserById(@PathVariable("id")Long id);
}
//注: 可使用的注解:路径的@pathVariable,请求方式,参数的注解等

//对应的远程controller:
@RestController
@RequestMapping("/users")
public class UserController {
 @Autowired
 private UserService userService;
 @GetMapping("/{id}")
 public User queryUserById(@PathVariable("id") Long id){
     return userService.queryUserById(id);
 }
}
d. 通过Spring工程获取上一步定义的Fegin客户端,使用客户端远程调用服务

2.2、GET请求多参数传递

一个服务方法需要接收多个请求参数时

  • 若服务接口参数时会使用实体类接收,使用OpenFeign定义客户端方法时保持相同即可。
  • 但在使用OpenFeign时,服务方法多个参数是零散接收的,OpenFeign客户端方法希望使用实体类传输数据,如何实现?

示例

  • 服务端user-service定义一个零散收集多个参数的接口:
@RestController
@RequestMapping("/user")
public class UserController {
	@GetMapping("login")
	public String login(String username,String password){
       System.out.println("username = " + username + ", password = " + password);
       return "success";
   }
}
  • 此时要使用对象传递多个参数,定义一个dto封装参数,使用@SpringQueryMap注解。
@FeignClient(value="user-service",contextId = "userClient")
@RequestMapping("user")
public interface UserClient {
@GetMapping("login")
   //等同于 String login(String username,String password);
   String login(@SpringQueryMap UserDTO userDTO);
}
  • service层注入userClient对象,调用其方法即可

2.4、Fegin日志配置

当 API 调用失败后,需要有详细的请求信息来分析失败原因,我们可以设置 Feign 的日志级别来输出详细的请求信息,Feign 的日志级别有四种:

  • NONE 表示不输出日志。
  • BASIC 表示只输出请求方法的 URL 和响应的状态码以及执行的时间。
  • HEADERS 将 BASIC 信息和请求头信息输出。
  • FULL 会输出全部完整的请求信息。
#注意:要将springboot提供的日志对远程服务客户端设为debug,配置才能生效
logging:
 level:
   包名.服务接口名: debug
feign:
 client:
   config:
     #默认全局配置
     default:
       loggerLevel: basic
     #特定的配置:要调用的Feign客户端的contextId
     userClient:
       loggerLevel: full

2.5、负载均衡策略

OpenFeign 工作时默认会引用 Ribbon 实现客户端负载均衡,配置方式与之前 Ribbon+RestTemplate 方案完全相同

服务名:
 ribbon:
   #设置对应的负载均衡类
   NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

2.6、数据压缩功能

在 OpenFeign 中,默认没有开启数据压缩功能。

  • 若服务间单次传递数据超过 1K 字节,强烈推荐开启数据压缩功能。默认使用 Gzip 方式压缩,对大文本通常压缩后尺寸是原数据的 10%~30%,极大提高了带宽利用率。
  • 但如果应用属于计算密集型,CPU 负载长期超过 70%,因数据压缩、解压缩都需要 CPU 运算,开启数据压缩功能反而会给 CPU 增加额外负担,导致系统性能降低,并不可取。
feign:
 compression:
   request:
     # 开启请求数据的压缩功能
     enabled: true
     # 压缩支持的MIME类型
     mime-types: text/xml,application/xml,application/json
     # 数据压缩下限 1024表示传输数据大于1024 才会进行数据压缩(最小压缩值标准)
     min-request-size: 1024
   # 开启响应数据的压缩功能
   response:
     enabled: true

2.7、okhttp替换默认通信组件

OpenFeign 和RestTemplate默认使用 Java 自带的 URLConnection 对象创建 HTTP 请求,但如果更换为 Apache HttpClient、OKHttp等专用通信组件,基于这些组件自带的连接池,可以更好地对 HTTP 连接对象进行重用与管理。

a. 引入feign-okhttp依赖包
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>
b. 通过Java Config初始化OkHttpClient对象
@Bean
public okhttp3.OkHttpClient okHttpClient(){
 	return new okhttp3.OkHttpClient.Builder()
 			//读取超时时间
 			.readTimeout(10, TimeUnit.SECONDS)
            //连接超时时间
            .connectTimeout(10, TimeUnit.SECONDS)
            //写超时时间
            .writeTimeout(10, TimeUnit.SECONDS)
            //设置连接池
            .connectionPool(new ConnectionPool())
            .build();
}
c. 在yaml中启用OkHttp
feign:
  okhttp:
    enabled: true

2.8、超时配置

和RestTemplate中类似,在OpenFeign中也可以使用Ribbon实现超时的失败重试,具体配置步骤如下:

a. 引入spring-retry依赖
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
b. 在application.yml中根据client的id配置超时时间
feign:
  client:
    config:
      #要调用的Feign客户端的contextId
      userClient:
        #这里的配置将覆盖代码对okhttp的超时配置
        readTimeout: 1000 #配置读取数据超时
        connectTimeout: 2000 #配置连接超时
c. 在application.yml中配置重试机制
  • 全局的重试配置
ribbon:
  MaxAutoRetries: 1 #单服务器重试次数(不包含第一次请求),这里失败则更换服务器
  MaxAutoRetriesNextServer: 2 #更换服务器的次数(不包含第一次)
  OkToRetryOnAllOperations: false #默认只对GET请求重试, 当设置为true时, 对POST等所有类型请求都重试 
  • 特定服务的重试配置
user-service: #服务名
  ribbon:
    MaxAutoRetries: 2
    MaxAutoRetriesNextServer: 1
    OkToRetryOnAllOperations: false

2.9、拦截器

OpenFeign提供了拦截器机制,允许我们对Feign的请求过程进行拦截处理。

a. 自定义拦截器:实现RequestInterceptor接口
public class MyFeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        System.out.println("MyFeignRequestInterceptor.apply");
    }
}
b. 配置拦截器
  • 全部配置(配置类加入bean,或者在拦截器加入@Component)
    @Bean
    public MyFeignRequestInterceptor feignRequestInterceptor(){
        return new MyFeignRequestInterceptor();
    }
    
  • 局部配置:针对特定OpenFeign客户端
    feign:
      client:
        config:
          default:
            loggerLevel: basic
          #要调用的Feign客户端的contextId
          userClient:
            loggerLevel: full
            #配置拦截器(数组形式)
            requestInterceptors:
              - com.baizhi.cloud.alibaba.feign.interceptor.MyFeignRequestInterceptor
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值