方便理解sentinel,假如我们自己要实现一套sentinel
sentinel前世今生
方便理解sentinel,假如我们自己要实现一套sentinel
第一阶段
一心助手业务服务出现异常,通过监控大盘,发现超过自身服务能够承载的流量,导致请求出现大量排队,服务阻塞,进而导致其他依赖服务出现雪崩效应。通过监控大盘我们发现在qps在200的时候开始出现请求时间增加。因为接口没有优化空间,我们意识到需要对接口进行限流200个QPS的请求。我们采用在服务内增加RateLimiter,采用令牌桶的方式记录时间线在1秒的请求(通过合理使用数据结构和算法解决快和省的问题)如果一秒内请求超过我们设置的阈值快速返回系统繁忙。
@PostMapping("/1.0/getColdChainShipmentOrder")@ApiOperation(value = "获取发货单详情", notes = "物流人员/门店查看发货单详情")public ResponseBase<GetColdChainShipmentOrderResDTO> getColdChainShipmentOrder(GetColdChainShipmentOrderReqDTO param, OperateContext operateContext) {RateLimiter rateLimiter = RateLimiter.create(200);// 2、尝试获取令牌,不论是否能获取到都直接返回boolean res = rateLimiter.tryAcquire();if (res) {GetColdChainShipmentOrderResDTO coldChainShipmentOrder = coldChainShipmentOrderApplicationService.getColdChainShipmentOrder(GetColdChainShipmentOrderCommand.convert(param), operateContext);return ResponseBase.success(coldChainShipmentOrder);} else {throw new YxtRuntimeException(ResponseCodeType.BIZ_EXCEPTION, "系统繁忙请稍后重试");}} |
第二阶段
一期限流符合预期,高并发接口不会被超过200的QPS的请求压垮,准备在别的服务推行,我们将这个RateLimiter独立成一个模块,其他服务通过引入jar包就可以快速接入使用
<dependency><artifactId>yxt-rate-core</artifactId><groupId>com.yxt</groupId> <version>1.0.0</version></dependency> |
第三阶段
线上getColdChainShipmentOrder 频繁超时告警,我们发现根据业务迭代以前设置的200QPS,现在只能支持100QPS。如果我们要快速解决只能改代码再发版,我们期望系统能够支持不停机的情况动态修改,我们修改我们的core,资源和规则分离,定期去数据库轮询的方式拉去规则配置进行更新
insert flow_rule values("/1.0/getColdChainShipmentOrder",200) |
insert rule values("/1.0/getColdChainShipmentOrder",200)
@PostMapping("/1.0/getColdChainShipmentOrder")@ApiOperation(value = "获取发货单详情", notes = "物流人员/门店查看发货单详情")public ResponseBase<GetColdChainShipmentOrderResDTO> getColdChainShipmentOrder(GetColdChainShipmentOrderReqDTO param, OperateContext operateContext) {RateLimiter rateLimiter = RateLimiter.create("/1.0/getColdChainShipmentOrder");// 2、尝试获取令牌,不论是否能获取到都直接返回boolean res = rateLimiter.tryAcquire();if (res) {GetColdChainShipmentOrderResDTO coldChainShipmentOrder = coldChainShipmentOrderApplicationService.getColdChainShipmentOrder(GetColdChainShipmentOrderCommand.convert(param), operateContext);return ResponseBase.success(coldChainShipmentOrder);} else {throw new YxtRuntimeException(ResponseCodeType.BIZ_EXCEPTION, "系统繁忙请稍后");}} |
第四阶段
通过数据库的方式,我们实现轮询拉去,可以实现动态的更改规则,但是发现,当流量冗余的时候,通过数据库配置的方式有一定滞后性,我们采用nacos或者zookeeper的方式进行配置,服务器启动的时候拉取一次规则进行配置,然后监听nacos进行配置。
public NacosDataSource(final Properties properties, final String groupId, final String dataId,Converter<String, T> parser) {super(parser);if (StringUtil.isBlank(groupId) || StringUtil.isBlank(dataId)) {throw new IllegalArgumentException(String.format("Bad argument: groupId=[%s], dataId=[%s]",groupId, dataId));}AssertUtil.notNull(properties, "Nacos properties must not be null, you could put some keys from PropertyKeyConst");this.groupId = groupId;this.dataId = dataId;this.properties = properties;this.configListener = new Listener() {@Overridepublic Executor getExecutor() {return pool;}@Overridepublic void receiveConfigInfo(final String configInfo) {RecordLog.info("[NacosDataSource] New property value received for (properties: {}) (dataId: {}, groupId: {}): {}",properties, dataId, groupId, configInfo);T newValue = NacosDataSource.this.parser.convert(configInfo);// Update the new value to the property.getProperty().updateValue(newValue);}};initNacosListener();loadInitialConfig();} |
第五阶段
nacos通过json的方式配置,如果规则过多或者过于复杂就会导致越来越难维护,我们思考是否有图形化的界面方式更友好的配置和监控
我们新建一个UI项目,然后应用服务配置UI项目的地址,应用服务单独开启一个端口去跟UI项目进行交互,UI项目动态的根据业务服务额外暴露的端口拉取监控信息,UI项目修改规则后动态的推送到nacos,业务服务更新监听规则
第六阶段
我们发现入口流量都在接口的入口,出口流量依赖的外部服务。我们通过spring-mvc拦截器进行统一的资源埋点。对于open-fegin我们使用FeginBuilder进行扩展,调用时进行资源埋点,对于dubbo使用dubbo的拦截器进行埋点
第七阶段
我们发现每个服务都要写一个这样的埋点代码,我们封装成starter自动装配的方式配置
sentinel项目结构介绍
sentinel-starter