本项目为sentinel与dubbo结合的改造项目。

一、由来

Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。

点此地址了解更多Sentinel。

二、问题

Sentinel设定除了限流异常以外都会被认为是需要进行熔断统计,你自己定义的业务异常也毫无例外的在熔断统计范围内。

Sentinel的启动配置太过原始,一定要在java -jar的时候加参数启动,调试困难。

Sentinel Dashboard不支持将配置发送到datasource中,需要进行一定的改造。

三、实现

新增dubbo的filter将异常包装成统一返回体,将异常状态码定义为>=500的值(与HttpStatus相对应)。并定义相关的状态码,并修改SentinelResourceAspect实现,判断返回的状态码是否为>=500,如果是则进行熔断统计。

并新增sentinel-dubbo-starter,进行自动配置化。

Sentinel Dashboard改造:控制台规则 -> 配置中心 -> 客户端。

四、Dashboard改造

改造前

客户端利用sentinel-transport-simple-http模块暴露一个特定的端口,Sentinel Dashboard通过http的形式进行数据推送,客户端接收后将规则保存在本地内存中。

java实现流量路由 java流量控制_推送

改造后

客户端注册到相关的注册中心中,Sentinel Dashboard控制台将配置信息推送到配置中心,如nacos,zookeeper中,由配置中心去进行配置推送。

java实现流量路由 java流量控制_客户端_02

路径约定

流量控制规则:/sentinel/rules/{appName}/flow

黑白名单规则:/sentinel/rules/{appName}/authority

熔断降级规则:/sentinel/rules/{appName}/degrade

热点参数规则:/sentinel/rules/{appName}/param

负载保护规则:/sentinel/rules/{appName}/system

改造支持

改造后Sentinel Dashboard支持api推送以及zookeeper推送。

其他配置中心,如nacos,改造入口可以参考以下实现,并加入Configuration注入。

DynamicRuleZookeeperProvider
DynamicRuleZookeeperPublisher

五、使用

1.运行sentinel-dubbo-dashboard

sentinel.application.name=sentinel-dashboard # 名字
sentinel.application.port=8719 # sentinel的http访问端口
sentinel.application.dashboard=localhost:8181 # 控制台地址
sentinel.zookeeper.enable=true # 是否开启zookeeper作为datasource
sentinel.zookeeper.address=localhost:2181 # zookeeper配置

配置并运行DashboardApplication

2.客户端加入相关的依赖

com.xmutca
sentinel-dubbo-starter
0.0.1-SNAPSHOT
客户端加入相关的配置
sentinel:
application:
name: sentinel-provider # 服务的名字
port: 8719 # sentinel的http访问端口
dashboard: localhost:8080 # sentinel dashboard地址
zookeeper:
enable: true # 是否使用zookeeper作为datasource
address: localhost:2181 # zookeeper的地址

六、异常处理

项目采用反射的形式获取注入的全部异常信息,写法类似于SpringMVC的ExceptionHandler。

/**
* @version Revision: 0.0.1
* @author: weihuang.peng
* @Date: 2018-12-24
*/
public class ExceptionHandler {
/**
* 默认异常
* @param ex
* @return
*/
@ExceptionProcessor(Exception.class)
public Result handleException(Exception ex) {
return Result.DEFAULT_ERROR_RESULT;
}
/**
* 业务异常处理
* @param ex
* @return
*/
@ExceptionProcessor({ServiceException.class})
public Result handleServiceException(ServiceException ex) {
Receipt result = ex.getExceptionResult();
result.setMessage("项目的实现 -》" + result.getMessage());
return result;
}
}

然后通过以下形式进行异常注册:

ExceptionRegistry.updateForPackage("你的异常中心放的包名")

注意:后面注册的异常处理方法,将会覆盖前面注册的异常处理方法,覆盖依据为ExceptionProcessor中的value类名,处理的是你如果不存在此异常将会从父异常进行搜索,如果实在找不到将会用默认的进行输出。

七、思考

sentinel针对问题1其实也可以采用直接抛异常的方式,然后在SentinelResourceAspect实现中对业务异常进行排除。没有用这个方案的主要原因是考虑到dubbo也有可能直接对外提供服务(如dubbo2.js),直接抛出异常的方式实在是太过粗暴,也不太友好。

sentinel客户端使用datasource需要控制台的相关支持,举个例子就是zookeeper的path要一一对应才能获取相关配置,因此也改了一版的dashboard。

项目中对外服务中异常的处理:如果是通过dubbo本身调用抛的异常,用dubbo的filter对异常进行了包装,如果是集成了web服务以后,http服务抛出来的异常可以使用ControllerAdvice来做相关异常处理。