KeepAlive,你优化了吗

Apr 09, 2014

一个关于公众出行服务的网站欲上线,我习惯性地使用工具来检查,工具之前是yahoo的slower,因为个人较常使用chrome浏览器,最近发现google也发布了类似的插件pagespeed insights。所以借此机会实践一下。(可惜这个插件只能在美国的chrome市场才能找到)

分析结果

本篇针对常见的"启动keep-Alive"优化来阐述

关于Keep-Alive

keep-alive是http1.0与http1.1的特性之一,意在提供长效的HTTP会话,以避免客户端频繁建立tcp连接的消耗。

想象一个这样的场景:一个客户端通过页面向服务器发送request,之后两者建立一个tcp连接,伴随着返回响应信息,服务器同时将连接关闭。这个过程分为四个步骤:

  1. 建立tcp连接

  2. 发出请求文档

  3. 发出响应文档

  4. 释放tcp连接

如果此时请求的页面中含有其他的连接,比如图片、js等,客户端将重复这个过程。keep-alive的作用在于第一次建立连接时,服务器器将保持这个tcp连接一段时间(不超过设定的KeepAliveTimeout),以节省重复过程1和4创建tcp连接与释放tcp连接的时间。一般可以节省50%以上的时间。

Keep-Alive工作原理

与HTTP1.0需要主动声明不同的是,HTTP1.1默认支持这一特性,两者的交互流程如下:

HTTP1.0 Keep-Alive的数据交互流程:

  1. 建立tcp连接

  2. Client 发出request,并声明HTTP版本为1.0,且包含header:"Connection: keep-alive"。

  3. Server收到request,通过HTTP版本1.0和"Connection: keep-alive",判断连接为长连接;故Server在response的header中也增加"Connection: keep-alive"。

  4. 同时,Server不释放tcp连接,在Client收到response后,认定为长连接,同样也不释放tcp连接。这样就实现了会话的保持。

  5. 直到会话保持的时间超过keepaliveTime时,client和server端将主动释放tcp连接。

HTTP1.1 Keep-Alive的数据交互流程:

  1. 建立tcp连接

  2. Client 发出request,并声明HTTP版本为1.1。

  3. Server收到request后,通过HTTP版本1.1就认定连接为长连接;此时Server在response的header中增加"Connection: keep-alive"。

  4. Server不释放tcp连接,在Client收到response后,通过"Connection: keep-alive"判断连接为长连接,同样也不释放tcp连接。

  5. 这个过程与http1.0类似,仅是http1.1时,客户端的request不用声明"Connection: keep-alive"。

比较

基于上述的比较,是不是采用HTTP1.1更好呢?如果不考虑浏览器并发等因素,是的。

以下的事实,仅供您参考

   IE 6,7在HTTP/1.0中默认最大并发连接数为4,在HTTP/1.1中默认最大并发连接数为2,IE8都为6。Firefox2在HTTP/1.0中
   默认最大并发连接数为2 在HTTP/1.1中默认最大并发连接数为8,firefox 3默认都是6。

Keep-Alive的优化

考虑一个这样的事实:

   假设keepalive的时间为60s,服务器1分钟内平均在线用户为500,开启keep-Alive后,Apache等服务器的消耗为:

总进程数为500个


   **假设一个进程平均占用2M的内存,总消耗内存数为500\*2=1G。**

   关闭keep-alive后,Apache等服务器的消耗为:

   **总进程数为500/60=10个**

   **总消耗内存为10\*2=20M**

这个事实要求我们在KeepAliveTimeout、MaxKeepAliveRequests和系统资源间寻找一个平衡。

   在以下的场景,不建议开启keep-alive:服务器提供的是一个接口服务,除了动态内容,几乎没有引用任何静态内容;而建议在服务器提供Web站点服务时(一个页面除了动态内容,还包含非常多的JS、图片、css文件等)开启keep-alive。

如何查询httpd进程占用的内存

pidof httpd

