问题年年有,今年特别多。最近公司对呼叫中心平台做了大幅度重构,基于OpenSIPS实现的会话管理服务,在高并发压测过程中,发现OpenSIPS的日志居然出现丢失的情况,简直让我食不知味,困惑不已。
最终虽解决了问题,但内部个中原理性尚未彻底弄明白,今日先记录在此,供同道中人参考,更希望有识之士能一解玉修心中之惑……
 
闲话不多说,来、来、来,翠花,上酸菜!!!!!!
 
1、运行环境配置
  • CentOS      7.4
  • Rsyslogd    8.24.0
  • OpenSIPS  2.4.2
1.1 OpenSIPS日志参数配置如下:
log_level=
log_stderror=no
log_facility=LOG_LOCAL0
log_name="/usr/local/opensips/sbin/opensips"
xlog_buf_size=

1.2 rsyslog.conf 自定义日志配置如下:

将设施为local0的所有级别的日志,都输出到指定文件

local0.* /var/log/opensips/opensips.log

1.3 OpenSIPS日志输出方式:

xlog("L_INFO", "[$fU->$rU] Route to user [$tu] [ci:$ci] [xcid:$hdr(X-CID)]");
 
2、问题现象:
  • 在低并发测试场景,按上述方式使用L_INFO级别输出日志时,都能在 /var/log/opensips/opensips.log 中正常打印日志,但是在高并发(20 CPS、1500并发)下,使用L_INFO级别输出的日志,经常会丢失。
  • 高并发下,opensips的日志文件,经常出现输出一次后,会等30秒才输出后续的日志。
  • 高并发下 /var/log/message 文件偶尔输出提示因日志输出速率超标,而强制丢弃部分日志的信息。如下所示:
Aug  :: uat16599 rsyslogd: imjournal:  messages lost due to rate-limiting
Aug :: uat16599 rsyslogd: imjournal: messages lost due to rate-limiting
Aug :: uat16599 rsyslogd: imjournal: messages lost due to rate-limiting
3、原因分析:

    OpenSIPS默认情况下日志内容是直接输出到Linux rsyslogd 服务的日志文件 /etc/message 中的,但可以通过修改rsyslogd服务的规则配置,将OpenSIPS的日志输出到指定文件(比如上面提到的在rsyslog.conf 中增加 local0.*的规则)。
    通过分析OpenSIPS源码我们可以得知:OpenSIPS先调用 xlog.c  的 int xlog_2(struct sip_msg* msg, char* lev, char* frm) ,然后在dprint.h文件中调用rsyslogd守护进程的 void syslog(int priority, const char *format, ...) 方法进行输出日志。从源码上看,OpenSIPS并没有控制日志输出的速率,而且没有当日志量达到某个阈值而直接丢弃日志的功能。
 
    因此,我有理由怀疑,日志丢失是Rsyslogd服务在作祟。
    那还等什么,我们去探究一下Rsyslogd的到底是怎么回事……
 
    从rsyslogd从5.7.1版本开始(我的系统采用的rsyslogd是8.24.0),新增了输出速率限制功能,默认情况下,如果一个PID在5秒内不能输出超过200条日志,否则超过200条之后的消息将被丢弃,所以会报 rate-limiting 记录。
    另外,CentOS7 的Rsyslogd已经默认采用 Systemd Journal来处理本地日志文件(从/etc/rsyslog.conf文件中下面几行配置可得知)
# Turn off message reception via local log socket;
# local messages are retrieved through imjournal now.
$OmitLocalLogging on

然而Systemd Journal 默认情况下,30秒内只允许记录1000条日志文件,并且每10分钟累计最大处理20000条日志,这显然不够用啊。所以我们可以通过修改 Systemd Journal 的配置/etc/systemd/journald.conf来解决该问题。

    Systemd Journal 是采用异步存储日志的,而老的rsyslog则是采用同步模式。
    
    OpenSIPS的xlog 默认的日志级别是 -1(ERR级别),如果输出日志时指定的级别大于-1, 那么就有丢失的风险。从源码上看,OpenSIPS是直接调用 syslog函数打印日志,按理不会因日志级别不同,而导致日志丢失的问题。这一点在下着实没能弄明白,要是有读者查明具体原由,烦请留言告知我,玉修在此谢过了(抱拳)。
 
4、解决办法:
 
首先我们解决Linux 系统层面 
 
4.1 解决使用“L_INFO”级别输出日志,高压下INFO级别日志容易丢失的问题
  • 方式一、去掉第一个参数“L_INFO”(将采用默认的 L_ERR级别),高压下打印也都正常
xlog("[$fU->$rU] Route to user [$tu] [ci:$ci] [xcid:$hdr(X-CID)]");
  • 方式二、指定OpenSIPS的默认日志级别:xlog_default_level=3或4, 这样高并发下基本正常,偶尔依旧会丢失部分日志
xlog_default_level=

