一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情。
feign简化http调用,接口式声明;
- pom.xml, 版本在springcloud中有对应,例如: spring cloud Hoxton.SR8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- springboot启动类中添加
@EnableFeignClients注解 - 编写远程调用接口
/**
* 使用FeignClient注解, name必填, url为远程调用的基础url ${}占位符,获取配置文件中的信息
*/
@FeignClient(name = "school", url = "${school.url}")
public interface SchoolClient {
/**
* 根据学校id批量查询学校
*
* @param dto
* @return com.test.growth.school.feign.resp.BasicSchoolResp<java.util.List<com.test.growth.school.third.phone.resp.SchoolResp>>
*/
@PostMapping("/school/getSchoolInfo")
DefaultResp<List<SchoolResp>> getSchoolByIds(@RequestBody SchoolInfoDTO dto);
}
- 由于有些内部服务调用,需要在请求头中额外的信息, 如何解决这种情况?
{
"X-Auth-appid": 2003251,
"x-auth-sign": "4ba37068755c6ddefe56ddb",
"x-auth-timestamp": 1617904989
}
使用Feign中Request Interceptor功能.
Request Interceptor 分为全局和部分,
-
如何设置Request Interceptor为全局的?
- 自定义的Request Interceptor被spring托管,使用
@Component注解
@Component public class DefaultRequestInterceptor implements RequestInterceptor{}- 自定义的自定义的Request Interceptor不被spring托管,通过配置文件配置,
feign.client.config.default配置的内容都是为全局默认的.
package com.test.growth.school.feign; public class DefaultRequestInterceptor implements RequestInterceptor{}feign: client: config: default: requestInterceptors: - com.test.growth.school.feign.DefaultRequestInterceptor - 自定义的Request Interceptor被spring托管,使用
-
如何设置Request Interceptor为部分的?仅针对于部分的
FeignClient- 通过配置文件配置
package com.test.growth.school.feign; /** * 测试拦截链路 */ public class OtherRequestInterceptor implements RequestInterceptor{ }feign: client: config: default: requestInterceptors: - com.test.growth.school.feign.DefaultRequestInterceptor school: # @FeignClient 中的name值 requestInterceptors: - com.test.growth.school.feign.DefaultRequestInterceptor # 实际链路 default -> other- 通过
@FeignClient中configuration属性指定配置类.
应用示例:
package com.test.growth.school.feign;
import com.test.growth.school.util.Md5Util;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* 负责请求头中添加
* X-Auth-appid: appid
* X-Auth-TimeStamp: timeMillis
* X-Auth-Sign: sign
*/
@Slf4j
@Component
public class DefaultRequestInterceptor implements RequestInterceptor{
@Override
public void apply(RequestTemplate template) {
String name = template.feignTarget().name();
Environment env = ApplicationContextUtils.getApplicationContext().getEnvironment();
String url = env.getProperty(name + ".url");
String appId = env.getProperty(name + ".appId");
String appKey = env.getProperty(name + ".appKey");
if (StringUtils.isBlank(url) || StringUtils.isBlank(appId) || StringUtils.isBlank(appKey)) {
log.warn("配置信息错误,请检查{}.url={}, appId={}, appKey={}等配置", name, url, appId, appKey);
return ;
}
log.info("内部服务调用,请求头信息设置");
String timeMillis = System.currentTimeMillis() / 1000 + "";
StringBuilder content = new StringBuilder();
content.append(appId).append("&").append(timeMillis).append(appKey);
String sign = Md5Util.string2MD5(content.toString());
log.info("请求头信息X-Auth-appid={}, X-Auth-TimeStamp={}, X-Auth-Sign={}", appId, timeMillis, sign);
template.header("X-Auth-appid", appId);
template.header("X-Auth-TimeStamp", timeMillis);
template.header("X-Auth-Sign", sign);
}
}
- 远程调用的接口具备比较统一的响应格式,例如:
@Data
public class DefaultResp<T> {
// code = 0时操作成功
private Integer code;
private String msg;
private T data;
}
比如远程调用的接口如下
@FeignClient(name = "school", url = "${school.url}")
public interface SchoolClient {
@PostMapping("/school/getSchoolInfo")
DefaultResp<List<SchoolResp>> getSchoolByIds(@RequestBody SchoolInfoDTO dto);
@GetMapping("/school/page")
DefaultResp<Page<SchoolResp>> page(SchoolInfoPageDTO dto);
@GetMapping("/~~")
DefaultResp<A> a(B dto);
@GetMapping("/~~")
DefaultResp<C> c(D dto);
}
那客户端使用的时候一般写法如下
@Autowired
SchoolClient schoolClient;
public void demo1() {
DefaultResp<A> resp = schoolClient.a(dto);
if (resp.getCode() == 0) {
// 调用成功
} else {
// 调用失败,抛出异常
}
}