浅谈高并发系统性能调优 - 360 OPSDEV

标签: | 发表时间:2018-09-01 09:41 | 作者:
出处:https://www.opsdev.cn

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

系统性能的关键指标

  • 吞吐量(Throughput) 系统单位时间内处理任务的数量
  • 延迟(Latency) 系统对单个任务的平均响应时间

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

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

系统性能测试

  • 业务场景 对于不同的业务系统,可以接受的延迟(Latency)也有所不同,例如邮件服务可以忍受的延迟显然要比Web服务高得多,所以首先我们需要根据业务场景的不同来定义理想的Latency值
  • 测试工具 我们需要一个能够制造高吞吐的工具来测试系统的性能,本文中使用的 Tsung,它是一个开源的支持分布式的压力测试工具,它功能丰富,性能强大,配置简单,并支持多种协议(HTTP、MySQL、LDAP、MQTT、XMPP等)。
  • 测试流程 测试的过程中需要不断加大吞吐量,同时注意观察服务端的负载,如果负载没有问题,那就观察延迟。一般这个过程需要反复很多次才能测出系统的极限值,而每次测试消耗的时间也比较长,需要耐心一些。

通用的系统参数调优

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

服务器参数调优

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

          fs.nr_open = 100000000
      fs.file-max = 100000000
    

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

          $ man proc
      /proc/sys/fs/file-max
                  This  file defines a system-wide limit on the number of open files for all processes.  (See also setrlimit(2), which can be
                  used by a process to set the per-process limit, RLIMIT_NOFILE, on the number of files it may open.)  If  you  get  lots  of
                  error  messages  in the kernel log about running out of file handles (look for "VFS: file-max limit <number> reached"), try
                  increasing this value:
    
                      echo 100000 > /proc/sys/fs/file-max
    
                  The kernel constant NR_OPEN imposes an upper limit on the value that may be placed in file-max.
    
                  If you increase /proc/sys/fs/file-max,  be  sure  to  increase  /proc/sys/fs/inode-max  to  3-4  times  the  new  value  of
                  /proc/sys/fs/file-max, or you will run out of inodes.
    
                  Privileged processes (CAP_SYS_ADMIN) can override the file-max limit.
    

    可以看到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说明:

          $ man proc
          /proc/sys/fs/inode-max
                  This  file  contains  the  maximum  number  of in-memory inodes.  On some (2.4) systems, it may not be present.  This value
                  should be 3-4 times larger than the value in file-max, since stdin, stdout and network sockets also need an inode to handle
                  them.  When you regularly run out of inodes, you need to increase this value.
    
    

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

    对于 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,添加如下内容:

          *      hard   nofile      4194304
      *      soft   nofile      4194304
    

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

          $ man limits.conf
                  <...skipping...>
             hard
                 for enforcing hard resource limits. These limits are set by the superuser and enforced by the Kernel. The user cannot
                 raise his requirement of system resources above such values.
    
             soft
                 for enforcing soft resource limits. These limits are ones that the user can move up or down within the permitted range by
                 any pre-existing hard limits. The values specified with this token can be thought of as default values, for normal system
                 usage.
                  <...skipping...>
             nofile
                 maximum number of open file descriptors
    
    

    这里的意思很明确: 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是针对整个系统层面的限制。

压测客户机参数调优

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

  • 文件描述符数量:修改过程同服务器
  • 可用端口数量: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中定义的保留端口,因此就不会出现服务端口被占用而无法启动的情况。

程序调优

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

