OpenSIPS 3.1 开发手册(四)-- 管理接口

https://www.opensips.org/Documentation/Development-Manual

目录

10.  管理接口 API

11.  统计API


10.  管理接口 API

        管理接口(MI)是一个抽象层,用于控制及监视OpenSIPS。MI接口支持多种实际的后台(比如:FIFO、数据报、JSON-RPC、 XML-RPC)。由于它的模块化,逻辑数据结构与传输数据结构的清晰分离,开发者只需要定义数据的析构函数,然后由脚本编写者选择使用哪种传输方式来控制OpenSIPS。

        MI大量使用JSON来与传输层对接:
 

  • 接口向用户定义的MI函数提供JSON (mi_item_t)格式的输入,携带要求的字段
  • MI函数返回JSON,然后由传输层将它转换为适当的表现形式


        在此基础上,我们将聚焦于核心MI函数,特别是log_level MI函数。请注意模块也可以导出MI函数(通常他们也是这样做的),关于这方面的信息,请模块开发MI函数相关的主题。
        用于导出MI函数的数据结构,通常在 mi/mi.h 里:

typedef struct mi_export_ {
    /* the name of the function users will invoke it from their transport of choice */
    char *name;

    /* short description of the usage of this function */
    char *help;

    /* flags for this function.  The current options are :
         - MI_ASYNC_RPL_FLAG - the function has an asynchronous behaviour (e.g: MI functions that send out SIP messages and do not wait for their reply)
         - MI_NO_INPUT_FLAG - the function does not receive any parameters
    */
    unsigned int flags;

    /* an initialization function to be called by OpenSIPS (one time) */
    mi_child_init_f *init_f;

    /* the possible combinations of arguments which may be supplied to this function, along with their handlers */
    mi_recipe_t recipes[MAX_MI_RECIPES];
} mi_export_t;

/* Example of core MI exported function */
static mi_export_t mi_core_cmds[] = {
...
    { "log_level", "gets/sets the per process or global log level in OpenSIPS",
        0, 0, {
        {w_log_level,   {0}},
        {w_log_level_1, {"level", 0}},
        {w_log_level_2, {"level", "pid", 0}},
        {EMPTY_MI_RECIPE}
        }
    },
...
};

/* For exporting the populated array of MI functions
Parameters :
      mod_name : the name of the module exporting these functions
      mis : the array of exported MI functions
Returns :
      0 on success, negative in case of error
*/
int register_mi_mod( char *mod_name, mi_export_t *mis);

/* Example of usage */
if (register_mi_mod( "core", mi_core_cmds) < 0) {
      LM_ERR("unable to register core MI cmds\n");
      return -1;
}



用于实现MI功能的数据结构,通常可以在里mi/mi.h 和mi/item.h 找:

/*
Parameters:
      params : the JSON tree which includes the input arguments
      async_hdl : if the function has async capabilities, this is its async handler

Returns:
      An mi_response_t JSON structure with the requested data or execution result
*/
typedef mi_response_t *(mi_cmd_f)(const mi_params_t *params,
                                        struct mi_handler *async_hdl);

/* Both mi_item_t and mi_response_t point to the same cJSON struct, see lib/cJSON.h */
typedef struct cJSON
{
    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *next;
    struct cJSON *prev;
    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
    struct cJSON *child;

    /* The type of the item, as above. */
    int type;

    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
    char *valuestring;
    /* The item's number, if type==cJSON_Number */
    int valueint;
    /* The item's number, if type==cJSON_Number */
    double valuedouble;

    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
    char *string;
} cJSON;

为了输入JSON,mi/item.h 提供了以下函数:

/* Use for creating a new output reply tree
Parameters :
      code : success code for this tree ( >=200<300 for success, anything else for errors )
      reason : string reasons representation for the code
      reason_len : length of the reason parameter
Returns :
      A new mi_root tree, or NULL in case of error. Note that this function will allocate the node in PKG and it typically has to be returned - the freeing will be done in the MI core, after the output tree is written by the transport module */
struct mi_root *init_mi_tree(unsigned int code, char *reason, int reason_len);

/* Adding a new child node to our tree - typically first called to mi_root->node.kids
Parameters :
      parent : the parent node for our newly added node
      flags : Current options are :
                   MI_DUP_NAME : the name of this node needs to be duplicated in PKG
                   MI_DUP_VALUE : the value of the current node needs to be duplicated in PKG
      name : the name of the current node
      name_len : length of the node's name
      value : the value of the current node
      value_len : length of the node's value
*/
struct mi_node *add_mi_node_child(struct mi_node *parent, int flags,
        char *name, int name_len, char *value, int value_len);
/* Adding a new sibling node to one of our nodes
Parameters :
      brother : the brother node for our newly added node
      flags : Current options are :
                   MI_DUP_NAME : the name of this node needs to be duplicated in PKG
                   MI_DUP_VALUE : the value of the current node needs to be duplicated in PKG
      name : the name of the current node
      name_len : length of the node's name
      value : the value of the current node
      value_len : length of the node's value
*/
struct mi_node *add_mi_node_sibling(struct mi_node *brother, int flags,
        char *name, int name_len, char *value, int value_len);
/* Adding a new attribute to one of our nodes
      node : the node we will be adding the key-value attribute to
      flags : Current options are :
                   MI_DUP_NAME : the name of this attribute needs to be duplicated in PKG
                   MI_DUP_VALUE : the value of the current attribute needs to be duplicated in PKG
      name : the name of the current attribute
      name_len : length of the node's attribute name
      value : the value of the current value
      value_len : length of the node's attribute value
*/
struct mi_attr *add_mi_attr(struct mi_node *node, int flags,                                      
        char *name, int name_len, char *value, int value_len)


        接下来,我们继续实现debug MI函数。不带参数调用时,返回OpenSIPS的调试级别。如果调用时传入一个整型参数,那么它将设置当前的的调试级别为参数所提供的值:

