打造千万级流量系统——秒杀系统(性能篇)

系统参数:如何按业务场景优化网络性能

一般来说,不同的参数类型对应不同的资源和性能,进而影响不同的业务场景,比如网络参数主要影响以网络请求为主的业务,文件系统参数主要影响以读写文件为主的业务场景。

那我们该如何获取参数类型呢,可以执行以下命令:

sudo /sbin/sysctl -a|awk -F "." '{print $1}'|sort -k1|uniq

结果如下所示:

    abi
    debug
    fs
    kernel
    net
    user
    vm
sudo /sbin/sysctl -a|grep "^net."|awk -F "[.| ]" '{print $2}'|sort -k1|uniq

我们将得到 net 类型下所有子类型:

    bridge
    core
    ipv4
    ipv6
    netfilter
    nf_conntrack_max
    unix

在 Linux 系统里,这些参数都可以在 /etc/sysctl.conf 文件里修改,如果没有,你可以自行添加。修改完后,可以使用 sudo sysctl -p 命令加载最新配置,让配置生效。

在这些子类型当中,我们需要重点关注 core 和 ipv4 的配置。 因为这两类配置里包含网络协议相关的各种参数,如缓冲区内存大小参数、快速回收资源的参数等。前面我提到的秒杀接口服务和图片存储系统这两个业务场景,就需要 core 和 ipv4 中的参数来优化。

1.优化套接字缓冲区参数

在操作系统中,用于网络通信的系统资源是以网络套接字(Socket,具有插座的意思)为单位来分配和管理的。在程控电话出现以前,人们打电话需要通过接线员把通信双方的电话线套接在一起来接通。在计算机网络通信出现以后,我们便用套接字来表示双向网络通信的端点。而网络套接字缓冲区的大小直接影响程序通过网络收发数据的性能。

套接字缓冲区的大小怎样设置呢?就是通过系统参数来设置。具体参数我们可以在终端执行以下命令来获取。

sudo /sbin/sysctl -a|grep "^net."|grep "[r|w|_]mem[_| ]"

结果如下:

上面的结果中,等号左边是参数名称,右边是参数的值。从等号左边的参数名称我们可以大致看出几个关键字:mem、rmem、wmem、max、default、min。

max、default、min 比较容易理解,分别是最大值、默认值、最小值。 mem、rmem、wmem 分别是总内存、接收缓冲区内存、发送缓冲区内存。

在上面那些内存参数里,rmem 和 wmem 的单位都是“字节”,而 mem 的单位是“页”。“页”是操作系统管理内存的最小单位,在 Linux 系统里,默认一页是 4KB 大小。

另外,你是否注意到,tcp_mem、tcp_rmem、tcp_wmem、udp_mem 这几个参数后面有三个值呢?对于 tcp_rmem 和 tcp_wmem 来说,这三个值是单个套接字可分配内存的大小,从左到右分别是最小值、默认值、最大值,如 tcp_rmem 的最小值是 4096、默认值是 131072、最大值是 6291456。

需要注意的是,这当中的默认值和最大值会分别被 net.core 下对应的 default 值和 max 值覆盖。比如 tcp_rmem 的默认值会被 rmem_default 覆盖,最大值将会被 rmem_max 覆盖。

对于 tcp_mem 和 udp_mem 来说,它后面的三个值用于控制内存压力,从左到右分别是内存压力的最小值、压力值、最大值,比如 tcp_mem 的最小值是 188964、压力值是251954、最大值是 377928。当 TCP 总内存使用量小于 188964 时,表示内存毫无压力,不用考虑回收;当内存使用量超过 251954 时,系统会开始回收内存,直到小于 188964;当内存使用量达到 377928 时,系统将会拒绝分配套接字,并输出日志“TCP: too many of orphaned sockets”。

那我们该如何优化这些参数呢?来看一个业务场景。

秒杀系统管理后台有个上传商品图片的功能,用于从前端上传图片到后端文件存储系统。商品图片小的也有上百 KB ,大的则达到十几 MB 。通常需要几秒甚至十几秒钟上传完一张图,如果批量上传,可能整个上传过程需要几十秒钟。对于这种文件上传的业务场景,该如何优化网络参数提升服务器的处理性能呢?

文件上传系统给前端提供的是 HTTP 接口,用的是 HTTP 协议,而 HTTP 协议底层是基于 TCP 连接传输数据的。所以,为了提升系统处理文件数据的性能,我们可以修改以下几个参数:

  1. net.core.rmem_default

  2. net.core.rmem_max

  3. net.core.wmem_default

  4. net.core.wmem_max

  5. net.ipv4.tcp_mem

  6. net.ipv4.tcp_rmem

  7. net.ipv4.tcp_wmem

 假如系统最大可以为 TCP 分配 2GB 内存,最小值为 256MB,压力值为 1.5GB。按照一页为 4KB 来计算, tcp_mem 的最小值、压力值、最大值分别是 65536、393216、524288,单位是“页” 。

