详解PHP使用Redis存储session时的一个Warning定位


Posted in PHP onJuly 05, 2017

1. 问题现象

系统页面刷新的时候,偶尔会报错下面的Warnning,但是不经常出现:

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

看网络有人说是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、继续查看php Session扩展源代码,定位出错提示语在函数php_session_save_current_state中

检查session扩展文件中出错提示:

static void php_session_save_current_state(TSRMLS_D) /* {{{ */
{
  int ret = FAILURE;

  //是否session数组
  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数据
        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);
      }
    }


    //看出错提示语在这里
    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

    这里调用...
    php_session_save_current_state
}

下面两个函数调用: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中调用,即在session保存、清空等刷新写session的时候会产生。

由php seesion源码没有发现什么问题,突然想到会不会是Redis本身有没有啥问题,导致的写出错。因而继续从redis里面找问题:

Redis问题定位

1. 首先查看Redis日志文件:

看到如下每5分钟会刷新一下如下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为啥会5分钟重启呢?继续追查!

2. 是系统内存不够、Redis core了?

查看系统和Redis内存使用状态:

系统内存状态:

$ free -m
       total    used    free   shared  buffers   cached
Mem:     3516    3171    345     0    684    1680
-/+ buffers/cache:    806    2709
Swap:     2055    724    1330

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重启,比如定时脚本。

因为本人没有root corntab权限,找老板提权,看到赫然存在着一个crontab,5分钟监控一次redis。

如下:

/5 * * * /data/scripts/check_redis.sh >/dev/null 2>&1 ##check_redis.tag.1

查看脚本内容

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

这是一个检查Redis端口是否提供服务的脚本,如果检查不成功,就拉起Redis。

可以看到这个脚本本来没有什么问题。

但是:通过手动执行这个命令发现:

这台机器没有安装nmap 这个命令,

所以这个脚本最后总是执行失败!然后Redis重启。

因为问题根源找到了,找运维安装相关命令,问题解决。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
php自动适应范围的分页代码
Aug 05 PHP
php ss7.5的数据调用 (笔记)
Mar 08 PHP
php strnatcmp()函数的用法总结
Nov 27 PHP
CentOS 6.3下安装PHP xcache扩展模块笔记
Sep 10 PHP
php启用sphinx全文搜索的实现方法
Dec 24 PHP
Apache服务器下防止图片盗链的办法
Jul 06 PHP
功能强大的PHP图片处理类(水印、透明度、旋转)
Oct 21 PHP
php版阿里大于(阿里大鱼)短信发送实例详解
Nov 30 PHP
php判断是否连接上网络的方法实例详解
Dec 14 PHP
php实现的简单中文验证码功能示例
Jan 03 PHP
php连接MSsql server的五种方法总结
Mar 04 PHP
CodeIgniter框架钩子机制实现方法【hooks类】
Aug 21 PHP
php如何修改SESSION的生存存储时间的实例代码
Jul 05 #PHP
PHP实现根据密码长度显示安全条
Jul 04 #PHP
PHP截取发动短信内容的方法
Jul 04 #PHP
phpcms配置列表页以及获得文章发布时间
Jul 04 #PHP
一个非常实用的php文件上传类
Jul 04 #PHP
php基于数组函数实现关联表的编辑操作示例
Jul 04 #PHP
PHP基于方差和标准差计算学生成绩的稳定性示例
Jul 04 #PHP
You might like
同一空间绑定多个域名而实现访问不同页面的PHP代码
2006/12/06 PHP
一键删除顽固的空文件夹 软件下载
2007/01/26 PHP
dedecms防止FCK乱格式化你的代码的修改方法
2007/03/17 PHP
求帮忙修改个php curl模拟post请求内容后并下载文件的解决思路
2015/09/20 PHP
session 加入redis的实现代码
2016/07/15 PHP
PHP树形结构tree类用法示例
2019/02/01 PHP
js右键菜单效果代码
2007/07/21 Javascript
JS仿flash上传头像效果实现代码
2011/07/18 Javascript
js鼠标点击事件在各个浏览器中的写法及Event对象属性介绍
2013/01/24 Javascript
jquery清空textarea等输入框实现代码
2013/04/22 Javascript
JQuery EasyUI 日期控件如何控制日期选择区间
2014/05/05 Javascript
jQuery中noconflict函数的实现原理分解
2015/02/03 Javascript
JavaScript常用代码书写规范的超全面总结
2016/09/11 Javascript
Vue.js双向绑定实现原理详解
2016/12/22 Javascript
jQuery使用eraser.js插件实现擦除、刮刮卡效果的方法【附eraser.js下载】
2017/04/28 jQuery
vue本地打开build后生成的dist文件夹index.html问题
2019/09/04 Javascript
mpvue实现小程序签到金币掉落动画(api实现)
2019/10/17 Javascript
在vue中使用axios实现post方式获取二进制流下载文件(实例代码)
2019/12/16 Javascript
[35:43]2018DOTA2亚洲邀请赛 4.1 小组赛B组 paiN vs Effect
2018/04/03 DOTA
python k-近邻算法实例分享
2014/06/11 Python
python实现通过pil模块对图片格式进行转换的方法
2015/03/24 Python
在Python的Flask框架中验证注册用户的Email的方法
2015/09/02 Python
在Python中通过threading模块定义和调用线程的方法
2016/07/12 Python
基于使用paramiko执行远程linux主机命令(详解)
2017/10/16 Python
解决Python3 控制台输出InsecureRequestWarning问题
2019/07/15 Python
Python实现的微信红包提醒功能示例
2019/08/22 Python
无畏的旅行:Intrepid Travel
2017/12/20 全球购物
结婚邀请函范文
2014/01/14 职场文书
关于旷工的检讨书
2014/02/02 职场文书
仲裁协议书
2014/09/26 职场文书
婚前协议书范本两则
2014/10/16 职场文书
2015年图书馆个人工作总结
2015/05/26 职场文书
结婚喜宴祝酒词
2015/08/10 职场文书
python pygame入门教程
2021/06/01 Python
css实现两栏布局,左侧固定宽,右侧自适应的多种方法
2021/08/07 HTML / CSS
Mysql中一千万条数据怎么快速查询
2021/12/06 MySQL