Sentinel-基于Curator、Apollo实现高可用独立模式TokenServer部署下的集群限流

本文介绍了Sentinel集群限流的不足,如人工配置成本高和应用服务压力增加等。通过Apollo规则存储实现独立TokenServer,解决这些问题。详细讲述了Apollo规则存储、客户端和TokenServer的设计实现,包括监听应用限流规则、故障切换策略,并提供了源码地址。同时,文中提及Sentinel控制台无需改造,但限流规则ID生成需用分布式ID生成器以避免冲突。
摘要由CSDN通过智能技术生成

嵌入式集群限流的不足之处

在之前一篇嵌入式模式实现Sentinel集群限流的方案中已经介绍过Sentinel集群限流的大致实现思路

文章路径如下
Sentinel-集成Apollo配置中心实现嵌入式集群流控
对Sentinel集群限流有疑问的可以先看下这篇文章属性几个概念,最主要的是如下图片的理解
在这里插入图片描述

但是嵌入式模式的实现方式有明显几个缺点

  1. 每个应用集群内部需要单独配置自己集群内部的TokenServer(在Sentinel的控制台中指定),是隐形的人工成本
  2. 假设集群内部该台TokenServer依附的应用服务宕机,则TokenServer不可用,不满足应用高可用的需求
  3. 假设应用中某接口(配置了集群限流规则)的调用量特别大,每个接口都去请求TokenServer,无形中增加了应用服务的压力(是个安全隐患,可能压垮应用服务),限流服务本不应该对业务产生影响

实现独立部署TokenServer的关键

正好Sentinel中也有提供独立模式的TokenServer启动类,因此前端时间琢磨了下如何将TokenServer剥离到应用之外,即独立集群模式部署

分为几个方面来介绍TokenServer独立模式部署时的实现思路

Apollo规则存储

由于配置中心采用的是Apollo,Apollo相关OPEN API不懂的这里不做具体介绍,可以看Apollo官方介绍。这里只介绍规则是如何存储的在Apollo当中的,其他配置中心思路类似,但是相关Apollo的坑我会做说明,防止大家踩坑

在这次针对TokenServer独立模式部署的探索、修改中,我发现把所有业务应用的规则放在一个AppId(Apollo中的一个应用)中来管理是比较方便的,什么意思呢?直接上图说明,我把所有业务应用的规则都放到一个名为Sentinel的AppId下管理,不同应用的规则放到不同nameSpace下
在这里插入图片描述
几个关键点如下

一、不同业务下的限流规则如何存储?

  1. Apollo中,我新建了个一个名为Sentinel的AppId来存储所有业务应用的规则,不同的业务用不同的NameSpace来区分,图中的sentinel-test就是一个演示测试用的业务应用名称(这个名称和注册到Sentinel控制台的保持一致,方便控制台写入规则到Apollo)。

  2. 具体业务下的限流规则以sentinel-xxxx-rules为key写入该nameSpace,xxxx可能是普通限流规则(flow),或者降级规则(degrade)等等

  3. 然后nameSpace一定要设置成public的,这算是Apollo里的一个坑,因为业务应用首先要监听自己的业务规则(比如sentinel-test这个应用要监听appId=sentinel-test的业务appId下的规则),同时也需要监听Sentinel这个appId下名为sentinel-test的nameSpace下的限流规则变化,但是实际应用代码中,只能指定一个appId,所以只能指定Sentinel限流规则这个appId下的规则所在的nameSpace为public,这样就能被业务应用所监听到

二、tokenServer这个nameSpace下存储什么东西?

  1. 首先要存储的是nameSpaceSet,什么是nameSpaceSet?在我的理解中,Sentinel中每个业务就是一个nameSpace,有的应用需要集群限流,有的业务不需要集群限流。假设某个业务中是需求集群限流的,那么TokenServer就需要监听这个业务(这个nameSpace)下的限流规则变化,因此 Sentinel中nameSpaceSet即标识TokenServer端需要监听的业务应用有哪些。在我的实现中,Apollo中的nameSpace是业务应用名称,同时也是Sentinel中的nameSpace名称,如下图设置时即标识TokenServer端目前监听了sentinel-test这个nameSpace下的所有集群限流规则变化
    在这里插入图片描述
  2. 第二个要存储的是token-server-cluster-map,我这的token-server-cluster-map又是存储的什么鬼?我这存储的是TokenServer应用的部署信息,即TokenServer的ip、端口信息,存储格式如下(假设我部署了2个TokenServer,分别部署在192.168.0.1、192.168.0.2上)
[
    {
        "ip": "192.168.0.1",
        //最大允许qps
        "maxAllowedQps": 20000,
        "port": 18730
    }
]

