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

集成 Feign 使用

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 的支持:

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<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的包装。

欢迎加入我的知识星球,一起交流技术,免费学习猿天地的课程(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. NopCommerce 4.2的安装与运行

    一.关于NopCommerce NopCommerce是国外ASP.Net领域一个高质量的B2C开源电商项目,最新版本4.2基于ASP.NET Core MVC 2.2和EF Core 2.2开发,其 ...

  2. selenium爬取NBA并将数据存储到MongoDB

    from selenium import webdriver driver = webdriver.Chrome() url = 'https://www.basketball-reference.c ...

  3. php逻辑运算符 异或

  4. java类生命周期,类的“加载,连接,初始化,使用,卸载过程”详解

    “ 如果说核心类库的 API 比做数学公式的话,那么 Java 虚拟机的知识就好比公式的推导过程” 每本Java入门书籍在介绍Java这门语言的时候都会提到Java跨平台,“一次解释,到处运行的特点“ ...

  5. 【CF704D】Captain America(上下界网络流)

    [CF704D]Captain America(上下界网络流) 题面 CF 洛谷 题解 如果没有限制,似乎就不用做了...因为我们只需要贪心的选择代价较小的颜色就行了. 那么我们不妨假设染红色的代价较 ...

  6. 3DES对称加密算法(ABAP 语言实现版)

    公司人事数据要求在系统间加密传输,而对接系统大部分是Java系统,要在不同的异构系统间能很好的加解密码,想到了标准的对称加密算法DES,因为是标准的算法,网络上存在大量公开用Java的DES算法,JA ...

  7. python处理sqlserver数据库的返回数据

    上代码: import SqlHelper.MSSQL as MS import pandas as pd if __name__ == '__main__': #连接数据库 ms = MS.MSSQ ...

  8. python3 对list对象的增删改查

    class peoples: people_list =[] class people: name='' age=-1 def __init__(self,name,age): self.name = ...

  9. ThreadLocal(线程本地存储)

    1. ThreadLocal,即线程本地变量或线程本地存储. threadlocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的 ...

  10. JavaScript之找LHS查询和RHS查询

    LHS和RHS,当变量出现在赋值操作的左侧时进行LHS 查询,出现在右侧时进行RHS 查询. LHS 查询是试图找到变量的容器本身,从而可以对其赋值. RHS 理解成retrieve his sour ...