资讯专栏INFORMATION COLUMN

Spring Cloud Alibaba Sentinel对Feign的支持

wthee / 3510人阅读

摘要:得到得到类得到类得到调用的服务名称检查和属性省略部分代码中的方法里面进行熔断限流的处理。在的方法中进行的包装。

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

集成 Feign 使用

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


    org.springframework.cloud
    spring-cloud-starter-alibaba-sentinel
    0.2.1.RELEASE

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

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:

@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中如何自动配置:

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.sentinel.enabled")
public Feign.Builder feignSentinelBuilder() {
    return SentinelFeign.builder();
}

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

接下来看 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 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 dispatch) {
          return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
        }
      });
      super.contract(new HystrixDelegatingContract(contract));
      return super.build();
}

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

欢迎加入我的知识星球,一起交流技术,免费学习猿天地的课程(http://cxytiandi.com/course) PS:目前星球中正在星主的带领下组队学习Sentinel,等你哦!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77682.html

相关文章

  • Spring Cloud Alibaba Sentinel 整合 Feign 设计实现

    摘要:作用跟一致跟属性作用一致给设置注解绝对路径,用于替换服务名。在服务名或与之间默认是,表示当前这个生成的是否是。内部的能获取服务名信息,的实现类能拿到对应的请求路径信息。很不幸,这个类也是包级别的类。整合的代码目前已经在仓库上,但是没未发版。 作者 | Spring Cloud Alibaba 高级开发工程师洛夜来自公众号阿里巴巴中间件投稿 前段时间 Hystrix 宣布不再维护之后(H...

    OldPanda 评论0 收藏0
  • Spring Cloud Alibaba 新版本发布:众多期待内容整合打包加入!

    摘要:在之后,也终于发布了最新的版本。该版本距离上一次发布,过去了整整个月下面就随我一起看看,这个大家期待已久的版本都有哪些内容值得我们关注。如果是用户,同时也是阿里云这些产品的用户,那么直接使用还是非常方便的。 在Nacos 1.0.0 Release之后,Spring Cloud Alibaba也终于发布了最新的版本。该版本距离上一次发布,过去了整整4个月!下面就随我一起看看,这个大家期...

    不知名网友 评论0 收藏0
  • [Spring-Cloud-Alibaba] Sentinel 整合RestTemplate &am

    摘要:开发阶段很有意义。源码整合配置文件中添加来开启编写类,实现默认用户远程调用被限流降级,默认用户应用定义可以拿到异常信息无法拿到异常信息若初启动应用,设置流控规则,结果展示如下默认用户源码 Sentinel API Github : WIKI Sphu (指明要保护的资源名称) Tracer (指明调用来源,异常统计接口) ContextUtil(标示进入调用链入口) 流控规则(针...

    libin19890520 评论0 收藏0
  • Spring Cloud Alibaba到底坑不坑?

    摘要:我没有能力去控制那些自媒体发布这些不实的内容,但是在我了解的范围内,还是尽力输出一些我的理解。 之前我发过一篇《说说我为什么看好Spring Cloud Alibaba》,然后这两天有网友给我转了这篇文章《坑爹项目spring-cloud-alibaba,我们也来一个》,问我的看法是怎么样的,聊天时候简单说了一下。今天在家休息,抽空整理一下内容,逐点说一下我的看法,主要还是觉得这篇文章...

    娣辩孩 评论0 收藏0
  • 公益:开放一台Nacos服务端给各位Spring Cloud爱好者

    摘要:之前开放过一台公益给大家,以方便大家在阅读我博客中教程时候做实验。由于目前在连载,所以对应的也部署了一台,并且也开放出来,给大家学习测试之用。 之前开放过一台公益Eureka Server给大家,以方便大家在阅读我博客中教程时候做实验。由于目前在连载Spring Cloud Alibaba,所以对应的也部署了一台Nacos,并且也开放出来,给大家学习测试之用。 Nacos控制台 ...

    jk_v1 评论0 收藏0

发表评论

0条评论

wthee

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<