同时我默认这个数组中的第一个TokenServer配置信息即为客户端需要连接的TokenServer地址信息

客户端也会监听这个nameSpace下的这个tokenServer地址的规则配置,TokenServer地址变化时(比如宕机后重新选举出了一个TokenServer,并且ip、端口有了修改),则客户端动态重新连接这个新的TokenServer

为什么我只指定一个TokenServer?

考虑到不同应用集群连接不同TokenServer实现的复杂度(因为要考虑TokenServer故障切换及Qps数据同步的问题),在我的公司应用中,需求集群限流的场景暂时不多,因此我暂定:所有的业务客户端都只连接一个TokenServer(这个TokenServer是通过Master选举出来的),否则怕出现集群限流不生效的场景

关于TokenServer的Master选举实现,在下面做具体介绍

客户端设计与实现

客户端的实现中,有如下几个关键处

  1. 监听业务应用的限流规则变化(即监听配置中心中的限流规则变化),这个不多说了,之前文章介绍过,不懂的去翻一翻,或者看下官网针对不同配置中心的适配
  2. 设置业务端状态为集群限流客户端
    每个接入了Sentinel的业务应用,其角色要么是TokenClient,要么是TokenServer,但在TokenServer独立部署时,业务端只能是TokenClient,故直接写死客户端角色为TokenClient(我的代码实现中是根据nameSpaceSet来设置的,本质上差不多)
ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
  1. 监听TokenServer地址的变化
    Apollo规则存储时介绍了,通过Master选举出来的TokenServer地址信息存储在NameSpace为token-server中,key为token-server-cluster-map,因此客户端需要监听这个配置,客户端的关键类为ClusterClientConfigManager.registerServerAssignProperty,这个方法是用来指定客户端连接的TokenServer地址的

    直接上代码:
    appId即为上图中的token-server(别忘了指定为public的,不然监听不到)
    ApolloConfigUtil.getTokenServerClusterMapDataId()即为token-server-cluster-map

private void initClientServerAssignProperty(String appId) {
   
        ReadableDataSource<String, ClusterClientAssignConfig> clientAssignDs = new ApolloDataSource<>(appId,
                ApolloConfigUtil.getTokenServerClusterMapDataId(), defaultRules, new ClusterAssignConfigParser());
        ClusterClientConfigManager.registerServerAssignProperty(clientAssignDs.getProperty());
    }

关键点就是如何从规则中解析出地址信息,我的实现类为ClusterAssignConfigParser,看下代码

public class ClusterAssignConfigParser implements Converter<String, ClusterClientAssignConfig> {
   
    @Override
    public ClusterClientAssignConfig convert(String source) {
   
        if (source == null) {
   
            return null;
        }
        RecordLog.info("[ClusterClientAssignConfigParser] Get data: " + source);
        //转换成对象List
        List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {
   
        });
        if (groupList == null || groupList.isEmpty()) {
   
            return null;
        }
        return extractClientAssignment(groupList);
    }

    private ClusterClientAssignConfig extractClientAssignment(List<ClusterGroupEntity> groupList) {
   
    	//获取第一个配置的TokenServer地址信息,解析出ip,端口
        ClusterGroupEntity group = groupList.get(0);
        String ip = group.getIp();
        Integer port = group.getPort();
        return new ClusterClientAssignConfig(ip, port);
    }
}

同时,贴上我的客户端接入Apollo的实现,本身是个Starter的实现,但是涉及到一些配置相关信息,就不全部贴上来了,只贴出关键部分代码。

/**
 * @author chenyin
 */
public class ApolloInitFunc implements InitFunc {
   
    private String tokenServerNameSpace = ApolloConfigUtil.getTokenServerNamespaceName();
    private String defaultRules = "[]";

    @Override
    public void init() throws Exception {
   

        initClientRules();
        initClusterConfig();

    }

    /**
     * 初始化普通限流规则
     */
    private void initClientRules() {
   
        String appId = System.getProperty(AppNameUtil.APP_NAME);
        //流控规则
        registerFlowRuleProperty(appId);
        //系统规则
        registerSystemRuleProperty(appId);
        //热点规则
        registerParamFlowRuleProperty(appId);
        //降级规则
        registerDegradeRuleProperty(appId);
        //授权规则
        registerAuthorityRuleProperty(appId);
    }

    /**
     * 初始化集群限流规则
     */
    private void initClusterConfig() {
   
        String tokenServerNameSpaceId = ApolloConfigUtil.getTokenServerNamespaceName();
        //等待transport端口分配完毕
        while (TransportConfig.getRuntimePort(
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值