简介:
Feign 是一个声明式的 REST 客户端,它的目的就是让 REST 调用更加简单,Feign 提供了 HTTP 请求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。
而且 Feign 会完全代理 HTTP 请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。
openfeign ,spring cloud 是再次封装,使其支持 SpringMVC 标准注解和 HttpMessageConverters。
Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡,与 Hystrix 组合使用,支持熔断回退。
Feign 重要组件
- Contract 契约组件:Contract 允许用户自定义契约去解析注解信息 ,如 Spring MVC 的注解来定义 Feign 的客户端,这是因为 Spring Cloud OpenFeign 中实现了自己的 SpringMvcContract 。 ↓
- Encoder 编码组件: 将请求信息采用指定的编码方式进行编码后传输
- Decoder 解码组件: Decoder 将相应数据解码成对象
- ErrorDecoder 异常解码器: 当被调用方发生异常后,我们可以在 ErrorDecoder 中将响应的数据转换成具体的异常返回给调用方,适合内部服务之间调用,但不想通过指定的字段来判断是否成功的场景,直接用自定义异常代替.
- Logger 日志记录: Logger 组件是负责 Feign 中记录日志的,可以指定 Logger 的级别以及自定义日志的输出.
- Client 请求执行组件: Client 是负责 HTTP 请求执行的组件,Feign 将请求信息封装好后会交由 Client 来执行.
- Retryer 重试组件: Retryer 是负责重试的组件,Feign 内置了重试器,当 HTTP 请求出现 IO 异常时,Feign 会限定一个最大重试次数来进行重试操作.
- InvocationHandlerFactory 代理: InvocationHandlerFactory 采用 JDK 的动态代理方式生成代理对象,我们定义的 Feign Client 是接口,当我们调用这个接口中定义的方法时,实际上是要去调用远程的 HTTP API,这里用了动态代理方式,当调用某个方法时,会进入代理中真正的去调用远程 HTTP API.
- RequestInterceptor 请求拦截器: 可以为 Feign 添加多个拦截器,在请求执行前设置一些扩展的参数信息.
- QueryMapEncoder 参数查询: QueryMapEncoder 是针对实体类参数查询的编码器,可以基于 QueryMapEncoder 将实体类生成对应的查询参数.
执行过程:
实践代码:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
/**
* feign-hystrix 支持, 不用在写@EnableCircuitBreaker
* @author 服务消费者
*/
@EnableFeignClients
@SpringBootApplication
public class EurekaClientCustomerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientCustomerApplication.class, args);
}
}
/**
* @program: client-customer
* @description: @FeignClient(" 应用名称 ") @RequestMapping(value = " 该服务的访问地址 ")
* @author: hexiaoshu
**/
@FeignClient(value = "single-provider",fallback = HelloServiceHystrixImpl.class)
public interface IHelloService {
@RequestMapping(value = "/hello")
Result hello();
@RequestMapping(value = "nice")
Result nice();
}
/**
* 熔断配置
* @author hexiaoshu
*/
@Component
public class HelloServiceHystrixImpl implements IHelloService {
@Override
public Result hello() {
return Result.error("报错");
}
@Override
public Result nice() {
return Result.error("报错");
}
}
/**
* @program: client-customer
* @description: fegin异常处理
* @author: he
* @create: 2020-07-06 17:35
**/
@Component
public class FeignClientErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String s, Response response) {
System.out.println(s+"\t"+response.status());
try {
String bodyStr= Util.toString(response.body().asReader(Util.UTF_8));
System.out.println(bodyStr);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
yaml 文件 (消费端) ,经测试 确实 ribbon结合 hristx 设置超时时间,管用
server:
port: 3002
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:3000/eureka
instance:
preferIpAddress: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
info:
app.name: eureka-customer
des.text: 消费端-client
spring:
application:
name: single-customer
#开启 Feign 对 Hystrix 的支持
feign:
hystrix:
enabled: true
client:
config:
servive-name:
error-decoder: eureka.client.customer.feign.errordecoder.FeignClientErrorDecoder #调用异常处理
#hystrix 配合 ribbon超时时间设置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000 #feign整合hystrix 光设置Hystrix 超时没用的要配合ribbon超时
circuitBreaker:
requestVolumeThreshold: 3 #默认20 ,熔断的阈值,如何user服务报错满足3次,熔断器就会打开,就算order之后请求正确的数据也不行。
sleepWindowInMilliseconds: 8000 #默认5S , 等5S之后熔断器会处于半开状态,然后下一次请求的正确和错误讲决定熔断器是否真的关闭和是否继续打开
ribbon:
ReadTimeout: 10000 #处理请求的超时时间,默认为5秒
ConnectTimeout: 10000 #连接建立的超时时长,默认5秒
MaxAutoRetries: 1 #同一台实例的最大重试次数,但是不包括首次调用,默认为1次
客户端
@Slf4j
@RestController
public class TestController {
@Resource
private DiscoveryClient discoveryClient;
@RequestMapping(value = "/hello")
public Result hello(){
List<String> services = discoveryClient.getServices();
for(String s : services){
log.info(s);
}
return Result.ok("hello");
}
@RequestMapping(value = "/nice")
public Result nice(){
//int i=1/0;
List<String> services = discoveryClient.getServices();
for(String s : services){
log.info("gogogo" + s);
}
return Result.ok("nice to meet you!");
}
}