当前位置:首页 > 服务端 > Spring Cloud Alibaba Sentinel对Feign的支持

Spring Cloud Alibaba Sentinel对Feign的支持

2022年11月06日 10:20:16服务端21

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,至于资源名称怎么定义的,接下面的源码分析你就知道了。

原理分析

feign包里的代码就是对feign支持的代码

首先看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,等你哦!

微信扫码加入猿天地知识星球

猿天地

作者:猿天地
来源链接:https://www.cnblogs.com/yinjihuan/p/10867622.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/server/68096.html

分享给朋友:

“Spring Cloud Alibaba Sentinel对Feign的支持” 的相关文章

SpringCloud系列之版本选择

SpringCloud系列之版本选择

SpringBoot版本   进入Spring官网,查看当前SpringBoot版本:   当前最新版本是2.5.4 GA,如果单纯使用SpringBoot框架,那么选择2.5.4 GA就行。 GA即General Av...

Spring Cloud实战|3.SpringCloud 整合common模块

Spring Cloud实战|3.SpringCloud 整合common模块

手把手教你从0开始搭建spring cloud alibaba 脚手架,关注公众号“AI码师” 获取项目完整源码 视频地址:视频教程 创建新模块 ams-common 当前目录结构 基础包封装 引入必备依...

Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)

Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)

摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第104篇原创 文章工程: * JDK 1.8 * Maven 3.5.2 * Spring Boot 2.1.3.R...

Spring Boot 整合 Log4j2 日志并压测性能

Spring Boot 整合 Log4j2 日志并压测性能

1/ Log4j2的性能测试从图中不难看出,在线程数为 2~16 之间,混合使用同步和异步的logger来打印日志,性能是最好的。2/ 目标混合 sync/async彩色日志分类输出到不同文件自动压缩日志文件并归档3/ 实现0x01 Maven 依赖 pom.xml<?xml ver...

Spring Boot 集成配置 HTTPS

Spring Boot 集成配置 HTTPS

这是泥瓦匠的第108篇原创 文章工程: JDK 1.8 Maven 3.5.2 Spring Boot 1.5.9.RELEASE 一、HTTPS 是什么 问:什么是HTTP?答:HTTP是一个客户...

Spring Boot WebFlux 集成 Mongodb 数据源操作

Spring Boot WebFlux 集成 Mongodb 数据源操作

WebFlux 整合 Mongodb 前言 上一讲用 Map 数据结构内存式存储了数据。这样数据就不会持久化,本文我们用 MongoDB 来实现 WebFlux 对数据源的操作。 什么是 MongoDB ? 官网:https://...

Spring Cloud 学习推荐

学习 Spring Boot Spring tutorials | Java Web Development, Spring Cloud Programming tutorials Spring Boot为您提供了Spring Framewo...

spring boot随笔二

spring boot随笔二

pring Boot与日志(日志框架、日志配置) 1、市面上的日志框架JUL、JCL、Jboss-logging、logback、log4、log4j2、slf4j……    ...

解决 spring-test 出现 Failed to load ApplicationContext 的异常

  在使用spring-test的时候,在启动@Test的方法时,spring-test会去加载spring的配置文件,这个时候如果配置文件没有在 @ContextConfiguration 中写全,就会导致加载到一半失败,然后抛出 jav...

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘externalDump

  在学习SpringCloud Alibaba Nacos时,出现了上述异常,因为我的数据库是8.0+版本,所以得在nacos的application.properties文件中db.url.0那一行加上参数useJDBCCompliantTimezoneShift=tru...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。