服务程序参数调优

  • 进程(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
    

压测脚本调优

压测工具使用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,目前只对流入流量有效。

定位系统性能瓶颈

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

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

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

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

结束语

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

相关 [并发 系统 性能调优] 推荐:

浅谈高并发系统性能调优 - 360 OPSDEV

- -
高并发系统的优化一直以来都是一个很重要的问题,下面基于笔者的实践,和大家聊聊高并发系统的一些调优和优化策略. 吞吐量(Throughput) 系统单位时间内处理任务的数量. 延迟(Latency) 系统对单个任务的平均响应时间. 一般来说,考量一个系统的性能主要看这两个指标. 而这两个指标之间又存在着一些联系:对于指定的系统来说,系统的吞吐量越大,处理的请求越多,服务器就越繁忙,响应速度就会慢下来;而延迟越低的系统,能够承载的吞吐量也相应的更高一些.

系统性能调优吐血总结分享:原创

- crystal - 博客园-首页原创精华区
首先是较为精准的定位问题,借助于相应的工具包,分析系统性能瓶颈在哪,在根据其性能指标,以及所处于层级决定选择优化的方式方法. 在选择优化的方式方法时,大家可以参照以下章节调优方法,架构优化递进,进行正确的,有针对性,有步骤的优化. 可能会发现部分指导思想或许有相悖嫌疑,大可不必较真,系统优化的过程本身就是一个不断分离+共享的组合拳,至于具体选择哪种优化方式,根据具体需求来定,但大型应用发展的总体思路是不断分离,在通过索引(非数据库)进行关联起来,.

Mysql Tomcat C3p0 系统性能调优个人总结

- - CSDN博客数据库推荐文章
应用逻辑 就是用c3p0 到数据库查询数据并http返回Json数据. 1 调优前的最初的测试结果   JMeter test result. 这个数据是从程序的log 中打印出的 数据库select语句 中得出的结果(正确与否后面会有讨论). 2 经过IOD系统打印 SQL query 的执行时间 和 tomcat 每个request 的 响应时间,找出 系统瓶颈 是因为一个 select语句 使用了 in:.

某证券清算系统的一次性能调优

- - Java - 编程语言 - ITeye博客
上线前,用户预估平均一天交易量约一万条,峰值约两万条. 项目上线第一天,交易量有4万条. 对于这4万条左右的交易信息的清算,花了一个多小时(清算时需要我们系统发指令给清算所,由清算所按照我们系统的指令进行清算,最后把结果通过MQ返回给我们). 用户提出以后交易的峰值可能达到一天5万条. 我们按照2倍的处理能力,定下一天10万条交易信息的处理量的目标.

JVM 性能调优实战之:一次系统性能瓶颈的寻找过程

- - ImportNew
玩过性能优化的朋友都清楚,性能优化的关键并不在于怎么进行优化,而在于怎么找到当前系统的性能瓶颈. 性能优化分为好几个层次,比如系统层次、算法层次、代码层次…JVM 的性能优化被认为是底层优化,门槛较高,精通这种技能的人比较少. 笔者呆过几家技术力量不算弱的公司,每个公司内部真正能够进行 JVM 性能调优的人寥寥无几、甚至没有.

HBase性能调优

- - 学着站在巨人的肩膀上
我们经常看到一些文章吹嘘某产品如何如何快,如何如何强,而自己测试时却不如描述的一些数据. 其实原因可能在于你还不是真正理解其内部结构,对于其性能调优方法不够了解. 本文转自TaoBao的Ken Wu同学的博客,是目前看到比较完整的HBase调优文章. 原文链接:HBase性能调优. 因官方Book Performance Tuning部分章节没有按配置项进行索引,不能达到快速查阅的效果.

hbase性能调优

- - 数据库 - ITeye博客
   1)、hbase.regionserver.handler.count:该设置决定了处理RPC的线程数量,默认值是10,通常可以调大,比如:150,当请求内容很大(上MB,比如大的put、使用缓存的scans)的时候,如果该值设置过大则会占用过多的内存,导致频繁的GC,或者出现OutOfMemory,因此该值不是越大越好.

Hadoop性能调优

- - 开源软件 - ITeye博客
是否对任务进行profiling,调用java内置的profile功能,打出相关性能信息. 对几个map或reduce进行profiling. 非常影响速度,建议在小数据量上尝试. 1表示不reuse,-1表示无限reuse,其他数值表示每个jvm reuse次数. reuse的时候,map结束时不会释放内存.

MapReduce - 性能调优

- - CSDN博客云计算推荐文章
        Hadoop为用户作业提供了多种可配置的参数,以允许用户根据作业特点调整这些参数值使作业运行效率达到最优.         对于一大批MapReduce程序,如果可以设置一个Combiner,那么对于提高作业性能是十分有帮助的. Combiner可减少Map Task中间输出的结果,从而减少各个Reduce Task的远程拷贝数据量,最终表现为Map Task和Reduce Task执行时间缩短.

Java 性能调优

- - 编程语言 - ITeye博客
1.用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用. 但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法. clone()方法不会调用任何类构造函数. 在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单.