xlog("L_INFO", "[$fU->$rU] Route to user [$tu] [ci:$ci] xcid:$hdr(X-CID)]");
xlog("[$fU->$rU] Route to user [$tu] [ci:$ci] [xcid:$hdr(X-CID)]");
4.2 解决日志输出速率达到rsyslog阈值的问题
     通过修改/etc/rsyslog.conf文件,调整rsyslog日志输出速率的配置。调整后需要重启rsyslog服务 : sudo service rsyslog restrat
  • 方式一、采用rsyslog同步模式处理日志【简单粗暴】
  • 注释掉下面一行即可
  • #$OmitLocalLogging on
  • 方式二、关闭ystemd Journal的速率限制【野蛮暴力】
  • 调整Systemd Journal的配置文件/etc/systemd/journald.conf
  • RateLimitInterval=0
  • RateLimitBurst=0
  •  
  • CentOS6下:修改/etc/rsyslog.conf文件,增加下面几行(CentOS6下没有Journal)
  • $SystemLogRateLimitInterval 0
  • $IMUXSockRateLimitInterval 0
  • 方式三、增大速率限制上限【温文尔雅】
  • 调整Systemd Journal的配置文件/etc/systemd/journald.conf
  • RateLimitInterval=5s      (默认值30s)
  • RateLimitBurst=20000   (默认值200)
  • 修改/etc/rsyslog.conf文件,增加下面几行(CentOS7下可不配置下面几项,CentOS6下没有Journal,只需增加下面前两项)
  • $SystemLogRateLimitInterval 5        (默认值5)
  • $SystemLogRateLimitBurst 20000   (默认值200)
  • $imjournalRatelimitInterval 600        (默认值600)
  • $imjournalRatelimitBurst 2400000   (默认值20000)
 
5、知识拓展
 
5.1 OpenSIPS 日志相关的参数介绍

参数
说明
默认值
log_level OpenSIPS输出的日志详细程度,值越大,代表输出日志越详细 [-3, 4]
xlog_default_level 是否需要将日志输出到启动OpenSIPS的控制台 -1
log_stderror 是否需要将日志输出到启动OpenSIPS的控制台 no
log_name 以守护进程方式运行OpenSIPS时,输出日志的进程名称,如默认是启动OpenSIPS的命令名 /usr/local/opensips/sbin/opensips argv[0]
log_facility 指定使用rsyslogd 的facility 输出日志,默认会将日志输出到 /var/log/messages 文件中 LOG_DAEMON
xlog_buf_size 用于缓存单行日志的空间大小,如果待输出的日志超过该阈值,OpenSIPS将丢弃,并输出一个 buffer overflow 的错误 4096
 

5.2 xlog函数介绍

函数:xlog([log_level, ]format_string)

    支持将format_string中的将伪变量(pseudo-variable)经过计算后打印出来。支持的日志级别参照syslog服务中的级别,具体可选值如下:
  • L_ALERT (-3)
  • L_CRIT (-2)
  • L_ERR (-1) - 如果不填写log_level,则默认选这个
  • L_WARN (1)
  • L_NOTICE (2)
  • L_INFO (3)
  • L_DBG (4)
样例:
xlog("Received $rm from $fu (callid: $ci)\n");
xlog("L_ERR", "key $var(username) not found in cache!\n");

5.3 syslog 功能测试

  • 使用下面命令,可以手动发送日志到syslog :
logger -p local1.info "hello world"
  • C语言代码测试syslog : ztest_rsyslog.c
#include<stdio.h>
#include<stdlib.h>
#include <syslog.h>
#include <unistd.h> void Info(void)
{
int i;
int j;
openlog("info",LOG_PID,LOG_LOCAL1);/*注意这里的数字1要跟 /etc/rsyslog.conf中的配置一致 local1.* /home/admin/z_test_rsyslog.log */
syslog(LOG_INFO, "hello %s","info log test");
for(j = ; j < ; j++) {
for(i = ; i < ; i++ ) {
syslog(LOG_INFO|LOG_LOCAL1, "hello not execute openlog for specify progress name : %s, loop=%d, index=%d", "info log test", j, i);
syslog(LOG_ERR|LOG_LOCAL1, "hello not execute openlog for specify progress name : %s, loop=%d, index=%d", "ERROR log test", j, i);
}
sleep();
}
} void Woring(void)
{
openlog("woring",LOG_PID,LOG_LOCAL1);
syslog(LOG_WARNING, "hello %s","warning log test");
} int main()
{
Woring();
Info();
closelog();
return ;
}

编译运行:

gcc -o ztest_rsyslog_bin ztest_rsyslog.c 
./ztest_rsyslog_bin
在/home/admin/z_test_rsyslog.log 文件中就会输出下面日志了
Aug  :: LPT0570 progress-name-info[]: hello info log test
Aug :: LPT0570 progress-name-woring[]: hello warning log test
Aug :: LPT0570 ztest_rsyslog_bin: hello not execute openlog for specify progress name : info log test, loop=, index=
Aug :: LPT0570 ztest_rsyslog_bin: hello not execute openlog for specify progress name : ERROR log test, loop=, index=
 相关文档:
 

OpenSIPS 2.4.2 高并发下,日志丢失怎么办的更多相关文章

  1. 为何要打印日志?C++在高并发下如何写日志文件(附源码)?

    为何要打印日志?让程序裸奔不是一件很快乐的事么? 有些BUG就像薛定谔的猫,具有波粒二象性,当你试图去观察它时它就消失了,当你不去观察它时,它又会出现.当你在测试人员面前赌咒发誓,亲自路演把程序跑一遍 ...

  2. 高吞吐koa日志中间件

    Midlog中间件 node服务端开发中少不了日志打点,而在koa框架下的日志打点在多进程环境中日志信息往往无法对应上下文,而且在高并发下直接进行写buffer操作(内核调用writev)也会造成内存 ...

  3. 高并发下的 Nginx 优化与负载均衡

    高并发下的 Nginx 优化   英文原文:Optimizing Nginx for High Traffic Loads 过去谈过一些关于Nginx的常见问题; 其中有一些是关于如何优化Nginx. ...

  4. 高并发下的Nginx优化

    高并发下的Nginx优化 2014-08-08 13:30 mood Nginx    过去谈过一些关于Nginx的常见问题; 其中有一些是关于如何优化Nginx. 很多Nginx新用户是从Apach ...

  5. 《高并发下的.NET》第2季 - 《memcached连接暴增案》第1集:问题表现

    在<.NET 5.0 背锅案>第7集-大结局之后,园子和 .NET 继续过上了幸福生活...剧情很美好,现实很残酷...现实是旧案刚结,新案立至,而且新案与旧案有关联,被迫继续拍剧,并对该 ...

  6. 《高并发下的.NET》第2季 - 故障公告:高并发下全线崩溃

    大家好,非常抱歉,在昨天下午(12月3日)的访问高峰,园子迎来更高的并发,在这样的高并发下,突发的数据库连接故障造成博客站点全线崩溃,由此给您带来很大的麻烦,请您谅解. 最近,我们一边在忙于AWS合作 ...

  7. php结合redis实现高并发下的抢购、秒杀功能

    抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:1 高并发对数据库产生的压力2 竞争状态下如何解决库存的正确减少("超卖"问题)对于第一个问题,已经很容易想到用缓存 ...

  8. 高并发下MySQL出现checking permissions

    在某些数据访问层框架中,会使用show full tables from test like 'demo',来检查数据库的状态.当数据库中表的数量较少时,并没有出现严重的问题.但是当数据库中的表数量多 ...

  9. EF+MySQL乐观锁控制电商并发下单扣减库存,在高并发下的问题

    下订单减库存的方式 现在,连农村的大姐都会用手机上淘宝购物了,相信电商对大家已经非常熟悉了,如果熟悉电商开发的同学,就知道在买家下单购买商品的时候,是需要扣减库存的,当然有2种扣减库存的方式, 一种是 ...

随机推荐

  1. Spring异常总结

    1.  Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean o ...

  2. 利用Python操作MySQL数据库

    前言 在工作中,我们需要经常对数据库进行操作,比如 Oracle.MySQL.SQL Sever 等,今天我们就学习如何利用Python来操作 MySQL 数据库. 本人环境:Python 3.7.0 ...

  3. Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependen

    Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependen ...

  4. 【Python 实例】面向对象 | 请输入一周中某天的名称的第一个字母来判断以下是星期几,如果第一个字母一样则继续判断第二个字母

    [Python 实例]面向对象 | 请输入一周中某天的名称的第一个字母来判断以下是星期几,如果第一个字母一样则继续判断第二个字母 题目: 解答: 运行结果: 题目: 请输入一周中某天的名称的第一个字母 ...

  5. “随手记”开发记录day06

    今天还是继续完成统计页面,完成的按钮有支出大头和收入来源的饼状图, 由于之前写过相关的代码,这次的任务对我们来说还挺简单的,没有出现什么太大的问题,

  6. Fault-Tolerance, Fast and Slow: Exploiting Failure Asynchrony in Distributed Systems

    本文(OSDI 18')主要介绍一种新的副本复制协议:SAUCR(场景可感知的更新与故障恢复).它是一种混合的协议: 在一定场景(正常情况)下:副本复制的数据缓存在内存中. 故障发生时(多个节点挂掉, ...

  7. Spring 中的反射与反射的原理

    作者:DeppWang.原文地址 在造轮子:实现一个简易的 Spring IoC 容器一文中提到 Spring 在创建 Bean 实例和依赖注入时使用了反射,本文来具体分析一下 Spring 中的反射 ...

  8. webgl实现发光线框(glow wireframe)效果

    在之前这篇文章, WebGL 单通道wireframe渲染 我们介绍了webgl如何实现单通道wireframe的效果. 本篇文章就是在此技术原理基础之上,来实现发光的wireframe效果. 要实现 ...

  9. Ubuntu用户都应该了解的快捷键

    无论我们使用什么操作系统还是什么软件,快捷键都是非常有用的,因为可以在启动应用程序或跳转到所需窗口,可以快速进行很多操作,而无需动鼠标到处点,节省时间和精力,提高效率. 就像在Windows中一样,U ...

  10. C#算法设计查找篇之05-二叉树查找

    二叉树查找(Binary Tree Search) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/706 访问. 二叉排 ...