Openfeign使用详解
1.什么是Openfeign:
Openfeign是Springcloud的一个组件,主要是用来远程服务调用的;它的底层原理还是发送HTTP协议的请求报文;

2.Openfeign的使用:
1)在主配置类添加@EnableFeignClients注解:
@EnableFeignClients
@SpringBootApplication
public class SystemApplication {
public static void main(String[] args) {
SpringApplication.run(SystemApplication.class, args);
}
}2)在interface类中添加@FeignClient:
@FeignClient(value = "home", url = "http://localhost:8080", path = "/tool")
public interface HomeApi {
@GetMapping("/acquireToken")
String getToken();
@GetMapping("/acquireToken")
CompletableFuture<String> getToken01();
}在接口上添加@FeignClient注解我们就可以像定义Controller接口一样定义我们要访问的远程服务接口了。
3.@FeignClient注解的介绍:
@FeignClient 注解的主要属性及其说明:
name 或 value: 指定服务名称,用于服务发现。
url: 直接指定服务URL,不使用服务发现。
contextId: 生成的 Feign 客户端的 Bean 的 ID,如果不指定,则默认为服务名称。
serviceId: 服务名称的别名,与 name 或 value 作用相同。
decode404: 是否将 404 异常解码为 null,而不是抛出 FeignException。
configuration: 指定自定义的配置类,可以是配置类本身,也可以是配置类的数组。
fallback: 指定 fallback 类,当服务调用失败时,执行 fallback 类中的逻辑。
fallbackFactory: 指定 fallback 工厂类,用于创建 fallback 实例。
path: 指定服务路径,用于当服务有多个路径时进行区分。
primary: 是否为主要的客户端,默认为 true,表示可以被注入。4.使用案列:
1)发送GET请求:
服务1:
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("testGet")
public String testGet() {
return "test";
}
@GetMapping("testGet2")
public String testGet2(String s) {
return s;
}
}服务2:
@FeignClient(name = "name", url = "http://localhost:8081", path = "/test")
public interface TestFeign {
@GetMapping("testGet")
String test();
@GetMapping("testGet2")
String test_1(@RequestParam("s") String s);
}注意:如果GET请求有参数如果不使用
@RequestParam注解则会报错。
@GetMapping("testGet2")
String test_1(String s);
2)发送多参数GET请求:
@GetMapping("testGet3")
public String testGet3(Params params) {
return params.toString();
}@Data
public class Params {
private String key;
private String val;
}当GET请求有多个请求参数,openfeign的访问方式:
方法1:
@GetMapping("testGet3")
String test_3(@RequestParam("key") String key, @RequestParam("val") String val);方法2:
@GetMapping("testGet3")
String test_3(@RequestParam Map<String, Object> map);注意:
Map前面得加上@RequestParam注解不然会报如上的错误。
方法3(推荐使用):
在服务2中创建一个Params类:
@Data
public class Params {
private String key;
private String val;
}@GetMapping("testGet3")
String test_3(@SpringQueryMap Params params);注意:Params前面一定记得加上
@SpringQueryMap注解,否则会报错
3)发送POST请求:
application/json格式:
@PostMapping("testPost5")
public String testPost5(@RequestBody Params params) {
return params.toString();
}@PostMapping("testPost5")
String test_5(@RequestBody Params params)application/x-www-form-urlencoded格式:
@PostMapping("ttestPost4")
public String testGet4(Params params) {
return params.toString();
}@PostMapping("testPost4")
String test_4(@SpringQueryMap Params params);4)发送POST请求混合参数:
@PostMapping("testGet6")
public String testGet6(@RequestBody Params params, String s) {
return params.toString() + "/" + "s";
}@PostMapping("testGet6")
String test_6(@RequestBody Params params, @RequestParam("s") String s);5)发送路径变量的请求:
@GetMapping("testGet7/{id}")
public String test7(@PathVariable("id") String id) {
return id;
}@GetMapping("testGet7/{id}")
String test_7(@PathVariable("id") String id);6)设置请求头参数HEADER:
@GetMapping("test8")
public String test8(@RequestHeader("id") String id) {
return id;
}方式1:
@GetMapping("test8")
String test_8(@RequestHeader("id") String id);方式2:
通过拦截器RequestInterceptor添加请求头:
@Configuration
public class HeaderRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("id", "12");
}
}@GetMapping("test8")
String test_8();注意:如果在
HeaderRequestInterceptor上添加了@Configuration注解,那么这个拦截器是全局的,会对所有的@Openfeign类起作用;如果我们只想在某个@Openfeign类使用拦截器,去掉拦截器上的@Configuration,并在@FeignClient(configuration = HeaderRequestInterceptor.class)配置拦截器。
7)fallback/fallbackFactory的使用:
fallback/fallbackFactory直接使用是不会生效的,必须加入服务熔断组件才会生效。
5.Openfeign组件说明:
RequestInterceptor:

