Spring Cloud Alibaba Sentinel 除了对 RestTemplate 作了支持,一样对于 Feign 也作了支持,若是咱们要从 Hystrix 切换到 Sentinel 是很是方便的,下面来介绍下如何对 Feign 的支持以及实现原理。spring
spring-cloud-starter-alibaba-sentinel 的依赖仍是要加的,以下:微信
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>0.2.1.RELEASE</version> </dependency>
须要在配置文件中开启 sentinel 对 feign 的支持:app
feign.sentinel.enabled=true
而后咱们定义本身须要调用的 Feign Client:框架
@FeignClient(name = "user-service", fallback = UserFeignClientFallback.class) public interface UserFeignClient { @GetMapping("/user/get") public String getUser(@RequestParam("id") Long id); }
定义 fallback 类 UserFeignClientFallback:ide
@Component public class UserFeignClientFallback implements UserFeignClient { @Override public String getUser(Long id) { return "fallback"; } }
测试代码:源码分析
@Autowired private UserFeignClient userFeignClient; @GetMapping("/testFeign") public String testFeign() { return userFeignClient.getUser(1L); }
你能够将这个 Client 对应的 user-service 停掉,而后就能够看到输出的内容是 "fallback"学习
若是要对 Feign 调用作限流,资源名称的规则是精确到接口的,以咱们上面定义的接口来分析,资源名称就是GET:http://user-service/user/get,至于资源名称怎么定义的,接下面的源码分析你就知道了。测试
首先看SentinelFeignAutoConfiguration中如何自动配置:ui
@Bean @Scope("prototype") @ConditionalOnMissingBean @ConditionalOnProperty(name = "feign.sentinel.enabled") public Feign.Builder feignSentinelBuilder() { return SentinelFeign.builder(); }
@ConditionalOnProperty 中 feign.sentinel.enabled 起了决定性做用,这也就是为何咱们须要在配置文件中指定 feign.sentinel.enabled=true
。this
接下来看 SentinelFeign.builder 里面的实现:
build方法中从新实现了super.invocationHandlerFactory方法,也就是动态代理工厂,构建的是InvocationHandler对象。
build中会获取Feign Client中的信息,好比fallback,fallbackFactory等,而后建立一个SentinelInvocationHandler,SentinelInvocationHandler继承了InvocationHandler。
@Override public Feign build() { super.invocationHandlerFactory(new InvocationHandlerFactory() { @Override public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) { // 获得Feign Client Bean Object feignClientFactoryBean = Builder.this.applicationContext .getBean("&" + target.type().getName()); // 获得fallback类 Class fallback = (Class) getFieldValue(feignClientFactoryBean, "fallback"); // 获得fallbackFactory类 Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean, "fallbackFactory"); // 获得调用的服务名称 String name = (String) getFieldValue(feignClientFactoryBean, "name"); Object fallbackInstance; FallbackFactory fallbackFactoryInstance; // 检查 fallback 和 fallbackFactory 属性 if (void.class != fallback) { fallbackInstance = getFromContext(name, "fallback", fallback, target.type()); return new SentinelInvocationHandler(target, dispatch, new FallbackFactory.Default(fallbackInstance)); } if (void.class != fallbackFactory) { fallbackFactoryInstance = (FallbackFactory) getFromContext(name, "fallbackFactory", fallbackFactory, FallbackFactory.class); return new SentinelInvocationHandler(target, dispatch, fallbackFactoryInstance); } return new SentinelInvocationHandler(target, dispatch); } // 省略部分代码 }); super.contract(new SentinelContractHolder(contract)); return super.build(); }
SentinelInvocationHandler中的invoke方法里面进行熔断限流的处理。
// 获得资源名称(GET:http://user-service/user/get) String resourceName = methodMetadata.template().method().toUpperCase() + ":" + hardCodedTarget.url() + methodMetadata.template().url(); Entry entry = null; try { ContextUtil.enter(resourceName); entry = SphU.entry(resourceName, EntryType.OUT, 1, args); result = methodHandler.invoke(args); } catch (Throwable ex) { // fallback handle if (!BlockException.isBlockException(ex)) { Tracer.trace(ex); } if (fallbackFactory != null) { try { // 回退处理 Object fallbackResult = fallbackMethodMap.get(method) .invoke(fallbackFactory.create(ex), args); return fallbackResult; } catch (IllegalAccessException e) { // shouldn't happen as method is public due to being an interface throw new AssertionError(e); } catch (InvocationTargetException e) { throw new AssertionError(e.getCause()); } } // 省略..... }
总的来讲,这些框架的整合都有类似之处,前面讲RestTemplate的整合其实和Ribbon中的@LoadBalanced原理差很少,此次的Feign的整合其实咱们从其余框架的整合也是能够参考出来的,最典型的就是Hystrix了。
咱们想下Hystrix要对Feign的调用进行熔断处理,那么确定是将Feign的请求包装了HystrixCommand。一样的道理,咱们只要找到Hystrix是如何包装的,无非就是将Hystrix的代码换成Sentinel的代码而已。
InvocationHandlerFactory是用于建立动态代理的工厂,有默认的实现,也有Hystrix的实现feign.hystrix.HystrixFeign。
Feign build(final FallbackFactory<?> nullableFallbackFactory) { super.invocationHandlerFactory(new InvocationHandlerFactory() { @Override public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) { return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory); } }); super.contract(new HystrixDelegatingContract(contract)); return super.build(); }
上面这段代码是否是跟Sentinel包装的相似,不一样的是Sentinel构造的是SentinelInvocationHandler ,Hystrix构造的是HystrixInvocationHandle。在HystrixInvocationHandler的invoke方法中进行HystrixCommand的包装。