按之前的文章,eureka 服务集群搭好了,eureka 客户端也集成到 springboot 中了。
那么接下来就是具体具体具体的业务接口调用了。
对于 eureka 这些都属于外网的基础,集成完成即可,而 feign 不同,那是需要我们实际编码的,所以得掌握它的具体使用方法,故本文的标题是 “使用feign”。
进入正题,关于使用 feign 主要包含如下几点:
- 工程集成 feign 添加pom依赖
- 注解开启 feign 客户端支持
- 有关 feign 的配置文件参数配置
- 编写 feign 具体的 Java 调用类、设置 feign 调用的服务名,指定 Hystrix 熔断托底处理类、编写 Hystrix 熔断托底处理类
正文内容不多,篇幅主要被代码示例占用。
一、添加依赖
本文使用的 springboot 版本 2.1.8.RELEASE
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
二、注解启用
注解 @EnableFeignClients
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class ShanhyService2Application {
public static void main(String[] args) {
SpringApplication.run(ShanhyService2Application.class, args);
}
}
三、属性配置文件
# 是否启用httpclient
feign.httpclient.enabled=false
# 是否启用httpok(性能比httpclient高)
feign.okhttp.enabled=true
# 是否启用hystrix
feign.hystrix.enabled=true
# 请求连接超时时间(毫秒)
feign.httpclient.connection-timeout=3000
四、编写 Restful 接口调用类
使用Feign 调用 Restful 接口,feign 内部基于 ribbon 实现负载均衡,无需做特殊配置处理。
这块的代码多说两句:
1.建议将提供服务的业务,Controller 抽象接口接口出来,并作为一个独立的 module 工程(包含API返回的一些VO类)。
2.接口调用方,也就是 FeignClient 端的工程,也直接引入服务提供方抽象的 API 接口依赖。
3.这样服务调用方 FeignClient 创建接口类继承依赖进来的 API 接口即可(不用重复的定义 GetMapping PostMapping RequestMapping 那一堆方法)
4.@FeignClient
建议使用 fallbackFactory 属性,而不是 fallback 属性。因为前者可以操作异常信息,可操作性相对更强一些。变成层面也没有增加什么复杂性,只不过匿名类的方式比 fallback 略显不够直观(这个根据自己实际情况选择)。
为了示范代码,下面是几个 Java 类示例:
/**
* 服务接口定义
*
* @author SHANHY
*
*/
@RequestMapping("/example")
public interface ExampleApi {
/**
* 漫天飞的帖子说这里不支持GetMapping和PathVariable还得必须设置value
* (我并没发现这个问题)
* 为什么都喜欢无脑拷贝帖子,就算曾经有bug,难道永远都有吗
*
* @param id
* @return
*/
@GetMapping("/test10/{id}")
public ResultVO demo10(@PathVariable String id);
}
/**
* 声明式服务<br/>
* Feign+Hystrix 熔断降级<br/>
* 其他业务模块可以直接依赖引用,通过调用接口即可完成服务的远程调用,open-feign会对此类做动态代理
*
* @FeignClient 中的 name 表示要调用的服务的实例名称(在eureka中的名称)
* @FeignClient 中的 fallbackFactory 表示调用服务出现问题时可以打印出相关日志
*
*
* @author SHANHY
*
*/
@FeignClient(name = "shanhy-example1", path = "/shanhy-example1", fallbackFactory = ExampleHystrixFeignFallBackFactory.class)
public interface ExampleFeignClient extends ExampleApi {
}
/**
* 熔断降级托底实现
*
* @author SHANHY
*
*/
@Component // @Component 这个不能缺失,否则会报错 No fallbackFactory instance of type class
public class ExampleHystrixFeignFallBackFactory implements FallbackFactory<ExampleFeignClient> {
private static final Logger LOG = LoggerFactory.getLogger(ExampleHystrixFeignFallBackFactory.class);
@Override
public ExampleFeignClient create(Throwable cause) {
LOG.error("fallback; reason was: " + cause.getMessage(), cause);
return new ExampleFeignClient() {
@Override
public ResultVO demo10(String id) {
return ResultVO.FAIL;
}
};
}
}
下面的代码是调用接口测试验证的方法片段:
@GetMapping("/feigntest")
public String feign(String msg) {
ResultVO res = exampleFeignClient.demo10(StringUtils.defaultIfBlank(msg, "Hello"));
String result = "SHOW,CODE=".concat(res.getCode()).concat(",MSG=").concat(res.getMessage());
if(res.getData() != null)
result = result.concat(",DATA=").concat(res.getData().toString());
return result;
}
最后是服务提供方的Controller 实现:
/**
* 示例
*
* @author 单红宇
*
*/
@RestController
public class ExampleController implements ExampleApi {
private static final Logger LOG = LoggerFactory.getLogger(ExampleController.class);
@Override
public ResultVO demo10(String id) {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
LOG.info("RemoteAddr={}", request.getRemoteAddr());
return new ResultVO("id=".concat(id));
}
}
注解 @FeignClient 的主要属性说明:
name: 指定要调用的微服务的名字,用于服务发现,必填
value: 同name属性,alias for name
url: url一般用于调试,可以手动指定调用的绝对地址
configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
decode404:当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException,默认为false
fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
path: 定义当前FeignClient的统一前缀,设置context-path的服务,这个值如果不注意配置就404了
思路建议划重点:
方法中的 request 我使用的是 RequestContextHolder 的方式获取的,你可以封装一个静态方法使用。如果你把 request 放到参数里,或者通过类属性注入的方式,实际上也是常用的。但是这样的话,我们抽象的 ExampleApi 接口,就有些侵入性了不够干净。所以我本人的建议是抽象的 ExampleApi 接口,里面只包含方法、常用数据类型和数据交换的 VO 对象。
写在最后
默认情况下,访问http://IP:PORT/actuator/hystrix.stream 是会返回404,这是因为Feign虽然整合了Hystrix,但并没有整合Hystrix的监控。如需使用Hystrix Stream进行监控,找一下度娘 “Feign Hystrix Dashboard”。
(END)