OpenSIPS 2.4.2 高並發下,日志丟失怎么辦


 
問題年年有,今年特別多。最近公司對呼叫中心平台做了大幅度重構,基於OpenSIPS實現的會話管理服務,在高並發壓測過程中,發現OpenSIPS的日志居然出現丟失的情況,簡直讓我食不知味,困惑不已。
 
最終雖解決了問題,但內部個中原理性尚未徹底弄明白,今日先記錄在此,供同道中人參考,更希望有識之士能一解玉修心中之惑……
 
閑話不多說,來、來、來,翠花,上酸菜!!!!!!
 
1、運行環境配置
  • CentOS      7.4
  • Rsyslogd    8.24.0
  • OpenSIPS  2.4.2
1.1 OpenSIPS日志參數配置如下:
log_level=3
log_stderror=no
log_facility=LOG_LOCAL0
log_name="/usr/local/opensips/sbin/opensips"
xlog_buf_size=409600

 

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 19 21:42:55 uat16599 rsyslogd: imjournal: 4188 messages lost due to rate-limiting
Aug 19 21:52:56 uat16599 rsyslogd: imjournal: 5108 messages lost due to rate-limiting
Aug 19 22:02:57 uat16599 rsyslogd: imjournal: 5073 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則是采用同步模式。
    更多關於Rsyslogd的知識請參見: https://www.rsyslog.com/doc/v8-stable/configuration/index.html
    
    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=3
 
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閾值的問題
     通過調整rsyslog和Systemd Journal日志輸出速率相關的配置。
     調整后需要重啟相關服務 : sudo service rsyslog restart 、sudo systemctl restart systemd-journald
     目前想到下面三種解決方案,其中后兩種方法下$OmitLocalLogging=on
  • 方式一、采用rsyslog同步模式處理日志【簡單粗暴】
  • 修改/etc/rsyslog.conf文件,注釋掉下面一行即可
  • #$OmitLocalLogging on

 

  • 方式二、關閉ystemd Journal的速率限制【野蠻暴力】
  • 調整Systemd Journal的配置文件/etc/systemd/journald.conf (CentOS6沒有)
  • RateLimitInterval=0
  • RateLimitBurst=0
  •  
  • 同時修改/etc/rsyslog.conf文件,增加下面幾行(CentOS6下沒有Journal,只需增加下面的最后兩項)
  • $imjournalRatelimitInterval 0        (CentOS7必選)
  • $SystemLogRateLimitInterval 0   (CentOS7可選)
  • $IMUXSockRateLimitInterval 0    (CentOS7可選)

 

  • 方式三、增大速率限制上限【溫文爾雅】
  • 調整Systemd Journal的配置文件/etc/systemd/journald.conf (CentOS6沒有)
  • RateLimitInterval=5s      (默認值30s)
  • RateLimitBurst=4000     (默認值200)
  • 同時修改/etc/rsyslog.conf文件,增加下面幾行(CentOS6下沒有Journal,只需增加下面的前兩項)
  • $SystemLogRateLimitInterval 5       (默認值5,      CentOS7可選)
  • $SystemLogRateLimitBurst 4000    (默認值200,   CentOS7可選)
  • $imjournalRatelimitInterval 25         (默認值600,   CentOS7必選:不配置時,輸出部分日志后,需要等10分鍾才能再寫入)
  • $imjournalRatelimitBurst 20000      (默認值20000,CentOS7必選:貌似修改該值不起作用)
 
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 = 0; j < 100000; j++) {
        for(i = 0; i < 35; 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(1);
    }
}

void Woring(void)
{
    openlog("woring",LOG_PID,LOG_LOCAL1);
    syslog(LOG_WARNING, "hello %s","warning log test");
}

int main()
{
    Woring();
    Info();
    closelog();
    return 0;
}

編譯運行:

gcc -o ztest_rsyslog_bin ztest_rsyslog.c 
./ztest_rsyslog_bin
在/home/admin/z_test_rsyslog.log 文件中就會輸出下面日志了
Aug 26 10:58:37 LPT0570 progress-name-info[30326]: hello info log test
Aug 26 10:58:37 LPT0570 progress-name-woring[30326]: hello warning log test
Aug 26 18:59:36 LPT0570 ztest_rsyslog_bin: hello not execute openlog for specify progress name : info log test, loop=0, index=0
Aug 26 18:59:36 LPT0570 ztest_rsyslog_bin: hello not execute openlog for specify progress name : ERROR log test, loop=0, index=0

 

 相關文檔:
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM