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的調試級別。如果調用時傳入一個整型參數,那麼它將設置當前的的調試級別爲參數所提供的值:

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。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章