OpenSIPS 2.4.2 高併發下,日誌丟失怎麼辦

玉修發表於2020-08-29
 
問題年年有,今年特別多。最近公司對呼叫中心平臺做了大幅度重構,基於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 restrat 、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=20000   (預設值200)
  • 同時修改/etc/rsyslog.conf檔案,增加下面幾行(CentOS6下沒有Journal,只需增加下面的前兩項)
  • $SystemLogRateLimitInterval 5       (預設值5,      CentOS7可選)
  • $SystemLogRateLimitBurst 20000  (預設值200,   CentOS7可選)
  • $imjournalRatelimitInterval 5           (預設值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

 

 相關文件:
 

相關文章