您现在的位置是:首页 > 文章详情

聊聊自定义SPI如何与sentinel整合实现熔断限流

日期:2021-12-07点击:469

前言

之前我们聊了一下聊聊如何实现一个带有拦截器功能的SPI。当时我们实现的核心思路是利用了责任链+动态代理。今天我们再聊下通过动态代理如何去整合sentinel实现熔断限流

前置知识

alibaba sentinel简介

Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

sentinel工作流程

在这里插入图片描述

sentinel关键词

资源 + 规则

sentinel实现模板套路

Entry entry = null; // 务必保证 finally 会被执行 try { // 资源名可使用任意有业务语义的字符串,注意数目不能太多(超过 1K),超出几千请作为参数传入而不要直接作为资源名 // EntryType 代表流量类型(inbound/outbound),其中系统规则只对 IN 类型的埋点生效 entry = SphU.entry("自定义资源名"); // 被保护的业务逻辑 // do something... } catch (BlockException ex) { // 资源访问阻止,被限流或被降级 // 进行相应的处理操作 } catch (Exception ex) { // 若需要配置降级规则,需要通过这种方式记录业务异常 Tracer.traceEntry(ex, entry); } finally { // 务必保证 exit,务必保证每个 entry 与 exit 配对 if (entry != null) { entry.exit(); } } 

sentinel wiki

https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5

实现思路

整体实现思路:动态代理 + sentinel实现套路模板

核心代码

动态代理部分

1、定义动态代理接口

public interface CircuitBreakerProxy { Object getProxy(Object target); Object getProxy(Object target,@Nullable ClassLoader classLoader); } 

2、定义JDK或者cglib具体动态实现

以jdk动态代理为例子

public class CircuitBreakerJdkProxy implements CircuitBreakerProxy, InvocationHandler { private Object target; @Override public Object getProxy(Object target) { this.target = target; return getProxy(target,Thread.currentThread().getContextClassLoader()); } @Override public Object getProxy(Object target, ClassLoader classLoader) { this.target = target; return Proxy.newProxyInstance(classLoader,target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { CircuitBreakerInvocation invocation = new CircuitBreakerInvocation(target,method,args); try { return new CircuitBreakerInvoker().proceed(invocation); //用InvocationTargetException包裹是java.lang.reflect.UndeclaredThrowableException问题 } catch (InvocationTargetException e) { throw e.getTargetException(); } } } 

3、动态代理具体调用

public class CircuitBreakerProxyFactory implements ProxyFactory{ @Override public Object createProxy(Object target) { if(target.getClass().isInterface() || Proxy.isProxyClass(target.getClass())){ return new CircuitBreakerJdkProxy().getProxy(target); } return new CircuitBreakerCglibProxy().getProxy(target); } } 

ps: 上面的动态代理实现思路是参考spring aop动态代理实现

具体参考类如下

org.springframework.aop.framework.AopProxy org.springframework.aop.framework.DefaultAopProxyFactory 

sentinel实现部分

public class CircuitBreakerInvoker { public Object proceed(CircuitBreakerInvocation circuitBreakerInvocation) throws Throwable { Method method = circuitBreakerInvocation.getMethod(); if ("equals".equals(method.getName())) { try { Object otherHandler = circuitBreakerInvocation.getArgs().length > 0 && circuitBreakerInvocation.getArgs()[0] != null ? Proxy.getInvocationHandler(circuitBreakerInvocation.getArgs()[0]) : null; return equals(otherHandler); } catch (IllegalArgumentException e) { return false; } } else if ("hashCode".equals(method.getName())) { return hashCode(); } else if ("toString".equals(method.getName())) { return toString(); } Object result = null; String contextName = "spi_circuit_breaker:"; String className = ClassUtils.getClassName(circuitBreakerInvocation.getTarget()); String resourceName = contextName + className + "." + method.getName(); Entry entry = null; try { ContextUtil.enter(contextName); entry = SphU.entry(resourceName, EntryType.OUT, 1, circuitBreakerInvocation.getArgs()); result = circuitBreakerInvocation.proceed(); } catch (Throwable ex) { return doFallBack(ex, entry, circuitBreakerInvocation); } finally { if (entry != null) { entry.exit(1, circuitBreakerInvocation.getArgs()); } ContextUtil.exit(); } return result; } } 

ps: 如果心细的朋友,就会发现这个逻辑就是sentinel原生的实现套路逻辑

示例演示

示例准备

1、定义接口实现类并加上熔断注解

@CircuitBreakerActivate(spiKey = "sqlserver",fallbackFactory = SqlServerDialectFallBackFactory.class) public class SqlServerDialect implements SqlDialect { @Override public String dialect() { return "sqlserver"; } } 

ps: @CircuitBreakerActivate这个是自定义熔断注解,用过springcloud openfeign的@FeignClient注解大概就会有种熟悉感,都是在注解上配置fallbackFactory或者fallbackBack

2、定义接口熔断工厂

@Slf4j @Component public class SqlServerDialectFallBackFactory implements FallbackFactory<SqlDialect> { @Override public SqlDialect create(Throwable ex) { return () -> { log.error("{}",ex); return "SqlServerDialect FallBackFactory"; }; } } 

ps: 看这个是不是也很熟悉,这不就是springcloud hystrix的熔断工厂吗

3、项目中配置sentinel dashbord地址

spring: cloud: sentinel: transport: dashboard: localhost:8080 filter: enabled: false 

示例验证

1、浏览器访问http://localhost:8082/test/ciruitbreak

此时访问正常,看下sentinel-dashbord

2、配置限流规则

3、再次快速访问

由图可以看出已经触发熔断

本示例项目,如果不配置fallback或者fallbackFactory,当触发限流时,它也会有个默认fallback

把示例注解的fallbackFactory去掉,如下

@CircuitBreakerActivate(spiKey = "sqlserver") public class SqlServerDialect implements SqlDialect { @Override public String dialect() { return "sqlserver"; } } 

再重复上面的访问流程,当触发限流时,会提示如下

总结

自定义spi的实现系列接近尾声了,其实这个小demo并没有多少原创的东西,大都从dubbo、shenyu、mybatis、spring、sentinel源码中抽出一些比较好玩的东西,东拼西凑出来的一个demo。

平时我们大部分时间还是在做业务开发,可能有些人会觉得天天crud,感觉没啥成长空间,但只要稍微留意一下我们平时经常使用的框架,说不定就会发现一些不一样的风景

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-spi-enhance/springboot-spi-framework-circuitbreaker

原文链接:https://my.oschina.net/u/4494662/blog/5346347
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章