“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

浅谈高并发系统性能调优

  • 2019-11-20
  • 本文字数:4048 字

    阅读完需:约 13 分钟

浅谈高并发系统性能调优

今天带来的是一个篇长文,主要讲解高并发系统架构指标及调优测试经验,希望能对您的研究有所帮助。本文最先发布于 OpsDev,转载已获取作者授权。


高并发系统的优化一直以来都是一个很重要的问题,下面基于笔者的实践,和大家聊聊高并发系统的一些调优和优化策略。

1 系统性能的关键指标

  • 吞吐量(Throughput)系统单位时间内处理任务的数量

  • 延迟(Latency)系统对单个任务的平均响应时间


一般来说,考量一个系统的性能主要看这两个指标。而这两个指标之间又存在着一些联系:对于指定的系统来说,系统的吞吐量越大,处理的请求越多,服务器就越繁忙,响应速度就会慢下来;而延迟越低的系统,能够承载的吞吐量也相应的更高一些。


一方面,我们需要提高系统的吞吐量,以便服务更多的用户,另一方面我们需要将延迟控制在合理的范围内,以保证服务质量。

2 系统性能测试

  • 业务场景

  • 对于不同的业务系统,可以接受的延迟(Latency)也有所不同,例如邮件服务可以忍受的延迟显然要比 Web 服务高得多,所以首先我们需要根据业务场景的不同来定义理想的 Latency 值。

  • 测试工具

  • 我们需要一个能够制造高吞吐的工具来测试系统的性能,本文中使用的 Tsung,它是一个开源的支持分布式的压力测试工具,它功能丰富,性能强大,配置简单,并支持多种协议(HTTP、MySQL、LDAP、MQTT、XMPP 等)。

  • 测试流程

  • 测试的过程中需要不断加大吞吐量,同时注意观察服务端的负载,如果负载没有问题,那就观察延迟。一般这个过程需要反复很多次才能测出系统的极限值,而每次测试消耗的时间也比较长,需要耐心一些。

3 通用的系统参数调优

Linux 内核默认的参数考虑的是最通用的场景,不能够满足高并发系统的需求。

4 服务器参数调优

编辑文件/etc/sysctl.conf,添加以下内容:


fs.nr_open = 100000000  fs.file-max = 100000000
复制代码


关于这两项配置的含义可以查看系统手册:



可以看到 file-max 的含义:它是系统所有进程一共可以打开的文件数量,它是系统级别的,因此应该尽量将它调的大一些,这里将它修改为一亿。 这里说到需要增大/proc/sys/fs/inode-max 的值,但是执行如下命令时发现没有该配置项:


$ cat /proc/sys/fs/inode-max  cat: /proc/sys/fs/inode-max: No such file or directory
复制代码


再看 inode-max 说明:



可以看到有的系统中没有这个参数,所以先不管了。


对于 nr_open,系统手册里没有找到关于它的定义,不过我们可以参考kernel文档


nr_open:      This denotes the maximum number of file-handles a process can      allocate. Default value is 1024*1024 (1048576) which should be      enough for most machines. Actual limit depends on RLIMIT_NOFILE      resource limit.
复制代码


可以看到 nr_open 的描述与 file-max 十分相近,不仔细看几乎分辨不出区别。重点在于,file-max 是对所有进程(all processes)的限制,而 nr_open 是对单个进程(a process)的限制。这里给出了 nr_open 的默认值:


1024*1024 = 1048576


编辑文件/etc/security/limits.conf,添加如下内容:


