- 數據庫體系
freeswitch默認使用sqlite3數據庫,但也支持odbc使用其它數據庫,或者PostgreSQL這是支持的三類數據庫類型,從定義可以看出。
- typedef enum {
- SCDB_TYPE_CORE_DB,
- SCDB_TYPE_ODBC,
- SCDB_TYPE_PGSQL
- } 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。
- typedef union {
- switch_core_db_t *core_db_dbh;
- switch_odbc_handle_t *odbc_dbh;
- switch_pgsql_handle_t *pgsql_dbh;
- } switch_cache_db_native_handle_t;
sqldb統一使用switch_cache_db_native_handle_t作爲數據庫實例,這是一個聯合體。
- 初始化
- SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switch_bool_t console, const char **err)
- {
- runtime.max_db_handles = 50;
- runtime.db_handle_timeout = 5000000;
- runtime.dbname = "core";
- runtime.odbc_dbtype = DBTYPE_DEFAULT;
- runtime.dbname = NULL;
- if (sqlite3_initialize() != SQLITE_OK) {
- *err = "FATAL ERROR! Could not initialize SQLite\n";
- return SWITCH_STATUS_MEMERR;
- }
- if (switch_core_sqldb_start(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_SQL) ? SWITCH_TRUE : SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
- *err = "Error activating database";
- return SWITCH_STATUS_FALSE;
- }
- return SWITCH_STATUS_SUCCESS;
- }
在覈心初始化的時候,調用了sqlite初始化核心數據庫,然後調用sqldb_start啓動數據庫。
-
- 數據庫管理者
- static struct {
- switch_memory_pool_t *memory_pool;
- switch_thread_t *db_thread;
- int db_thread_running;
- switch_bool_t manage;
- switch_mutex_t *io_mutex;
- switch_mutex_t *dbh_mutex;
- switch_mutex_t *ctl_mutex;
- switch_cache_db_handle_t *handle_pool;
- uint32_t total_handles;
- uint32_t total_used_handles;
- switch_cache_db_handle_t *dbh;
- switch_sql_queue_manager_t *qm;
- int paused;
- } sql_manager;
sql_manager管理所有的數據庫,其中handle_pool是數據實例池,所以freeswitch同時支持多個不同的數據庫,比如某個數據庫使用sqlite,另一個數據庫使用mysql,dbh則指向當前使用的數據庫。qm是core.db使用的sql隊列,sql的執行是在獨立線程上執行的,其它數據庫比如sip,需要使用自己的sql_queue。
-
- 啓動數據庫
啓動數據庫,初始化一些sql_manage的成員,然後通過switch_core_sqldb_start打開數據庫,並重建表,最後啓動數據庫線程switch_core_sqldb_start_thread。
- switch_status_t switch_core_sqldb_start(switch_memory_pool_t *pool, switch_bool_t manage)
- {
- switch_threadattr_t *thd_attr;
- sql_manager.memory_pool = pool;
- sql_manager.manage = manage;
- if (!sql_manager.manage) goto skip;
- top:
- /* 打開數據庫 */
- if (switch_core_db_handle(&sql_manager.dbh) != SWITCH_STATUS_SUCCESS) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n");
- return SWITCH_STATUS_FALSE;
- }
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening DB\n");
- //執行清空表、創建表等操作
- switch (sql_manager.dbh->type) {
- case SCDB_TYPE_PGSQL:
- case SCDB_TYPE_ODBC:
- break;
- case SCDB_TYPE_CORE_DB:
- {
- }
- break;
- }
- skip:
- if (sql_manager.manage) {
- switch_threadattr_create(&thd_attr, sql_manager.memory_pool);
- switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
- switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
- //啓動數據庫線程
- switch_core_sqldb_start_thread();
- switch_thread_create(&sql_manager.db_thread, thd_attr, switch_core_sql_db_thread, NULL, sql_manager.memory_pool);
- }
- switch_cache_db_release_db_handle(&sql_manager.dbh);
- return SWITCH_STATUS_SUCCESS;
- }
- 打開數據庫
- switch_core_db_handle
- SWITCH_DECLARE(switch_status_t) _switch_core_db_handle(switch_cache_db_handle_t **dbh, const char *file, const char *func, int line)
- {
- switch_status_t r;
- char *dsn;
- if (!sql_manager.manage) {
- return SWITCH_STATUS_FALSE;
- }
- if (!zstr(runtime.odbc_dsn)) {
- dsn = runtime.odbc_dsn;
- } else if (!zstr(runtime.dbname)) {
- dsn = runtime.dbname;
- } else {
- dsn = "core";
- }
- if ((r = _switch_cache_db_get_db_handle_dsn(dbh, dsn, file, func, line)) != SWITCH_STATUS_SUCCESS) {
- *dbh = NULL;
- }
- return r;
- }
打開數據庫實際就是使用全局配置runtime.odbc_dsn作爲路徑,這個參數在解析配置的時候有講過,這裏不再說明,然後調用 _switch_cache_db_get_db_handle_dsn底層接口去打開數據庫。
-
- _switch_cache_db_get_db_handle_dsn
- SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle_dsn(switch_cache_db_handle_t **dbh, const char *dsn,
- const char *file, const char *func, int line)
- {
- switch_cache_db_connection_options_t connection_options = { {0} };
- switch_cache_db_handle_type_t type;
- char tmp[256] = "";
- char *p;
- switch_status_t status = SWITCH_STATUS_FALSE;
- int i;
- if (!strncasecmp(dsn, "pgsql://", 8)) {
- type = SCDB_TYPE_PGSQL;
- connection_options.pgsql_options.dsn = (char *)(dsn + 8);
- } else if (!strncasecmp(dsn, "sqlite://", 9)) {
- type = SCDB_TYPE_CORE_DB;
- connection_options.core_db_options.db_path = (char *)(dsn + 9);
- } else if ((!(i = strncasecmp(dsn, "odbc://", 7))) || strchr(dsn+2, ':')) {
- type = SCDB_TYPE_ODBC;
- ...
- } else {
- type = SCDB_TYPE_CORE_DB;
- connection_options.core_db_options.db_path = (char *)dsn;
- }
- status = _switch_cache_db_get_db_handle(dbh, type, &connection_options, file, func, line);
- if (status != SWITCH_STATUS_SUCCESS) *dbh = NULL;
- return status;
- }
_switch_cache_db_get_db_handle_dsn接口解析dsn得到數據庫類型,然後調用最底層接口去打開連接數據庫_switch_cache_db_get_db_handle。
-
- _switch_cache_db_get_db_handle
- 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)
- {
- switch_cache_db_handle_t *new_dbh = NULL;
- //如果存在實例,直接使用,否則創建新實例
- if ((new_dbh = get_handle(db_str, db_callsite_str, thread_str))) {
- switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG10,
- "Reuse Unused Cached DB handle %s [%s]\n", new_dbh->name, switch_cache_db_type_name(new_dbh->type));
- } else {
- switch_core_db_t *db = NULL;
- switch_odbc_handle_t *odbc_dbh = NULL;
- switch_pgsql_handle_t *pgsql_dbh = NULL;
- //根據類型,調用相關的NEW函數創建實例
- switch (type) {
- case SCDB_TYPE_PGSQL:
- {
- if ((pgsql_dbh = switch_pgsql_handle_new(connection_options->pgsql_options.dsn))) {
- if (switch_pgsql_handle_connect(pgsql_dbh) != SWITCH_PGSQL_SUCCESS) {
- switch_pgsql_handle_destroy(&pgsql_dbh);
- }
- }
- }
- break;
- case SCDB_TYPE_ODBC:
- {
- if ((odbc_dbh = switch_odbc_handle_new(connection_options->odbc_options.dsn,
- connection_options->odbc_options.user, connection_options->odbc_options.pass))) {
- if (switch_odbc_handle_connect(odbc_dbh) != SWITCH_ODBC_SUCCESS) {
- switch_odbc_handle_destroy(&odbc_dbh);
- }
- }
- }
- break;
- case SCDB_TYPE_CORE_DB:
- {
- db = switch_core_db_open_file(connection_options->core_db_options.db_path);
- }
- break;
- default:
- goto end;
- }
- //創建並添加操作實例
- new_dbh = create_handle(type);
- if (db) {
- new_dbh->native_handle.core_db_dbh = db;
- } else if (odbc_dbh) {
- new_dbh->native_handle.odbc_dbh = odbc_dbh;
- } else {
- new_dbh->native_handle.pgsql_dbh = pgsql_dbh;
- }
- add_handle(new_dbh, db_str, db_callsite_str, thread_str);
- }
- end:
- *dbh = new_dbh;
- return *dbh ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
- }
流程簡化後如上,先看操作實例是否已經在池,沒有再創建,創建的時候根據類型創建實例,然後添加到池。這裏不同類型創建的實例類型不同,但是會統一到switch_cache_db_handle,
作爲native_handle成員,這個數據結構纔是sql管理層使用的。
- struct switch_cache_db_handle {
- char name[CACHE_DB_LEN];
- switch_cache_db_handle_type_t type;
- switch_cache_db_native_handle_t native_handle;
- time_t last_used;
- switch_mutex_t *mutex;
- switch_mutex_t *io_mutex;
- switch_memory_pool_t *pool;
- int32_t flags;
- unsigned long hash;
- unsigned long thread_hash;
- char creator[CACHE_DB_LEN];
- char last_user[CACHE_DB_LEN];
- uint32_t use_count;
- uint64_t total_used_count;
- struct switch_cache_db_handle *next;
- };
- 啓動數據庫線程
- switch_core_sqldb_start_thread
- static void switch_core_sqldb_start_thread(void)
- {
- switch_mutex_lock(sql_manager.ctl_mutex);
- if (sql_manager.manage) {
- if (!sql_manager.qm) {
- char *dbname = runtime.odbc_dsn;
- if (zstr(dbname)) {
- dbname = runtime.dbname;
- if (zstr(dbname)) {
- dbname = "core";
- }
- }
- switch_sql_queue_manager_init_name("CORE",
- &sql_manager.qm,
- 4,
- dbname,
- SWITCH_MAX_TRANS,
- runtime.core_db_pre_trans_execute,
- runtime.core_db_post_trans_execute,
- runtime.core_db_inner_pre_trans_execute,
- runtime.core_db_inner_post_trans_execute);
- }
- switch_sql_queue_manager_start(sql_manager.qm);
- } else {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL is not enabled\n");
- }
- 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的數據庫不是在這裏。
-
- switch_sql_queue_manager_start
- SWITCH_DECLARE(switch_status_t) switch_sql_queue_manager_start(switch_sql_queue_manager_t *qm)
- {
- switch_threadattr_t *thd_attr;
- if (!qm->thread_running) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Starting SQL thread.\n", qm->name);
- switch_threadattr_create(&thd_attr, qm->pool);
- switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
- switch_threadattr_priority_set(thd_attr, SWITCH_PRI_NORMAL);
- switch_thread_create(&qm->thread, thd_attr, switch_user_sql_thread, qm, qm->pool);
- return SWITCH_STATUS_SUCCESS;
- }
- return SWITCH_STATUS_FALSE;
- }
這裏會創建隊列的線程,執行函數爲switch_user_sql_thread。
-
- 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,然後去執行。
- switch_core_sql_db_thread
啓動數據庫的時候,除了調用switch_core_sqldb_start_thread去創建隊列外,還啓動一條線程switch_core_sql_db_thread,注意這和switch_user_sql_thread是不一樣的。
- static void *SWITCH_THREAD_FUNC switch_core_sql_db_thread(switch_thread_t *thread, void *obj)
- {
- int sec = 0, reg_sec = 0;;
- sql_manager.db_thread_running = 1;
- while (sql_manager.db_thread_running == 1) {
- if (++sec == SQL_CACHE_TIMEOUT) {
- sql_close(switch_epoch_time_now(NULL));
- sec = 0;
- }
- if (switch_test_flag((&runtime), SCF_USE_SQL) && ++reg_sec == SQL_REG_TIMEOUT) {
- switch_core_expire_registration(0);
- reg_sec = 0;
- }
- switch_yield(1000000);
- }
- return NULL;
- }
這條線程就是定時檢查一些超時狀態,沒有特別重要的業務。