Spring Cloud Alibaba Sentinel 除了对 RestTemplate 做了支持,同样对于 Feign 也做了支持,如果我们要从 Hystrix 切换到 Sentinel 是非常方便的,下面来介绍下如何对 Feign 的支持以及实现原理。

集成 Feign 使用

spring-cloud-starter-alibaba-sentinel 的依赖还是要加的,如下:

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  4. <version>0.2.1.RELEASE</version>
  5. </dependency>

需要在配置文件中开启 sentinel 对 feign 的支持:

  1. feign.sentinel.enabled=true

然后我们定义自己需要调用的 Feign Client:

  1. @FeignClient(name = "user-service", fallback = UserFeignClientFallback.class)
  2. public interface UserFeignClient {
  3. @GetMapping("/user/get")
  4. public String getUser(@RequestParam("id") Long id);
  5. }

定义 fallback 类 UserFeignClientFallback:

  1. @Component
  2. public class UserFeignClientFallback implements UserFeignClient {
  3. @Override
  4. public String getUser(Long id) {
  5. return "fallback";
  6. }
  7. }

测试代码:

  1. @Autowired
  2. private UserFeignClient userFeignClient;
  3. @GetMapping("/testFeign")
  4. public String testFeign() {
  5. return userFeignClient.getUser(1L);
  6. }

你可以将这个 Client 对应的 user-service 停掉,然后就可以看到输出的内容是 "fallback"

如果要对 Feign 调用做限流,资源名称的规则是精确到接口的,以我们上面定义的接口来分析,资源名称就是GET:http://user-service/user/get,至于资源名称怎么定义的,接下面的源码分析你就知道了。

原理分析

首先看SentinelFeignAutoConfiguration中如何自动配置:

  1. @Bean
  2. @Scope("prototype")
  3. @ConditionalOnMissingBean
  4. @ConditionalOnProperty(name = "feign.sentinel.enabled")
  5. public Feign.Builder feignSentinelBuilder() {
  6. return SentinelFeign.builder();
  7. }

@ConditionalOnProperty 中 feign.sentinel.enabled 起了决定性作用,这也就是为什么我们需要在配置文件中指定 feign.sentinel.enabled=true

接下来看 SentinelFeign.builder 里面的实现:

build方法中重新实现了super.invocationHandlerFactory方法,也就是动态代理工厂,构建的是InvocationHandler对象。

build中会获取Feign Client中的信息,比如fallback,fallbackFactory等,然后创建一个SentinelInvocationHandler,SentinelInvocationHandler继承了InvocationHandler。

  1. @Override
  2. public Feign build() {
  3. super.invocationHandlerFactory(new InvocationHandlerFactory() {
  4. @Override
  5. public InvocationHandler create(Target target,
  6. Map<Method, MethodHandler> dispatch) {
  7. // 得到Feign Client Bean
  8. Object feignClientFactoryBean = Builder.this.applicationContext
  9. .getBean("&" + target.type().getName());
  10. // 得到fallback类
  11. Class fallback = (Class) getFieldValue(feignClientFactoryBean,
  12. "fallback");
  13. // 得到fallbackFactory类
  14. Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,
  15. "fallbackFactory");
  16. // 得到调用的服务名称
  17. String name = (String) getFieldValue(feignClientFactoryBean, "name");
  18. Object fallbackInstance;
  19. FallbackFactory fallbackFactoryInstance;
  20. // 检查 fallback 和 fallbackFactory 属性
  21. if (void.class != fallback) {
  22. fallbackInstance = getFromContext(name, "fallback", fallback,
  23. target.type());
  24. return new SentinelInvocationHandler(target, dispatch,
  25. new FallbackFactory.Default(fallbackInstance));
  26. }
  27. if (void.class != fallbackFactory) {
  28. fallbackFactoryInstance = (FallbackFactory) getFromContext(name,
  29. "fallbackFactory", fallbackFactory,
  30. FallbackFactory.class);
  31. return new SentinelInvocationHandler(target, dispatch,
  32. fallbackFactoryInstance);
  33. }
  34. return new SentinelInvocationHandler(target, dispatch);
  35. }
  36. // 省略部分代码
  37. });
  38. super.contract(new SentinelContractHolder(contract));
  39. return super.build();
  40. }

