feign的是一个http请求调用轻量级的框架,可以使Java接口注解的方式调用Http请求。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。Feign支持多种注解,例如Feign自带的注解或者JAXRS注解等。Spring Cloud对Feign进行了增强,使其支持Spring MVC注解,另外还整合了Ribbon和Eureka,从而使得Feign的使用更加方便.
* 此文档主要围绕以下图片开展
先看一下feign的自动装配,看一下spring.factories中有哪些自动装配类
其中最主要的就是FeignRibbonClientAutoConfiguration和FeignHalAutoConfiguration
1.FeignRibbonClientAutoConfiguration,看类名可以发现跟ribbon的负载均衡有关,feign默认使用ribbon作为负载均衡器
<dependencies>
<!--feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--支持 HTTP 协议的客户端编程工具包-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!--fluent-hc用于构建http请求-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>fluent-hc</artifactId>
</dependency>
</dependencies>
<!--cloud的版本配置-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.6.7</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
#配置端口
server.port=9999
# spring开启允许循环依赖
spring.main.allow-circular-references=true
#日志的级别
logging.level.cn.ton.controller.feign.DemoFeign = debug
@EnableFeignClients
1、配置文件
2、实体类
共有八个级别,从低到高为:ALL< TRACE< DEBUG< INFO< WARN < ERROR < FATAL
第一步:自定义一个FeignConfig配置类
**
* 这个类上千万不要添加@Configuration,不然会被作为全局配置文件共享 *
*/
class FeignConfig {
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
}
第二部:在feignClient上指定configuration
@FeignClient(nurl = "localhost:8080",name = "FeignDemo1" , configuration = FeignConfig.class)
public interface DemoFeign {
@GetMapping(value = "/get" ,produces = "application/json; charset=utf-8" ,consumes = "application/json; charset=utf-8")
String get();
}
第三步:设置配置文件,启动调试模式
这里如果不开启,所有日志都不会被打印出来,也就是相当于日志的开关,这里设置了,上面的自定义日志级别才能生效
logging.level.cn.ton.controller.feign.DemoFeign = debug
在调用方 通过feign:client:config:微服务名称:loggerLevel: 日志级别来指定
feign.client.config.DemoFeign.loggerLevel:HEADERS
添加了@FeignClient
注解的接口为使用feign进行远程调用的客户端
@FeignClient( url = "localhost:8080",name = "FeignDemo1" ,path = "/student",contextId = "客户端id",fallbackFactory = DemoFeignImpl.class,configuration = DemoFeignConfig.class)
@FeignClient 注解说明:
url:要远程调用的服务的地址
name/value:客户端/服务端的名字(若配置Nacos/Eureka服务的注册一起使用,此处配置的nane/value会与其服务地址映射)
path:给客户端添加一个通用的路径
context:客户端的id
fallbackFactory:设置的回退处理类
configuration:私有配置类
package cn.ton.controller.feign;
import cn.ton.common.config.DemoFeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient( url = "localhost:8080/student",name = "123213" ,fallbackFactory = DemoFeignImpl.class,configuration = DemoFeignConfig.class)
public interface DemoFeign {
/*@PostMapping中说明
* produces:指定返回值类型
* consumes:指定处理请求当中的提交内容类型(Content-Type)
* */
@PostMapping(value = "/post",produces = "application/json; charset=utf-8",consumes = "application/json; charset=utf-8")
String postDemo(@RequestBody String a);
@GetMapping(value = "/get" ,produces = "application/json; charset=utf-8" ,consumes = "application/json; charset=utf-8")
String getDemo();
}
加有@Configuration注解的类为全局配置类(若不设置配置类,feign会默认创建)可根据需求自定义编解码,错误处理,日志,拦截器等的配置。
package cn.ton.common.config;
import feign.*;
import feign.codec.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.lang.reflect.Type;
import static java.lang.String.format;
@Configuration
public class FeignConfig {
/**
* Feign 的日志打印设置
* @return
*/
@Bean
public Logger.Level log(){
return Logger.Level.FULL;
}
/**
* 编码
* @return
*/
@Bean
public Encoder encoder(){
return new Encoder() {
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
if (bodyType == String.class) {
template.body(object.toString());
} else if (bodyType == byte[].class) {
template.body((byte[]) object, null);
} else if (object != null) {
throw new EncodeException(
format("%s is not a type supported by this encoder.", object.getClass()));
}
}
};
}
/**
* 解码
* @return
*/
@Bean
public Decoder decoder(){
return new Decoder() {
@Override
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
if (response.status() == 404 || response.status() == 204)
return Util.emptyValueOf(type);
if (response.body() == null)
return null;
if (byte[].class.equals(type)) {
return Util.toByteArray(response.body().asInputStream());
}
return null;
}
};
}
/**
* 错误处理
* @return
*/
@Bean
public ErrorDecoder errorDecoder(){
return new ErrorDecoder() {
@Override
public Exception decode(String methodKey, Response response) {
System.out.println("方法名?"+methodKey);
if (response.status() != 200) {
return new RuntimeException("不正常的请求"+response.status());
}
return null;
}
};
}
/**
* 自定义拦截器
*/
@Bean
public RequestInterceptor getRequestInterceptor() {
return new RequestInterceptor(){
@Override
public void apply(RequestTemplate template) {
//设置拦截器内容
}
};
}
}
私有配置类为@FeignClient
注解中configuration="XXX.class"
参数所指定的类
package cn.ton.common.config;
import feign.Response;
import feign.codec.ErrorDecoder;
import org.springframework.context.annotation.Bean;
public class DemoFeignConfig {
@Bean
public ErrorDecoder errorDecoder(){
return new ErrorDecoder() {
@Override
public Exception decode(String methodKey, Response response) {
System.out.println("方法名?"+methodKey);
if (response.status() != 200) {
return new RuntimeException("不正常的请求"+response.status());
}
return null;
}
};
}
}
私有配置类为@FeignClient
注解中fallbackFactory="XXX.class"
参数所指定的类
package cn.ton.controller.feign;
import feign.hystrix.FallbackFactory;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DemoFeignImpl implements FallbackFactory<DemoFeign> {
@Override
public DemoFeign create(Throwable throwable) {
return new DemoFeign() {
@Override
public String postDemo(String a) {
return null;
}
@Override
public String getDemo() {
return "出错了,但是我有应对策略 返回一个默认的字符串 ...";
}
};
}
}
此项目使用默认的8080端口
package cn.ton.controller;
import org.springframework.web.bind.annotation.*;
@RequestMapping("/student")
@RestController
public class Student {
@PostMapping("/post")
public String post(@RequestBody String date){
return "post请求返回"+date+"数据";
}
@GetMapping("/get")
public String get() {
return "Get请求返回";
}
}