编辑文件/etc/sysctl.conf,添加以下内容:



    关于这两项配置的含义可以查看系统手册:



    这里的意思很明确:hard 意为硬资源限制:一旦被 superuser 设置后不能增加;soft 为软资源设置:设置后在程序运行期间可以增加,但不能超过 hard 的限制。程序读取的是 soft,它是一个告警值。 nofile 意为用户打开最大文件描述符数量,在 Linux 下运行的网络服务器程序,每个 tcp 连接都要占用一个文件描述符,一旦文件描述符耗尽,新的连接到来就会返回"Too many open files"这样的错误,为了提高并发数,需要提高这项配置的数值。


    这里有一点需要特别注意,而手册里面也没有细说:在 CentOS7 下(其他系统还未测试过),nofile 的值一定不能高于 nr_open,否则用户 ssh 登录不了系统,所以操作时务必小心:可以保留一个已登录的 root 会话,然后换个终端再次尝试 ssh 登录,万一操作失败,还可以用之前保留的 root 会话抢救一下。


    修改完毕执行:


    # sysctl -p
    复制代码


    通过以上,我们修改了/etc/security/limits.conf 和/etc/sysctl.conf 两个配置文件,它们的区别在于 limits.conf 是用户层面的限制,而 sysctl.conf 是针对整个系统层面的限制。

    5 压测客户机参数调优

    对于压测机器来说,为了模拟大量的客户端,除了需要修改文件描述符限制外,还需要配置可用端口范围,可用端口数量决定了单台压测机器能够同时模拟的最大用户数量。


    • 文件描述符数量:修改过程同服务器

    • 可用端口数量:1024 以下的端口是操作系统保留的,我们可用的端口范围是 1024-65535,由于每个 TCP 连接都要用一个端口,这样单个 IP 可以模拟的用户数大概在 64000 左右 修改/etc/sysctl.conf 文件,添加如下内容:


    net.ipv4.ip_local_port_range = 1024 65535
    复制代码


    修改完毕执行:


    # sysctl -p
    复制代码


    需要注意的是,服务器最好不要这样做,这是为了避免服务监听的端口被占用而无法启动。如果迫于现实(例如手头可用的机器实在太少),服务器必须同时用作压测机器的话,可以将服务监听端口添加到 ip_local_reserved_ports 中。下面举例说明:


    修改/etc/sysctl.conf 文件,添加如下内容:


    net.ipv4.ip_local_reserved_ports = 5222, 5269, 5280-5390
    复制代码


    修改完毕执行:


    # sysctl -p
    复制代码


    TCP/IP 协议栈从 ip_local_port_range 中选取端口时,会排除 ip_local_reserved_ports 中定义的保留端口,因此就不会出现服务端口被占用而无法启动的情况。

    6 程序调优

    对于不同的业务系统,需要有针对性的对其进行调优,本文中测试的目标服务使用 Erlang/OTP 写就,Erlang/OTP 本身带有许多的限制,对于一般场景来说这些默认的设置是足够的;但是为了支持高并发,需要对 Erlang 虚拟机进行一些必要的参数调优,具体可以参考官方性能指南

    7 服务程序参数调优

    进程(process)数量

    Erlang 虚拟机默认的进程数量限制为 2^18=262144 个,这个值显然是不够的,我们可以在 erl 启动时添加参数+P 来突破这个限制


    $ erl +P 10000000
    复制代码


    需要注意的是:这样启动,erlang 虚拟机的可用进程数量可能会比 10000000 大,这是因为 erlang 通常(但不总是)选择 2 的 N 次方的值作为进程数量上限。

    原子(atom)数量

    Erlang 虚拟机默认的原子数量上限为 1048576,假如每个会话使用一个原子,那么这个默认值就不够用了,我们可以在 erl 启动时添加参数+t:


    $ erl +t 10000000
    复制代码


    从另一个角度来说,我们在编写 Erlang 程序时,使用原子需要特别小心:因为它消耗内存,而且不参与 GC,一旦创建就不会被移除掉;一旦超出原子的数量上限,Erlang 虚拟机就会 Crash,参见 How to Crash Erlang


    端口(port)数量 端口提供了与外部世界通讯的基本机制(这里的端口与 TCP/IP 端口的概念不同,需要注意区别),每个 Socket 连接需要消耗 1 个端口,官方文档里面说默认端口上限通常是 16384,但根据实测,Linux 系统默认为 65536,Windows 系统默认为 8192,无论多少,在这里都是需要调整的:在 erl 启动时添加参数+Q Number,其中 Number 取值范围是[1024-134217727]:


     $ erl +Q 10000000
    复制代码

    8 压测脚本调优

    压测工具使用 Tsung。Tsung 支持多种协议,有着丰富的功能,并且配置简单灵活。下面是几点需要注意的地方:

    内存

    对于单个 Socket 连接来说消耗内存不多,但是几万甚至几十万个连接叠加起来就非常可观了,配置不当会导致压测端内存成为瓶颈。


    • TCP 发送、接收缓存 Tsung 默认的 TCP/UDP 缓存大小为 32KB,本例中我们测试的服务采用 MQTT 协议,它是一种非常轻量级的协议,32KB 还是显得过大了,我们将它设置为 4KB 大小就足够了:


    <option name="tcp_snd_buffer" value="4096"></option>  <option name="tcp_rcv_buffer" value="4096"></option>
    复制代码


    • Tsung 能够让模拟用户的进程在空闲时间(thinktime)进入休眠,用以降低内存消耗,默认空闲 10 秒触发,我们可以将它降低到 5 秒:


    <option name="hibernate" value="5"></option>
    复制代码

    IO

    • 不要启用 dumptraffic,除非用于调试,因为它需要将客户机和服务器往来的协议写入磁盘日志中,是一个 IO 开销非常大的行为。笔者曾经遇到过一次这样的问题,测试部门的同事使用 JMeter,压测过程中,服务端一直处于较低的负载,但 JMeter 最终得出的压测报告却包含很多超时错误。经过仔细排查,才定位到原来是压测端默认开启了 debug 日志,海量的 debug 日志生生拖垮了压测机器。所以遇到这种情况时,可以先检查一下压测端配置是否正确。


    <tsung loglevel="error" dumptraffic="false" version="1.0">
    复制代码


    日志等级要调高一些,日志等级过低会打印很多无用信息:一方面会加大 IO 开销,另一方面会让有用的 ERROR 信息淹没在海量的调试日志中。


    如果 Tsung 从 CSV 文件读取用户名密码,那么该 CSV 文件不能过大,否则读取该 CSV 将会变成一个极其耗时的操作,特别是当压测程序需要每秒产生大量用户时。

    网络

    有时候为了避免网络拥塞,需要限制压测客户机的带宽,使流量以比较平滑的速率发送和接收。


    <option name="rate_limit" value="1024"></option>
    复制代码


    其采用令牌桶算法(token bucket),单位 KB/s,目前只对流入流量有效。

    9 定位系统性能瓶颈

    当系统吞吐和延迟上不去时,首先需要定位问题,而不是急于修改代码。


    常见的性能瓶颈包括 CPU/内存/磁盘 IO/网络带宽等,其中每一项都有一到多个简单实用的工具: 对于 CPU 和内存,我们只要使用 top 就可以了;对于磁盘 IO,可以用 iotop 或 iostat;对于网络带宽,可以使用 iftop。


    如果依然没能定位到问题,可能系统配置不当,参考通用的系统参数调优。


    最后检查代码是否有单点瓶颈,例如程序被阻塞了:在笔者实测过程中,发现每个用户创建会话进程都需要对同一个 supervisor 发起同步请求,同时登录的用户数量很大时,这些同步请求会排队,甚至引发超时。

    10 结束语

    以上就是笔者做压力测试时遇到的一些问题以及应对办法,鉴于笔者水平有限,错漏难免。抛砖引玉,欢迎交流指正。


    本文转载自公众号 360 云计算(ID:hulktalk)。


    原文链接:


    https://mp.weixin.qq.com/s/4LclaIGDBj4y6xZQRFiDeg


    2019-11-20 17:101342

    评论

    发布
    暂无评论
    发现更多内容

    架构师训练营第 11 周学习笔记

    郎哲158

    第 07 周 性能压测(响应时间和吞吐量变化)

    Airship

    极客大学架构师训练营

    第十一周 作业

    熊桂平

    极客大学架构师训练营

    密码学,心底的凉暖

    X.F

    密码学

    ShardingSphere RAW JDBC 分布式事务 Narayana XA 代码示例

    Java 数据库 分布式事务 ShardingSphere

    架构师训练营 2 期 Week07 总结

    第十一周 学习总结

    熊桂平

    极客大学架构师训练营

    Spring 源码学习 08:register 注册配置类

    程序员小航

    Java spring 源码 源码阅读

    架构训练营第七周作业

    一期一会

    性能测试 Go 语言

    与前端训练营的日子 --Week06

    SamGo

    学习

    话题讨论 | 未来十年,你认为哪门语言将要脱颖而出

    soolaugust

    话题讨论

    架构师训练营 11 周作业

    郎哲158

    KMP —— 字符串分析算法

    三钻

    算法 大前端 KMP

    week02

    ルンルン

    话题讨论 | 2021要来了,你对未来的一年有什么期待

    soolaugust

    话题讨论

    CSS flex 排版与动画 — 重学 CSS

    三钻

    CSS 大前端 排版 Flex

    架构师训练营第 1 期 - 第 10 周 - 命题作业

    wgl

    week7-性能优化

    未来已来

    week7-作业二:根据当周学习情况,完成一篇学习总结

    未来已来

    训练营 - 第11周

    袭望

    腾讯iOA与绿盟、天融信完成产品互认证,助力零信任市场协同发展

    第11周作业

    paul

    从一次不佳的小组讨论展开

    sherlockq

    【架构师训练营 1 期】第十一周作业

    诺乐

    话题讨论 | 遇到的印象最深的开源项目

    soolaugust

    话题讨论

    话题讨论 | 2020就要结束了,你最想对自己说的是什么

    soolaugust

    话题讨论

    腾讯牵头零信任产业标准工作组正式发布零信任联盟标准

    Week_11 总结

    golangboy

    极客大学架构师训练营

    Week_11 作业

    golangboy

    极客大学架构师训练营

    【架构师训练营 1 期】第十一周学习总结

    诺乐

    8张图带你了解大型应用架构演进历程

    Silently9527

    架构 互联网架构设计

    浅谈高并发系统性能调优_文化 & 方法_OpsDev- 杨玉东_InfoQ精选文章