前言
之前咱们聊了一下聊聊如何实现一个带有拦截器性能的 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/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