前言
虽然此篇文章介绍Feign客户端调用工具,但是我们也要知道SpringCloud 中支持两种客户端调用工具: ①RestTemplate(基本上不用)、②Feign客户端工具(项目开发中用的较多)。这篇文章重点介绍Feign客户端的调用工具的使用方法。
Feign是一个声明式得Http客户端调用工具,采用接口+注解方式实现,易读性较强。主要基于两个注解实现:@EnableFeignClients和@FeignClient,下边具体步骤详细介绍这两个注解的使用方法与参数定义。
接下来,我以近期项目开发中利用Feign客户端工具进行RPC调用,从头搭建整个服务的调用过程,以及接口规范化的书写,尽量贴合实际的项目开发规范,毕竟良好的代码书写规范是很值得Leader的青睐。
1、pom.xml文件需引入对应的jar包
基于SpringCloud开发环境,只列入相关jar包
<!-- Eureka Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--Feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、application.yml的配置文件
### 服务端口
server:
port: 7878
###定义服务名称
spring:
application:
name: server1
# Eureka注册中心
eureka:
instance:
hostname: 127.0.0.1
instance-id: ${spring.application.name}:${eureka.instance.hostname}:${server.port}
client:
service-url:
defaultZone: http://127.0.0.1:9999/bureka
register-with-eureka: true
fetch-registry: true
spring:
datasource:
# 数据源1
his:
username: root
password: 123
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
redis:
host: 127.0.0.1
3、Feign声明接口的编写
前面我们提到Feign是一个声明式得Http客户端调用工具,采用接口+注解方式实现,核心注解为 @FeignClient注解,接下来重点介绍下此注解。
FeignClient注解被@Target(ElementType.TYPE)修饰,表示FeignClient注解的作用目标在接口上,源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
@AliasFor("name")
String value() default "";
/** @deprecated */
@Deprecated
String serviceId() default "";
@AliasFor("value")
String name() default "";
String qualifier() default "";
String url() default "";
boolean decode404() default false;
Class<?>[] configuration() default {};
Class<?> fallback() default void.class;
Class<?> fallbackFactory() default void.class;
String path() default "";
boolean primary() default true;
}
声明接口之后,在代码中通过@Autowired注入即可应用。@FeignClient标签的常用属性如下:
- value: 是服务提供方在eureka注册的名字
- name: 指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
- url: 一般用于调试,可以手动指定@FeignClient调用的地址
- decode404: 当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException
- configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
- fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口;
- fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
- path: 定义当前FeignClient的统一前缀
插入Feign请求超时问题
Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码。而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了
解决方案有三种,以feign为例。
方法一
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
该配置是让Hystrix的超时时间改为5秒
方法二
hystrix.command.default.execution.timeout.enabled: false
该配置,用于禁用Hystrix的超时时间
方法三
feign.hystrix.enabled: false
该配置,用于索性禁用feign的hystrix。该做法除非一些特殊场景,不推荐使用。
项目的实际运用:
比如我们的项目中,一个数据中心服务,一个是同步服务,数据中心服务已经写好了所有跟第三方调用的接口。同步服务只需要按照数据中心提供的接口文档,书写对应的接口。最后将这两个服务统一注册到Eureka注册中心,通过Feign客户端调用工具实现RPC远程调用。
规范书写,紧贴实际开发需求,延展性和可扩展性强
@FeignClient(value = "server", path = "/serverPath", url = "127.0.0.1:8899")
public interface FeignClientIn {
@ApiOperation(value = "获取服务接口一")
@GetMapping("/serverOne")
FeignResponse<List<user>> getUserInfo(@RequestParam("name") String name);
@ApiOperation(value = "获取服务接口二")
@GetMapping("/serverTwo")
FeignResponse<List<Order>> getOrderInfo(@RequestParam("age") String age);
}
全局API接口返回的FeignResponse类
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class FeignResponse<T>{
@ApiModelProperty(value = "状态码,成功为2000", example = "2000")
private int code;
@ApiModelProperty(value = "成功标识", example = "true")
private boolean success;
@ApiModelProperty(value = "状态码为2000时,msg为OK, 否则为错误提示信息", example = "OK")
private String message;
@ApiModelProperty(value = "返回数据")
private T data;
@ApiModelProperty(value = "时间戳", example = "1554787157007")
private long timestamp;
}
4、启动类
@EnableEurekaClient
@EnableFeignClients("com.lbn.generic.feign") //启用feign客户端
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
}