背景
在当前业务中,项目需要远程http服务时,往往使用HttpClient进行post/get调用。但是这种编码方式经常需要try catch包裹住这段调用,避免在httpClient调用时,目标服务器出错不可用。为了改善编码,并且兼容现有的微服务框架,我将项目里的httpClient调用替换成Feign调用。
方案
- 老的HttpClient调用暂不修改,让业务继续跑。
- 新的外部Http服务调用,使用Feign。
- 在日常迭代与优化中,逐步替代掉旧的调用方式。
实行
1) 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
2) 定义feign调用时的传参
(为了方便feign调用时候的参数传递)
@Configurable
@AllArgsConstructor
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {
private final ObjectMapper objectMapper;
@Override
public void apply(RequestTemplate template) {
// get-pojo贯穿
if (template.method().equals(HttpMethod.GET.name()) && template.body() != null) {
try {//template.requestBody().asBytes()
JsonNode jsonNode = objectMapper.readTree(template.body());
Map<String, Collection<String>> queries = new HashMap<>();
//feign 不支持 GET 方法传 POJO, json body转query
buildQuery(jsonNode, "", queries);
template.queries(queries);
template.body(Request.Body.empty());
} catch (IOException e) {
e.printStackTrace();
}
}
}
//处理 get-pojo贯穿
private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
if (!jsonNode.isContainerNode()) { //叶子节点
if (jsonNode.isNull()) {
return;
}
Collection<String> values = queries.computeIfAbsent(path, k -> new ArrayList<>());
values.add(jsonNode.asText());
return;
}
if (jsonNode.isArray()) { //数组节点
Iterator<JsonNode> it = jsonNode.elements();
while (it.hasNext()) {
buildQuery(it.next(), path, queries);
}
} else {
Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
if (StringUtils.hasText(path)) {
buildQuery(entry.getValue(), path + SymbolEnum.DOT.getValue() + entry.getKey(), queries);
} else { //根节点
buildQuery(entry.getValue(), entry.getKey(), queries);
}
}
}
}
}
3) 定义序列化bean
@Configuration
public class FeignAutoConfiguration {
@Bean
public FeignBasicAuthRequestInterceptor interceptor () {
return new FeignBasicAuthRequestInterceptor(new ObjectMapper());
}
@Bean
feign.Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
4) 声明feign接口
@FeignClient(name = "test",
contextId = "testRemoteService",
url = "${host}",
path = "通用路径")
public interface TestRemoteService {
@GetMapping("/test")
ApiResponse<String> test(
@RequestParam("testId") Long testId, @RequestParam("userId") Long userId);
}
5) 启动feign
> 在启动类上加 @EnableFeignClients(basePackages = { "包名" })
6) 使用feign接口
> 直接在spring里@Autowrid注入, 正常调用于使用
7) 如果需要对接口做熔断,直接使用hystrix。
(这一块先略)