sentinel 集群流控原理
如果服务调用使用轮训或者随机路由方式,理论上可以通过在各个单机上设置流控规则即可( 集群流控中共有两种身份:
Sentinel 1.4.0 开始引入了集群流控模块,主要包含以下几部分:
? 大致了解集群流控概念之后,下面一起分析下集群流控规则、client端和server端各自处理机制~ 集群流控规则FlowRule 添加了两个字段用于集群限流相关配置,如下所示。clusterMode在方法 1 private boolean clusterMode; // 标识是否为集群限流配置 2 private ClusterFlowConfig clusterConfig; 集群限流相关配置项 3 ? 4 ClusterFlowConfig属性 5 private Long flowId; 全局唯一的规则 ID,由集群限流管控端分配. 6 int thresholdType = ClusterRuleConstant.FLOW_THRESHOLD_AVG_LOCAL; 阈值模式,默认(0)为单机均摊,1 为全局阈值. 7 int strategy = ClusterRuleConstant.FLOW_CLUSTER_STRATEGY_NORMAL; 8 boolean fallbackToLocalWhenFail = true; 在 client 连接失败或通信失败时,是否退化到本地的限流模式 9 10 public boolean canPassCheck(/*@NonNull*/ FlowRule rule,Context context,DefaultNode node,int acquireCount,1)">boolean prioritized) { 11 String limitApp = rule.getLimitApp(); 12 if (limitApp == null) { 13 return true; 14 } 15 if (rule.isClusterMode()) { 集群模式 16 return passClusterCheck(rule,context,node,acquireCount,prioritized); 17 18 单机模式流控 19 passLocalCheck(rule,1)">20 }
client端处理机制client端的处理机制和单机是一样的,只不过clusterMode和clusterConfig属性配置上了而已,具体的client使用可以参考官方文档?集群流控,这里不再赘述。如果是集群流控,在 static boolean passClusterCheck(FlowRule rule,1)">int acquireCount, 2 3 try { 4 TokenService clusterService = pickClusterService(); 5 if (clusterService == 6 为null降级处理 7 fallbackToLocalOrPass(rule,1)"> } 9 long flowId = rule.getClusterConfig().getFlowId(); 10 TokenResult result = clusterService.requestToken(flowId,1)">11 applyTokenResult(result,rule,1)">12 } catch (Throwable ex) { 13 RecordLog.warn("[FlowRuleChecker] Request cluster token unexpected Failed",ex); 降级处理 本地限流 16 17 } requestToken负责与token server端通信,入参包括 了解了client端处理流程,接下来看下server端处理流程,client和server端都是用netty作为底层网络通信服务,关于netty的原理不是本文讨论的重点因此会简单带过。如果小伙伴们还不太熟悉netty,请参阅对应资料即可。对于netty,每个Java开发者都需要了解甚至是熟悉的,这样不仅仅帮助我们理解NIO及Reactor模型,还能再阅读基于netty的框架源码(比如dubbo/rocketmq等)时,将重点关注在框架本身实现上,而不是网络通信流程及细节上。 server端处理机制Sentinel 集群限流服务端有两种启动方式:
? 目前针对token server高可用,sentinel并没有对应的解决方案,不过没有并不意味着没考虑,因为默认可以降级走本地流控。sentinel作为一个限流组件,在大部分应用场景中,如果token server挂了降级为本地流控就可以满足了。 如果必须考虑token server高可用,可考虑token server集群部署,每个token server都能访问(或存储)全量规则数据,多个client通过特定路由规则分配到不同的token server(相同类型服务路由到同一个token server,不同类型服务可路由到不同token server),token server故障时提供failover机制即可。如果此时考虑到相同类型服务出现网络分区,也就是一部分服务可以正常与token server通信,另一个部分服务无法正常与token server通信,如果无法正常通信的这部分服务直接进行failover,会导致集群限流不准的问题,可通过zookeeper来保存在线的token server,如果zookeeper中token server列表有变化,再进行failover;此情况下再出现任何形式的网络分区,再执行降级逻辑,执行本地限流。 ? server端不管是独立模式还是嵌入模式,都是通过 void start() { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new LengthFieldBasedFrameDecoder(1024,2,2)); p.addLast( NettyRequestDecoder()); p.addLast(new LengthFieldPrepender(2 NettyResponseEncoder()); p.addLast( TokenServerHandler(connectionPool)); } }); b.bind(port).addListener(new GenericFutureListener<ChannelFuture>() { // }); } 以上逻辑主要是netty启动逻辑,重点关注initChannel方法,这些是往pipeline添加自定义channelHandler,主要是处理粘包、编解码器和业务处理Handler,这里最重要的是TokenServerHandler,因为是请求处理逻辑,所以重点关注其channelRead方法: void channelRead(ChannelHandlerContext ctx,Object msg) Exception { 2 全局保存channel globalConnectionPool.refreshLastReadTime(ctx.channel()); 4 if (msg instanceof ClusterRequest) { 5 ClusterRequest request = (ClusterRequest)msg; 6 if (request.getType() == ClusterConstants.MSG_TYPE_PING) { ping请求处理,会记录namespace信息 handlePingRequest(ctx,request); 9 根据request type获取对应处理器 12 针对集群流控,type为MSG_TYPE_FLOW 13 RequestProcessor<?,?> processor = RequestProcessorProvider.getProcessor(request.getType()); 14 ClusterResponse<?> response = processor.processRequest(request); 15 writeResponse(ctx,response); 16 17 } ? 针对集群流控,type为
根据限流规则检查之后,会统计相关的 ? 往期精选
觉得文章不错,对你有所启发和帮助,希望能转发给更多的小伙伴。如果有问题,请关注下面公众号,发送问题给我,多谢。 欢迎小伙伴关注【TopCoder】阅读更多精彩好文。 (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |