无论你是经验丰富的OpenSIPS管理员,或者你仅仅想找到为什么ACK消息在你的网络中循环发送,唯一可以确定的是:我们或早或晚会需要OpenSIPS提供数据来回答以下问题

  • OpenSIPS运行了多久?
  • 我们是否被恶意流量攻击了?
  • 我们的平台处理了多少个来自运营商的无效SIP包
  • 在流量峰值时,OpenSIPS是否拥有足够的内存来支撑运行

幸运的是,OpenSIPS提供内置的统计支持,来方便我们快速解决以上问题。详情可以查看OpenSIPS统计接口。在本篇文章中,我们将会了解统计引擎,但是,什么是引擎?

统计引擎

总的来说,下图就是OpenSIPS引擎的样子。

统计引擎内置于OpenSIPS。它管理所有的统计数据,并且暴露一个标准的CRUD操作接口给所有的模块,让模块可以推送或者管理他们自己的统计数据。

一下有三种方式来和统计引擎进行交互

  • 直接通过脚本访问。如通过$script(my-stat)变量
  • 使用HTTP请求来访问
  • 使用opensipsctl fifo命令

统计引擎是非常灵活并且可以通过不同方式与其交互,那么它怎么能让我们的使用变得方便呢?下面的一些建议,能够让你全面的发挥静态统计引擎的能力,来增强某些重要的层面。

系统开发维护

当你处理OpenSIPS的DevOps时,你经常需要监控OpenSIPS的一些运行参数。你的关注点不同,那么你就需要监控不同的方面,例如SIP事务、对话、内存使用、系统负载等等

下面是OpenSIPS统计分组的一个概要,以及组内的每一个统计值,详情可以参考wiki

统计简介

假如我们想通过sipp对我们的平台进行流量测试,我们想压测期间观测当前的事务、对话、共享内存的值变化。或者我们我们有了一个新的SIP提供商,他们每天早上9点会开始向我们平台推送数据,我们需要监控他们的推送会对我们系统产生的影响。

你可以在OpenSIPS实例中输入以下指令:

watch -n0.2 'opensipsctl fifo get_statistics inuse_transactions dialog: shmem:'

注意 get_statistics命令即可以接受一个统计值项,也可以接受一个统计组的项。统计组都是以冒号(:)结尾。

与递增的统计指标进行交互

统计指标看起来相同,实际上分为两类

  • 累计值。累计是一般是随着时间增长,例如rcv_requests, processed_dialogs,表示从某个时间点开始累计收到或者处理了多少个任务
  • 计算值。计算值一般和系统运行负载有关,和时间无关。例如active_dialogs, real_used_size, 这些值都是由内部函数计算出来的计算值

一般来说,脚本中定义的统计值都是递增的,OpenSIPS无法重新计算它,只能我们自己来计算或者维护它的值。

以下方式可以快速查看计算值类的统计项

opensipsctl fifo list_statistics

某些场景,你可能需要周期性的重置累计值类的统计项。例如你可以只需要统计当天的processed_dialogsdaily_routed_minutes,那么你只需要设置一个定时任务,每天0点,重置这些统计值。

opensipsctl fifo reset_statistics processed_dialogs

在脚本中自定义统计项

在脚本中自定义统计项是非常简单的,只需要做两步

  1. 加载statistics.so模块
  2. 在某些位置调用函数, update_stat("daily_routed_minutes", "+1")

实战:脚本中有许多的自定义统计项

  • 统计每天收到的SIP消息的请求方式, 以及处理的消息长度
  • 每隔24小时,以JSON的形式,将消息写到SIP服务器
# 设置统计组
modparam("statistics", "stat_groups", "method, packet")

# 请求路由
route {
    ...
    update_stat("method:$rm", "+1");
    update_stat("packet:count", "+1");
    update_stat("packet:total_size", "$ml") # message length
    ...
}

# 响应路由
onreply_route {
    update_stat("packet:count", "+1");
    update_stat("packet:total_size", "$ml")
}

# 定时器路由,定时通过HTTP发请求
timer_route [daily_stat_push, 86400] {
    $json(all_stats) := "{\"method\": {}, \"packet\": {}}";
    # pack and clear all method-related statistics
    stat_iter_init("method", "iter");
    while (stat_iter_next("$var(key)", "$var(val)", "iter")) {
        $json(all_stats/method/$var(key)) = $var(val);
        reset_stat("$var(key)");
    }
    # pack and clear all packet-related statistics
    stat_iter_init("packet", "iter");
    while (stat_iter_next("$var(key)", "$var(val)", "iter")) {
        $json(all_stats/packet/$var(key)) = $var(val);
        reset_stat("$var(key)");
    }
    # push the data to our web server
    if (!rest_post("https://WEB_SERVER", "$json(all_stats)", , "$var(out_body)", , "$var(status)"))
        xlog("ERROR: during HTTP POST, $json(all_stats)\n");
    
    if ($var(status) != 200)
        xlog("ERROR: web server returned $var(status), $json(all_stats)\n");
}