前言
之前咱们聊了一下聊聊如何实现一个带有拦截器性能的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.AopProxyorg.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@Componentpublic 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/ci...
此时拜访失常,看下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