詳解PHP使用Redis存儲(chǔ)session時(shí)的一個(gè)Warning定位
1. 問題現(xiàn)象
系統(tǒng)頁面刷新的時(shí)候,偶爾會(huì)報(bào)錯(cuò)下面的Warnning,但是不經(jīng)常出現(xiàn):
Warning: Unknown: Failed to write session data (Redis). Please verify that the current setting of session.save_path is correct (tcp://x.x.x.x:6379?auth=yyy) in Unknown on line 0
看網(wǎng)絡(luò)有人說是redis版本的問題、但是沒有具體結(jié)論,那么本著學(xué)習(xí)的態(tài)度,自己試試看看能不能捉出這個(gè)bug.
定位問題:
查看PHP文件是否有設(shè)置session的地方,發(fā)現(xiàn)沒有
繼續(xù)檢查php配置文件,發(fā)現(xiàn)配置了session存儲(chǔ)到redis里面
[Session] session.save_handler = redis session.save_path = "tcp://x.x.x.x:6379?auth=yyyy" session.use_cookies = 1
1、繼續(xù)查看php Session擴(kuò)展源代碼,定位出錯(cuò)提示語在函數(shù)php_session_save_current_state中
檢查session擴(kuò)展文件中出錯(cuò)提示:
static void php_session_save_current_state(TSRMLS_D) /* {{{ */ { int ret = FAILURE; //是否session數(shù)組 IF_SESSION_VARS() { if (PS(mod_data) || PS(mod_user_implemented)) { char *val; int vallen; //變量編碼 val = php_session_encode(&vallen TSRMLS_CC); if (val) { //保存session數(shù)據(jù) ret = PS(mod)->s_write(&PS(mod_data), PS(id), val, vallen TSRMLS_CC); efree(val); } else { //清空session ret = PS(mod)->s_write(&PS(mod_data), PS(id), "", 0 TSRMLS_CC); } } //看出錯(cuò)提示語在這里 if (ret == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write session data (%s). Please " "verify that the current setting of session.save_path " "is correct (%s)", PS(mod)->s_name, PS(save_path)); } } if (PS(mod_data) || PS(mod_user_implemented)) { PS(mod)->s_close(&PS(mod_data) TSRMLS_CC); } } static void php_session_flush(TSRMLS_D){ 如果 (PS(session_status) == php_session_active) session_status = none 這里調(diào)用... php_session_save_current_state }
下面兩個(gè)函數(shù)調(diào)用:php_session_flush()
static PHP_FUNCTION(session_write_close) { php_session_flush(TSRMLS_C); } static PHP_FUNCTION(session_register_shutdown){ }
由上面可以看到,php_session_save_current_state是在 php_session_flush中調(diào)用,即在session保存、清空等刷新寫session的時(shí)候會(huì)產(chǎn)生。
由php seesion源碼沒有發(fā)現(xiàn)什么問題,突然想到會(huì)不會(huì)是Redis本身有沒有啥問題,導(dǎo)致的寫出錯(cuò)。因而繼續(xù)從redis里面找問題:
Redis問題定位
1. 首先查看Redis日志文件:
看到如下每5分鐘會(huì)刷新一下如下log:
[16723] 04 Jul 15:15:01.987 # Server started, Redis version 2.8.9 [16723] 04 Jul 15:15:01.987 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. [16723] 04 Jul 15:15:01.996 * DB loaded from disk: 0.008 seconds [16723] 04 Jul 15:15:01.996 * The server is now ready to accept connections on port 6379
懷疑這就是問題的PHP寫日志失敗的原因了。那么Redis為啥會(huì)5分鐘重啟呢?繼續(xù)追查!
2. 是系統(tǒng)內(nèi)存不夠、Redis core了?
查看系統(tǒng)和Redis內(nèi)存使用狀態(tài):
系統(tǒng)內(nèi)存狀態(tài):
$ free -m total used free shared buffers cached Mem: 3516 3171 345 0 684 1680 -/+ buffers/cache: 806 2709 Swap: 2055 724 1330
Redis服務(wù)器占用的內(nèi)存狀態(tài):
used_memory:2841648 used_memory_human:2.71M used_memory_rss:3710976 used_memory_peak:2877576 used_memory_peak_human:2.74M used_memory_lua:33792 mem_fragmentation_ratio:1.31 mem_allocator:jemalloc-3.2.0
可以看到Redis其實(shí)內(nèi)存占用非常少。redis重啟原因暫且不明。
3. 定時(shí)腳本
因?yàn)橹貑⑷罩?分鐘一次、非常規(guī)律,因而懷疑是不是別的原因?qū)е翿edis重啟,比如定時(shí)腳本。
因?yàn)楸救藳]有root corntab權(quán)限,找老板提權(quán),看到赫然存在著一個(gè)crontab,5分鐘監(jiān)控一次redis。
如下:
/5 * * * /data/scripts/check_redis.sh >/dev/null 2>&1 ##check_redis.tag.1
查看腳本內(nèi)容
PORT='6379' ETH1_ADDR=`/sbin/ifconfig eth1 | awk -F ':' '/inet addr/{print $2}' | sed 's/[a-zA-Z ]//g'` retval=`nmap --system-dns -sT -p ${PORT} ${ETH1_ADDR} | grep open` if [ "${retval}X" = "X" ]; then /sbin/service redis restart >/dev/null 2>&1 fi
這是一個(gè)檢查Redis端口是否提供服務(wù)的腳本,如果檢查不成功,就拉起Redis。
可以看到這個(gè)腳本本來沒有什么問題。
但是:通過手動(dòng)執(zhí)行這個(gè)命令發(fā)現(xiàn):
這臺(tái)機(jī)器沒有安裝nmap 這個(gè)命令,
所以這個(gè)腳本最后總是執(zhí)行失?。∪缓驲edis重啟。
因?yàn)閱栴}根源找到了,找運(yùn)維安裝相關(guān)命令,問題解決。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
destoon二次開發(fā)模板及調(diào)用語法匯總
這篇文章主要介紹了destoon二次開發(fā)模板及調(diào)用語法,需要的朋友可以參考下2014-06-06PHP中如何使用session實(shí)現(xiàn)保存用戶登錄信息
這篇文章主要給大家介紹在php中是如何使用session實(shí)現(xiàn)保存用戶登錄信息的,涉及到php session 用戶登錄等一些知識(shí)點(diǎn),使用session保存用戶登錄信息要比cookie安全很多。感興趣的朋友一起學(xué)習(xí)吧2015-10-10PHP實(shí)現(xiàn)微信公眾號(hào)驗(yàn)證Token的示例代碼
這篇文章主要介紹了PHP實(shí)現(xiàn)微信公眾號(hào)驗(yàn)證Token的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12PHP獲取客戶端真實(shí)IP地址的5種情況分析和實(shí)現(xiàn)代碼
這篇文章主要介紹了PHP獲取客戶端真實(shí)IP地址的幾種情況分析和實(shí)現(xiàn)代碼,重點(diǎn)在幾種干擾獲得真實(shí)IP的幾種情況介紹,需要的朋友可以參考下2014-07-07PHP date()函數(shù)警告: It is not safe to rely on the system解決方法
這篇文章主要介紹了PHP date()函數(shù)警告: It is not safe to rely on the system解決方法,其實(shí)就是時(shí)區(qū)設(shè)置不正確造成的,本文提供了兩種方法來解決這個(gè)問題,需要的朋友可以參考下2014-08-08yii框架創(chuàng)建與設(shè)置默認(rèn)控制器并載入模板操作示例
這篇文章主要介紹了yii框架創(chuàng)建與設(shè)置默認(rèn)控制器并載入模板操作,結(jié)合實(shí)例形式分析了Yii框架控制器與模板相關(guān)操作技巧及使用注意事項(xiàng),需要的朋友可以參考下2020-03-03