分析服务的特性:我的服务应该开多少个进程、多少个线程?

Linux 专栏收录该内容
26 篇文章 0 订阅
  • 在平时工作中,你应该经常会遇到自己设计的服务即将上线,这就需要从整体评估各项指标,比如应该开多少个容器、需要多少 CPU 呢?
  • 另一方面,应该开多少个线程、多少个进程呢?——如果结合服务特性、目标并发量、目标吞吐量、用户可以承受的延迟等分析,又应该如何调整各种参数?
  • 资源分配多了,CPU、内存等资源会产生资源闲置浪费。
  • 资源给少了,则服务不能正常工作,甚至雪崩,因此这里就产生了一个性价比问题

计算密集型和 I/O 密集型

通常我们会遇到两种任务,一种是计算、一种是 I/O

计算密集型

  • 就是利用 CPU 处理算数运算。比如深度神经网络(Deep Neural Networks),需要大量的计算来计算神经元的激活和传播。
  • 再比如,根据营销规则计算订单价格,虽然每一个订单只需要少量的计算,但是在并发高的时候,所有订单累计加起来就需要大量计算。
  • 如果一个应用的主要开销在计算上,我们称为计算密集型

I/O 密集型

  • I/O 本质是对设备的读写。读取键盘的输入是 I/O,读取磁盘(SSD)的数据是 I/O。
  • 通常 CPU 在设备 I/O 的过程中会去做其他的事情,当 I/O 完成,设备会给 CPU 一个中断,告诉 CPU 响应 I/O 的结果。
  • 比如说从硬盘读取数据完成了,那么硬盘给 CPU 一个中断。如果操作对 I/O 的依赖强,比如频繁的文件操作(写日志、读写数据库等),可以看作I/O 密集型

你可能会有一个疑问,读取硬盘数据到内存中这个过程,CPU 需不需要一个个字节处理

  • 通常是不用的,因为在今天的计算机中有一个叫作 Direct Memory Access(DMA)的模块
  • 这个模块允许硬件设备直接通过 DMA 写内存,而不需要通过 CPU(占用 CPU 资源)。

衡量 CPU 的工作情况的指标

  • 我们先来看一下 CPU 关联的指标。如下图所示:CPU 有 2 种状态,忙碌和空闲。此外,CPU 的时间还有一种被偷走的情况。

  • 忙碌就是 CPU 在执行有意义的程序,空闲就是 CPU 在执行让 CPU 空闲(空转)的指令。
  • 通常让 CPU 空转的指令能耗更低,因此让 CPU 闲置时,我们会使用特别的指令,最终效果和让 CPU 计算是一样的,都可以把 CPU 执行时间填满,只不过这类型指令能耗低一些而已。
  • 除了忙碌和空闲,CPU 的时间有可能被宿主偷走,比如一台宿主机器上有 10 个虚拟机,宿主可以偷走给任何一台虚拟机的时间。

CPU 空闲有 2 种情况。

  • CPU 无事可做,执行空闲指令(注意,不能让 CPU 停止工作,而是执行能耗更低的空闲指令)。
  • CPU 因为需要等待 I/O 而空闲,比如在等待磁盘回传数据的中断,这种我们称为 I/O Wait

下图是我们执行 top 指令看到目前机器状态的快照,接下来我们仔细研究一下这些指标的含义:

你可以细看下 %CPU(s) 开头那一行(第 3 行)

  • us(user),即用户空间 CPU 使用占比。
  • sy(system),即内核空间 CPU 使用占比。
  • ni(nice),nice 是 Unix 系操作系统控制进程优先级用的。-19 是最高优先级, 20 是最低优先级。这里代表了调整过优先级的进程的 CPU 使用占比。
  • id(idle),闲置的 CPU 占比。
  • wa(I/O Wait),I/O Wait 闲置的 CPU 占比。
  • hi(hardware interrupts),响应硬件中断 CPU 使用占比。
  • si(software interrrupts),响应软件中断 CPU 使用占比。
  • st(stolen),如果当前机器是虚拟机,这个指标代表了宿主偷走的 CPU 时间占比。对于一个宿主多个虚拟机的情况,宿主可以偷走任何一台虚拟机的 CPU 时间。

 上面我们用 top 看的是一个平均情况,如果想看所有 CPU 的情况可以 top 之后,按一下1键。结果如下图所示:

  • 当然,对性能而言,CPU 数量也是一个重要因素。可以看到我这台虚拟机一共有 16 个核心。

负载指标 

  • 上面的指标非常多,在排查问题的时候,需要综合分析。其实还有一些更简单的指标,比如上图中 top 指令返回有一项叫作load average——平均负载
  • 负载可以理解成某个时刻正在排队执行的进程数除以 CPU 核数。平均负载需要多次采样求平均值。 如果这个值大于1,说明 CPU 相当忙碌。因此如果你想发现问题,可以先检查这个指标。
  • 具体来说,如果平均负载很高,CPU 的 I/O Wait 也很高, 那么就说明 CPU 因为需要大量等待 I/O 无法处理完成工作。
  • 产生这个现象的原因可能是:线上服务器打日志太频繁,读写数据库、网络太频繁。你可以考虑进行批量读写优化
  • 到这里,你可能会有一个疑问:为什么批量更快呢?我们知道一次写入 1M 的数据,就比写一百万次一个 byte 快。因为前者可以充分利用 CPU 的缓存、复用发起写操作程序的连接和缓冲区等。
  • 如果想看更多load average,你可以看/proc/loadavg文件。