扫描二维码关注公众号,回复: 11576882 查看本文章
struct mi_root *mi_debug(struct mi_root *cmd, void *param)
{
      struct mi_root *rpl_tree;
      struct mi_node *node;
      char *p;
      int len;
      int new_debug;

      /* check the kids member of our root node -
      if the input root node has kids, our command was called with parameters */
      node = cmd->node.kids;
      if (node!=NULL) {
            /* take the node's value and convert it to int, to make sure the parameter is valid */
            if (str2sint( &node->value, &new_debug) < 0)
                  /* if failed to convert to int, still return a RPL tree with an >=400 code and reason */
                  return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
      } else
            new_debug = *debug;

      /* all is good so far, initialize a new output ROOT tree which has a 200 OK code & reason */
      rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
      if (rpl_tree==0)
              return 0;

      p = sint2str((long)new_debug, &len);
      /* add a new node to our output tree, which the current debug level */
      node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE,
             MI_SSTR("DEBUG"),p, len);
      if (node==0) {
              free_mi_tree(rpl_tree);
              return 0;
      }

      /* if all was successful, overwrite the actual debug level, and return our tree */
      *debug = new_debug;
      return rpl_tree;
}

    如果需要MI相关的进一步信息,或opensipsctl 工具执行MI命令的实例说明,请参考以下页面:https://www.opensips.org/Documentation/Interface-MI-2-2


11.  统计API

        OpenSIPS 提供了一套统计API,可以在核心或模块中使用。统计本质上就是一些计数器,可以由OpenSIPS 内部递增或递减,并允许外部世界获取这些信息(通过MI接口),以了解OpenSIPS 的负载、健康状态等等。

        使用OpenSIPS 统计API而不是常规的计数器,优势在于:
 

  • 可以从MI接口轻松获取
  • 从支持的架构,统计API不使用显式的锁(其一致性由嵌入的汇编代码保证),因此性能更高


        扩展统计API最重要的数据结构声明在 statistics.h 中:

typedef struct stat_export_ {
        char* name;                /* null terminated statistic name */
        unsigned short flags;      /* flags */
        stat_var** stat_pointer;   /* pointer to the variable's mem location *
                                    * NOTE - it's in shm mem */
} stat_export_t;


比如说,OpenSIPS导出的核心统计信息定义在以下数组中:

stat_var* rcv_reqs;
stat_var* rcv_rpls;
stat_var* fwd_reqs;
stat_var* fwd_rpls;
stat_var* drp_reqs;
stat_var* drp_rpls;
stat_var* err_reqs;
stat_var* err_rpls;
stat_var* bad_URIs;
stat_var* unsupported_methods;
stat_var* bad_msg_hdr;

stat_export_t core_stats[] = {
        {"rcv_requests" ,         0,  &rcv_reqs              },
        {"rcv_replies" ,          0,  &rcv_rpls              },
        {"fwd_requests" ,         0,  &fwd_reqs              },
        {"fwd_replies" ,          0,  &fwd_rpls              },
        {"drop_requests" ,        0,  &drp_reqs              },
        {"drop_replies" ,         0,  &drp_rpls              },
        {"err_requests" ,         0,  &err_reqs              },
        {"err_replies" ,          0,  &err_rpls              },
        {"bad_URIs_rcvd",         0,  &bad_URIs              },
        {"unsupported_methods",   0,  &unsupported_methods   },
        {"bad_msg_hdr",           0,  &bad_msg_hdr           },
        {"timestamp",  STAT_IS_FUNC, (stat_var**)get_ticks   },
        {0,0,0}
};

   从上面代码结构中我们可以看出,统计既可以是一个简单的计数器(比如说rcv_requests),也可以是一个函数。当开发人员在最终输出之前,需要对原始数据进行处理时,函数就派上用场了。 



        在定义你希望导出的统计数组之后,应当用以下函数导出,以供其它人访问:

/*
Parameters :
      module - a string describing the module the current statistics belong to. Will be used when fetching the statistics via MI
      stats - the statistics to be registered
Returns :
      0 in case of success, negative in case of error
*/
int register_module_stats(char *module, stat_export_t *stats;


注意:register_module_stats 不仅导出统计信息,而且在SHM里分配内存,因为它们对所有OpenSIPS 进程都是可见的


    重要注意事项:上述所有统计相关的函数,必须在attendant 进程的上下文中调用,而且是在fork之前。



        运行时,开发者可以通过以下函数操作统计:

/*
Parameters :
      var : the statistics to be updated
      n : the value ( if positive -> stat will be increment. negative -> stat will be decremented )
*/
void update_stat(stat_var* var, int n);

/*
Parameters :
      var : the statistics to be reseted
*/
void reset_stat(stat_var* var);

/*
Parameters :
      var : the statistics to be fetched
Returns :
      statistic value
*/
unsigned long get_stat_val(stat_var* var)

    所有统计相关的代码应当由宏#ifdef STATISTICS 保护,因为统计并不是OpenSIPS 核心的必要组成部分,它可以在menuconfig 中禁用。


    要获取mynewmod 模块导出的mynewstat 统计信息,可以这样使用opensipsctl :opensipsctl fifo get_statistics mynewmod mynewstat;要获取mynewmod 模块导出的的所有统计信息,要以通过:opensipsctl fifo get_statistics mynewmod。


猜你喜欢

转载自blog.csdn.net/yetyongjin/article/details/106619087