mongos 连接数问题解析

在这里插入图片描述
笔者:李大
有任何疑问欢迎关注微信公众号:网易游戏运维平台。(长按识别上图二维码)
微信公众号原文链接:mongos 连接数问题解析


Ocean 项目的 mongo 分片架构中,mongos 作为 mongo 分片架构的路由选择器,目前有两种使用方式,lbc 模式和传统的直连模式。直连模式下,mongos 由项目 SRE 维护管理,在使用中,项目 SRE 提到的比较多的一个问题是 : 客户端到 mongos 和 mongos 到 mongod 的连接数是什么样的关系?是 1: 1 的关系吗?
基于以上疑问,本文深度解析 mongos 的连接数问题 。

客户端到 mongos 的连接

每连接每线程

由于传输层 IO 模式的不同(ASIO 和 legacy), 不同版本 mongos 接收客户端连接的模块代码有些许差异,但是无论底层是 asio 还是 legacy , 默认情况下 mongos 对客户端连接都是【 每连接每线程 】的模型,每个线程分配 1 M 的内存。

        static const size_t STACK_SIZE = 
            1024 * 1024;  // if we change this we need to update the warning    // 分配的栈空间,1M

        struct rlimit limits;
        invariant(getrlimit(RLIMIT_STACK, &limits) == 0);
        if (limits.rlim_cur > STACK_SIZE) {
            size_t stackSizeToSet = STACK_SIZE;
#if !__has_feature(address_sanitizer)
            if (kDebugBuild)
                stackSizeToSet /= 2;
#endif
            int failed = pthread_attr_setstacksize(&attrs, stackSizeToSet);
            if (failed) {
                const auto ewd = errnoWithDescription(failed);
                warning() << "pthread_attr_setstacksize failed: " << ewd;
            }
        } else if (limits.rlim_cur < 1024 * 1024) {
            warning() << "Stack size set to " << (limits.rlim_cur / 1024) << "KB. We suggest 1MB";
        }

        pthread_t thread;
        int failed = pthread_create(&thread, &attrs, runFunc, ctx.get()); // 创建新的线程

所以,当 mongos 接收大量的连接时,对内存的消耗还是挺大的。

adaptive 线程池

为了减少每次新连接时创建与销毁线程的消耗,mongos 在 3.6 版本引入了 一种 adaptive 的线程池。预先创建 adaptiveServiceExecutorReservedThreads 个 worker 线程 和一个 controller 线程,当新的连接时,线程池内分配一个空闲 woker 线程。如果线程池内无空闲线程时,则会创建新的 worker 线程。当 worker 线程在一个执行内周期结束时,检测到真正执行 IO 操作的时间小于 adaptiveServiceExecutorIdlePctThreshold 比例时,则会自动销毁线程。

adaptiveServiceExecutorReservedThreads : 线程池预创建线程数。默认 CPU 核数 / 2 个。 
adaptiveServiceExecutorIdlePctThreshold : 线程池 worker 线程空闲时间的百分比,小于该百分比时自动销毁。

3.6 及以上版本才有 adaptive 的线程池功能,而且默认不开启,可以设置以下参数启动线程池功能。

//yaml
net:
	serviceExecutor: adaptive
//ini
serviceExecutor = adaptive

adaptive 线程池模式下,每个 worker 线程需要处理多个连接的 IO 操作,具体的连接数情况和 worker 线程无固定的比例关系,mongos 根据内部worker线程的执行情况,自适应的新建或销毁线程。可以通过 mongo shell 执行 db.serverStatus().network.serviceExecutorTaskStats 查看线程池的使用情况。

mongos> db.serverStatus().network.serviceExecutorTaskStats
{
        "executor" : "adaptive",
        "totalQueued" : NumberLong(25),
        "totalExecuted" : NumberLong(25),
        "threadsInUse" : 1,   // 正在使用(有IO操作)的worker线程数
        "totalTimeRunningMicros" : NumberLong(282344119),
        "totalTimeExecutingMicros" : NumberLong(11322),
        "totalTimeQueuedMicros" : NumberLong(180),
        "threadsRunning" : 8, // 当前的 worker 线程数 
        "threadsPending" : 0,  // 创建中的wroker 数量
        "threadCreationCauses" : {  // 3.6.4 版本新增模块  创建worker线程的原因情况统计 
                "stuckThreadsDetected" : NumberLong(0),  // 因为worker卡住而创建新的worker的次数
                "starvation" : NumberLong(0),  //线程池饥饿(即worker不够用)而创建worker的次数
                "belowReserveMinimum" : NumberLong(8), // 保持最低worker数量(adaptiveServiceExecutorReservedThreads) 而创建worker的数量
                "replacingCrashedThreads" : NumberLong(0) // worker crash 而创建新的worker的数量
        },
        "metricsByTask" : {
	        /* 3.6.4 版本新增模块,按照执行任务的种类汇总所有worker线程执行情况。所谓不同的任务种类,就是不同的函数。
	         1、一个连接进来时,首先要经过 startSession 开启一个回话 (session)。所以startSession可以理解为所有接受过的连接的总数。
	         2、开启 session 以后进去 sourceMessage ,等待接受客户端的命令(message)
	         3、收到客户端的命令(message) 以后就要处理对应命令,进入 processMessage 
	         4、processMessage 处理完消息以后,如果正常的执行完,得到正常的回复(response)或者无回复则循环执行 sourceMessage  等待新的命令;如果response 是一个 exhaust 的游标,那么继续执行 processMessage, 并记录一次 exhaustMessage的任务种类。
	         5、对每个任务种类:
		         totalQueued : 在线程池内排队的次数
		         totalExecuted : 在线程池内执行的次数
		         totalTimeExecutingMicros : 在线程池内执行的时间,单位微秒。注意这个时间是所有worker的线程的汇总信息,包含历史worker的统计时间。
		         totalTimeQueuedMicros : 在线程池内排队的时间,单位微秒。同上。
	        */
                "processMessage" : {
                        "totalQueued" : NumberLong(12),
                        "totalExecuted" : NumberLong(12),
                        "totalTimeExecutingMicros" : NumberLong(11114),
                        "totalTimeQueuedMicros" : NumberLong(5)
                },
                "sourceMessage" : {
                        "totalQueued" : NumberLong(12),
                        "totalExecuted" : NumberLong(12),
     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值