Openfeign请求拦截器,会在我们发送请求前执行apply(RequestTemplate template),RequestTemplate封装的则是我们的请求数据:
public class OpenfeignConfig {
@Bean
public RequestInterceptor interceptor() {
return requestTemplate -> {
requestTemplate.header("a","1" );
};
}
}我们通过拦截器修改/添加请求头、请求参数等操作。
注意:拦截器里面的方法入参
requestTemplate此时已经被创建出来了,所有可以获取到请求相关的数据。

Encoder:
编码器,将请求数据转换成接口需要的数据格式;在springcloud中默认使用的编码器是SpringEncoder;

@Bean
public Encoder encoder() {
return new Encoder() {
/**
* 编码
*
* @param object 请求数据对象
* @param bodyType 请求数据对象的类型
* @param template 请求模板,封装请求数据
* @throws EncodeException 编码异常
*/
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
System.out.println(object);
template.header("id", "13");
}
};
}我们自定义的编码器,只会在请求的接口有入参(请求参数,非请求头参数)的时候才会执行,否则不会执行。
我们通常在请求数据需要进行加密传输的时候会使用到自定义编码器,我们需要对encode方法中的object进行加密处理,将加密后的数据设置到RequestTemplate中;这个过程等于我们把入参数据对象修改为了接口实际需要的参数对象。
注意:编码器在拦截器之前执行,此时RequestTemplate还未被设置请求数据,因此我们不能通过RequestTemplate获取到任何请求参数。自定义的编码器会覆盖SpringEncoder编码器使之失效。
Decoder:
解码器,将响应的数据转成我们系统需要的对象类型;在springcloud中默认的解码器是ResponseEntityDecoder;

@Bean
private Decoder decoder() {
return new Decoder() {
@Override
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
String content = IoUtil.read(response.body().asInputStream(), CharsetUtil.UTF_8);
cn.hutool.json.JSONObject o = JSONUtil.parseObj(content);
String data = o.getStr("data");
if (StrUtil.isNotBlank(data)) {
JSON json = JSONUtil.parse(data);
o.set("data", json);
}
Object bean = JSONUtil.toBean(o.toString(), type, true);
return bean;
}
};
}我们在对响应数据进行特殊处理的时候会自定义解码器,如响应数据解密,响应数据格式转换等;只有接口反正状态码是200才会执行解码器里面的方法,如果返回状态码是4xx、5xx不会执行解码器,而是执行ErrorDecoder解码方法。
ErrorDecoder:
错误解码器,专门用来处理响应状态码为4xx、5xx的返回结果的解码器。

@Bean
public ErrorDecoder errorDecoder() {
return new ErrorDecoder() {
// 从Response最终解码出的必须是个异常类型
// methodKey:由feign.Feign#configKey生成
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() == 400) {
throw new IllegalArgumentException("bad zone name");
}
// 如果不是400,还是交给Default去解码处理
// 改进:这里使用单例即可,Default不用每次都去new
return new ErrorDecoder.Default().decode(methodKey, response);
}
};
}错误解码器,只会在响应状态码为4xx、5xx的时候执行,一般用于处理响应码不为2xx的情况。
Request.Options
超时时间选项,用来配置openfeign连接超时和读超时。
@Bean
public Request.Options options() {
//连接超时1000毫秒,读取超时1000毫秒
return new Request.Options(1000, 1000);
}Retryer:
重试器,当openfeign调用远程接口超时的时候会进行再次访问,以确保接口被成功执行。
@Bean
public Retryer retryer() {
System.out.println("执行");
//最大请求次数为3(1+2),初识间隔时间为100ms,重试最大间隔是1s
return new Retryer.Default(100,1,3);
}
注意:当调用幂等性要求严格接口时特别要注意重试带来的多次调用的风险。