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;
...