問題介紹
最近有 PHP 使用者反饋對ApsaraDB for Memcache做效能測試的結果,達不到預期的效能指標。通過瞭解具體情況,大多數使用者在使用 PHP 串連ApsaraDB for Memcache時,都是通過走 Apache WEB 服務再連ApsaraDB for Memcache,使用的是短串連。而每個短串連的開銷不止是 socket 重連,還有複雜的重新鑒權流程,開銷比一個普通請求大許多,因此對網站的效率是有很大影響的。
解決方案
於是我們建議使用者改短串連為長串連,但是KVStore for Memcache要求使用的PHP MEMCACHED擴充,不像memcache擴充那樣有個pconnect介面。如何才能在PHP中建立長串連,以下教程供大家參考。
在PHP 官網介紹 memcached 建構函式時有下面一段話:
說明
Memcached::__construct ([ string $persistent_id ] )建立一個代表到Memcached服務端串連的Memcached執行個體。
參數
persistent_id預設情況下,Memcached執行個體在請求結束後會被銷毀。但可以在建立時通過persistent_id為每個執行個體指定唯一的ID, 在請求間共用執行個體。所有通過相同的persistent_id值建立的執行個體共用同一個串連。
即在調用建構函式時傳給它一個同樣的 persistent_id 就能實現共用串連。代碼實現如下:
<?php
$memc = new Memcached(‘ocs’);//這裡的ocs,就是persistent_id
if (count($memc->getServerList()) == 0) /*建立串連前,先判斷*/
{
echo "New connection"."<br>";
/*所有option都要放在判斷裡面,因為有的option會導致重連,讓長串連變短串連!*/
$memc->setOption(Memcached::OPT_COMPRESSION, false);
$memc->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
$memc->setOption(Memcached::OPT_TCP_NODELAY, true); //重要,php memcached有個bug,當get的值不存在,有固定40ms延遲,開啟這個參數,可以避免這個bug
/* addServer 代碼必須在判斷裡面,否則相當於重複建立’ocs’這個串連池,可能會導致用戶端php程式異常*/
$memc->addServer("your_ip", 11212);
$memc->setSaslAuthData("user", "password");
}
else
{
echo "Now connections is:".count($memc->getServerList())."<br>";
}
$memc->set("key", "value");
echo "Get from OCS: ".$memc->get("key");
//$memc->quit();/*代碼結束的地方一定不能加quit,否則變短串連!*/
?>
上述代碼要特別注意的三個地方都加了注釋。建構函式裡的“ocs”關鍵字,就相當於一個串連池了,需要使用的串連調用 new Memcached(‘ocs’) 就能從池裡擷取串連。
執行結果
將上述代碼放到 Apache 工作路徑/var/www/html/下面,命名為 test.php。然後再瀏覽器輸入: http://your_ip:80/test.php。前8次瀏覽器都輸出結果為:
New connection
Get from OCS: value
即這8次都是建立串連,而8次後都是複用這8個連結了。抓包結果也顯示8次後都是長串連,無 socket 重連無鑒權。
那麼為什麼有8個連結?通過查看 httpd.conf 設定檔,是因為 appache 啟動了8個子進程:
#StartServers:numbers of server processes to start
StartServers 8
於是剛好在‘ocs’這個 persistent_id 的串連池裡面初始化8個串連,此後的請求就用這8個連結了。
接下來測試頁面跳轉,拷貝一個 php 檔案,建立串連的建構函式的 persistent_id 還是用 “ocs”。得到的結果是從一個串連換到了另一個串連上(因為調用的 Apache 子進程不一樣),但無鑒權無 socket 重連過程。即 PHP memcached 的長串連設定是有效。通常我們使用的都是 PHP-FPM 模式, FPM 進程會和 memcached server 保持長串連,因此該串連的生命週期同 Apache 進程。