freeswitch系列6數據庫

  1. 數據庫體系

freeswitch默認使用sqlite3數據庫,但也支持odbc使用其它數據庫,或者PostgreSQL這是支持的三類數據庫類型,從定義可以看出。

 

  1. typedef enum {  
  2.     SCDB_TYPE_CORE_DB,  
  3.     SCDB_TYPE_ODBC,  
  4.     SCDB_TYPE_PGSQL  
  5. } switch_cache_db_handle_type_t;  

 

 

switch_core_db.c實現sqlite核心數據庫,switch_odbc.c實現例如MySQL的數據庫,switch_pgsql.c實現PostgreSQL數據庫。另外switch_core_sqldb.c是數據庫抽象層,屏蔽調用具體的數據庫API,向外提供統一的數據庫操作API。

 

  1. typedef union {  
  2.     switch_core_db_t *core_db_dbh;  
  3.     switch_odbc_handle_t *odbc_dbh;  
  4.     switch_pgsql_handle_t *pgsql_dbh;  
  5. } switch_cache_db_native_handle_t;  

 

sqldb統一使用switch_cache_db_native_handle_t作爲數據庫實例,這是一個聯合體。

  1. 初始化
  1. SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switch_bool_t console, const char **err)  
  2. {  
  3.     runtime.max_db_handles = 50;  
  4.     runtime.db_handle_timeout = 5000000;  
  5.     runtime.dbname = "core";  
  6.     runtime.odbc_dbtype = DBTYPE_DEFAULT;  
  7.     runtime.dbname = NULL;  
  8.   
  9.     if (sqlite3_initialize() != SQLITE_OK) {  
  10.         *err = "FATAL ERROR! Could not initialize SQLite\n";  
  11.         return SWITCH_STATUS_MEMERR;  
  12.     }  
  13.   
  14.     if (switch_core_sqldb_start(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_SQL) ? SWITCH_TRUE : SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {  
  15.         *err = "Error activating database";  
  16.         return SWITCH_STATUS_FALSE;  
  17.     }  
  18.   
  19.     return SWITCH_STATUS_SUCCESS;  

 

在覈心初始化的時候,調用了sqlite初始化核心數據庫,然後調用sqldb_start啓動數據庫。

    1. 數據庫管理者

 

  1. static struct {  
  2.     switch_memory_pool_t *memory_pool;  
  3.     switch_thread_t *db_thread;  
  4.     int db_thread_running;  
  5.     switch_bool_t manage;  
  6.     switch_mutex_t *io_mutex;  
  7.     switch_mutex_t *dbh_mutex;  
  8.     switch_mutex_t *ctl_mutex;  
  9.     switch_cache_db_handle_t *handle_pool;  
  10.     uint32_t total_handles;  
  11.     uint32_t total_used_handles;  
  12.     switch_cache_db_handle_t *dbh;  
  13.     switch_sql_queue_manager_t *qm;  
  14.     int paused;  
  15. } sql_manager;  

 

sql_manager管理所有的數據庫,其中handle_pool是數據實例池,所以freeswitch同時支持多個不同的數據庫,比如某個數據庫使用sqlite,另一個數據庫使用mysql,dbh則指向當前使用的數據庫。qm是core.db使用的sql隊列,sql的執行是在獨立線程上執行的,其它數據庫比如sip,需要使用自己的sql_queue。

 

    1. 啓動數據庫

啓動數據庫,初始化一些sql_manage的成員,然後通過switch_core_sqldb_start打開數據庫,並重建表,最後啓動數據庫線程switch_core_sqldb_start_thread。

 

  1. switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_t manage)  
  2. {  
  3.     switch_threadattr_t *thd_attr;  
  4.   
  5.     sql_manager.memory_pool = pool;  
  6.     sql_manager.manage = manage;  
  7.   
  8.     if (!sql_manager.manage) goto skip;  
  9.   
  10.  top:     
  11.   
  12.     /* 打開數據庫 */  
  13.     if (switch_core_db_handle(&sql_manager.dbh) != SWITCH_STATUS_SUCCESS) {  
  14.         switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n");  
  15.         return SWITCH_STATUS_FALSE;  
  16.     }  
  17.   
  18.   
  19.     switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening DB\n");  
  20.   
  21.     //執行清空表、創建表等操作  
  22.     switch (sql_manager.dbh->type) {  
  23.     case SCDB_TYPE_PGSQL:  
  24.     case SCDB_TYPE_ODBC:  
  25.         break;  
  26.     case SCDB_TYPE_CORE_DB:  
  27.         {  
  28.         }  
  29.         break;  
  30.     }  
  31.   
  32.  skip:  
  33.   
  34.     if (sql_manager.manage) {  
  35.         switch_threadattr_create(&thd_attr, sql_manager.memory_pool);  
  36.         switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);  
  37.         switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);  
  38.         //啓動數據庫線程  
  39.         switch_core_sqldb_start_thread();  
  40.         switch_thread_create(&sql_manager.db_thread, thd_attr, switch_core_sql_db_thread, NULL, sql_manager.memory_pool);  
  41.   
  42.     }  
  43.   
  44.     switch_cache_db_release_db_handle(&sql_manager.dbh);  
  45.   
  46.     return SWITCH_STATUS_SUCCESS;  

 

 

  1. 打開數據庫
    1. switch_core_db_handle
  1. SWITCH_DECLARE(switch_status_t) _switch_core_db_handle(switch_cache_db_handle_t **dbh, const char *file, const char *func, int line)  
  2. {  
  3.     switch_status_t r;  
  4.     char *dsn;  
  5.       
  6.     if (!sql_manager.manage) {  
  7.         return SWITCH_STATUS_FALSE;  
  8.     }  
  9.   
  10.     if (!zstr(runtime.odbc_dsn)) {  
  11.         dsn = runtime.odbc_dsn;  
  12.     } else if (!zstr(runtime.dbname)) {  
  13.         dsn = runtime.dbname;  
  14.     } else {  
  15.         dsn = "core";  
  16.     }  
  17.   
  18.     if ((r = _switch_cache_db_get_db_handle_dsn(dbh, dsn, file, func, line)) != SWITCH_STATUS_SUCCESS) {  
  19.         *dbh = NULL;  
  20.     }  
  21.       
  22.     return r;  
  23. }  

 

打開數據庫實際就是使用全局配置runtime.odbc_dsn作爲路徑,這個參數在解析配置的時候有講過,這裏不再說明,然後調用 _switch_cache_db_get_db_handle_dsn底層接口去打開數據庫。

    1. _switch_cache_db_get_db_handle_dsn
  1. SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle_dsn(switch_cache_db_handle_t **dbh, const char *dsn,   
  2.                                                                    const char *file, const char *func, int line)  
  3. {  
  4.     switch_cache_db_connection_options_t connection_options = { {0} };  
  5.     switch_cache_db_handle_type_t type;  
  6.     char tmp[256] = "";  
  7.     char *p;  
  8.     switch_status_t status = SWITCH_STATUS_FALSE;  
  9.     int i;  
  10.   
  11.     if (!strncasecmp(dsn, "pgsql://", 8)) {  
  12.         type = SCDB_TYPE_PGSQL;  
  13.         connection_options.pgsql_options.dsn = (char *)(dsn + 8);  
  14.     } else if (!strncasecmp(dsn, "sqlite://", 9)) {  
  15.         type = SCDB_TYPE_CORE_DB;  
  16.         connection_options.core_db_options.db_path = (char *)(dsn + 9);  
  17.     } else if ((!(i = strncasecmp(dsn, "odbc://", 7))) || strchr(dsn+2, ':')) {  
  18.         type = SCDB_TYPE_ODBC;  
  19.         ... 
  20.     } else {  
  21.         type = SCDB_TYPE_CORE_DB;  
  22.         connection_options.core_db_options.db_path = (char *)dsn;  
  23.     }  
  24.   
  25.     status = _switch_cache_db_get_db_handle(dbh, type, &connection_options, file, func, line);  
  26.   
  27.     if (status != SWITCH_STATUS_SUCCESS) *dbh = NULL;  
  28.   
  29.     return status;  
  30. }  

_switch_cache_db_get_db_handle_dsn接口解析dsn得到數據庫類型,然後調用最底層接口去打開連接數據庫_switch_cache_db_get_db_handle。

    1. _switch_cache_db_get_db_handle
  1. SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_handle_t **dbh,                                                         switch_cache_db_handle_type_t type,                                                             switch_cache_db_connection_options_t *connection_options,                                                               const char *file, const char *func, int line)  
  2. {  
  3.     switch_cache_db_handle_t *new_dbh = NULL;  
  4.   
  5. //如果存在實例,直接使用,否則創建新實例  
  6.     if ((new_dbh = get_handle(db_str, db_callsite_str, thread_str))) {  
  7.         switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10,  
  8.                           "Reuse Unused Cached DB handle %s [%s]\n", new_dbh->name, switch_cache_db_type_name(new_dbh->type));  
  9.     } else {  
  10.         switch_core_db_t *db = NULL;  
  11.         switch_odbc_handle_t *odbc_dbh = NULL;  
  12.         switch_pgsql_handle_t *pgsql_dbh = NULL;  
  13. //根據類型,調用相關的NEW函數創建實例  
  14.         switch (type) {  
  15.         case SCDB_TYPE_PGSQL:  
  16.             {  
  17.                 if ((pgsql_dbh = switch_pgsql_handle_new(connection_options->pgsql_options.dsn))) {  
  18.                     if (switch_pgsql_handle_connect(pgsql_dbh) != SWITCH_PGSQL_SUCCESS) {  
  19.                         switch_pgsql_handle_destroy(&pgsql_dbh);  
  20.                     }  
  21.                 }  
  22.             }  
  23.             break;  
  24.         case SCDB_TYPE_ODBC:  
  25.             {  
  26.                 if ((odbc_dbh = switch_odbc_handle_new(connection_options->odbc_options.dsn,  
  27.                                                        connection_options->odbc_options.user, connection_options->odbc_options.pass))) {  
  28.                     if (switch_odbc_handle_connect(odbc_dbh) != SWITCH_ODBC_SUCCESS) {  
  29.                         switch_odbc_handle_destroy(&odbc_dbh);  
  30.                     }  
  31.                 }  
  32.             }  
  33.             break;  
  34.         case SCDB_TYPE_CORE_DB:  
  35.             {  
  36.                 db = switch_core_db_open_file(connection_options->core_db_options.db_path);  
  37.             }  
  38.             break;  
  39.   
  40.         default:  
  41.             goto end;  
  42.         }  
  43. //創建並添加操作實例  
  44.         new_dbh = create_handle(type);  
  45.   
  46.         if (db) {  
  47.             new_dbh->native_handle.core_db_dbh = db;  
  48.         } else if (odbc_dbh) {  
  49.             new_dbh->native_handle.odbc_dbh = odbc_dbh;  
  50.         } else {  
  51.             new_dbh->native_handle.pgsql_dbh = pgsql_dbh;  
  52.         }  
  53.   
  54.         add_handle(new_dbh, db_str, db_callsite_str, thread_str);  
  55.     }  
  56.   
  57.  end:  
  58.   
  59.     *dbh = new_dbh;  
  60.     return *dbh ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;  
  61. }  

 

流程簡化後如上,先看操作實例是否已經在池,沒有再創建,創建的時候根據類型創建實例,然後添加到池。這裏不同類型創建的實例類型不同,但是會統一到switch_cache_db_handle,

