当时方位: 主页 > Linux学院 > 网络服务 > Web > 打造支撑海量用户高功能server的内存办理

打造支撑海量用户高功能server的内存办理

2014-03-17 14:22 来历:腾讯 作者:codebox 人气指数: 我要谈论
以此系列文章留念因 QQ 空间、QQ 农场等互联网事务爆破式添加,而日夜 不眠地为公共组件做架构调整、功能优化的热情年月。

以 QQ 农场、老友生意为代表的 SNS 网页游戏的忽然鼓起,全国男女老少一 起日夜不眠的在电脑面前奋战,鼠标、键盘还有各路外挂汇成的恳求像洪水一般 涌来。当流量把机房中心交换机冲得乱七八糟,当咱们把市场上的悉数买 光都不行时,我总算懂了,这便是 TMD 的所谓的海量。
在这个进程中,从 web 层到逻辑层,再到数据层,每一个组件都通过了一次
洗礼,从小男孩生长为真实的男人。
现在我现已投身于云渠道的建设了,这个支撑海量用户的高功能 server 系列 文章就想为这段热情焚烧的年月做个迟到的总结。现在计划从以下几个方面打开 来:
1) 高功能 server 的内存办理。
2) 大并发下的 server 模型挑选。
3) 高效定时器的实践。
4) 异步结构的规划与威力。
5) socket 及 OS 优化。
6) 事务运用的架构调整与考虑。
这其间大部分是泥腿子方法,土法炼钢思路,不过却的的确确处理了问题, 饱尝住了检测。
在这儿把这些东东放出来,我按我的主意写,您按您的主意看,然后,然后 就没有了。。。

怎么衡量好与坏

首要,要做好一件作业,就要知道什么是好与坏。那怎么点评一个 server 写 得怎么样?我的观点是,能最有用地榨干体系资源。
两个关键词,有用和榨干。 有用标明全在干正事儿,不做无用功;榨干标明充沛运用,不糟蹋。
拿我自己完结的一个 web server 来举例(嘿嘿,我比较满意的一个著作 TencentWebProxy),在压力测验进程中,体现如下:
点击这儿给我发消息
i. 充沛有利地势用了 CPU,机器共四个核,TencentWebProxy 相应地发动了 四个作业线程,每个线程都彻底把体系 CPU 吃尽。每个 CPU 运用都非 常均匀,不管是体系态,用户态,仍是软中止。当有更多的 CPU 中心 时,也有通过发动相匹配的线程数,很好的运用多核。此为榨干。

ii. 按 CPU 各个维度的份额来说,体系态和软中止占了近 90%,阐明体系大

部分资源在处理网络服务,只要 10%多一点的用在用户态,阐明对 HTTP 协议解析等操作的资源耗费操控地比较抱负,全在干正事儿。此为有用。
下面就进入这篇的主题,假如构建高功能 server 的第一项,内存办理。

内存办理

最省力的方法--PRELOAD 高效内存分配库

tcmalloc

业界最有名的内存分配库,当数 google 的 tcmalloc。Tcmalloc 在办理小 内存块时十分有用,而且能够防止在大内存分配时的 mmap()体系调用。它在多 线程中的体现也不错能很好的削减锁磕碰(glibc 丧命的问题)。Tcmalloc 现在基 本上成了 mysql DBA 的标配了。

lockless

另一个比较常用的内存分配器是 lockless,着眼点是运用远锁技术来进步多 线程内存分配功能。官方测验数据显现,在多线程环境下体现比 tcmalloc 还好。 当然,官方数据也只能当参阅,信一半就差不多了,呵呵。

(The Lockless memory allocator can take about half the time as

other allocators to complete the benchmarks. The Lockless memory

allocator does not suffer from slowdowns at larger allocation sizes.)

hoard

最终一个是 fory 最近向我引荐的 hoard 库,他在 UMass 的教师写的,hoard 最主要的着眼点是在 SMP 体系上,当中心越多的时分它的优势适当显着。用它 自现已的话来说便是“dramatically improve application performance,

especially for multithreaded programs running on multiprocessors and

multicore CPUs”


我还没用过这个 hoard 库,后边有时间试试。
我不爽 glibc 的内存分配算法主要是两点:一是大内存的 mmap,其实这也
不是很大的问题,但凡逼得 libc 调用 mmap,一般都是用法太不考究了;其二, 这才是最大的问题,多线程分配内存时的锁磕碰,会形成 server 功能直线下降, 直到不行忍耐的程度!
用 preload 高效内存库的优点便是立杆见影,不必改代码,不必从头编译。 把 LD_PRELOAD 环境变量导出来就好了。假如线上体系呈现内存办理的问题, 我一般都会把 tcmalloc 加载上去看看作用。
要注意的是,一切的动态内存分配算法,肯定是在很多方面做了 trade-off, 用之前要了解清楚了,方能起到预期作用。

通用内存池

内存池是常见的一种自己办理内存的方法,比如说 nginx 内部的一切内存分 配都是根据 ngx_palloc 和 ngx_pfree 及其宗族函数进行办理的。
Nginx 分配内存进程一般如下:
1. 假如分配大内存,则直接调 malloc 分配,用完后挂到 large 链表中,下 次分配大内存可从 large 链表中分配。
2. 假如分配的是小内存,则从 current 所指的链表中寻觅巨细适宜的内存, 找到就回来,找不到就新分配。
腾讯运用的三网署理 qhttpd 也运用了内存池技术,不同的是 qhttpd 的内 存是预分配的,发动的时分就分配了一大块内存,运转进程中不再分配。
内存池的缓存等级仍是比较低,对应的是内存 buffer 等级笼统。但因为
高功能内存分配库的不断改时,这些自己完结的内存池,不一定就更有用, 而且是各种 bug 的温床。我对这种方法持比较保存的定见。

通用目标池

目标池最有名的当数 linux 内核中的 slab 高速缓冲区,为内核中各种常 见数据结构做缓存,如 skb 等。
在用户空间里一般这样完结:把闲暇目标挂到一个链表上,分配的时分 就能够从链表上取下来,开释的时分再挂回去。这都是 O(1)的操作。这样做 的优点是,每种目标基本上都是固定巨细的,所以能够直接从闲暇链表上拿 下来用。
不过这儿还有一个问题,便是多线程操作同一个链表的锁问题,假如不 做特别规划,会呈现相似 malloc 在多线程环境下的锁磕碰问题。
这个问题的处理思路一般有两个:

i. 目标池部分化。部分化后,每个线程都有自己独立的目标池,这样可 以彻底防止锁,可是资源不能大局调度。

ii. 目标池半部分化。这是一种折中的方法,指主线程操控一切的资源,线 程来取目标的时分选用批发的方法要一大批目标曩昔,交还的时分也 批量交还,这样基本上就有用地处理了锁磕碰的问题。

目标池能够很好的处理固定巨细的目标问题,可是关于变长类型,如字
符串就不大起作用了,而这也是 web 常常耗费十分大的一些方面。

高功能 Server 专用目标池!!

这才是我真实想要说的终极计划!!!

我写的一切 server 简直无一例外的运用了这一方法的目标缓存,极端简 单,极端有用,彻底处理了内存分配的一切问题……
作业是这样的:
Server 的规划都是围绕着 socket 打开的,一般会把 socket fd 包 装成一个目标,TencentWebProxy 运用的是 epoll,每个 fd 包装成一个 poller 目标。而 fd 是天然的数组下标!!!而且天然地具有仅有性!!!
所以,直接静态分配 poller 目标数组,数组巨细为 ulimit 中 fd 的上限。 当 accept 到一个 fd 时,直接到 poller[fd]就找到仅有归于它的 poller 目标 了。对,数组能够随机拜访!
那字符串类的目标呢。。。
因为整个 server 是以 poller 为中心规划的,poller 目标的内存 buffer 都是跟 poller 有相关的,比如说 recv_buff, send_buff, tmp_buff 等等,都 会被 poller 用指针或都内嵌 buffer 目标引用到(TencentWebProxy 中运用 xbuffer_t 来办理字符串和变长 buffer)。这些内存被运用到的时分,直接分 配,fd 封闭时,调用 poller 的 reset 函数,将 poller 的目标里边的一切内
容重置,以供下次运用。关于现已分配的 buffer 不开释,只要把符号 buffer
里数据长度(len)的变量置为 0 即可。

这样一来,只要 server 一发动的时分会分配内存,当收到更大的数据包 的时分 xbuffer_t 的 buf 会 remalloc 一下,相应的 size 也会变大。当运转 一段时间后,server 简直没有内存分配操作了。
当体系中的内存吃紧的时分,能够沿着 poller[]一致收回一下内存。不过 在实践运营进程中发现,这个规划其实没大有必要。

!!!运用以 fd 为下标的目标数据,一定要记住目标 reset 完结之后, 才干封闭 fd。提早封闭 fd 会形成其它线程从头运用了这个 fd,从而两个线程 一起操作同一个 poller 目标,形成内存过错。自己耗费了一下午的时分来解 决这个 bug。。。这儿的时序是一定要确保的,为了安全,我还在这儿加了 内存屏障。

相关代码:
分配 poller 静态数组

Conncetion 是 poller 的子类。
NEW_ARRAY 是分配数组目标的宏,界说如下:

poller 目标时序确保的内存屏障:

字符串方面操作的优化:

1. 用数字比较替代字符串比较,nginx 中也很多运用了这个技巧。这个技巧我 之前在微博上提过。
2. 不必 memset 将 buffer 置 0,只要将第一个字节置 0,然后运用安全的字符
串操作函数就不行能犯错。
3. 当 xbuffer_t 在扩展 buf 巨细的时分,每次以 128 的粒度添加,防止频频扩 展。(128 是经验值)
4. 将 memcpy,strlen 等函数自己完结或将代码 copy 过来然后 inline 掉,将会 极大削减函数调用开支。

作用

通过以上方面的优化,咱们在运用 perf 来看 TWP 的运转状况来看,现已非 常抱负了,占用率高的,不管是内核态仍是用户态的,都是有用操作。

呵呵,作用还不错吧。。

昨天晚上写到 3 点多,今日又起了个大早上写完了这篇文章,心里肯定是狂野地 等待各位网友的反应和定见。
这也是我把这个系列文章写下去的一切动力。。

咱们感兴趣的内容
小同伴独爱的新闻
小同伴还重视了以下信息
小同伴重视的焦点

小同伴都在重视的抢手词

新服 缤纷活动 帆海世纪 芈月传 暗黑道具 萌乐网 苹果发布会 最新谍照 三国令 剑雨江湖 怎样修炼战骑 页游 怎样修炼同伴 木甲国际 仙侠道2 推黑科技 页游形式 武圣试炼场 街机玩法 蓝月传奇 个人BOSS玩法 哥们网 九阴绝学 仗剑出鞘 全新形式 范伟打天下 全新元神玩法 七大神兵简介 新手攻略 跑腿使命 门派五行 城战礼包 页游界 泥石流 傅园慧 经典网页游戏 耐玩 盘点 玉石攻略 进步人物 大黑 实装特点 神兵攻略 闻名莽荒 莽荒纪 手持神兵 土豪梦 万世 开学清单 财富赚不断 天书国际 大黑游戏 资源战场 ppwan 天问 激战 全国大战 雄霸一方 新增宠物技术 肯定小能手 花千骨 三尾章鱼 风色轨道 双枪手 弑之神 缤纷好礼 惊喜六重连 帮会 中秋福利 克己月饼 九阴真经 玩家 五周年留念 留念银币 名动三界 新服资料片 画江山 勇战妖魔 邪恶势力 上古降魔 老司机玩法 坐骑揭秘 黑科技 竞技场攻略 铁血皇城 披风玩法 书剑恩仇录 配备强化攻略 户外BOSS玩法 全网曝光 赤壁传说 半回合制国 ACT 奇珍商城 热血战歌 传奇瑰宝抽奖 打开方法 门徒 门徒获取玩法 三大萌宠简介