详解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 生成随机验证码图片代码
Feb 08 PHP
PHP,ASP.JAVA,JAVA代码格式化工具整理
Jun 15 PHP
让PHP COOKIE立即生效,不用刷新就可以使用
Mar 09 PHP
php后台如何避免用户直接进入方法实例
Oct 15 PHP
1亿条数据如何分表100张到Mysql数据库中(PHP)
Jul 29 PHP
Zend Framework教程之Loader以及PluginLoader用法详解
Mar 09 PHP
iOS10推送通知开发教程
Sep 19 PHP
PHP面向对象程序设计方法实例详解
Dec 24 PHP
基于PHP常用文件函数和目录函数整理
Aug 17 PHP
PHP 布尔值的自增与自减的实现方法
May 03 PHP
php接口隔离原则实例分析
Nov 11 PHP
PHP设计模式(五)适配器模式Adapter实例详解【结构型】
May 02 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 shell命令合并图片的代码
2011/06/23 PHP
探讨:php中在foreach中使用foreach ($arr as &$value) 这种类型的解释
2013/06/24 PHP
zf框架的registry(注册表)使用示例
2014/03/13 PHP
PHP中的排序函数sort、asort、rsort、krsort、ksort区别分析
2014/08/18 PHP
php使用Image Magick将PDF文件转换为JPG文件的方法
2015/04/01 PHP
PHP 中魔术常量的实例详解
2017/10/26 PHP
Laravel框架使用Seeder实现自动填充数据功能
2018/06/13 PHP
JS+JSP checkBox 全选具体实现
2014/01/02 Javascript
返回上一页并自动刷新的JavaScript代码
2014/02/19 Javascript
javascript监听鼠标滚轮事件浅析
2014/06/05 Javascript
JS实现文字链接感应鼠标淡入淡出改变颜色的方法
2015/02/26 Javascript
js实现防止被iframe的方法
2015/07/03 Javascript
jQuery prototype冲突的2种解决方法(附demo示例下载)
2016/01/21 Javascript
你一定会收藏的Nodejs代码片段
2016/02/04 NodeJs
单击按钮发送验证码,出现倒计时的简单实例
2017/03/17 Javascript
JS使用setInterval实现的简单计时器功能示例
2018/04/19 Javascript
JQuery+Bootstrap 自定义全屏Loading插件的示例demo
2019/07/03 jQuery
聊聊鉴权那些事(推荐)
2019/08/22 Javascript
解决vue项目中遇到 Cannot find module ‘chalk‘ 报错的问题
2020/11/05 Javascript
[49:35]LGD vs OG 2018国际邀请赛淘汰赛BO3 第二场 8.25
2018/08/29 DOTA
[51:53]完美世界DOTA2联赛循环赛 LBZS vs DM BO2第二场 11.01
2020/11/02 DOTA
Python基于whois模块简单识别网站域名及所有者的方法
2018/04/23 Python
python  创建一个保留重复值的列表的补码
2018/10/15 Python
使用python代码进行身份证号校验的实现示例
2019/11/21 Python
Python unittest生成测试报告过程解析
2020/09/08 Python
Sublime Text3最新激活注册码分享适用2020最新版 亲测可用
2020/11/12 Python
css3动画鼠标放上图片逐渐变大鼠标离开图片逐渐缩小效果
2021/01/27 HTML / CSS
HTML5 canvas基本绘图之绘制五角星
2016/06/27 HTML / CSS
HTML5 Canvas 旋转风车绘制
2017/08/18 HTML / CSS
文秘专业毕业生就业推荐信
2013/11/08 职场文书
师范应届毕业生自荐信
2013/11/18 职场文书
诉讼代理人授权委托书
2014/10/11 职场文书
2014年保险业务员工作总结
2014/12/23 职场文书
稽核岗位职责范本
2015/04/13 职场文书
MybatisPlus EntityWrapper如何自定义SQL
2022/03/22 Java/Android
Nebula Graph解决风控业务实践
2022/03/31 MySQL