通信量(Traffic)

  • 如果怀疑瓶颈发生在网络层面,或者想知道当前网络状况。可以查看/proc/net/dev,下图是在我的虚拟机上的查询结果:

 表头分成了 3 段:

  • Interface(网络接口),可以理解成网卡
  • Receive:接收的数据
  • Transmit:发送的数据

然后再来看具体的一些参数:

  • byte 是字节数
  • package 是封包数
  • erros 是错误数
  • drop 是主动丢弃的封包,比如说时间窗口超时了
  • fifo: FIFO 缓冲区错误
  • frame: 底层网络发生了帧错误,代表数据出错了

如果你怀疑自己系统的网络有故障,可以查一下通信量部分的参数,相信会有一定的收获。

衡量磁盘工作情况

  • 有时候 I/O 太频繁导致磁盘负载成为瓶颈,这个时候可以用iotop指令看一下磁盘的情况,如图所示:

  • 上图中是磁盘当前的读写速度以及排行较靠前的进程情况。另外,如果磁盘空间不足,可以用df指令:

 

  • 其实 df 是按照挂载的文件系统计算空间。
  • 图中每一个条目都是一个文件系统。
  • 有的文件系统直接挂在了一个磁盘上,比如图中的/dev/sda5挂在了/上,因此这样可以看到各个磁盘的使用情况。
  • 如果想知道更细粒度的磁盘 I/O 情况,可以查看/proc/diskstats文件。

监控平台

  • Linux 中有很多指令可以查看服务器当前的状态,有 CPU、I/O、通信、Nginx 等维度。
  • 如果去记忆每个指令自己搭建监控平台,会非常复杂。
  • 这里你可以用市面上别人写好的开源系统帮助你收集这些资料。
  • 比如 Taobao System Activity Report(tsar)就是一款非常好用的工具。
  • 它集成了大量诸如上面我们使用的工具,并且帮助你定时收集服务器情况,还能记录成日志。
  • 你可以用 logstash 等工具,及时将日志收集到监控、分析服务中,比如用 ELK 技术栈。

决定进程/线程数量

下面请你思考一个问题:如果线程或进程数量 = CPU 核数,是不是一个好的选择

  • 有的应用不提供线程,比如 PHP 和 Node.js。
  • Node.js 内部有一个事件循环模型,这个模型可以理解成协程(Coroutine),相当于大量的协程复用一个进程,可以达到比线程池更高的效率(减少了线程切换)。PHP 模型相对则差得多。
  • Java 是一个多线程的模型,线程和内核线程对应比 1:1;
  • Go 有轻量级线程,多个轻量级线程复用一个内核级线程。
  • 以 Node.js 为例,如果现在是 8 个核心,那么开 8 个 Node 进程,是不是就是最有效利用 CPU 的方案呢?
  • 乍一看——8 个核、8 个进程,每个进程都可以使用 1 个核,CPU 利用率很高——其实不然。
  • 你不要忘记,CPU 中会有一部分闲置时间是 I/O Wait,这个时候 CPU 什么也不做,主要时间用于等待 I/O。
  • 假设我们应用执行的期间只用 50% CPU 的执行时间,其他 50% 是 I/O Wait。那么 1 个 CPU 同时就可以执行两个进程/线程。

  • 我们考虑一个更一般的模型,如果你的应用平均 I/O 时间占比是 P,假设现在内存中有 n 个这样的线程,那么 CPU 的利用率是多少呢?
  • 假设我们观察到一个应用 (进程),I/O 时间占比是 P,那么可以认为这个进程等待 I/O 的概率是 P。那么如果有 n 个这样的线程,n 个线程都在等待 I/O 的概率是Pn。而满负荷下,CPU 的利用率就是 CPU 不能空转——也就是不能所有进程都在等待 I/O。因此 CPU 利用率 = 1-p^n
  • 理论上,如果 P = 50%,两个这样的进程可以达到满负荷。 但是从实际出发,何时运行线程是一个分时的调度行为,实际的 CPU 利用率还要看开了多少个这样的线程,如果是 2 个,那么还是会有一部分闲置资源。
  • 因此在实际工作中,开的线程、进程数往往是超过 CPU 核数的。你可能会问,具体是多少最好呢?——这里没有具体的算法,要以实际情况为准。比如:你先以 CPU 核数 3 倍的线程数开始,然后进行模拟真实线上压力的测试,分析压测的结果。
  • 如果发现整个过程中,瓶颈在 CPU,比如load average很高,那么可以考虑优化 I/O Wait,让 CPU 有更多时间计算。
  • 当然,如果 I/O Wait 优化不动了,算法都最优了,就是磁盘读写速度很高达到瓶颈,可以考虑延迟写、延迟读等等技术,或者优化减少读写。
  • 如果发现 idle 很高,CPU 大面积闲置,就可以考虑增加线程。

总结

  •  计算密集型一般接近核数,如果负载很高,建议留一个内核专门给操作系统。
  • I/O 密集型一般都会开大于核数的线程和进程。 但是无论哪种模型,都需要实地压测,以压测结果分析为准;
  • 另一方面,还需要做好监控,观察服务在不同并发场景的情况,避免资源耗尽。
  • 然后具体语言的特性也要考虑,Node.js 每个进程内部实现了大量类似协程的执行单元,因此 Node.js 即便在 I/O 密集型场景下也可以考虑长期使用核数 -1 的进程模型。
  • 而 Java 是多线程模型,线程池通常要大于核数才能充分利用 CPU 资源
  • 所以核心就一句,眼见为实,上线前要进行压力测试
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:Age of Ai 设计师:meimeiellie 返回首页

打赏作者

HCH996

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值