記一次PHP Memcache擴(kuò)展故障排查

背景

服務(wù)器用了PHP Memcache擴(kuò)展,但上線后頻繁出現(xiàn)set/add失敗草巡。

測試

為了重現(xiàn)故障,寫了一個簡單腳本測試test_mc.php,如下:

<?php
define('HOST', 'YOUR_HOST');
define('PORT', 'YOUR_PORT');

$mc = new Memcache();
$mc->connect(HOST, PORT);
while(1) {
    $key = 'test' . microtime(1);
    $value = 'val';
    $expire = 100;
    $result = $mc->set($key, $value, $expire);
    if($result) {
        echo date('Y-m-d H:i:s') . "\tset success" . PHP_EOL;
    }else {
        echo date('Y-m-d H:i:s') . "\tset fail" . PHP_EOL;
    }
    sleep(1);
}

執(zhí)行/path/to/php test_mc.php佛舱,結(jié)果如下:


從執(zhí)行可以看出,的確會間歇出現(xiàn)set不成功的情況,而且"set fail"會連續(xù)出現(xiàn)15次请祖。
那么問題就來了:

  1. 為什么會出現(xiàn)fail订歪?
  2. 為什么每次都連續(xù)15次fail?

排查

執(zhí)行strace /path/to/php test_mc.php肆捕,分析系統(tǒng)調(diào)用陌粹,得到結(jié)果如下:


綠色的是執(zhí)行成功的"set success",紅色的是執(zhí)行失敗的"set fail"福压。
從上述信息可以看出兩個問題:

  1. 第1次的"set fail"是因?yàn)?strong>poll timeout掏秩,超時(shí)時(shí)間是1000ms。網(wǎng)絡(luò)問題荆姆,我們可以根據(jù)實(shí)際情況蒙幻,把超時(shí)時(shí)間再設(shè)置長一點(diǎn)。
  2. 第2次~第15次就有點(diǎn)奇怪了胆筒,并沒有發(fā)生網(wǎng)絡(luò)請求邮破,就直接失敗了。

針對這兩個問題仆救,繼續(xù)排查抒和。

為什么fail剛好是15次?

查閱Memcache擴(kuò)展的文檔沒有說明彤蔽,那就直接看擴(kuò)展源碼摧莽。Memcache擴(kuò)展版本是2.2.7。
源碼地址:https://pecl.php.net/package/memcache
在源碼里直接搜索"15"顿痪,就可以找到以下的常量定義:


在mmc_open函數(shù)有這樣的用法:
注:mmc->retry_interval = MMC_DEFAULT_RETRY

那么結(jié)論就很明顯了镊辕,在同一個mc連接,一旦出現(xiàn)了MMC_STATUS_FAILED(連接失敗)蚁袭,只能在15s過后再重新連接征懈。期間客戶端的任何調(diào)用都是失敗。所以就出現(xiàn)上面的測試結(jié)果揩悄。
MMC_DEFAULT_RETRY單例模式守護(hù)進(jìn)程會比較麻煩卖哎。
所以建議一旦發(fā)現(xiàn)連接失敗,就調(diào)用close()把連接干掉删性。

如何設(shè)置超時(shí)時(shí)間

先看文檔:http://php.net/manual/zh/memcache.connect.php


文檔比較簡單亏娜,Memcache::connect()第3個參數(shù)就是超時(shí)選項(xiàng)。
我們把超時(shí)時(shí)間改為3s镇匀,修改如下:

<?php
...
$mc->connect(HOST, PORT, 3);
...

為了驗(yàn)證是否真的設(shè)置成功照藻,執(zhí)行strace /path/to/php test_mc.php


奇怪了,怎么超時(shí)還是1000ms汗侵?設(shè)置3s不生效幸缕?
會不會是個bug群发?于是又翻起了Memecache擴(kuò)展的源代碼。
Memcache::connect()方法的實(shí)現(xiàn)在php_mmc_connect()里:

這里發(fā)現(xiàn)兩個很有趣的事情:

  1. 超時(shí)時(shí)間其實(shí)有兩個選項(xiàng):timeout(s)和timeoutms(ms)
  2. 文檔里Memcache::connect()的參數(shù)列表只列了3個參數(shù)(host, port, timeout)发乔,但實(shí)際上是可以接收第4個參數(shù)作為timeoutms熟妓。
    如果timeoutms沒有傳(小于1),那就取默認(rèn)值:default_timeout_ms(1000)栏尚。

這段代碼看起來還算比較正常起愈,但答案還是沒出來,timeout參數(shù)明明是有讀取的译仗,為什么就是設(shè)置不成功抬虽?不急,再看看處理連接的_mmc_open()函數(shù):


