详解PHP使用Redis存储session时的一个Warning定位
1.问题现象
系统页面刷新的时候,偶尔会报错下面的Warnning,但是不经常出现:
Warning:Unknown:Failedtowritesessiondata(Redis).Pleaseverifythatthecurrentsettingofsession.save_pathiscorrect(tcp://x.x.x.x:6379?auth=yyy)inUnknownonline0
看网络有人说是redis版本的问题、但是没有具体结论,那么本着学习的态度,自己试试看看能不能捉出这个bug.
定位问题:
查看PHP文件是否有设置session的地方,发现没有
继续检查php配置文件,发现配置了session存储到redis里面
[Session] session.save_handler=redis session.save_path="tcp://x.x.x.x:6379?auth=yyyy" session.use_cookies=1
1、继续查看phpSession扩展源代码,定位出错提示语在函数php_session_save_current_state中
检查session扩展文件中出错提示:
staticvoidphp_session_save_current_state(TSRMLS_D)/*{{{*/ { intret=FAILURE; //是否session数组 IF_SESSION_VARS(){ if(PS(mod_data)||PS(mod_user_implemented)){ char*val; intvallen; //变量编码 val=php_session_encode(&vallenTSRMLS_CC); if(val){ //保存session数据 ret=PS(mod)->s_write(&PS(mod_data),PS(id),val,vallenTSRMLS_CC); efree(val); }else{ //清空session ret=PS(mod)->s_write(&PS(mod_data),PS(id),"",0TSRMLS_CC); } } //看出错提示语在这里 if(ret==FAILURE){ php_error_docref(NULLTSRMLS_CC,E_WARNING,"Failedtowritesessiondata(%s).Please" "verifythatthecurrentsettingofsession.save_path" "iscorrect(%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); } } staticvoidphp_session_flush(TSRMLS_D){ 如果(PS(session_status)==php_session_active) session_status=none 这里调用... php_session_save_current_state }
下面两个函数调用:php_session_flush()
staticPHP_FUNCTION(session_write_close) { php_session_flush(TSRMLS_C); } staticPHP_FUNCTION(session_register_shutdown){ }
由上面可以看到,php_session_save_current_state是在php_session_flush中调用,即在session保存、清空等刷新写session的时候会产生。
由phpseesion源码没有发现什么问题,突然想到会不会是Redis本身有没有啥问题,导致的写出错。因而继续从redis里面找问题:
Redis问题定位
1.首先查看Redis日志文件:
看到如下每5分钟会刷新一下如下log:
[16723]04Jul15:15:01.987#Serverstarted,Redisversion2.8.9 [16723]04Jul15:15:01.987#WARNINGovercommit_memoryissetto0!Backgroundsavemayfailunderlowmemorycondition.Tofixthisissueadd'vm.overcommit_memory=1'to/etc/sysctl.confandthenrebootorrunthecommand'sysctlvm.overcommit_memory=1'forthistotakeeffect. [16723]04Jul15:15:01.996*DBloadedfromdisk:0.008seconds [16723]04Jul15:15:01.996*Theserverisnowreadytoacceptconnectionsonport6379
怀疑这就是问题的PHP写日志失败的原因了。那么Redis为啥会5分钟重启呢?继续追查!
2.是系统内存不够、Rediscore了?
查看系统和Redis内存使用状态:
系统内存状态:
$free-m totalusedfreesharedbufferscached Mem:3516317134506841680 -/+buffers/cache:8062709 Swap:20557241330
Redis服务器占用的内存状态:
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其实内存占用非常少。redis重启原因暂且不明。
3.定时脚本
因为重启日志5分钟一次、非常规律,因而怀疑是不是别的原因导致Redis重启,比如定时脚本。
因为本人没有rootcorntab权限,找老板提权,看到赫然存在着一个crontab,5分钟监控一次redis。
如下:
/5***/data/scripts/check_redis.sh>/dev/null2>&1##check_redis.tag.1
查看脚本内容
PORT='6379' ETH1_ADDR=`/sbin/ifconfigeth1|awk-F':''/inetaddr/{print$2}'|sed's/[a-zA-Z]//g'` retval=`nmap--system-dns-sT-p${PORT}${ETH1_ADDR}|grepopen` if["${retval}X"="X"];then /sbin/serviceredisrestart>/dev/null2>&1 fi
这是一个检查Redis端口是否提供服务的脚本,如果检查不成功,就拉起Redis。
可以看到这个脚本本来没有什么问题。
但是:通过手动执行这个命令发现:
这台机器没有安装nmap这个命令,
所以这个脚本最后总是执行失败!然后Redis重启。
因为问题根源找到了,找运维安装相关命令,问题解决。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。