用PHP 编写tomcat支持最大并发数高并发的网站,需要做什么处理

PHP如何解决网站大流量与高并发的问题
字体:[ ] 类型:转载 时间:
普通的P4服务器一般最多能支持每天10万独立IP,如果访问量比这个还要大,那么必须首先配置一台更高性能的专用服务器才能解决问题
首先,确认服务器硬件是否足够支持当前的流量。 普通的P4服务器一般最多能支持每天10万独立IP,如果访问量比这个还要大, 那么必须首先配置一台更高性能的专用服务器才能解决问题 ,否则怎么优化都不可能彻底解决性能问题。 其次,优化数据库访问。 前台实现完全的静态化当然最好,可以完全不用访问数据库,不过对于频繁更新的网站, 静态化往往不能满足某些功能。 缓存技术就是另一个解决方案,就是将动态数据存储到缓存文件中,动态网页直接调用 这些文件,而不必再访问数据库,WordPress和Z-Blog都大量使用这种缓存技术。我 自己也写过一个Z-Blog的计数器插件,也是基于这样的原理。 如果确实无法避免对数据库的访问,那么可以尝试优化数据库的查询SQL.避免使用 Select * from这样的语句,每次查询只返回自己需要的结果,避免短时间内的大 量SQL查询。 第三,禁止外部的盗链。 外部网站的图片或者文件盗链往往会带来大量的负载压力,因此应该严格限制外部对 于自身的图片或者文件盗链,好在目前可以简单地通过refer来控制盗链,Apache自 己就可以通过配置来禁止盗链,IIS也有一些第三方的ISAPI可以实现同样的功能。当 然,伪造refer也可以通过代码来实现盗链,不过目前蓄意伪造refer盗链的还不多, 可以先不去考虑,或者使用非技术手段来解决,比如在图片上增加水印。 第四,控制大文件的下载。 大文件的下载会占用很大的流量,并且对于非SCSI硬盘来说,大量文件下载会消耗 CPU,使得网站响应能力下降。因此,尽量不要提供超过2M的大文件下载,如果需要 提供,建议将大文件放在另外一台服务器上。 第五,使用不同主机分流主要流量 将文件放在不同的主机上,提供不同的镜像供用户下载。比如如果觉得RSS文件占用 流量大,那么使用FeedBurner或者FeedSky等服务将RSS输出放在其他主机上,这 样别人访问的流量压力就大多集中在FeedBurner的主机上,RSS就不占用太多资源了。 第六,使用流量分析统计软件。 在网站上安装一个流量分析统计软件,可以即时知道哪些地方耗费了大量流量,哪些页 面需要再进行优化,因此,解决流量问题还需要进行精确的统计分析才可以。我推荐使 用的流量分析统计软件是Google Analytics(Google分析)。我使用过程中感觉其 效果非常不错,稍后我将详细介绍一下Google Analytics的一些使用常识和技巧。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具用 PHP 编写支持高并发的网站,需要做什么处理? - 知乎821被浏览142056分享邀请回答4添加评论分享收藏感谢收起第4期-PHP电商网站高并发的秘诀之电商秒杀活动 - 知乎专栏
{"debug":false,"apiRoot":"","paySDK":"/api/js","wechatConfigAPI":"/api/wechat/jssdkconfig","name":"production","instance":"column","tokens":{"X-XSRF-TOKEN":null,"X-UDID":null,"Authorization":"oauth c3cef7c66aa9e6a1e3160e20"}}
{"database":{"Post":{"":{"title":"第4期-PHP电商网站高并发的秘诀之电商秒杀活动","author":"phpgod","content":"电商网站秒杀活动秒杀活动通常是这样定义:活动方在有限的时间段内(通常是M分钟到H小时不等的时间)给出指定数量O个P商品的大减价抢购名额。这类秒杀活动一般都会出现如下情况↓↓第一、在某一时间内QPS超过系统负载;第二、架构不合理导致系统的其它与秒杀活动不相关的模块变得异常缓慢;第三、少数用户重复抢到名额;第四、最终抢到的名额数量超过库存数量;第五、服务器宕机后恢复迟缓导致大量用户流入竞争对手的网站;第六、机器人流量占用了网站访问导致真实用户访问迟缓。解决方案都是人想出来的,只是时间问题罢了。解决方案背景:LNMP技术栈第一、六个问题:舍即是得:既然指定时间内,秒杀活动的QPS达到峰值Peak1,那么在秒杀活动并发测试的时候我们应该首先得到这个值得平均范围,然后取其中的极小值(min),这样就可以通过nginx的ngx_http_limit_req_module和ngx_http_limit_conn_module两个模块来限制,nginx的配置如下:http {
#geot和map两段用于处理限速白名单,map段映射名单到$limit,处于geo内的IP将被映射为空值,否则为其IP地址。
#limit_conn_zone和limit_req_zone指令对于键为空值的将会被忽略,从而实现对于列出来的IP不做限制
geo $whiteiplist
default 1;
127.0.0.1 0;
121.199.16.249 0;
map $whiteiplist
1 $binary_remote_
#limit_conn_zone定义每个IP的并发连接数量
#设置一个缓存区保存不同key的状态,大小10m。使用$limit来作为key,以此限制每个源IP的链接数
limit_conn_zone $limit
zone=perip:10m;
#limit_req_zone定义每个IP的每秒请求数量
#设置一个缓存区reqps保存不同key的状态,大小10m。这里的状态是指当前的过量请求数。
#$limit为空值则不限速,否则对应的IP进行限制每秒5个连接请求。
limit_req_zone
zone=reqps:10m rate=5r/s;
#只对PHP的秒杀页面的请求进行限速
location ~ [^/]miaosha\\.php(/|$)
#对应limit_conn_zone块
#限制每IP的PHP页面请求并发数量为5个
limit_conn perip 5;
#对应limit_req_zone块
#限制每IP的每秒的PHP页面请求次数为上面定义的rate的值:每秒5个请求,不延迟
limit_req zone=
}}上面的这段nginx配置其实是对单个IP进行限制,效果是有的,但不够明显。2.过滤无效请求:前端生成签名字符串,例如通过crypto.js,对当前unix时间戳time,产生随机字符串nonce,还有一个key必须是用户填写好验证码后主机返回给浏览器客户端一个token名称的cookie字段值(有一个过期时间)结合混淆算法生成的,最后然后经过自定义的签名算法在前端生成签名字符串signature,最后在发送抢购表单时带上以上4个字段信息,当请求到达nginx之后,我们使用nginx的lua模块编写lua脚本验证signature的正确性,并且限定以上token的过期时间为30秒,且客户端返回过来的time参数必须跟服务器的unix时间戳相差不超过5秒钟,否则直接在nginx的lua层面上直接屏蔽掉该请求,这里面就不得不说Openresty技术了,感兴趣的小伙伴可以去深入研究一下。3.概率性丢弃超负载的请求:既然我们已经在前期并发测试的时候获得了一个峰值参数PeakMin,我们应该尽量保证所有的有效秒杀请求不大于这个值,首先我们得获得当前nginx的总连接数CurrentConnectionCount,当QPS达到PeakMin的时候,我们测算出来的连接数是PeakMinConnectionCount,那么我们使用nginx的lua模块获取这个值,在系统负载达到0.8*PeakMinConnectionCount的时候,我们就对超出的部分90%的丢弃率,返回一个未能秒杀中的提示,并把用户对此次活动的秒杀结果写入memcached缓存进行记录,当系统负载达到PeakMinConnectionCount时,我们直接100%丢弃请求,前端根据状态码是5XX来给出用户未能秒杀中的消息提示,当然我想说的是这里必须保证用户的体验是正常的。第二个问题:分功能模块设计系统:一个成熟的电商系统,一般会分成很多相对独立的模块,比如产品中心,会员中心,订单中心,物流中心,配置中心,搜索中心等大模块,这些大模块之间的库表数据通常是低耦合的,因此还可以把这些大模块分割成很多子功能模块,这样就可以让整个电商系统的模块彼此的影响最大化缩小,其中的分布式服务端架构包含了很多架构实践,在这里就不细讲了。第三个问题:
1.缓存key原子验证:同一个用户重复抢到名额这个问题比较简单,最有可能是用户(机器人程序)在非常短的时间内(假设是0.01秒)提交了2次以上的并发请求,以ProductId+ActivityId+UserId命名的key写入用户成功秒杀的记录值,利用memcached的add原子性来写入信息,如果add出错则证明已经add过一次,那就返回。第四个问题:
1.乐观锁:
memcached有一个很不错的CAS检查机制,就是二话不说我先抢到一个一个名额,到真的要保存的数据的时候我再看看CAS值是否跟一开始的时候一样,如果不一样就不操作返回没有秒杀到的消息提示,否则就减掉一个有效秒杀名额,直到保存秒杀库存的key为0即止。第五个问题:
1.冷热多备份:
不管是应用服务器,缓存服务器,数据库服务器,消息队列服务器等,都应该有自己的多备份,尤其是数据库服务器与缓存服务器更是直接影响了系统数据层面的东西,有条件的还需要做好异地多活,多数据中心等架构设施。
2.自动化运维:
多使用批量管理与配置工具,例如ansible,docker等技术,这里面包含的学问比较多,本人对这一块的技术实践也掌握不够,需要不断磨练啊。","updated":"T15:08:46.000Z","canComment":false,"commentPermission":"anyone","commentCount":2,"collapsedCount":0,"likeCount":7,"state":"published","isLiked":false,"slug":"","lastestTipjarors":[],"isTitleImageFullScreen":false,"rating":"none","titleImage":"","links":{"comments":"/api/posts//comments"},"reviewers":[],"topics":[{"url":"/topic/","id":"","name":"LNMP"}],"adminClosedComment":false,"titleImageSize":{"width":0,"height":0},"href":"/api/posts/","excerptTitle":"","column":{"slug":"phpgod","name":"PHP技术大全"},"tipjarState":"activated","tipjarTagLine":"真诚赞赏,手留余香","sourceUrl":"","pageCommentsCount":2,"tipjarorCount":0,"annotationAction":[],"hasPublishingDraft":false,"snapshotUrl":"","publishedTime":"T23:08:46+08:00","url":"/p/","lastestLikers":[{"bio":"PHP工程师","isFollowing":false,"hash":"afaac33eb0fe446fd8118917","uid":703700,"isOrg":false,"slug":"xiao-ma-yi-4-50-54","isFollowed":false,"description":"善待生活善待工作善待身边的所有人们所有事物","name":"Lant","profileUrl":"/people/xiao-ma-yi-4-50-54","avatar":{"id":"v2-bac0cde7aaad","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"公众号:gongchengyusuan","isFollowing":false,"hash":"8d1dfbaa574d65c9ca78a2bc26c33834","uid":24,"isOrg":false,"slug":"he-xiao-ming-95-5","isFollowed":false,"description":"工程造价 ","name":"何小明","profileUrl":"/people/he-xiao-ming-95-5","avatar":{"id":"7efdd449e","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"与其为讨好别人而虚伪地活着,不如做真实的自己而被人讨厌。","isFollowing":false,"hash":"c8e4be6af979c190faae77","uid":00,"isOrg":false,"slug":"lu-xi-yao-75","isFollowed":false,"description":"","name":"陆曦瑶","profileUrl":"/people/lu-xi-yao-75","avatar":{"id":"v2-cfc8206eed8d01c029adfe8242f00eba","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"代码搬运工","isFollowing":false,"hash":"0c0c628dff05ea93e706f6","uid":72,"isOrg":false,"slug":"qing-jiao-wo-xiao-xue-di","isFollowed":false,"description":"","name":"请叫我小学弟","profileUrl":"/people/qing-jiao-wo-xiao-xue-di","avatar":{"id":"f5777aef8","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"集安全于开发,崇拜作家和漫画家,将来可能是安全架构师!程序员话题优秀阅读者。","isFollowing":false,"hash":"7f457ee33fcd450ba4798","uid":92,"isOrg":false,"slug":"yang-da-xian-97","isFollowed":false,"description":"沉睡的人。","name":"ydxred","profileUrl":"/people/yang-da-xian-97","avatar":{"id":"v2-f2afc7bca268a","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false}],"summary":"电商网站秒杀活动 秒杀活动通常是这样定义:活动方在有限的时间段内(通常是M分钟到H小时不等的时间)给出指定数量O个P商品的大减价抢购名额。这类秒杀活动一般都会出现如下情况↓↓ 第一、在某一时间内QPS超过系统负载;第二、架构不合理导致系统的其它与…","reviewingCommentsCount":0,"meta":{"previous":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"/v2-cee25c10dff2fdfe036eac1d216d2236_r.jpg","links":{"comments":"/api/posts//comments"},"topics":[{"url":"/topic/","id":"","name":"PHP 7"}],"adminClosedComment":false,"href":"/api/posts/","excerptTitle":"","author":{"bio":"PHP,GO,Node,C,JAVA技术爱好者","isFollowing":false,"hash":"50a104ad67af63312c6bedbaba96b690","uid":64,"isOrg":false,"slug":"phpgod","isFollowed":false,"description":"微信公众号:phpgod\n技术专栏:/phpgod\nPHP问答网:\n","name":"舒铭","profileUrl":"/people/phpgod","avatar":{"id":"74ea936d4","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"column":{"slug":"phpgod","name":"PHP技术大全"},"content":"伴随业务的增长,系统压力也在不断增加,再加上机房机架趋于饱和,无法更加有效应对各种突发事件。在这样的情况下,PC主站升级为PHP 7,有哪些技术细节可以分享?背景新浪微博在2016年Q2季度公布月活跃用户(MAU)较上年同期增长33%,至2.82亿;日活跃用户(DAU)较上年同期增长36%,至1.26亿,总注册用户达8亿多。PC主站作为重要的流量入口,承载部分用户访问和流量落地,其中我们提供的部分服务(如:头条文章)承担全网所有流量。随着业务的增长,系统压力也在不断的增加。峰值时,服务器Hits达10W+,CPU使用率也达到了80%,远超报警阈值。另外,当前机房的机架已趋于饱和,遇到突发事件,只能对非核心业务进行降低,挪用这些业务的服务器来进行临时扩容,这种方案只能算是一种临时方案,不能满足长久的业务增长需求。再加上一年一度的三节(圣诞、元旦、春节),系统需预留一定的冗余来应对,所以当前系统面临的问题非常严峻,解决系统压力的问题也迫在眉急。面对当前的问题,我们内部也给出两套解决方案同步进行。方案一:申请新机房,资源统一配置,实现弹性扩容。方案二:对系统进行优化,对性能做进一步提升。针对方案一,通过搭建与新机房之间的专线与之打通,高峰时,运用内部自研的混合云DCP平台,对所有资源进行调度管理,实现了真正意义上的弹性扩容。目前该方案已经在部分业务灰度运行,随时能对重点业务进行小流量测试。针对方案二,系统层面,之前做过多次大范围的优化,比如:将Apache升级至Nginx应用框架升级至YafCPU计算密集型的逻辑扩展化弃用smarty并行化调用优化效果非常明显,如果再从系统层面进行优化,性能可提升的空间非常有限。好在业界传出了两大福音,分别为HHVM和PHP7。方案选型在PHP7还未正式发布时,我们也研究过HHVM(HipHop Virtual Machine),关于HHVM更多细节,这里就不再赘述,可参考官方说明。下面对它提升性能的方式进行一个简单的介绍。默认情况下,Zend引擎先将PHP源码编译为opcode,然后Zend解析引擎逐条执行。这里的opcode码,可以理解成C语言级的函数。而HHVM提升性能方式为替代Zend引擎将PHP代码转换成中间字节码(HHVM自己的中间字节码,通常称为中间语言),然后在运行时通过即时(JIT)编译器将这些字节码转换成x64的机器码,类似于Java的JVM。HHVM为了达到最佳优化效果,需要将PHP的变量类型固定下来,而不是让编译器去猜测。Facebook的工程师们就定义一种Hack写法,进而来达到编译器优化的目的,写法类似如下:&?hh \nclass point {
public float $x, $y;
function __construct(float $x, float $y) {
$this-&x = $x;
$this-&y = $y;\n
}\n通过前期的调研,如果使用HHVM解析器来优化现有业务代码,为了达到最佳的性能提升,必须对代码进行大量修改。另外,服务部署也比较复杂,有一定的维护成本,综合评估后,该方案我们也就不再考虑。当然,PHP7的开发进展我们也一直在关注,通过官方测试数据以及内部自己测试,性能提升非常明显。令人兴奋的是,在去年年底(日),官方终于正式发布了PHP7,并且对原生的代码几乎可以做到完全兼容,性能方面与PHP5比较能提升达一倍左右,和HHVM相比已经是不相上下。无论从优化成本、风险控制,还是从性能提升上来看,选择PHP7无疑是我们的最佳方案。系统现状以及升级风险微博PC主站从日发布第一版开始,先后经历了6个大的版本,系统架构也随着需求的变化进行过多次重大调整。截止目前,系统部分架构如下。从系统结构层面来看,系统分应用业务层、应用服务层,系统所依赖基础数据由平台服务层提供。从服务部署层面来看,业务主要部署在三大服务集群,分别为Home池、Page池以及应用服务池。为了提升系统性能,我们自研了一些PHP扩展,由于PHP5和PHP7底层差别太大,大部分Zend API接口都进行了调整,所有扩展都需要修改。所以,将PHP5环境升级至PHP7过程中,主要面临如下风险:使用了自研的PHP扩展,目前这些扩展只有PHP5版本,将这些扩展升级至PHP7,风险较大。PHP5与PHP7语法在某种程度上,多少还是存在一些兼容性的问题。由于涉及主站代码量庞大,业务逻辑分支复杂,很多测试范围仅仅通过人工测试是很难触达的,也将面临很多未知的风险。软件新版本的发布,都会面临着一些未知的风险和版本缺陷。这些问题,是否能快速得到解决。涉及服务池和项目较多,基础组件的升级对业务范围影响较大,升级期间出现的问题、定位会比较复杂。对微博这种数亿用户级别的系统的基础组件进行升级,影响范围将非常之大,一旦某个环节考虑不周全,很有可能会出现比较严重的责任事故。PHP7升级实践1. 扩展升级一些常用的扩展,在发布PHP7时,社区已经做了相应升级,如:Memcached、PHPRedis等。另外,微博使用的Yaf、Yar系列扩展,由于鸟哥(laruence)的支持,很早就全面支持了PHP7。对于这部分扩展,需要详细的测试以及现网灰度来进行保障。PHP7中,很多常用的API接口都做了改变,例如HashTable API等。对于自研的PHP扩展,需要做升级,比如我们有个核心扩展,升级涉及到代码量达1500行左右。新升级的扩展,刚开始也面临着各式各样的问题,我们主要通过官方给出的建议以及测试流程来保证其稳定可靠。官方建议在PHP7下编译你的扩展,编译错误与警告会告诉你绝大部分需要修改的地方。在DEBUG模式下编译与调试你的扩展,在run-time你可以通过断言捕捉一些错误。你还可以看到内存泄露的情况。测试流程首先通过扩展所提供的单元测试来保证扩展功能的正确性。其次通过大量的压力测试来验证其稳定性。然后再通过业务代码的自动化测试来保证业务功能的可用性。最后再通过现网流量灰度来确保最终的稳定可靠。整体升级过程中,涉及到的修改比较多,以下只简单列举出一些参数变更的函数。(1)addassocstringl参数4个改为了3个。//PHP5\n add_assoc_stringl(parray, key, value, value_len);\n//PHP7\n add_assoc_stringl(parray, key, value);\n(2)addnextindex_stringl 参数从3个改为了2个。//PHP5\n add_assoc_stringl(parray, key, value, value_len);\n//PHP7\n add_assoc_stringl(parray, key, value);\n(3)RETURN_STRINGL 参数从3个改为了2个。//PHP5 \nRETURN_STRINGL(value, length,dup); \n//PHP7 \nRETURN_STRINGL(value, length);\n(4)变量声明从堆上分配,改为栈上分配。//PHP5 \nzval* sarray_l; \nALLOC_INIT_ZVAL(sarray_l); \narray_init(sarray_l);
\n\n//PHP7 \nzval sarray_l; \narray_init(&sarray_l);\n(5)zendhashgetcurrentkey_ex参数从6个改为4个。//PHP5 \nZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex (\n
HashTable* ht,\n
char** str_index,
uint* str_length,
ulong* num_index,
zend_bool duplicate,
HashPosition* pos);
\n//PHP7 \nZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(
const HashTable *ht,
zend_string **str_index,
zend_ulong *num_index,
HashPosition *pos);\n更详细的说明,可参考官方PHP7扩展迁移文档:。2. PHP代码升级整体来讲,PHP7向前的兼容性正如官方所描述那样,能做到99%向前兼容,不需要做太多修改,但在整体迁移过程中,还是需要做一些兼容处理。另外,在灰度期间,代码将同时运行于PHP5.4和PHP7环境,现网灰度前,我们首先对所有代码进行了兼容性修改,以便同一套代码能同时兼容两套环境,然后再按计划对相关服务进行现网灰度。同时,对于PHP7的新特性,升级期间,也强调不允许被使用,否则代码与低版本环境的兼容性会存在问题。接下来简单介绍下升级PHP7代码过程中,需要注意的地方。(1)很多致命错误以及可恢复的致命错误,都被转换为异常来处理,这些异常继承自Error类,此类实现了 Throwable 接口。对未定义的函数进行调用,PHP5和PHP7环境下,都会出现致命错误。undefine_function();\n错误提示:PHP Fatal error:
Call to undefined function \nundefine_function() in /tmp/test.PHP on line 4\n在PHP7环境下,这些致命的错误被转换为异常来处理,可以通过异常来进行捕获。try {
undefine_function(); \n
catch (Throwable $e) {
echo $e; \n}\n提示:Error: Call to undefined function undefine_function() in /tmp/test.PHP:5 Stack trace: \n#0 {main}\n(2)被0除,PHP 7 之前,被0除会导致一条 E_WARNING 并返回 false 。一个数字运算返回一个布尔值是没有意义的,PHP 7 会返回如下的 float 值之一。+INF-INFNAN如下:var_dump(42/0);
// float(INF)
+ E_WARNING \nvar_dump(-42/0); // float(-INF) + E_WARNING \nvar_dump(0/0);
// float(NAN)
+ E_WARNING\n当使用取模运算符( % )的时候,PHP7会抛出一个 DivisionByZeroError 异常,PHP7之前,则抛出的是警告。echo 42 % 0;\nPHP5输出:PHP Warning:
Division by zero in /tmp/test.PHP on line 4\nPHP7输出:PHP Fatal error:
Uncaught DivisionByZeroError: Modulo by zero in /tmp/test.PHP:4 Stack trace: #\n0 {main} \nthrown in /tmp/test.PHP on line 4\nPHP7环境下,可以捕获该异常:try {
echo 42 % 0; \n
} catch (DivisionByZeroError $e) {
echo $e-&getMessage(); \n}\n输出:Modulo by zero\n(3)pregreplace() 函数不再支持 \"\\e\" (PREGREPLACEEVAL). 使用 pregreplace_callback() 替代。$content = preg_replace(\"/#([^#]+)#/ies\", \"strip_tags('#\\\\1#')\", $content);\nPHP7:$content = preg_replace_callback(\"/#([^#]+)#/is\", \"self::strip_str_tags\", $content); \npublic static function strip_str_tags($matches){
return \"#\".strip_tags($matches[1]).'#'; \n}\n(4)以静态方式调用非静态方法。class foo { function bar() { echo 'I am not static!'; } } foo::bar();以上代码PHP7会输出:PHP Deprecated:
Non-static method foo::bar() should not be called statically in /tmp/test.PHP on line 10 \nI am not static!\n(5)E_STRICT 警告级别变更。原有的 ESTRICT 警告都被迁移到其他级别。 ESTRICT 常量会被保留,所以调用 errorreporting(EALL|E_STRICT) 不会引发错误。关于代码兼容PHP7,基本上是对代码的规范要求更严谨。以前写的不规范的地方,解析引擎只是输出NOTICE或者WARNING进行提示,不影响对代码上下文的执行,而到了PHP7,很有可能会直接抛出异常,中断上下文的执行。如:对0取模运行时,PHP7之前,解析引擎只抛出警告进行提示,但到了PHP7则会抛出一个DivisionByZeroError异常,会中断整个流程的执行。对于警告级别的变更,在升级灰度期间,一定要关注相关NOTICE或WARNING报错。PHP7之前的一个NOTICE或者WARNING到了PHP7,一些报警级变成致命错误或者抛出异常,一旦没有对相关代码进行优化处理,逻辑被触发,业务系统很容易因为抛出的异常没处理而导致系统挂掉。以上只列举了PHP7部分新特性,也是我们在迁移代码时重点关注的一些点,更多细节可参考官方文档。3. 研发流程变更一个需求的开发到上线,首先我们会通过统一的开发环境来完成功能开发,其次经过内网测试、仿真测试,这两个环境测试通过后基本保证了数据逻辑与功能方面没有问题。然后合并至主干分支,并将代码部署至预发环境,再经过一轮简单回归,确保合并代码没有问题。最后将代码发布至生产环境。为了确保新编写的代码能在两套环境(未灰度的PHP5.4环境以及灰度中的PHP7环境)中正常运行,代码在上线前,也需要在两套环境中分别进行测试,以达到完全兼容。所以,在灰度期间,对每个环节的运行环境除了现有的PHP5.4环境外,我们还分别提供了一套PHP7环境,每个阶段的测试中,两套环境都需要进行验证。4. 灰度方案之前有过简单的介绍,系统部署在三大服务池,分别为Home池、Page池以及应用服务池。在准备好安装包后,先是在每个服务池分别部署了一台前端机来灰度。运行一段时间后,期间通过错误日志发现了不少问题,也有用户投诉过来的问题,在问题都基本解决的情况下,逐渐将各服务池的机器池增加至多台。经过前期的灰度测试,主要的问题得到基本解决。接下是对应用服务池进行灰度,陆续又发现了不少问题。前后大概经历了一个月左右,完成了应用服务池的升级。然后再分别对Home池以及Page池进行灰度,经过漫长灰度,最终完成了PC主站全网PHP7的升级。虽然很多问题基本上在测试或者灰度期间得到了解决,但依然有些问题是全量上线后一段时间才暴露出来,业务流程太多,很多逻辑需要一定条件才能被触发。为此BUG都要第一时间同步给PHP7升级项目组,对于升级PHP引起的问题,要求必须第一时间解决。5. 优化方案(1)启用Zend Opcache,启用Opcache非常简单, 在PHP.ini配置文件中加入:zend_extension=opcache.so \nopcache.enable=1 \nopcache.enable_cli=1\"\n(2)使用GCC4.8以上的编译器来编译安装包,只有GCC4.8以上编译出的PHP才会开启Global Register for opline and execute_data支持。(3)开启HugePage支持,首先在系统中开启HugePages, 然后开启Opcache的hugecodepages。关于HugePage操作系统默认的内存是以4KB分页的,而虚拟地址和内存地址需要转换, 而这个转换要查表,CPU为了加速这个查表过程会内建TLB(Translation Lookaside Buffer)。 显然,如果虚拟页越小,表里的条目数也就越多,而TLB大小是有限的,条目数越多TLB的Cache Miss也就会越高, 所以如果我们能启用大内存页就能间接降低这个TLB Cache Miss。PHP7与HugePagePHP7开启HugePage支持后,会把自身的text段, 以及内存分配中的huge都采用大内存页来保存, 减少TLB miss, 从而提高性能。相关实现可参考Opcache实现中的accel_move_code_to_huge_pages()函数。开启方法以CentOS 6.5为例, 通过命令:sudo sysctl vm.nr_hugepages=128\n分配128个预留的大页内存。$ cat /proc/meminfo | grep Huge \nAnonHugePages:
444416 kB \nHugePages_Total:
128 \nHugePages_Free:
128 \nHugePages_Rsvd:
0 \nHugePages_Surp:
0 \nHugepagesize:
2048 kB\n然后在PHP.ini中加入opcache.huge_code_pages=1\n6. 关于负载过高,系统CPU使用占比过高的问题当我们升级完第一个服务池时,感觉整个升级过程还是比较顺利,当灰度Page池,低峰时一切正常,但到了流量高峰,系统CPU占用非常高,如图:系统CPU的使用远超用户程序CPU的使用,正常情况下,系统CPU与用户程序CPU占比应该在1/3左右。但我们的实际情况则是,系统CPU是用户CPU的2~3倍,很不正常。对比了一下两个服务池的流量,发现Page池的流量正常比Home池高不少,在升级Home池时,没发现该问题,主要原因是流量没有达到一定级别,所以未触发该问题。当单机流量超过一定阈值,系统CPU的使用会出现一个直线的上升,此时系统性能会严重下降。这个问题其实困扰了我们有一段时间,通过各种搜索资料,均未发现任何升级PHP7会引起系统CPU过高的线索。但我们发现了另外一个比较重要的线索,很多软件官方文档里非常明确的提出了可以通过关闭Transparent HugePages(透明大页)来解决系统负载过高的问题。后来我们也尝试对其进行了关闭,经过几天的观察,该问题得到解决,如图:什么是Transparent HugePages(透明大页)简单的讲,对于内存占用较大的程序,可以通过开启HugePage来提升系统性能。但这里会有个要求,就是在编写程序时,代码里需要显示的对HugePage进行支持。而红帽企业版Linux为了减少程序开发的复杂性,并对HugePage进行支持,部署了Transparent HugePages。Transparent HugePages是一个使管理Huge Pages自动化的抽象层,实现方案为操作系统后台有一个叫做khugepaged的进程,它会一直扫描所有进程占用的内存,在可能的情况下会把4kPage交换为Huge Pages。为什么Transparent HugePages(透明大页)对系统的性能会产生影响在khugepaged进行扫描进程占用内存,并将4kPage交换为Huge Pages的这个过程中,对于操作的内存的各种分配活动都需要各种内存锁,直接影响程序的内存访问性能。并且,这个过程对于应用是透明的,在应用层面不可控制,对于专门为4k page优化的程序来说,可能会造成随机的性能下降现象。怎么关闭Transparent HugePages(透明大页)(1)查看是否启用透明大页。[root@venus153 ~]# cat
/sys/kernel/mm/transparent_hugepage/enabled \n[always] madvise never\n使用命令查看时,如果输出结果为[always]表示透明大页启用了,[never]表示透明大页禁用。(2)关闭透明大页。echo never & /sys/kernel/mm/transparent_hugepage/enabled \necho never & /sys/kernel/mm/transparent_hugepage/defrag\n(3)启用透明大页。echo always &
/sys/kernel/mm/transparent_hugepage/enabled \necho always & /sys/kernel/mm/transparent_hugepage/defrag\n(4)设置开机关闭。修改/etc/rc.local文件,添加如下行:if test -f /sys/kernel/mm/redhat_transparent_hugepage/ then
echo never & /sys/kernel/mm/transparent_hugepage/enabled
echo never & /sys/kernel/mm/transparent_hugepage/defrag \nfi\n升级效果由于主站的业务比较复杂,项目较多,涉及服务池达多个,每个服务池所承担业务与流量也不一样,所以我们在对不同的服务池进行灰度升级,遇到的问题也不尽相同,导致整体升级前后达半年之久。庆幸的是,遇到的问题,最终都被解决掉了。最让人兴奋的是升级效果非常好,基本与官方一致,也为公司节省了不少成本。以下简单地给大家展示下这次PHP7升级的成果。(1)PHP5与PHP7环境下,分别对我们的某个核心接口进行压测(压测数据由QA团队提供),相关数据如下:同样接口,分别在两个不现的环境中进行测试,平均TPS从95提升到220,提升达130%。(2)升级前后,单机CPU使用率对比如下。升级前后,1小时流量情况变化:升级前后,1小时CPU使用率变化:升级前后,在流量变化不大的情况下,CPU使用率从45%降至25%,CPU使用率降低44.44%。(3)某服务集群升级前后,同一时间段1小时CPU使用对比如下。PHP5环境下,集群近1小时CPU使用变化:PHP7环境下,集群近1小时CPU使用变化:升级前后,CPU变化对比:升级前后,同一时段,集群CPU平均使用率从51.6%降低至22.9%,使用率降低56.88%。以上只简单从三个维度列举了一些数据。为了让升级效果更加客观,我们实际的评估维度更多,如内存使用、接口响应时间占比等。最终综合得出的结论为,通过本次升级,PC主站整体性能提升在48.82%,效果非常好。团队今年的职能KPI就算是提前完成了。总结整体升级从准备到最终PC主站全网升级完成,时间跨度达半年之久,无论是扩展编写、准备安装脚本、PHP代码升级还是全网灰度,期间一直会出现各式各样的问题。最终在团队的共同努力下,这些问题都彻底得到了解决。一直以来,对社区的付出深怀敬畏之心,也是因为他们对PHP语言性能极限的追求,才能让大家的业务坐享数倍性能的提升。同时,也让我们更加相信,PHP一定会是一门越来越好的语言。作者简介侯青龙,微博主站研发负责人。2010年加入新浪微博,先后参与过微博主站V2版至V6版的研发,主导过主站V6版以及多机房消息同步系统等重大项目的架构设计工作。致力于提升产品研发效率以及优化系统性能。感谢韩婷对本文的审校。给InfoQ中文站投稿或者参与内容翻译工作,请邮件至q.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号:
)关注我们。","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":,"publishedTime":"T21:50:46+08:00","url":"/p/","title":"亿级用户PC主站的PHP7升级实践","summary":"伴随业务的增长,系统压力也在不断增加,再加上机房机架趋于饱和,无法更加有效应对各种突发事件。在这样的情况下,PC主站升级为PHP 7,有哪些技术细节可以分享?背景新浪微博在2016年Q2季度公布月活跃用户(MAU)较上年同期增长33%,至2.82亿;日活跃用户…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":4,"likesCount":6},"next":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"","links":{"comments":"/api/posts//comments"},"topics":[{"url":"/topic/","id":"","name":"PHP"}],"adminClosedComment":false,"href":"/api/posts/","excerptTitle":"","author":{"bio":"PHP,GO,Node,C,JAVA技术爱好者","isFollowing":false,"hash":"50a104ad67af63312c6bedbaba96b690","uid":64,"isOrg":false,"slug":"phpgod","isFollowed":false,"description":"微信公众号:phpgod\n技术专栏:/phpgod\nPHP问答网:\n","name":"舒铭","profileUrl":"/people/phpgod","avatar":{"id":"74ea936d4","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"column":{"slug":"phpgod","name":"PHP技术大全"},"content":"MemCache是什么MemCache是一个自由、源码开放、高性能、分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度。MemCaChe是一个存储键值对的HashMap,在内存中对任意的数据(比如字符串、对象等)所使用的key-value存储,数据可以来自数据库调用、API调用,或者页面渲染的结果。MemCache设计理念就是小而强大,它简单的设计促进了快速部署、易于开发并解决面对大规模的数据缓存的许多难题,而所开放的API使得MemCache能用于Java、C/C++/C#、Perl、Python、PHP、Ruby等大部分流行的程序语言。另外,说一下MemCache和MemCached的区别:1、MemCache是项目的名称2、MemCached是MemCache服务器端可以执行文件的名称MemCache的官方网站为MemCache访问模型为了加深理解,我模仿着原阿里技术专家李智慧老师《大型网站技术架构 核心原理与案例分析》一书MemCache部分,自己画了一张图:特别澄清一个问题,MemCache虽然被称为\"分布式缓存\",但是MemCache本身完全不具备分布式的功能,MemCache集群之间不会相互通信(与之形成对比的,比如JBoss Cache,某台服务器有缓存数据更新时,会通知集群中其他机器更新缓存或清除缓存数据),所谓的\"分布式\",完全依赖于客户端程序的实现,就像上面这张图的流程一样。同时基于这张图,理一下MemCache一次写缓存的流程:1、应用程序输入需要写缓存的数据2、API将Key输入路由算法模块,路由算法根据Key和MemCache集群服务器列表得到一台服务器编号3、由服务器编号得到MemCache及其的ip地址和端口号4、API调用通信模块和指定编号的服务器通信,将数据写入该服务器,完成一次分布式缓存的写操作读缓存和写缓存一样,只要使用相同的路由算法和服务器列表,只要应用程序查询的是相同的Key,MemCache客户端总是访问相同的客户端去读取数据,只要服务器中还缓存着该数据,就能保证缓存命中。这种MemCache集群的方式也是从分区容错性的方面考虑的,假如Node2宕机了,那么Node2上面存储的数据都不可用了,此时由于集群中Node0和Node1还存在,下一次请求Node2中存储的Key值的时候,肯定是没有命中的,这时先从数据库中拿到要缓存的数据,然后路由算法模块根据Key值在Node0和Node1中选取一个节点,把对应的数据放进去,这样下一次就又可以走缓存了,这种集群的做法很好,但是缺点是成本比较大。一致性Hash算法从上面的图中,可以看出一个很重要的问题,就是对服务器集群的管理,路由算法至关重要,就和负载均衡算法一样,路由算法决定着究竟该访问集群中的哪台服务器,先看一个简单的路由算法。1、余数Hash比方说,字符串str对应的HashCode是50、服务器的数目是3,取余数得到2,str对应节点Node2,所以路由算法把str路由到Node2服务器上。由于HashCode随机性比较强,所以使用余数Hash路由算法就可以保证缓存数据在整个MemCache服务器集群中有比较均衡的分布。如果不考虑服务器集群的伸缩性(什么是伸缩性,请参见),那么余数Hash算法几乎可以满足绝大多数的缓存路由需求,但是当分布式缓存集群需要扩容的时候,就难办了。就假设MemCache服务器集群由3台变为4台吧,更改服务器列表,仍然使用余数Hash,50对4的余数是2,对应Node2,但是str原来是存在Node1上的,这就导致了缓存没有命中。如果这么说不够明白,那么不妨举个例子,原来有HashCode为0~19的20个数据,那么:HashCode171819路由到的服务器现在我扩容到4台,加粗标红的表示命中:HashCode171819路由到的服务器01201230123如果我扩容到20+的台数,只有前三个HashCode对应的Key是命中的,也就是15%。当然这只是个简单例子,现实情况肯定比这个复杂得多,不过足以说明,使用余数Hash的路由算法,在扩容的时候会造成大量的数据无法正确命中(其实不仅仅是无法命中,那些大量的无法命中的数据还在原缓存中在被移除前占据着内存)。这个结果显然是无法接受的,在网站业务中,大部分的业务数据度操作请求上事实上是通过缓存获取的,只有少量读操作会访问数据库,因此数据库的负载能力是以有缓存为前提而设计的。当大部分被缓存了的数据因为服务器扩容而不能正确读取时,这些数据访问的压力就落在了数据库的身上,这将大大超过数据库的负载能力,严重的可能会导致数据库宕机。这个问题有解决方案,解决步骤为:(1)在网站访问量低谷,通常是深夜,技术团队加班,扩容、重启服务器(2)通过模拟请求的方式逐渐预热缓存,使缓存服务器中的数据重新分布2、一致性Hash算法一致性Hash算法通过一个叫做一致性Hash环的数据结构实现Key到缓存服务器的Hash映射,看一下我自己画的一张图:具体算法过程为:先构造一个长度为232的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 232-1])将缓存服务器节点放置在这个Hash环上,然后根据需要缓存的数据的Key值计算得到其Hash值(其分布也为[0, 232-1]),然后在Hash环上顺时针查找距离这个Key值的Hash值最近的服务器节点,完成Key到服务器的映射查找。就如同图上所示,三个Node点分别位于Hash环上的三个位置,然后Key值根据其HashCode,在Hash环上有一个固定位置,位置固定下之后,Key就会顺时针去寻找离它最近的一个Node,把数据存储在这个Node的MemCache服务器中。使用Hash环如果加了一个节点会怎么样,看一下:看到我加了一个Node4节点,只影响到了一个Key值的数据,本来这个Key值应该是在Node1服务器上的,现在要去Node4了。采用一致性Hash算法,的确也会影响到整个集群,但是影响的只是加粗的那一段而已,相比余数Hash算法影响了远超一半的影响率,这种影响要小得多。更重要的是,集群中缓存服务器节点越多,增加节点带来的影响越小,很好理解。换句话说,随着集群规模的增大,继续命中原有缓存数据的概率会越来越大,虽然仍然有小部分数据缓存在服务器中不能被读到,但是这个比例足够小,即使访问数据库,也不会对数据库造成致命的负载压力。至于具体应用,这个长度为232的一致性Hash环通常使用二叉查找树实现,至于二叉查找树,就是算法的问题了,可以自己去查询相关资料。MemCache实现原理首先要说明一点,MemCache的数据存放在内存中,存放在内存中个人认为意味着几点:1、访问数据的速度比传统的关系型数据库要快,因为Oracle、MySQL这些传统的关系型数据库为了保持数据的持久性,数据存放在硬盘中,IO操作速度慢2、MemCache的数据存放在内存中同时意味着只要MemCache重启了,数据就会消失3、既然MemCache的数据存放在内存中,那么势必受到机器位数的限制,这个之前的文章写过很多次了,32位机器最多只能使用2GB的内存空间,64位机器可以认为没有上限然后我们来看一下MemCache的原理,MemCache最重要的莫不是内存分配的内容了,MemCache采用的内存分配方式是固定空间分配,还是自己画一张图说明:这张图片里面涉及了slab_class、slab、page、chunk四个概念,它们之间的关系是:1、MemCache将内存空间分为一组slab2、每个slab下又有若干个page,每个page默认是1M,如果一个slab占用100M内存的话,那么这个slab下应该有100个page3、每个page里面包含一组chunk,chunk是真正存放数据的地方,同一个slab里面的chunk的大小是固定的4、有相同大小chunk的slab被组织在一起,称为slab_classMemCache内存分配的方式称为allocator,slab的数量是有限的,几个、十几个或者几十个,这个和启动参数的配置相关。MemCache中的value过来存放的地方是由value的大小决定的,value总是会被存放到与chunk大小最接近的一个slab中,比如slab[1]的chunk大小为80字节、slab[2]的chunk大小为100字节、slab[3]的chunk大小为128字节(相邻slab内的chunk基本以1.25为比例进行增长,MemCache启动时可以用-f指定这个比例),那么过来一个88字节的value,这个value将被放到2号slab中。放slab的时候,首先slab要申请内存,申请内存是以page为单位的,所以在放入第一个数据的时候,无论大小为多少,都会有1M大小的page被分配给该slab。申请到page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk数组,最后从这个chunk数组中选择一个用于存储数据。如果这个slab中没有chunk可以分配了怎么办,如果MemCache启动没有追加-M(禁止LRU,这种情况下内存不够会报Out Of Memory错误),那么MemCache会把这个slab中最近最少使用的chunk中的数据清理掉,然后放上最新的数据。针对MemCache的内存分配及回收算法,总结三点:1、MemCache的内存分配chunk里面会有内存浪费,88字节的value分配在128字节(紧接着大的用)的chunk中,就损失了30字节,但是这也避免了管理内存碎片的问题2、MemCache的LRU算法不是针对全局的,是针对slab的3、应该可以理解为什么MemCache存放的value大小是限制的,因为一个新数据过来,slab会先以page为单位申请一块内存,申请的内存最多就只有1M,所以value大小自然不能大于1M了再总结MemCache的特性和限制上面已经对于MemCache做了一个比较详细的解读,这里再次总结MemCache的限制和特性:1、MemCache中可以保存的item数据量是没有限制的,只要内存足够2、MemCache单进程在32位机中最大使用内存为2G,这个之前的文章提了多次了,64位机则没有限制3、Key最大为250个字节,超过该长度无法存储4、单个item最大数据是1MB,超过1MB的数据不予存储5、MemCache服务端是不安全的,比如已知某个MemCache节点,可以直接telnet过去,并通过flush_all让已经存在的键值对立即失效6、不能够遍历MemCache中所有的item,因为这个操作的速度相对缓慢且会阻塞其他的操作7、MemCache的高性能源自于两阶段哈希结构:第一阶段在客户端,通过Hash算法根据Key值算出一个节点;第二阶段在服务端,通过一个内部的Hash算法,查找真正的item并返回给客户端。从实现的角度看,MemCache是一个非阻塞的、基于事件的服务器程序8、MemCache设置添加某一个Key值的时候,传入expiry为0表示这个Key值永久有效,这个Key值也会在30天之后失效,见memcache.c的源代码:#define REALTIME_MAXDELTA 60*60*24*30\nstatic rel_time_t realtime(const time_t exptime) {\n
if (exptime == 0) return 0;\n
if (exptime & REALTIME_MAXDELTA) {
if (exptime &= process_started)
return (rel_time_t)1;
return (rel_time_t)(exptime - process_started);
return (rel_time_t)(exptime + current_time);
}\n}\n这个失效的时间是memcache源码里面写的,开发者没有办法改变MemCache的Key值失效时间为30天这个限制MemCache指令汇总上面说过,已知MemCache的某个节点,直接telnet过去,就可以使用各种命令操作MemCache了,下面看下MemCache有哪几种命令:命
用get返回Key对应的Value值add 添加一个Key值,没有则添加成功并提示STORED,有则失败并提示NOT_STOREDset
无条件地设置一个Key值,没有就增加,有就覆盖,操作成功提示STOREDreplace 按照相应的Key值替换数据,如果Key值不存在则会操作失败 stats返回MemCache通用统计信息(下面有详细解读)stats items返回各个slab中item的数目和最老的item的年龄(最后一次访问距离现在的秒数)stats slabs返回MemCache运行期间创建的每个slab的信息(下面有详细解读)version返回当前MemCache版本号flush_all清空所有键值,但不会删除items,所以此时MemCache依旧占用内存quit关闭连接stats指令解读stats是一个比较重要的指令,用于列出当前MemCache服务器的状态,拿一组数据举个例子:STAT pid 1023\nSTAT uptime \nSTAT time \nSTAT version 1.4.5\nSTAT pointer_size 64\nSTAT rusage_user \nSTAT rusage_system \nSTAT curr_connections 29\nSTAT total_connections 21\nSTAT connection_structures 49\nSTAT cmd_get 49\nSTAT cmd_set 7458\nSTAT cmd_flush 0\nSTAT get_hits 7401\nSTAT get_misses 57\n..(delete、incr、decr、cas的hits和misses数,cas还多一个badval)\nSTAT auth_cmds 0\nSTAT auth_errors 0\nSTAT bytes_read \nSTAT bytes_written 8930466\nSTAT limit_maxbytes \nSTAT accepting_conns 1\nSTAT listen_disabled_num 0\nSTAT threads 4\nSTAT bytes \nSTAT current_items 57146\nSTAT total_items 580656\nSTAT evicitions 0\n这些参数反映着MemCache服务器的基本信息,它们的意思是:参
用pidMemCache服务器的进程id uptime服务器已经运行的秒数time服务器当前的UNIX时间戳 versionMemCache版本 pointer_size当前操作系统指针大小,反映了操作系统的位数,64意味着MemCache服务器是64位的 rusage_user进程的累计用户时间 rusage_system 进程的累计系统时间 curr_connections
当前打开着的连接数total_connections
当服务器启动以后曾经打开过的连接数connection_structures 服务器分配的连接构造数 cmd_get get命令总请求次数 cmd_setset命令总请求次数 cmd_flush flush_all命令总请求次数 get_hits 总命中次数,重要,缓存最重要的参数就是缓存命中率,以get_hits / (get_hits + get_misses)表示,比如这个缓存命中率就是99.2% get_misses 总未命中次数 auth_cmds 认证命令的处理次数 auth_errors 认证失败的处理次数 bytes_read 总读取的字节数bytes_written 总发送的字节数
limit_maxbytes分配给MemCache的内存大小(单位为字节) accepting_conns 是否已经达到连接的最大值,1表示达到,0表示未达到listen_disabled_num 统计当前服务器连接数曾经达到最大连接的次数,这个次数应该为0或者接近于0,如果这个数字不断增长, 就要小心我们的服务了threads 当前MemCache总线程数,由于MemCache的线程是基于事件驱动机制的,因此不会一个线程对应一个用户请求 bytes 当前服务器存储的items总字节数current_items 当前服务器存储的items总数量 total_items 自服务器启动以后存储的items总数量 stats slab指令解读如果对上面的MemCache存储机制比较理解了,那么我们来看一下各个slab中的信息,还是拿一组数据举个例子: 1 STAT1:chunk_size 96\n 2 ...\n 3 STAT 2:chunk_size 144\n 4 STAT 2:chunks_per_page 7281\n 5 STAT 2:total_pages 7\n 6 STAT 2:total_chunks 50967\n 7 STAT 2:used_chunks 45197\n 8 STAT 2:free_chunks 1\n 9 STAT 2:free_chunks_end 5769\n10 STAT 2:mem_requested
STAT 2:get_hits 48084\n12 STAT 2:cmd_set
STAT 2:delete_hits 0\n14 STAT 2:incr_hits 0\n15 STAT 2:decr_hits 0\n16 STAT 2:cas_hits 0\n17 STAT 2:cas_badval 0\n18 ...\n19 STAT 3:chunk_size 216\n20 ...\n首先看到,第二个slab的chunk_size(144)/第一个slab的chunk_size(96)=1.5,第三个slab的chunk_size(216)/第二个slab的chunk_size(144)=1.5,可以确定这个MemCache的增长因子是1.5,chunk_size以1.5倍增长。然后解释下字段的含义:参
用chunk_size当前slab每个chunk的大小,单位为字节chunks_per_page每个page可以存放的chunk数目,由于每个page固定为1M即字节,所以这个值就是(/chunk_size)total_pages分配给当前slab的page总数total_chunks当前slab最多能够存放的chunk数,这个值是total_pages*chunks_per_pageused_chunks已经被分配给存储对象的chunks数目free_chunks曾经被使用过但是因为过期而被回收的chunk数free_chunks_end新分配但还没有被使用的chunk数,这个值不为0则说明当前slab从来没有出现过容量不够的时候mem_requested当前slab中被请求用来存储数据的内存空间字节总数,(total_chunks*chunk_size)-mem_requested表示有多少内存在当前slab中是被闲置的,这包括未用的slab+使用的slab中浪费的内存get_hits当前slab中命中的get请求数cmd_set当前slab中接收的所有set命令请求数delete_hits当前slab中命中的delete请求数incr_hits当前slab中命中的incr请求数decr_hits当前slab中命中的decr请求数cas_hits当前slab中命中的cas请求数cas_badval当前slab中命中但是更新失败的cas请求数看到这个命令的输出量很大,所有信息都很有作用。举个例子吧,比如第一个slab中使用的chunks很少,第二个slab中使用的chunks很多,这时就可以考虑适当增大MemCache的增长因子了,让一部分数据落到第一个slab中去,适当平衡两个slab中的内存,避免空间浪费。MemCache的Java实现实例讲了这么多,作为一个Java程序员,怎么能不写写MemCache的客户端的实现呢?MemCache的客户端有很多第三方jar包提供了实现,其中比较好的当属XMemCached了,XMemCached具有效率高、IO非阻塞、资源耗费少、支持完整的协议、允许设置节点权重、允许动态增删节点、支持JMX、支持与Spring框架集成、使用连接池、可扩展性好等诸多优点,因而被广泛使用。这里利用XMemCache写一个简单的MemCache客户单实例,也没有验证过,纯属抛砖引玉:public class MemCacheManager\n{\n
private static MemCacheManager instance = new MemCacheManager();\n
/** XMemCache允许开发者通过设置节点权重来调节MemCache的负载,设置的权重越高,该MemCache节点存储的数据越多,负载越大 */\n
private static MemcachedClientBuilder mcb = \n
new XMemcachedClientBuilder(AddrUtil.getAddresses(\"127.0.0.1:.0.2:.0.3:11211\"), new int[]{1, 3, 5});\n
private static MemcachedClient mc = null;\n
/** 初始化加载客户端MemCache信息 */\n
mcb.setCommandFactory(new BinaryCommandFactory()); // 使用二进制文件\n
mcb.setConnectionPoolSize(10); // 连接池个数,即客户端个数\n
mc = mcb.build();\n
catch (IOException e)\n
e.printStackTrace();\n
private MemCacheManager()\n
public MemCacheManager getInstance()\n
return instance;\n
/** 向MemCache服务器设置数据 */\n
public void set(String key, int expiry, Object obj) throws Exception\n
mc.set(key, expiry, obj);\n
/** 从MemCache服务器获取数据 */\n
public Object get(String key) throws Exception\n
return mc.get(key);\n
* MemCache通过compare and set即cas协议实现原子更新,类似乐观锁,每次请求存储某个数据都要附带一个cas值,MemCache\n
* 比对这个cas值与当前存储数据的cas值是否相等,如果相等就覆盖老数据,如果不相等就认为更新失败,这在并发环境下特别有用\n
public boolean update(String key, Integer i) throws Exception\n
GetsResponse&Integer& result = mc.gets(key);\n
long cas = result.getCas();\n
// 尝试更新key对应的value\n
if (!mc.cas(key, 0, i, cas))\n
return false;\n
return true;\n
}\n}\n本文转载自:","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":,"publishedTime":"T22:47:42+08:00","url":"/p/","title":"MemCache超详细解读","summary":"MemCache是什么MemCache是一个自由、源码开放、高性能、分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度。MemCaChe是一个存储键值对的HashMap,在内存…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":0,"likesCount":3}},"annotationDetail":null,"commentsCount":2,"likesCount":7,"FULLINFO":true}},"User":{"phpgod":{"isFollowed":false,"name":"舒铭","headline":"微信公众号:phpgod\n技术专栏:/phpgod\nPHP问答网:\n","avatarUrl":"/74ea936d4_s.jpg","isFollowing":false,"type":"people","slug":"phpgod","bio":"PHP,GO,Node,C,JAVA技术爱好者","hash":"50a104ad67af63312c6bedbaba96b690","uid":64,"isOrg":false,"description":"微信公众号:phpgod\n技术专栏:/phpgod\nPHP问答网:\n","profileUrl":"/people/phpgod","avatar":{"id":"74ea936d4","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false,"badge":{"identity":null,"bestAnswerer":null}}},"Comment":{},"favlists":{}},"me":{},"global":{},"columns":{"phpgod":{"following":false,"canManage":false,"href":"/api/columns/phpgod","name":"PHP技术大全","creator":{"slug":"phpgod"},"url":"/phpgod","slug":"phpgod","avatar":{"id":"v2-92e619dd39a6c65cef07c848c37bf6eb","template":"/{id}_{size}.jpg"}}},"columnPosts":{},"columnSettings":{"colomnAuthor":[],"uploadAvatarDetails":"","contributeRequests":[],"contributeRequestsTotalCount":0,"inviteAuthor":""},"postComments":{},"postReviewComments":{"comments":[],"newComments":[],"hasMore":true},"favlistsByUser":{},"favlistRelations":{},"promotions":{},"switches":{"couldAddVideo":false},"draft":{"titleImage":"","titleImageSize":{},"isTitleImageFullScreen":false,"canTitleImageFullScreen":false,"title":"","titleImageUploading":false,"error":"","content":"","draftLoading":false,"globalLoading":false,"pendingVideo":{"resource":null,"error":null}},"drafts":{"draftsList":[],"next":{}},"config":{"userNotBindPhoneTipString":{}},"recommendPosts":{"articleRecommendations":[],"columnRecommendations":[]},"env":{"isAppView":false,"appViewConfig":{"content_padding_top":128,"content_padding_bottom":56,"content_padding_left":16,"content_padding_right":16,"title_font_size":22,"body_font_size":16,"is_dark_theme":false,"can_auto_load_image":true,"app_info":"OS=iOS"},"isApp":false},"sys":{}}

我要回帖

更多关于 10m带宽支持多少并发 的文章

 

随机推荐