作爲native_handle成員,這個數據結構纔是sql管理層使用的。

 

  1. struct switch_cache_db_handle {  
  2.     char name[CACHE_DB_LEN];  
  3.     switch_cache_db_handle_type_t type;  
  4.     switch_cache_db_native_handle_t native_handle;  
  5.     time_t last_used;  
  6.     switch_mutex_t *mutex;  
  7.     switch_mutex_t *io_mutex;  
  8.     switch_memory_pool_t *pool;  
  9.     int32_t flags;  
  10.     unsigned long hash;  
  11.     unsigned long thread_hash;  
  12.     char creator[CACHE_DB_LEN];  
  13.     char last_user[CACHE_DB_LEN];  
  14.     uint32_t use_count;  
  15.     uint64_t total_used_count;  
  16.     struct switch_cache_db_handle *next;  
  17. }; 
  1. 啓動數據庫線程
    1. switch_core_sqldb_start_thread
  1. static void switch_core_sqldb_start_thread(void)  
  2. {  
  3.     switch_mutex_lock(sql_manager.ctl_mutex);  
  4.     if (sql_manager.manage) {  
  5.         if (!sql_manager.qm) {  
  6.             char *dbname = runtime.odbc_dsn;  
  7.   
  8.             if (zstr(dbname)) {  
  9.                 dbname = runtime.dbname;  
  10.                 if (zstr(dbname)) {  
  11.                     dbname = "core";  
  12.                 }  
  13.             }  
  14.   
  15.             switch_sql_queue_manager_init_name("CORE",  
  16.                                                &sql_manager.qm,  
  17.                                                4,  
  18.                                                dbname,  
  19.                                                SWITCH_MAX_TRANS,  
  20.                                         runtime.core_db_pre_trans_execute,  
  21.                                         runtime.core_db_post_trans_execute,  
  22.                                        runtime.core_db_inner_pre_trans_execute, 
  23.                                        runtime.core_db_inner_post_trans_execute);  
  24.   
  25.         }  
  26.         switch_sql_queue_manager_start(sql_manager.qm);  
  27.     } else {  
  28.         switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL is not enabled\n");  
  29.     }  
  30.     switch_mutex_unlock(sql_manager.ctl_mutex);  

 

開始線程實質上是創建sql隊列,這裏需要綁定一組sql操作函數,pre_trans、post_trans等。調用switch_sql_queue_manager_init_name創建隊列,然後調用switch_sql_queue_manager_start啓動隊列。注意,這裏都是在創建core.db數據庫,sip的數據庫不是在這裏。

    1. switch_sql_queue_manager_start
  1. SWITCH_DECLARE(switch_status_t) switch_sql_queue_manager_start(switch_sql_queue_manager_t *qm)  
  2. {  
  3.     switch_threadattr_t *thd_attr;  
  4.   
  5.     if (!qm->thread_running) {  
  6.         switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Starting SQL thread.\n", qm->name);  
  7.         switch_threadattr_create(&thd_attr, qm->pool);  
  8.         switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);  
  9.         switch_threadattr_priority_set(thd_attr, SWITCH_PRI_NORMAL);  
  10.         switch_thread_create(&qm->thread, thd_attr, switch_user_sql_thread, qm, qm->pool);  
  11.         return SWITCH_STATUS_SUCCESS;  
  12.     }  
  13.   
  14.     return SWITCH_STATUS_FALSE;  
  15. }  

 

這裏會創建隊列的線程,執行函數爲switch_user_sql_thread。

    1. switch_user_sql_thread

switch_user_sql_thread@sql_manager.qm  

{  

do_trans  

{  

switch_queue_trypop(qm->sql_queue[i], &pop);  

switch_cache_db_execute_sql  

}  

}  

sql線程簡化後,就是從隊列裏一直讀sql,然後去執行。

  1. switch_core_sql_db_thread

啓動數據庫的時候,除了調用switch_core_sqldb_start_thread去創建隊列外,還啓動一條線程switch_core_sql_db_thread,注意這和switch_user_sql_thread是不一樣的。

 

  1. static void *SWITCH_THREAD_FUNC switch_core_sql_db_thread(switch_thread_t *threadvoid *obj)  
  2. {  
  3.     int sec = 0, reg_sec = 0;;  
  4.   
  5.     sql_manager.db_thread_running = 1;  
  6.   
  7.     while (sql_manager.db_thread_running == 1) {  
  8.         if (++sec == SQL_CACHE_TIMEOUT) {  
  9.             sql_close(switch_epoch_time_now(NULL));       
  10.             sec = 0;  
  11.         }  
  12.   
  13.         if (switch_test_flag((&runtime), SCF_USE_SQL) && ++reg_sec == SQL_REG_TIMEOUT) {  
  14.             switch_core_expire_registration(0);  
  15.             reg_sec = 0;  
  16.         }  
  17.         switch_yield(1000000);  
  18.     }  
  19.   
  20.   
  21.     return NULL;  
  22. }

 

這條線程就是定時檢查一些超時狀態,沒有特別重要的業務。

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