打印结果如下:

   60257 29860 29854 29835 29834 29833 29832 29830 29829 29828 29822 29813
   29812 29809 29804 29800 28051 28050 28049 28048 28047 28046 28045 28044
   28042 28041 28040 28039 28038 28037 28036 28020 28008 28007 28006 28002
   28001 27999 27980 27978 27402 27396 27375 27292 24414 24413 24412 24405
   24032 24023 24018 24017 24015 24014 24010 24006 23572 23554 23553 23552
   23530 23526 23523 23514 23512 23505 23499 23496 23494 23493 23491 23489
   23487 23482 23481 23479 23476 23475 23474 23473 23472 23471 23468 23461
   23443 23442 23441 23440 23439 23438 23434 23281 23177 23176 23175 23174
   23170 23169 23166 23023 22969 22721 22720 22719 22718 22717 22716 22715
   22714 22713 22712 22711 22710 22709 22708 22707 22706 22705 22704 22703
   22702 22701 22700 22699 22698 22697 22696 22695 22694 22693 22692 22691
   22690 22689 22688 22687 22686 22685 22684 22683 22682 22681 22680 22679
   22678 22677 22676 22675 22674 22673 22672 22671 22670 22669 22668 22667
   22666 22662 22661 22660 22659 22658 22657 22656 22655 22654 22653 22652
   22651 22650 22649 22648 22647 22646 22645 22644 22643 22642 22641 22640
   22639 22638

查看任意进程占用的内存

top -p 60257或者
cat /proc/60257/status

打印结果分别为


   top - 18:51:38 up 480 days,  5:16,  4 users,  load average: 0.00, 0.01, 0.05
   Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie Cpu(s):
   0.1%us,  0.1%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st Mem:
   32909664k total, 30598768k used,  2310896k free,   560268k buffers Swap:
   34996220k total,      864k used, 34995356k free, 21710184k cached

   PID USER      PR  NI  VIRT  **RES**  SHR S %CPU %MEM    TIME+  COMMAND  
   22670 apache    20   0  558m 106m 1720 S  0.0  0.3   0:00.06 httpd

结果中的RES 106m,和下面的VMRSS 108964 kB结果都一样。

   sched      sessionid  stack      statm      syscall  
   schedstat  smaps      stat       status  
   [developer01@cqjt-middle02 ~]\$ cat /proc/22670/s sched      sessionid stack
   statm      syscall  
   schedstat  smaps      stat       status  
   [developer01@cqjt-middle02 ~]\$ cat /proc/22670/status  Name:    httpd State:    S
   (sleeping) Tgid: 22670 Pid:  22670 PPid: 60257 TracerPid:    0 Uid:  48  48  48  48
   Gid: 48  48  48  48 Utrace:  0 FDSize:   64 Groups:  48  VmPeak:   571720 kB
   VmSize:    571716 kB VmLck:         0 kB VmPin:         0 kB VmHWM:    108964 kB
   **VmRSS:   108964 kB** VmData:     182768 kB VmStk:       136 kB VmExe: 336 kB
   VmLib:      29772 kB VmPTE:       756 kB VmSwap:        0 kB Threads:    1
   SigQ:    0/256954 SigPnd:    0000000000000000 ShdPnd:    0000000000000000
   SigBlk:  0000000000000000 SigIgn:    0000000000001000 SigCgt:    00000001880046eb
   CapInh:  0000000000000000 CapPrm:    0000000000000000 CapEff:    0000000000000000
   CapBnd:  ffffffffffffffff Cpus_allowed:  ffffffff,ffffffff
   Cpus_allowed_list:   0-63
   Mems_allowed:    00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000003
   Mems_allowed_list:   0-1 voluntary_ctxt_switches:    445
   nonvoluntary_ctxt_switches:  0

HTTPD占用的内存非常大的,有100M之多,也就是说,可以使用1G内存的服务器,我们最多也只能设置MaxKeepAliveRequests为10。

而KeepAliveTimeout的设定则以单个页面资源加载时间为准,比如出行的用户,页面请求资源的时间大约为2s,所以我们设定这个值为2秒。

如何开启Keep-Alive

Apache http Server

修改httpd.conf文件

KeepAlive On
MaxKeepAliveRequests 300

重新启动

apachectl -k graceful

Nginx

不同于apache,nginx本身仅支持一个keepalive_timeout 指令,其使用0值来停用keep-alive。

在http、server、location指令中添加指令

location /cqjt/ {   
    alias /url/var/www/html/;   
    keepalive_timeout  75;   
    expires 5m;   
} 

总结

大多数时候,是否能保存长连接以及设定长连接的时间,并不由服务器决定,有时浏览器(比如火狐等),其默认60秒后自动断开任何长连接。这时服务器的tomeout时间将失效。