微服务的概念现在已越来越普及,简单来说微服务就是将之前一个功能复杂的单体应用拆分成多个功能单一的服务。比如,一个复杂的ERP系统现在就会拆分成订单服务、仓储服务、财务服务等多个服务,他们之间互不影响。但是有时他们之间需要相互调用,那么就需要用到远程调用技术。
常用的远程调用技术有基于RPC的Dubbo和基于Http的Feign,本文主要介绍下Feign的使用,基于上一篇文章《原来Nacos这么好用,注册、配置中心全用它了》,准备好注册中心,此处使用的是Nacos。
准备
新建两个微服务项目,一个主项目(admin-web)和辅助项目(sub-web)。
项目引入feign相关依赖:
<!--spring cloud openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--openfeign性能优化okhttp-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
启动类或者配置类上增加Feign注解
@EnableFeignClients(basePackages = "org.gourd.hu.api")
@EnableDiscoveryClient
注:
basePackages配置的包路径一定要包含 @FeignClient注解的类位置,否则加载不到Feign调用类(如下图)。
项目增加配置:
ribbon:
eager-load:
enabled: true #开启饥饿加载
clients: admin-web,sub-web #为哪些服务的名称开启饥饿加载,多个用逗号分隔
MaxAutoRetries: 1 #最大重试次数,当注册中心中可以找到服务,但是服务连不上时将会重试
MaxAutoRetriesNextServer: 1 #切换实例的重试次数
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
ConnectTimeout: 10000 #请求连接的超时时间
ReadTimeout: 20000 #请求处理的超时时间
# feign 配置
feign:
httpclient:
enabled: false
okhttp:
enabled: true
client:
config:
default:
# 连接超时时间5s
connectTimeout: 5000
# 接口超时时间60s
readTimeout: 60000
# 开启feign的熔断功能
hystrix:
enabled: true
注
feign和ribbon的超时时间需要一起设置,否则超时配置不生效。
调用/熔断类
/**
* sub服务API调用类
* contextId属性:解决相同name不同client不同配置的问题
* feign不支持下划线"_",支持"-",否则报错:Service id not legal hostname
* @author gourd
*/
@FeignClient(name = "sub",contextId = "sub-one" ,path = "/test",fallbackFactory = SubFallbackFactory.class)
public interface SubApi {
@GetMapping("/hello/{id}")
BaseResponse helloTest(@PathVariable("id") Long id);
@GetMapping("/hello")
BaseResponse helloTestParam(@RequestParam("id") Long id);
}
注
-
如果你调用报如下错误,请检查@PathVariable、@RequestParam是否设置了value值。
Caused by: java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
-
如果一个服务定义多个Feign(通常也会定义多个,一个controller对应一个feign类),需要在@FeignClient中增加contextId属性。否则最后一个Feign会覆盖前面的Feign,会报一些奇怪的错误,我遇到的一个就是一直报FallbackBean不存在。
-
如果你没有使用功能注册中心,是不可以通过name调用到的,@FeignClient还支持url调用,可直接配置常量路径,也可以从配置项中读取,如下
@FeignClient(url = "127.0.0.1:8080")
@FeignClient(url = "${api.url}")
熔断配置方式有两种,一种是直接配置Fallback,一种是通过FallBackFactory配置,如下
@FeignClient(fallback = SubFallback.class)
@FeignClient(fallbackFactory = SubFallbackFactory.class)
我这里采用的是通过FallBackFactory的方式,因为这种方式可以取到throwable异常信息。下面是代码实现:
@Component
public class SubFallbackFactory implements FallbackFactory<SubFallback> {
@Override
public SubFallback create(Throwable throwable) {
return new SubFallback(throwable);
}
}
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class SubFallback implements SubApi {
private Throwable throwable;
@Override
public BaseResponse helloTest(Long id) {
log.info("helloTest调用失败:",throwable);
return BaseResponse.fail("helloTest调用失败:"+throwable.getCause());
}
@Override
public BaseResponse helloTestParam(Long id) {
log.info("helloTest调用失败:",throwable);
return BaseResponse.fail("helloTest调用失败:"+throwable.getCause());
}
}
其实到这里就整合完成了,我们可以写个测试接口,将SubApi注入即可直接调用了,非常方便。
测试结果
拓展
有时候我们的接口可能会有鉴权,通过OpenFeign调用如果没带验证信息,会直接被拦截掉。那么我需要在调用时进行设置,@FeignClient支持一个configuration参数配置。
@FeignClient(configuration = FeignConfig.Class)
Feignnfig实现如下,可根据业务实现不同逻辑。
@Slf4j
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// header里面添加约定值,或验证信息
requestTemplate.header("", "");
log.info("header:" + requestTemplate.headers());
}
}
写在最后
相关代码摘自本人开源项目:https://gitee.com/gourd-hu/spring-cloud-plus
「往期文章」
END
扫码二维码,获取更多精彩