當(dāng)我看到這段代碼的時(shí)候纵菌,倒吸一口涼氣阐污,心情久久不能平復(fù)。
認(rèn)真看下代碼咱圆,紅框里的是說笛辟,如果timeoutms大于0,那就用它作為超時(shí)時(shí)間序苏,否則就用timeout手幢。
剛剛我們只傳入timeout,那么timeoutms就會默認(rèn)賦值default_timeout_ms(默認(rèn)1000大于0)忱详。于是timeout就沒有用了围来,超時(shí)永遠(yuǎn)都是取timeoutms的1000ms。
也就是說踱阿,正確的設(shè)置超時(shí)時(shí)間應(yīng)該是使用第4個隱藏參數(shù)timeoutms(問你服了沒有管钳?):

<?php
...
$mc->connect(HOST, PORT, 3, 3000);    //第三個參數(shù)"3"可以隨便設(shè)钦铁,因?yàn)樗疾粫?...

再執(zhí)行strace /path/to/php test_mc.php驗(yàn)證是否設(shè)置成功:


OK软舌,果然如此,超時(shí)時(shí)間設(shè)置成功牛曹。
當(dāng)然佛点,還有一種簡單粗暴的方法:

<?php
...
ini_set('memcache.default_timeout_ms', 3000);
...

在php.ini修改或者動態(tài)修改都可以。

總結(jié)

不太清楚Memcache擴(kuò)展2.2.7以后的版本有沒有修復(fù)這個bug黎比。但2.2.7以后的都是beta版超营,2013年后就沒更新了。

建議有條件都把Memcache擴(kuò)展換成Memcached阅虫,不過要注意兩者的數(shù)據(jù)兼容情況演闭,因?yàn)閮烧叩男蛄谢绞接胁町惖摹?/p>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市颓帝,隨后出現(xiàn)的幾起案子米碰,更是在濱河造成了極大的恐慌窝革,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吕座,死亡現(xiàn)場離奇詭異虐译,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)吴趴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門漆诽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锣枝,你說我怎么就攤上這事厢拭。” “怎么了撇叁?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵蚪腐,是天一觀的道長。 經(jīng)常有香客問我税朴,道長回季,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任正林,我火速辦了婚禮泡一,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘觅廓。我一直安慰自己鼻忠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布杈绸。 她就那樣靜靜地躺著帖蔓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瞳脓。 梳的紋絲不亂的頭發(fā)上塑娇,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天,我揣著相機(jī)與錄音劫侧,去河邊找鬼埋酬。 笑死,一個胖子當(dāng)著我的面吹牛烧栋,可吹牛的內(nèi)容都是我干的写妥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼审姓,長吁一口氣:“原來是場噩夢啊……” “哼珍特!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起魔吐,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤扎筒,失蹤者是張志新(化名)和其女友劉穎呼猪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砸琅,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宋距,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了症脂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谚赎。...
    茶點(diǎn)故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖诱篷,靈堂內(nèi)的尸體忽然破棺而出壶唤,到底是詐尸還是另有隱情,我是刑警寧澤棕所,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布闸盔,位于F島的核電站,受9級特大地震影響琳省,放射性物質(zhì)發(fā)生泄漏迎吵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一针贬、第九天 我趴在偏房一處隱蔽的房頂上張望击费。 院中可真熱鬧,春花似錦桦他、人聲如沸蔫巩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽圆仔。三九已至,卻和暖如春蔫劣,著一層夾襖步出監(jiān)牢的瞬間坪郭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工拦宣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留截粗,地道東北人。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓鸵隧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親意推。 傳聞我的和親對象是個殘疾皇子豆瘫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1、memcache的概念菊值? Memcache是一個高性能的分布式的內(nèi)存對象緩存系統(tǒng)外驱,通過在內(nèi)存里維護(hù)一個統(tǒng)一的巨...
    桖辶殤閱讀 2,235評論 2 12
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理育灸,服務(wù)發(fā)現(xiàn),斷路器昵宇,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 一磅崭、MemCache簡介 session MemCache是一個自由、源碼開放瓦哎、高性能砸喻、分布式的分布式內(nèi)存對象緩存...
    李偉銘MIng閱讀 3,810評論 2 13
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,929評論 2 89
  • 監(jiān)聽鍵盤的通知 通過通知方法獲取鍵盤Rect 跟 Duration 當(dāng)滾動tableview時(shí),就收起鍵盤: 不同...
    coder_hong閱讀 2,895評論 0 0