假如平均每个文件数据包为 512KB,每个套接字读写缓冲区最小可以各容纳 2 个数据包,默认可以各容纳 4 个数据包,最大可以各容纳 10 个数据包,那我们可以算出 tcp_rmem 和 tcp_wmem 的最小值、默认值、最大值分别是 1048576、2097152、5242880,单位是“字节”。而 rmem_default 和 wmem_default 是 2097152,rmem_max 和 wmem_max 是 5242880。

另外,由于缓冲区超过了 65535,还需要将 net.ipv4.tcp_window_scaling 参数设置为 1,告知系统使用大的 TCP 缓冲区。

最终,我们的参数配置如下:

  1. net.core.rmem_default = 2097152

  2. net.core.rmem_max = 5242880

  3. net.core.wmem_default = 2097152

  4. net.core.wmem_max = 5242880

  5. net.ipv4.tcp_mem = 65536 393216 524288

  6. net.ipv4.tcp_rmem = 1048576 2097152 5242880

  7. net.ipv4.tcp_wmem = 1048576 2097152 5242880

 需要注意的是,不同业务场景连接数的量级不同,缓冲区的配置也不同。对于秒杀接口这种大量短连接的业务场景,需要减少 rmem 和 wmem 相关的数值。比如将最小值、默认值、最大值分别改为 4096、4096、8192,就能建立更多的连接。

2.优化 TCP 协议参数和最大连接数

当网络带宽非常好的时候,“慢启动”机制反而会限制数据传输速度。再比如,“粘包算法”会将一些小的数据包合并成一个 TCP 包发出去,或者一直等到定时器超时后发送。在某些情况下该算法确实能提升网络吞吐量,但对于一些对实时性要求较高的数据来说,它会导致接收方无法及时接收到数据。

秒杀的抢购接口,负责接收大量用户的抢购请求。对于有资格的用户,执行扣减库存并下单然后返回抢购成功给前端;对于没有资格或者扣减库存失败的用户,则返回抢购失败给前端。发起抢购请求以及请求返回的 HTTP 协议数据大概在 500 字节,秒杀接口服务需要快速处理连接的建立、断开、回收。那么,该如何优化秒杀接口服务的网络参数提升处理网络连接的性能呢?

 首先,秒杀接口服务是通过公网给用户提供服务的,而公网的数据帧能承载的应用数据是 1472 字节。秒杀抢购接口处理的数据大小 500 字节相当于 1472 的 1/3,由此可以判断这些数据包是小数据包,可能会受“粘包算法”影响。

用户对请求的耗时很敏感,这就需要关闭“粘包算法”,确保数据包立即投递出去。在 TCP 套接字加上 TCP_NODELAY 参数来关闭该算法。另外,为了抵御攻击者用大量 SYN 报文发起的短报文攻击,需要将 net.ipv4.tcp_syncookies 参数设置为 1。

第二,秒杀用户量大,这就需要秒杀接口服务处理大量短连接,由此会导致什么结果呢?需要秒杀接口服务创建、回收套接字的速度非常快,以便有足够资源处理大量连接。我们可以通过关闭空闲连接、复用套接字等方法快速回收或重用已分配的资源。具体设置如下:

# 重用处于 TIME-WAIT 状态的套接字

net.ipv4.tcp_tw_reuse = 1

# 快速回收 TIME-WAIT 状态的套接字

net.ipv4.tcp_tw_recycle = 1

# 关闭处于 FIN-WAIT-2 状态 30 秒以上的套接字

net.ipv4.tcp_fin_timeout = 30

# 设置空闲 TCP 连接存活时间,以便即时关闭空闲连接,回收资源

net.ipv4.tcp_keepalive_time=1800

第三,由于秒杀请求量大,偶尔的网络抖动可能导致部分数据包丢失,这将会触发“超时重传”。为了避免重传网络抖动后的所有包,我们可以设置选择性重传的参数,避免重传已成功发送的数据包,导致浪费网络带宽。具体的办法是,将 net.ipv4.tcp_sack 这一参数设置为 1。

最后,秒杀活动中用户日活达到了百万以上,这就需要尽可能提升单机网络连接容量,确保并发能力。在操作系统中,一个网络连接会占用一个文件描述符,这需要将最大文件打开数的参数设置为一个比较大的值,以免文件描述符不够用导致性能问题。如 fs.file-max = 65535 表示最多可以打开 65535 个文件。

TCP 协议的参数还有很多,感兴趣的话,可以查阅 TCP 协议手册和 Linux TCP 参数手册。

高性能缓存:多级缓存是如何提升服务性能的

缓存是一种存储器,它位于访问速度、容量和成本相差较大的计算单元和目标存储器之间,主要用于存储热点数据的中间状态,为计算单元处理热点数据时提供良好的访问性能。

但缓存中的热点数据通常具有时效性,比如当后端活动数据有更新的时候,浏览器缓存中的原活动信息就会失效,进而导致缓存穿透问题。为此,我们通常会采用多级缓存来避免单级缓存穿透,提升服务性能。

什么是多级缓存

多级缓存通常是由多种不同容量、不同性能的存储器按顺序组成,最有名的是存储器金字塔。