SentinelInvocationHandler中的invoke方法里面进行熔断限流的处理。

  1. // 得到资源名称(GET:http://user-service/user/get)
  2. String resourceName = methodMetadata.template().method().toUpperCase() + ":"
  3. + hardCodedTarget.url() + methodMetadata.template().url();
  4. Entry entry = null;
  5. try {
  6. ContextUtil.enter(resourceName);
  7. entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
  8. result = methodHandler.invoke(args);
  9. }
  10. catch (Throwable ex) {
  11. // fallback handle
  12. if (!BlockException.isBlockException(ex)) {
  13. Tracer.trace(ex);
  14. }
  15. if (fallbackFactory != null) {
  16. try {
  17. // 回退处理
  18. Object fallbackResult = fallbackMethodMap.get(method)
  19. .invoke(fallbackFactory.create(ex), args);
  20. return fallbackResult;
  21. }
  22. catch (IllegalAccessException e) {
  23. // shouldn't happen as method is public due to being an interface
  24. throw new AssertionError(e);
  25. }
  26. catch (InvocationTargetException e) {
  27. throw new AssertionError(e.getCause());
  28. }
  29. }
  30. // 省略.....
  31. }

总结

总的来说,这些框架的整合都有相似之处,前面讲RestTemplate的整合其实和Ribbon中的@LoadBalanced原理差不多,这次的Feign的整合其实我们从其他框架的整合也是可以参考出来的,最典型的就是Hystrix了。

我们想下Hystrix要对Feign的调用进行熔断处理,那么肯定是将Feign的请求包装了HystrixCommand。同样的道理,我们只要找到Hystrix是如何包装的,无非就是将Hystrix的代码换成Sentinel的代码而已。

InvocationHandlerFactory是用于创建动态代理的工厂,有默认的实现,也有Hystrix的实现feign.hystrix.HystrixFeign。

  1. Feign build(final FallbackFactory<?> nullableFallbackFactory) {
  2. super.invocationHandlerFactory(new InvocationHandlerFactory() {
  3. @Override public InvocationHandler create(Target target,
  4. Map<Method, MethodHandler> dispatch) {
  5. return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
  6. }
  7. });
  8. super.contract(new HystrixDelegatingContract(contract));
  9. return super.build();
  10. }

上面这段代码是不是跟Sentinel包装的类似,不同的是Sentinel构造的是SentinelInvocationHandler ,Hystrix构造的是HystrixInvocationHandle。在HystrixInvocationHandler的invoke方法中进行HystrixCommand的包装。

