当前位置: 首页 > 后端技术 > Java

说说自定义SPI如何与sentinel集成实现熔断和限流

时间:2023-04-01 18:45:48 Java

前言之前我们说了如何实现一个带有拦截器功能的SPI。当时我们实现的核心思想是使用责任链+动态代理。今天我们就来说说如何通过动态代理集成sentinel实现断路限流预知alibabasentinel介绍Sentinel是分布式服务架构的一个流控组件。、系统负载保护、热点保护等维度帮助开发者保障微服务的稳定性。sentinelworkflowsentinelkeywordresource+rulesentinelimplementstemplateroutineentry=null;//确保finally会被执行1K),如果大于几千,请作为参数传入,不要直接作为资源名//EntryType表示流量类型(入站/出站),系统规则只对埋点生效INtypeentry=SphU.entry("自定义资源名称");//受保护的业务逻辑//做一些事情...}catch(BlockExceptionex){//资源访问被阻塞、限制或降级//执行相应的处理操作}catch(Exceptionex){//如果需要配置降级rules,需要通过这种方式记录业务异常Tracer.traceEntry(ex,entry);}finally{//一定要保证exit,保证每个entry都和exit配对if(entry!=null){entry.出口();}}sentinelwikihttps://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5实现思路总体实现思路:动态代理+sentinel实现例程模板核心代码动态代理部分1.定义动态代理接口publicinterfaceCircuitBreakerProxy{ObjectgetProxy(Objecttarget);ObjectgetProxy(Objecttarget,@NullableClassLoaderclassLoader);}2.定义JDK或cglib具体动态实现以jdk动态代理为例publicclassCircuitBreakerJdkProxyimplementsCircuitBreakerProxy,InvocationHandler{@OverridepublicObjectgetProxy(Objecttarget){this.target=target;返回getProxy(target,Thread.currentThread().getContextClassLoader());}@OverridepublicObjectgetProxy(Objecttarget,ClassLoaderclassLoader){this.target=target;返回Proxy.newProxyInstance(classLoader,target.getClass().getInterfaces(),this);}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{CircuitBreakerInvocationinvocation=newCircuitBreakerInvocation(target,method,args);尝试{返回新的CircuitBreakerInvoker().proceed(invocation);//用InvocationTargetException封装是java.lang.reflect.UndeclaredThrowableException问题}catch(InvocationTargetExceptione){throwe.getTargetException();}}}3、动态代理工具调用publicclassCircuitBreakerProxyFactoryimplementsProxyFactory{@OverridepublicObjectcreateProxy(Objecttarget){if(target.getClass().isInterface()||Proxy.isProxyClass(target.getClass())){返回新的CircuitBreakerJdkProxy().getProxy(target);}返回新的CircuitBreakerCglibProxy().getProxy(target);}}ps:上面动态代理实现的思路是参考了springaop动态代理的实现。具体参考类如下:=circuitBreakerInvocation.getMethod();if("equals".equals(method.getName())){try{ObjectotherHandler=circuitBreakerInvocation.getArgs().length>0&&circuitBreakerInvocation.getArgs()[0]!=null?Proxy.getInvocationHandler(circuitBreakerInvocation.getArgs()[0]):null;返回等于(otherHandler);}赶上(IllegalArgumentExceptione){返回假;}}elseif("hashCode".equals(method.getName())){returnhashCode();}elseif("toString".equals(method.getName())){returntoString();}对象结果=空;StringcontextName="spi_circuit_breaker:";字符串类名=ClassUtils.getClassName(circuitBreakerInvocation.getTarget());StringresourceName=contextName+className+"."+方法.getName();条目条目=空;试试{ContextUtil.enter(contextName);entry=SphU.entry(resourceName,EntryType.OUT,1,circuitBreakerInvocation.getArgs());结果=circuitBreakerInvocation.proceed();}catch(Throwableex){returndoFallBack(ex,entry,circuitBreakerInvocation);}finally{if(entry!=null){entry.exit(1,circuitBreakerInvocation.getArgs());}ContextUtil.exit();}返回结果;}}ps:细心的朋友会发现这个逻辑是Sentinel原生实现例程逻辑示例Demonstration示例准备1.定义接口实现类,添加熔断注解@CircuitBreakerActivate(spiKey="sqlserver",fallbackFactory=SqlServerDialectFallBackFactory.class)publicclassSqlServerDialectimplementsSqlDialect{@OverridepublicStringdialect(){返回“sqlserver”;}}ps:@CircuitBreakerActivate这是一个自定义的断路器注解,springcloudopenfeign的@FeignClient注解大概会有一个熟悉的就是在注解上配置fallbackFactory或者fallbackBack2,定义接口熔断工厂@Slf4j@ComponentpublicclassSqlServerDialectFallBackFactoryimplementsFallbackFactory{@OverridepublicSqlDialectcreate(Throwableex){return()->{log.error("{}",ex);返回“SqlServerDialectFallBackFactory”;};}}ps:看看你是不是熟悉这个,这不就是springcloudhystrix的熔断工厂吗?3、在项目spring中配置sentineldashbord地址:cloud:sentinel:transport:dashboard:localhost:8080filter:enabled:false实例验证1、浏览器访问http://localhost:8082/test/ci...此时访问正常。查看sentinel-dashbord2,配置限流规则3,再次快速接入。从图中可以看出断路器已经被触发。如果不配置fallback或fallbackFactory,当触发限流时,它也会有一个默认的fallback去掉示例注解的fallbackFactory,如下@CircuitBreakerActivate(spiKey="sqlserver")publicclassSqlServerDialectimplementsSqlDialect{@OverridepublicStringdialect(){返回“sqlserver”;}}再次重复上述访问过程。当触发限流时,会提示如下,总结自定义spi的实现系列即将结束。其实这个小demo并没有多少原创的东西,大部分都是从dubbo,shenyu,mybatis,spring,sentinel的源码中提取一些比较有意思的东西,我们整理的一个demo大部分还是在做业务开发的时间。有的人可能每天都觉得crud,觉得没有成长的空间,但是关注一下我们平时经常用到的就可以了。框架,也许你会发现一些不同的景观演示链接https://github.com/lyb-geek/springboot-learning/tree/master/springboot-spi-enhance/springboot-spi-framework-circuitbreaker