上层的寄存器、L1 缓存、L2 缓存是位于 CPU 核内的高速缓存,访问延迟通常在 10 纳秒以下。L3 缓存是位于 CPU 核外部但在芯片内部的共享高速缓存,访问延迟通常在十纳秒左右。高速缓存具有成本高、容量小的特点,容量最大的 L3 缓存通常也只有几十MB。

本地内存是计算机内的主存储器,相比 CPU 芯片内部的高速缓存,内存的成本要低很多,容量通常是 GB 级别,访问延迟通常在几十到几百纳秒。

本地磁盘是计算机内的辅助存储,负责存储一些希望掉电不丢失的数据,主要有机械磁盘和固态硬盘。它的容量通常在几百 GB ,大的达到了 TB。固态硬盘访问延迟通常在几十到几百微秒,而机械硬盘通常在十几到几十毫秒。

网络内存和网络磁盘属于外部存储,通常用于多个计算机之间共享数据。比如 Redis 属于网络内存存储, NAS 文件系统和 MySQL 之类的关系型数据库属于网络磁盘存储。网络内存和本地磁盘通常用来当作网络磁盘存储的缓存。

秒杀系统多级缓存设计

秒杀系统包含动态数据和静态数据,它的多级缓存包括浏览器端缓存和服务器端缓存。其中浏览器端缓存分为本地内存缓存和本地磁盘缓存,服务器端缓存有本地内存缓存、网络内存缓存

  • 浏览器本地内存缓存
  • 服务端本地内存缓存
  • 服务端网络内存缓存

高性能日志:如何提升日志性能避免 IO 瓶颈

秒杀日志面临的问题

对于并发不高的服务,我们可以把所有需要的日志写入到磁盘上的日志文件里。但是,在高峰期间,秒杀服务单节点需要处理的请求 QPS 可能达到 10 万以上。一个请求从进入秒杀服务到处理失败或者成功,至少会产生两条日志。也就是说,高峰期间,一个秒杀节点每秒产生的日志可能达到 30 万条以上。

这是什么概念?

磁盘有个性能指标:IOPS,即每秒读写次数。一块性能比较好的固态硬盘,IOPS 大概在 3 万左右。也就是说,一个秒杀节点的每秒日志条数是固态硬盘 IOPS 的 10 倍!如果这些日志每次请求时都立即写入磁盘,磁盘根本扛不住,更别说通过网络写入到监控系统中。

所以,秒杀日志会面临的第一个问题是,每秒日志量远高于磁盘 IOPS,直接写磁盘会影响服务性能和稳定性。

另外,服务在输出日志前,需要先分配内存对日志信息进行拼接。日志输出完,还需要释放该日志的内存。这将会导致什么问题呢?

对于那些有内存垃圾回收器的语言,如 Java 和 Golang ,频繁分配和释放内存,可能会导致内存垃圾回收器频繁回收内存,而回收内存的时候又会导致 CPU 占用率大幅升高,进而影响服务性能和稳定性。

那些没有内存垃圾回收器的语言,如 C++ ,又会受什么影响呢?它们通常是从堆内存中分配内存,而大量的分配、释放堆内存可能会导致内存碎片,影响服务性能。

所以,秒杀日志会面临的第二个问题是,大量日志导致服务频繁分配,频繁释放内存,影响服务性能。

最后,秒杀日志还会面临服务异常退出丢失大量日志的问题。

我们知道,由于秒杀服务处理的请求量太大,每秒都会有很多请求的日志未写入磁盘。如果秒杀服务突然出问题挂掉了,那这批日志可能就会丢失。

对于高并发系统,这在所难免,问题是如何把控好写入日志的时间窗口,将丢失的日志条数控制在一个很小的可接受范围内。

这就是秒杀日志面临的第三个问题。通过上面的介绍,想必你也明白了,像秒杀这种大流量业务场景下,日志收集是个大难题,也是个必须要解决的性能问题。

如何优化秒杀日志性能?

  • 磁盘 IO 性能优化

Linux 有一种特殊的文件系统:tmpfs,即临时文件系统,它是一种基于内存的文件系统。当使用临时文件系统时,你以为在程序中写文件是写入到磁盘,实际上是写入到了内存中。临时文件系统中的文件虽然在内存中,但不会随着应用程序退出而丢失,因为它是由操作系统管理的。

由于云架构保障了云主机的高可用,只要操作系统正常运行,也没有人删除文件,临时文件系统中的文件就不会丢失。所以,我们可以将秒杀服务写日志的文件放在临时文件系统中。相比直接写磁盘,在临时文件系统中写日志的性能至少能提升 100 倍

当然,临时文件系统中的日志文件也不能无限制地写,否则临时文件系统的内存迟早被占满。那该怎么办呢?可以这样处理,比如,每当日志文件达到 20MB 的时候,就将日志文件转移到磁盘上,并将临时文件系统中的日志文件清空。 相比频繁的小数据写入,磁盘在顺序写入大文件的时候性能更高,也就降低了写入压力。

  • 内存分配性能优化
  • 如何减小丢日志的风险

引自:拉勾打造千万级流量系统

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值