详解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下读取文本文件的代码
Jul 02 PHP
解决163/sohu/sina不能够收到PHP MAIL函数发出邮件的问题
Mar 13 PHP
php/js获取客户端mac地址的实现代码
Jul 08 PHP
php中time()和mktime()方法的区别
Sep 28 PHP
php强制运行广告的方法
Dec 01 PHP
微信公众平台开发之配置与请求
Aug 26 PHP
PHP闭包函数详解
Feb 13 PHP
给大家分享几个常用的PHP函数
Jan 15 PHP
PHP简单获取随机数的常用方法小结
Jun 07 PHP
Laravel中获取路由参数Route Parameters的五种方法示例
Sep 29 PHP
详解PHP素材图片上传、下载功能
Apr 12 PHP
PHP正则之正向预查与反向预查讲解与实例
Apr 06 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查找任何页面上的所有链接的方法
2013/12/03 PHP
几道坑人的PHP面试题 试试看看你会不会也中招
2014/08/19 PHP
Laravel框架学习笔记(二)项目实战之模型(Models)
2014/10/15 PHP
Windows2003下php5.4安装配置教程(IIS)
2016/06/30 PHP
window.showModalDialog使用手册
2007/01/11 Javascript
js 函数的执行环境和作用域链的深入解析
2009/11/01 Javascript
js中indexof的用法详细解析
2013/12/24 Javascript
js中的如何定位固定层的位置
2014/06/15 Javascript
基于jQuery倾斜打开侧边栏菜单特效代码
2015/09/15 Javascript
URL中“#” “?” &“”号的作用浅析
2017/02/04 Javascript
原生JS实现自定义滚动条效果
2020/10/27 Javascript
AngularJS基于MVC的复杂操作实例讲解
2017/12/31 Javascript
vue.js与element-ui实现菜单树形结构的解决方法
2018/04/21 Javascript
node.js爬虫框架node-crawler初体验
2020/10/29 Javascript
[04:30]显微镜下的DOTA2第五期——拉比克
2013/09/26 DOTA
[08:06]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Elephant 选手采访
2021/03/11 DOTA
推荐11个实用Python库
2015/01/23 Python
Django添加KindEditor富文本编辑器的使用
2018/10/24 Python
Python函数装饰器常见使用方法实例详解
2019/03/30 Python
Python基础学习之基本数据结构详解【数字、字符串、列表、元组、集合、字典】
2019/06/18 Python
简单了解python高阶函数map/reduce
2019/06/28 Python
Python高级特性 切片 迭代解析
2019/08/23 Python
Python远程开发环境部署与调试过程图解
2019/12/09 Python
python适合做数据挖掘吗
2020/06/16 Python
matplotlib更改窗口图标的方法示例
2021/02/03 Python
html5视频媒体标签video的使用方法及完整参数说明详解
2019/09/27 HTML / CSS
Columbia美国官网:美国著名的户外服装品牌
2016/11/24 全球购物
美国著名的品牌折扣店:Burlington
2017/06/08 全球购物
Funko官方商店:源自美国,畅销全球搪胶收藏玩偶
2018/09/15 全球购物
轻松制作精彩视频:Animoto
2018/09/19 全球购物
求职简历中个人的自我评价
2013/12/25 职场文书
地球一小时宣传标语
2014/06/24 职场文书
年终晚会活动方案
2014/08/21 职场文书
医院2014国庆节活动策划方案
2014/09/21 职场文书
Python+Appium新手教程
2021/04/17 Python
GO语言异常处理分析 err接口及defer延迟
2022/04/14 Golang