详解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 06 PHP
php中sprintf与printf函数用法区别解析
Feb 17 PHP
ThinkPHP中U方法的使用浅析
Jun 13 PHP
分享常见的几种页面静态化的方法
Jan 08 PHP
PHP获取文件相对路径的方法
Feb 26 PHP
PHP使用适合阅读的格式显示文件大小的方法
Mar 05 PHP
php实现TCP端口检测的方法
Apr 01 PHP
全新Mac配置PHP开发环境教程
Feb 03 PHP
PHP页面间传递值和保持值的方法
Aug 24 PHP
ThinkPHP整合datatables实现服务端分页的示例代码
Feb 10 PHP
php的instanceof和判断闭包Closure操作示例
Jan 26 PHP
yii框架结合charjs实现统计30天数据的方法
Apr 04 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内存缓存功能memcached示例
2016/10/19 PHP
PHP高精确度运算BC函数库实例详解
2017/08/15 PHP
php生成随机数/生成随机字符串的方法小结【5种方法】
2020/05/27 PHP
js加解密 脚本解密
2008/02/22 Javascript
jquery 打开窗口返回值实现代码
2010/03/04 Javascript
让你的CSS像Jquery一样做筛选的实现方法
2011/07/10 Javascript
sencha touch 模仿tabpanel导航栏TabBar的实例代码
2013/10/24 Javascript
JQuery each()嵌套使用小结
2014/04/18 Javascript
JS图片自动轮换效果实现思路附截图
2014/04/30 Javascript
jQuery使用$.ajax提交表单完整实例
2015/12/11 Javascript
JS中对象与字符串的互相转换详解
2016/05/20 Javascript
AngularJS表单基本操作
2017/01/09 Javascript
Vue非父子组件通信详解
2017/06/12 Javascript
js获取元素的偏移量offset简单方法(必看)
2017/07/05 Javascript
Angular4 ElementRef的应用
2018/02/26 Javascript
vue2.0使用v-for循环制作多级嵌套菜单栏
2018/06/25 Javascript
AngularJs分页插件使用详解
2018/06/30 Javascript
layui table设置前台过滤转义等方法
2018/08/17 Javascript
vue实现键盘输入支付密码功能
2018/08/18 Javascript
详解Vue中watch对象内属性的方法
2019/02/01 Javascript
JavaScript canvas实现雪花随机动态飘落
2020/02/08 Javascript
wxpython学习笔记(推荐查看)
2014/06/09 Python
django框架基于模板 生成 excel(xls) 文件操作示例
2019/06/19 Python
用Python将Excel数据导入到SQL Server的例子
2019/08/24 Python
tensorflow实现从.ckpt文件中读取任意变量
2020/05/26 Python
详解pycharm配置python解释器的问题
2020/10/15 Python
Perry Ellis官网:美国男士品味服装
2016/12/09 全球购物
物业管理求职自荐信
2013/09/25 职场文书
师范毕业生自荐信
2013/10/17 职场文书
教师实习的自我鉴定
2013/10/26 职场文书
多媒体教室标语
2014/06/26 职场文书
2014年餐厅服务员工作总结
2014/11/18 职场文书
2014年社团工作总结范文
2014/11/27 职场文书
会计工作总结范文2014
2014/12/23 职场文书
2015年高中生国庆节演讲稿
2015/07/30 职场文书
CSS 鼠标选中文字后改变背景色的实现代码
2023/05/21 HTML / CSS