欢迎加入我的知识星球,一起交流技术,免费学习猿天地的课程(http://cxytiandi.com/course)

PS:目前星球中正在星主的带领下组队学习Sentinel,等你哦!

Spring Cloud Alibaba Sentinel对Feign的支持的更多相关文章

  1. Spring Cloud Alibaba Sentinel对RestTemplate的支持

    Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,在构造 RestTemplate bean的时候需要加上 @S ...

  2. Spring Cloud Alibaba Sentinel 整合 Feign 的设计实现

    作者 | Spring Cloud Alibaba 高级开发工程师洛夜 来自公众号阿里巴巴中间件投稿 前段时间 Hystrix 宣布不再维护之后(Hystrix 停止开发...Spring Cloud ...

  3. 0.9.0.RELEASE版本的spring cloud alibaba sentinel+feign降级处理实例

    既然用到了feign,那么主要是针对服务消费方的降级处理.我们基于0.9.0.RELEASE版本的spring cloud alibaba nacos+feign实例添油加醋,把sentinel功能加 ...

  4. Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵进阶实战

    Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵进阶实战 在阅读本文前,建议先阅读<Spring Cloud Alibaba | Sentinel:分布式系 ...

  5. Spring Cloud Alibaba | Sentinel: 分布式系统的流量防卫兵初探

    目录 Spring Cloud Alibaba | Sentinel: 分布式系统的流量防卫兵初探 1. Sentinel 是什么? 2. Sentinel 的特征: 3. Sentinel 的开源生 ...

  6. Spring Cloud Alibaba | Sentinel: 服务限流基础篇

    目录 Spring Cloud Alibaba | Sentinel: 服务限流基础篇 1. 简介 2. 定义资源 2.1 主流框架的默认适配 2.2 抛出异常的方式定义资源 2.3 返回布尔值方式定 ...

  7. Spring Cloud Alibaba | Sentinel: 服务限流高级篇

    目录 Spring Cloud Alibaba | Sentinel: 服务限流高级篇 1. 熔断降级 1.1 降级策略 2. 热点参数限流 2.1 项目依赖 2.2 热点参数规则 3. 系统自适应限 ...

  8. Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵基础实战

    Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵基础实战 Springboot: 2.1.8.RELEASE SpringCloud: Greenwich.SR2 ...

  9. Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵动态限流规则

    Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵动态限流规则 前面几篇文章较为详细的介绍了Sentinel的使用姿势,还没看过的小伙伴可以访问以下链接查看: &l ...

随机推荐

  1. bzoj5092 分割序列

    题目链接 problem 对于一个长度为n的非负整数序列\(b_1,b_2,...,b_n\),定义这个序列的能量为:\(f(b)=\max\limits_{i=0,1,...,n}(b_1 \oti ...

  2. python做中学(二)bool()函数的用法

    定义: bool() 函数用于将给定参数转换为布尔类型,如果没有参数,返回 False. bool 是 int 的子类. 语法: 以下是 bool() 方法的语法: class bool([x] 参数 ...

  3. 【OCR技术系列之二】文字定位于切割

    要做文字识别,第一步要考虑的就是怎么将每一个字符从图片中切割下来,然后才可以送入我们设计好的模型进行字符识别.现在就以下面这张图片为例,说一说最一般的字符切割的步骤是哪些. 当然,我们实际上要识别的图 ...

  4. Samba基础配置

    本文环境:CentOS 7 简介 在UNIX-like之间共享文件系统主要是通过NFS实现的,而Windows之间共享文件系统主要是通过基于NetBIOS的网上邻居实现的,1984年Andrew Tr ...

  5. oracle学习笔记(十七) PL/SQL高级应用

    PL/SQL高级应用 动态SQL 在PL/SQL中,不能直接执行DDL(create,alter,drop),得使用动态SQL,当然,除了DDL,动态SQL也可以执行DML(select,insert ...

  6. CAD绘图大师都在用的46组快捷键,高效绘图必备

    学习CAD 是一个需要慢慢积累的过程,千万不要遇到一点小困难就退缩,有困难我们就一起克服它!今天小编也是来帮助大家克服困难的!很多小伙伴学习CAD已经有一段时间了,但是发现自己的绘图效率还是不高,没关 ...

  7. gradle使用基础

    说明 介绍gradle使用基础,gradle基础脚本结构和常规使用方法,以及一个简单的gradle示例.主要是为了简单的介绍gradle使用. gradle环境配置 gradle可以通过两种方式运行g ...

  8. Android MediaRecorder录制播放音频

    1.请求录制音频权限 <user-permission android:name="android.permission.RECORD_AUDIO"/> RECORD_ ...

  9. SoapUI、Postman测试WebService

    之前装了SoapUI,最近装了Postman,分别用它们测试了WebService,下面为用法. 测试的在线WebService(数据来源于中国气象局)Endpoint: http://www.web ...

  10. Element-ui中自定义表单校验规则

    先看一个场景图: 给一个标签el-tag添加表单的校验,且在内容有了以后关闭校验 看代码: <el-form-item class="baseinfo-tags" label ...