详解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 相关文章推荐
从MySQL数据库表中取出随机数据的代码
Sep 05 PHP
PHP 变量类型的强制转换
Oct 23 PHP
php cli 方式 在crotab中运行解决
Feb 08 PHP
PHP中开发XML应用程序之基础篇 添加节点 删除节点 查询节点 查询节
Jul 09 PHP
使用Smarty 获取当前日期时间和格式化日期时间的方法详解
Jun 18 PHP
PHP实现算式验证码和汉字验证码实例
Mar 09 PHP
PHP执行linux命令常用函数汇总
Feb 02 PHP
PHP生成图片验证码功能示例
Jan 12 PHP
PHP函数rtrim()使用中的怪异现象分析
Feb 24 PHP
PHP封装的数据库模型Model类完整示例【基于PDO】
Mar 14 PHP
PHP创建对象的六种方式实例总结
Jun 27 PHP
thinkphp3.2同时连接两个数据库的简单方法
Aug 13 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求圆周率的简单实现方法
2016/05/30 PHP
浅析PHP中的 inet_pton 网络函数
2019/12/16 PHP
JavaScript内核之基本概念
2011/10/21 Javascript
JavaScript 原型继承
2011/12/26 Javascript
使用jQuery清空file文件域的解决方案
2013/04/12 Javascript
JS 添加千分位与去掉千分位的示例
2013/07/11 Javascript
form表单action提交的js部分与html部分
2014/01/07 Javascript
js实现顶部可折叠的菜单工具栏效果实例
2015/05/09 Javascript
javascript实现的淘宝旅行通用日历组件用法实例
2015/08/03 Javascript
学习JavaScript设计模式(接口)
2015/11/26 Javascript
javascript实现查找数组中最大值方法汇总
2016/02/13 Javascript
Windows 系统下设置Nodejs NPM全局路径
2016/04/26 NodeJs
深入浅出讲解ES6的解构
2016/08/03 Javascript
微信小程序 监听手势滑动切换页面实例详解
2017/06/15 Javascript
js 只比较时间大小的实例
2017/10/26 Javascript
Vue使用vux-ui自定义表单验证遇到的问题及解决方法
2018/05/10 Javascript
Element中的Cascader(级联列表)动态加载省\市\区数据的方法
2019/03/27 Javascript
利用Angular7开发一个Radio组件的全过程
2019/07/11 Javascript
JS Html转义和反转义(html编码和解码)的实现与使用方法总结
2020/03/10 Javascript
搭建vscode+vue环境的详细教程
2020/08/31 Javascript
[07:57]DOTA2热力大趴狂欢夜 广州站活动回顾
2013/11/27 DOTA
[01:04]不如跳舞!DOTA2新英雄玛尔斯的欢乐日常
2019/03/11 DOTA
Python 40行代码实现人脸识别功能
2017/04/02 Python
python中resample函数实现重采样和降采样代码
2020/02/25 Python
jupyter notebook 的工作空间设置操作
2020/04/20 Python
浅谈python锁与死锁问题
2020/08/14 Python
前端制作动画的几种方式(css3,js)
2016/12/12 HTML / CSS
Viking Direct荷兰:购买办公用品
2019/06/20 全球购物
数据库基础的一些面试题
2012/02/25 面试题
环境工程专业自荐信
2014/03/03 职场文书
硕士生工作推荐信
2014/03/07 职场文书
团队拓展活动方案
2014/08/28 职场文书
教师个人总结范文
2015/02/11 职场文书
个人廉洁自律总结
2015/03/06 职场文书
一年级语文教学随笔
2015/08/14 职场文书
2016年小学教师师德承诺书
2016/03/25 职场文书