详解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里面的抽象类
Jan 28 PHP
ecshop 订单确认中显示省市地址信息的方法
Mar 15 PHP
php笔记之常用文件操作
Oct 12 PHP
PHP测试程序运行时间的类
Feb 05 PHP
php笔记之:文章中图片处理的使用
Apr 26 PHP
使用Apache的htaccess防止图片被盗链的解决方法
Apr 27 PHP
深入分析PHP引用(&)
Sep 04 PHP
php找出指定范围内回文数且平方根也是回文数的方法
Mar 23 PHP
Session 失效的原因汇总及解决丢失办法
Sep 30 PHP
Yii2中事务的使用实例代码详解
Sep 07 PHP
CI框架(CodeIgniter)公共模型类定义与用法示例
Aug 10 PHP
PHP使用glob方法遍历文件夹下所有文件的实例
Oct 17 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为什么选mysql作为数据库? Mysql 创建用户方法
2007/07/02 PHP
PHP下编码转换函数mb_convert_encoding与iconv的使用说明
2009/12/16 PHP
页面乱码问题的根源及其分析
2013/08/09 PHP
PHP使用CURL模拟登录的方法
2015/07/08 PHP
win7系统配置php+Apache+mysql环境的方法
2015/08/21 PHP
PHP生成和获取XML格式数据的方法
2016/03/04 PHP
thinkPHP5.0框架事务处理操作简单示例
2018/09/07 PHP
Javascript延迟执行实现方法(setTimeout)
2010/12/30 Javascript
jquery实现奇偶行赋值不同css值
2012/02/17 Javascript
js控制表单奇偶行样式的简单方法
2013/07/31 Javascript
javascript字符串对象常用api函数小结(连接,替换,分割,转换等)
2016/09/20 Javascript
jquery mobile实现可折叠的导航按钮
2017/03/11 Javascript
解决vue build打包之后首页白屏的问题
2018/03/06 Javascript
js实现的格式化数字和金额功能简单示例
2019/07/30 Javascript
Vue是怎么渲染template内的标签内容的
2020/06/05 Javascript
如何利用node转发请求详解
2020/09/17 Javascript
python求素数示例分享
2014/02/16 Python
python实现在字符串中查找子字符串的方法
2015/07/11 Python
浅谈Django自定义模板标签template_tags的用处
2017/12/20 Python
详解pytorch 0.4.0迁移指南
2019/06/16 Python
Pycharm运行加载文本出现错误的解决方法
2019/06/27 Python
Python 日期的转换及计算的具体使用详解
2020/01/16 Python
django实现将后台model对象转换成json对象并传递给前端jquery
2020/03/16 Python
如何使用 Python 读取文件和照片的创建日期
2020/09/05 Python
what is the difference between ext2 and ext3
2013/11/03 面试题
自荐信格式技巧有哪些呢
2013/11/19 职场文书
家长学校实施方案
2014/03/15 职场文书
大学生找工作求职信
2014/07/09 职场文书
2014年服务行业工作总结
2014/11/18 职场文书
初中班长竞选稿
2015/11/20 职场文书
幼儿园2016年圣诞活动总结
2016/03/31 职场文书
Golang 空map和未初始化map的注意事项说明
2021/04/29 Golang
对Keras自带Loss Function的深入研究
2021/05/25 Python
Java实现房屋出租系统详解
2021/10/05 Java/Android
Python利用capstone实现反汇编
2022/04/06 Python
WIN10使用IIS部署ftp服务器详细教程
2022/08/05 Servers