Feign 简介
Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
Feign和OpenFeign的关系
Feign本身不支持Spring MVC的注解,它有一套自己的注解
OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。
OpenFeign的@FeignClient
可以解析SpringMVC的@RequestMapping注解下的接口,
并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
使用
依赖
<!-- 引入open-feign --> | |
<dependency> | |
<groupId>org.springframework.cloud</groupId> | |
<artifactId>spring-cloud-starter-openfeign</artifactId> | |
</dependency> | |
<!-- Feign默认所有带参数的请求都是Post,想要使用指定的提交方式需引入依赖 --> | |
<dependency> | |
<groupId>io.github.openfeign</groupId> | |
<artifactId>feign-okhttp</artifactId> | |
</dependency> |
权限拦截器
这里针对服务与服务之间权限验证
// 定义拦截器 | |
public class MyBasicAuthRequestInterceptor implements RequestInterceptor { | |
@Override | |
public void apply(RequestTemplate template) { | |
// TODO Auto-generated method stub | |
template.header("Authorization", "Basic cm9vdDpyb290"); | |
} | |
} | |
配置文件 | |
feign: | |
client: | |
config: | |
service-valuation: | |
request-interceptors: | |
- com.online.taxi.passenger.feign.interceptor.MyBasicAuthRequestInterceptor | |
通用配置
feign: | |
compression: | |
# 配置请求GZIP压缩 | |
request: | |
enabled: true | |
# 配置压缩支持的MIME TYPE | |
mime-types: text/xml,application/xml,application/json | |
# 配置压缩数据大小的下限 | |
min-request-size: 2048 | |
# 配置响应GZIP压缩 | |
response: | |
enabled: true | |
# 采用 apache的 okhttp 作为 http访问 | |
okhttp: | |
enabled: true | |
# feign 客户端配置 | |
client: | |
config: | |
# 默认配置 -> 可单独指定 feignName | |
default: | |
# 链接超时时间 | |
connectTimeout: 5000 | |
# 读取超时时间 | |
readTimeout: 5000 | |
# 日志等级 | |
loggerLevel: full |
调用
脱离注册中心
@FeignClient(name = "single",url = "192.168.0.102:7601") | |
public interface ConsumerApiBySingle { | |
// 针对 Feign 的 单独调用 | |
@GetMapping("getHi") | |
String getHi(); | |
} |
Eureka 调用 (Feign - Ribbon 实现的负载均衡,RestTemplate发起的调用)
@FeignClient(name = "provider") | |
public interface UserApi { | |
@GetMapping("/users/getUser") | |
ResultDto<User> getUser(); | |
/** | |
* 三种 传参格式 | |
* 1、 默认 @RequestParam | |
* 2、 指定参数名 @RequestParam(name = "xxx") | |
* 3、 传输一个Json格式对象 @RequestBody | |
* @param userName | |
* @return | |
*/ | |
@PostMapping("/users/saveUser") | |
ResultDto<User> saveUser(@RequestParam(name = "userName") String userName); | |
} |
踩坑
在做 SpringCloud 多服务调用时,有些人可能习惯直接使用Entity来做返回值。但是针对 熔断、降级、隔离问题时,需要做好异常状态判断就很麻烦了
于是,封装了DTO层来做服务之间的数据传输
DTO 范型大坑 ⭐️⭐️⭐️
中间数据传输层为了方便数据的传输 , 使用范型可以很好的封装起来传输对象 , 并且可以封装数据状态类型和其他信息, 坑就在这里 !!!
import com.fasterxml.jackson.annotation.JsonProperty; | |
import org.springframework.http.HttpStatus; | |
import java.io.Serializable; | |
import java.util.HashMap; | |
import java.util.Map; | |
/** | |
* 统一返回参数 | |
* | |
* @date 2020年5月15日10:40:54 | |
* @author Parker | |
* | |
* 在 Feign 的调用过程中,无法直接序列化数据 | |
* | |
* 所以要加上 @JsonProperty ,否者返回则为一个null | |
* | |
*/ | |
public class ResultDto<T> implements Serializable { | |
/** Map 容器 */ | |
private final Map<String,Object> resultMap = new HashMap<>(3); | |
/** 数据 */ | |
private T data; | |
public ResultDto(){ | |
resultMap.put("success", true); | |
resultMap.put("code", HttpStatus.OK.value()); | |
resultMap.put("msg", "操作成功"); | |
} | |
/** | |
* 获得编号 | |
* @return | |
*/ | |
public int getCode() { | |
return (int)resultMap.get("code"); | |
} | |
/** | |
* 设置编号 | |
* @param code | |
*/ | |
public void setCode(int code) { | |
resultMap.put("code", code); | |
} | |
/** | |
* 获得信息 | |
* @return | |
*/ | |
public String getMsg() { | |
return (String)resultMap.get("msg"); | |
} | |
/** | |
* 设置信息 | |
* @param msg | |
*/ | |
public void setMsg(String msg) {//向json中添加属性,在js中访问,请调用data.msg | |
resultMap.put("msg", msg); | |
} | |
/** | |
* 获得状态 | |
* @return | |
*/ | |
public boolean isSuccess() { | |
return (boolean)resultMap.get("success"); | |
} | |
/** | |
* 设置状态 | |
* @param success | |
*/ | |
public void setSuccess(boolean success) { | |
resultMap.put("success", success); | |
} | |
// --------------------------------- | |
/** | |
* 设置值 | |
* @param value | |
* @return | |
*/ | |
public ResultDto<T> put(T value) { | |
data = value; | |
return this; | |
} | |
/** | |
* 获得值 | |
* @return | |
*/ | |
public T get(){ | |
return data; | |
} | |
} |
应用场景
-
序列化以及反序列化采用jackson
-
调用第三方采用feign注解式接口
问题分析
ResultDto 是一个api通用接口返回泛型类,User 为传入的具体泛型类
在Feign接口中返回泛型类时,由于Java的泛型机制,在实例化之前无法得到具体的类型 ,因此,虽然服务提供方返回的是具体实例的数据,但是在客户端decode时,无法转化为具体的类。
解决方案
针对范型的字段必须要用@JsonProperty("字段名")
或者```@JsonSetter("字段名")``注解来显示声明属性名字,尤其是首字母为大写的情况,否则反序列化后的数据就为空值。
/** | |
* 统一返回参数 | |
* | |
* @date 2020年5月15日10:40:54 | |
* @author Parker | |
* | |
* 在 Feign 的调用过程中,无法直接序列化数据 | |
* | |
* 所以要加上 @JsonProperty ,否者返回则为一个null | |
* | |
*/ | |
public class ResultDto<T> implements Serializable { | |
/** Map 容器 */ | |
@JsonProperty("resultMap") | |
private final Map<String,Object> resultMap = new HashMap<>(3); | |
/** 数据 */ | |
@JsonProperty("data") | |
private T data; | |
... |