如何做網(wǎng)站微信支付鄭州百度快照優(yōu)化
本專題的第一篇文章,配置項(xiàng)的定義及使用方法,詳細(xì)闡述了配置項(xiàng)的基礎(chǔ)用法。對(duì)于那些對(duì)源碼抱有濃厚興趣的同學(xué)來(lái)說(shuō),或許還希望深入了解配置項(xiàng)的實(shí)現(xiàn)原理,甚至渴望親自添加新的配置項(xiàng),以滿足個(gè)性化的功能需求。
本文通過(guò)剖析“如何新增配置項(xiàng)”這一話題,并結(jié)合配置項(xiàng)源碼的具體實(shí)現(xiàn),來(lái)詳細(xì)講解配置項(xiàng)的定義、初始化、內(nèi)部訪問(wèn)及同步機(jī)制。
如何新增配置項(xiàng)?
要新增一個(gè)配置項(xiàng),首先在 src/share/parameter/ob_parameter_seed.ipp 文件中,按照下面的格式定義配置項(xiàng)。
// 定義一個(gè)集群級(jí)別的、INT類型的配置項(xiàng),動(dòng)態(tài)生效
DEF_INT(cpu_count, OB_CLUSTER_PARAMETER, "0", "[0,]","the number of CPU\\'s in the system. ""If this parameter is set to zero, the number will be set according to sysconf; ""otherwise, this parameter is used. Range: [0,+∞) in integer",ObParameterAttr(Section::OBSERVER, Source::DEFAULT, EditLevel::DYNAMIC_EFFECTIVE));// 定義一個(gè)租戶級(jí)別的、TIME類型的配置項(xiàng),動(dòng)態(tài)生效,并附帶一個(gè)合法性檢查類 ObConfigStaleTimeChecker
DEF_TIME_WITH_CHECKER(max_stale_time_for_weak_consistency, OB_TENANT_PARAMETER, "5s",common::ObConfigStaleTimeChecker,"[5s,)","the max data stale time that cluster weak read version behind current timestamp,""no smaller than weak_read_version_refresh_interval, range: [5s, +∞)",ObParameterAttr(Section::TENANT, Source::DEFAULT, EditLevel::DYNAMIC_EFFECTIVE));
其中每個(gè)參數(shù)的含義如下:
參數(shù) | 說(shuō)明 | 舉例 |
配置項(xiàng)宏 | 定義配置的宏,可以選擇附帶合法性檢查函數(shù) | DEF_XXX、DEF_XXX_WITH_CHECKER |
配置項(xiàng)名 | 配置項(xiàng)的名字 | 一般格式:xxx_xxx_xxx |
生效范圍 | 租戶配置項(xiàng) or 集群配置項(xiàng) | OB_CLUSTER_PARAMETER、OB_TENANT_PARAMETER |
默認(rèn)值 | 默認(rèn)值必須在取值范圍中 | "0", "True", "100ms", "20GB", "random" |
取值范圍 | 布爾類型和字符串類型不需要取值范圍 | "[0,]", "[0M,]", "[1s, 180s]" |
描述 | 介紹該配置項(xiàng)的功能和注意事項(xiàng) | "enable xxx" |
配置項(xiàng)屬性 | ① Section:所屬模塊; ② Source:來(lái)源; ③ EditLevel:生效方式; | ① OBSERVER、TENANT、TRANS ② DEFAULT ③ DYNAMIC_EFFECTIVE、STATIC_EFFECTIVE |
DEF_XXX 宏最終展開(kāi)是一個(gè)類的聲明,并同時(shí)定義一個(gè)對(duì)象,對(duì)象名就是配置項(xiàng)名。
比如 DEF_TIME_WITH_CHECKER 宏,最終展開(kāi)是一個(gè) ObConfigTimeItem 類,重載 operator= 操作符讓該類可以像基礎(chǔ)數(shù)據(jù)類型一樣進(jìn)行賦值操作。
#define _DEF_PARAMETER_CHECKER_EASY(access_specifier, param, scope, name, def, checker, args...) \
access_specifier: \class ObConfig ## param ## Item ## _ ## name \: public common::ObConfig ## param ## Item \{ \public: \ObConfig ## param ## Item ## _ ## name() \: common::ObConfig ## param ## Item( \local_container(), scope, #name, def, args) \{ \add_checker(OB_NEW(checker, g_config_mem_attr)); \} \template <class T> \ObConfig ## param ## Item ## _ ## name& operator=(T value) \{ \common::ObConfig ## param ## Item::operator=(value); \return *this; \} \} name;
ObConfigTimeItemXxx 繼承的是一個(gè) ObConfigIntegralItem 類,所以 time 類型的數(shù)據(jù)本質(zhì)上是一個(gè) int64_t 變量。
class ObConfigTimeItem: public ObConfigIntegralItem
{
public:......static const uint64_t VALUE_BUF_SIZE = 32UL;char value_str_[VALUE_BUF_SIZE];char value_reboot_str_[VALUE_BUF_SIZE];
};
ObConfigIntegralItem 類重載了操作符 operator const int64_t &() const,在代碼中訪問(wèn)該類對(duì)象的對(duì)象名時(shí),實(shí)際上返回的是對(duì)象的 value_值。
class ObConfigIntegralItem: public ObConfigItem
{// get_value() return the real-time valueint64_t get_value() const { return value_; }// get() return the real-time value if it does not need reboot, otherwise it return initial_valueint64_t get() const { return value_; }operator const int64_t &() const { return value_; }private:int64_t value_;
配置項(xiàng)定義完成后,重新編譯部署 OBServer,就可以對(duì)這個(gè)配置項(xiàng)進(jìn)行查詢和修改了。不過(guò)現(xiàn)在這個(gè)配置項(xiàng)沒(méi)有任何功能,還需要在代碼中用起來(lái)才能生效。
配置項(xiàng)初始化
集群配置項(xiàng) ObServerConfig
集群配置項(xiàng)在 OBServer 啟動(dòng)時(shí)進(jìn)行初始化,首先會(huì)從配置項(xiàng)的定義中獲取默認(rèn)值,然后從持久化配置文件中獲取歷史值(非首次啟動(dòng)),最后從 OBServer 的執(zhí)行參數(shù)中獲取最新值。它們的優(yōu)先級(jí)是:執(zhí)行參數(shù) > 持久化配置文件 > 默認(rèn)值。
配置項(xiàng)對(duì)象構(gòu)造
集群配置項(xiàng)定義在 ObServerConfig 類中,通過(guò)在 ObConfigManager 類中定義了一個(gè) ObServerConfig 實(shí)例,當(dāng) OBServer 啟動(dòng)時(shí),它們及其成員變量的構(gòu)造函數(shù)就會(huì)被調(diào)用。
class ObServerConfig : public ObCommonConfig
{
public:friend class ObServerMemoryConfig;int init(const ObSystemConfig &config);static ObServerConfig &get_instance();#undef OB_CLUSTER_PARAMETER
#define OB_CLUSTER_PARAMETER(args...) args
#include "share/parameter/ob_parameter_seed.ipp"
#undef OB_CLUSTER_PARAMETER
}
每個(gè)配置項(xiàng)都有默認(rèn)值,在配置項(xiàng)的構(gòu)造函數(shù)中會(huì)將 value_ 置為默認(rèn)值。
以下是相關(guān)函數(shù)的調(diào)用流程,以及關(guān)鍵函數(shù)的代碼解析。有的函數(shù)只是省略了參數(shù)列表,不代表沒(méi)有參數(shù)。
- ObConfigTimeItem::ObConfigTimeItem()
- ObConfigIntegralItem::init()
- ObConfigItem::init()
- ObConfigItem::set_value(const char *str)
- ObConfigIntegralItem::set(const char *str)
其中 str 就是一開(kāi)始定義的配置項(xiàng)默認(rèn)值,將 str 中的字符串轉(zhuǎn)化為對(duì)應(yīng)類型的數(shù)據(jù),然后賦值給 value_。
inline bool ObConfigIntegralItem::set(const char *str)
{bool valid = true;const int64_t value = parse(str, valid);if (valid) {value_ = value;}return valid;
}
配置文件解析
當(dāng)集群配置項(xiàng)都構(gòu)造完成后,再?gòu)某志没呐渲梦募?etc/observer.config.bin 加載配置項(xiàng)。(在 OBServer 初次啟動(dòng)時(shí),配置文件為空,所以不會(huì)執(zhí)行這一步)
- ObServer::init()
- ObServer::init_config()
- ObConfigManager::load_config(const char *path)
- ObServerConfig::deserialize_with_compat()
- OB_DEF_DESERIALIZE(ObServerConfig)
先調(diào)用 ObCommonConfig::deserialize() 函數(shù)解析集群配置項(xiàng),再調(diào)用 OTC_MGR.deserialize() 函數(shù)解析租戶配置項(xiàng)。
OB_DEF_DESERIALIZE(ObServerConfig)
{} else if (OB_FAIL(ObCommonConfig::deserialize(buf, data_len, pos))) {LOG_ERROR("deserialize cluster config failed", K(ret));} else if (OB_FAIL(OTC_MGR.deserialize(buf, data_len, pos))){LOG_ERROR("deserialize tenant config failed", K(ret));}
- OB_DEF_DESERIALIZE(ObCommonConfig)
- ObCommonConfig::add_extra_config()
將文件中集群配置項(xiàng)的值加載到內(nèi)存數(shù)據(jù)結(jié)構(gòu)中。
int ObCommonConfig::add_extra_config(const char *config_str,int64_t version /* = 0 */ ,bool check_name /* = false */,bool check_unit /* = true */)
{......while (OB_SUCC(ret) && OB_NOT_NULL(token)) {if (strncmp(token, "enable_production_mode=", 23) == 0) {......} else if (OB_ISNULL(pp_item = container_.get(ObConfigStringKey(name)))) {......if (OB_FAIL(ret) || OB_ISNULL(pp_item)) {} else if (check_unit && !(*pp_item)->check_unit(value)) {ret = OB_INVALID_CONFIG;LOG_ERROR("Invalid config value", K(name), K(value), K(ret));} else if (!(*pp_item)->set_value(value)) {ret = OB_INVALID_CONFIG;LOG_ERROR("Invalid config value", K(name), K(value), K(ret));} else if (!(*pp_item)->check()) {ret = OB_INVALID_CONFIG;const char* range = (*pp_item)->range();if (OB_ISNULL(range) || strlen(range) == 0) {LOG_ERROR("Invalid config, value out of range", K(name), K(value), K(ret));} else {_LOG_ERROR("Invalid config, value out of %s (for reference only). name=%s, value=%s, ret=%d", range, name, value, ret);}} else {(*pp_item)->set_version(version);LOG_INFO("Load config succ", K(name), K(value));}
執(zhí)行參數(shù)解析
當(dāng)配置文件中的數(shù)據(jù)加載完成后,再解析 OBServer 執(zhí)行命令中的配置項(xiàng)參數(shù)。
- ObServer::init_config()
其中 config_.add_extra_config(opts_.optstr_, start_time_) 函數(shù)將參數(shù)解析為配置項(xiàng)的值,然后加載到配置項(xiàng)結(jié)構(gòu)中。最后調(diào)用 dump2file() 函數(shù),將前面解析出來(lái)的配置項(xiàng)全部持久化到"etc/observer.config.bin"文件中。
int ObServer::init_config()
{int ret = OB_SUCCESS;bool has_config_file = true;// set dump pathconst char *dump_path = "etc/observer.config.bin";config_mgr_.set_dump_path(dump_path);if (OB_FILE_NOT_EXIST == (ret = config_mgr_.load_config())) {has_config_file = false;ret = OB_SUCCESS;}......if (opts_.optstr_ && strlen(opts_.optstr_) > 0) {if (OB_FAIL(config_.add_extra_config(opts_.optstr_, start_time_))) {LOG_ERROR("invalid config from cmdline options", K(opts_.optstr_), KR(ret));}}......if (is_arbitration_mode()) {// arbitration mode, dump config params to file directlyif (OB_FAIL(config_mgr_.dump2file())) {LOG_ERROR("config_mgr_ dump2file failed", KR(ret));} else {LOG_INFO("config_mgr_ dump2file success", KR(ret));}
如果是用 OBD 部署集群,OBServer 第一次啟動(dòng)時(shí),命令中會(huì)帶上"xxx.yaml"文件中的啟動(dòng)參數(shù)。例如:
/data/user/observer1/bin/observer -p 23400 -P 23401 -z zone1 -c 1 -d /data/user/observer1/store -i lo -r 127.0.0.1:23401:23400 -o __min_full_resource_pool_memory=268435456,major_freeze_duty_time=Disable,datafile_size=20G,memory_limit=10G,system_memory=5G,cpu_count=24,stack_size=512K,cache_wash_threshold=1G,workers_per_cpu_quota=10,schema_history_expire_time=1d,net_thread_count=4,minor_freeze_times=10,enable_separate_sys_clog=False,enable_merge_by_turn=False,syslog_io_bandwidth_limit=10G,enable_async_syslog=False
集群第一次部署完成之后,"etc/observer.config.bin"文件才會(huì)被創(chuàng)建,之后重啟 OBServer 就可以不帶參數(shù)了,進(jìn)程會(huì)從該文件中讀取修改后的配置項(xiàng)。
租戶配置項(xiàng) ObTenantConfig
創(chuàng)建租戶時(shí)初始化
租戶級(jí)別配置項(xiàng)首先在創(chuàng)建租戶時(shí)進(jìn)行初始化,在代碼中是一個(gè) ObTenantConfig 對(duì)象。
(不只這一條調(diào)用路徑,但最終會(huì)調(diào)用 add_tenant_config() 函數(shù))
- ObRpcNotifyTenantServerUnitResourceP::process()
- ObTenantNodeBalancer::notify_create_tenant(oceanbase::obrpc::TenantServerUnitConfig const&)
- ObTenantNodeBalancer::check_new_tenant(oceanbase::share::ObUnitInfoGetter::ObTenantConfig const&, long)
- ObMultiTenant::create_tenant(oceanbase::omt::ObTenantMeta const&, bool, long)
- ObTenantConfigMgr::add_tenant_config(uint64_t tenant_id)
申請(qǐng)一個(gè)新的 ObTenantConfig 對(duì)象,調(diào)用 init() 初始化,然后添加到 config_map_中。
int ObTenantConfigMgr::add_tenant_config(uint64_t tenant_id)
{int ret = OB_SUCCESS;ObTenantConfig *const *config = nullptr;DRWLock::WRLockGuard guard(rwlock_);if (is_virtual_tenant_id(tenant_id)|| OB_NOT_NULL(config = config_map_.get(ObTenantID(tenant_id)))) {if (nullptr != config) {ObTenantConfig *new_config = *config;new_config->set_deleting(false);}} else {ObTenantConfig *new_config = nullptr;new_config = OB_NEW(ObTenantConfig, SET_USE_UNEXPECTED_500("TenantConfig"), tenant_id);if (OB_NOT_NULL(new_config)) {if(OB_FAIL(new_config->init(this))) {LOG_WARN("new tenant config init failed", K(ret));} else if (OB_FAIL(config_map_.set_refactored(ObTenantID(tenant_id),new_config, 0))) {LOG_WARN("add new tenant config failed", K(ret));}......
因?yàn)?ObTenantConfig 引入了租戶配置項(xiàng)的定義,因此在構(gòu)造 ObTenantConfig 對(duì)象時(shí)就完成了配置項(xiàng)的構(gòu)造。
class ObTenantConfig : public ObCommonConfig
{#undef OB_TENANT_PARAMETER
#define OB_TENANT_PARAMETER(args...) args
#include "share/parameter/ob_parameter_seed.ipp"
#undef OB_TENANT_PARAMETER
};
重啟時(shí)初始化
在 OBServer 重新啟動(dòng)時(shí),已有租戶的配置項(xiàng)也會(huì)進(jìn)行初始化。
- OB_DEF_DESERIALIZE(ObServerConfig)
調(diào)用 OTC_MGR.deserialize() 函數(shù)。
OB_DEF_DESERIALIZE(ObServerConfig)
{} else if (OB_FAIL(ObCommonConfig::deserialize(buf, data_len, pos))) {LOG_ERROR("deserialize cluster config failed", K(ret));} else if (OB_FAIL(OTC_MGR.deserialize(buf, data_len, pos))){LOG_ERROR("deserialize tenant config failed", K(ret));}
- OB_DEF_DESERIALIZE(ObTenantConfigMgr)
讀取文件中租戶配置項(xiàng),根據(jù) tenant_id 獲取 config,然后將buf中的配置項(xiàng)解析到 config 中。
OB_DEF_DESERIALIZE(ObTenantConfigMgr)
{int ret = OB_SUCCESS;if (data_len == 0 || pos >= data_len) {} else {while(OB_SUCC(ret) && pos < data_len) {......ObTenantConfig *config = nullptr;if (OB_FAIL(config_map_.get_refactored(ObTenantID(tenant_id), config))) {if (ret != OB_HASH_NOT_EXIST || OB_FAIL(add_tenant_config(tenant_id))) {LOG_ERROR("get tenant config failed", K(tenant_id), K(ret));break;}ret = config_map_.get_refactored(ObTenantID(tenant_id), config);}if (OB_SUCC(ret)) {pos = saved_pos;ret = config->deserialize(buf, data_len, pos);}......
在代碼中查詢配置項(xiàng)
外部查詢
當(dāng)用戶使用"show ..."類型的命令時(shí),OBServer 內(nèi)部有統(tǒng)一的處理函數(shù):ObShowResolver::resolve()。查詢配置項(xiàng)的命令會(huì)被解析為 T_SHOW_PARAMETERS 類型,通過(guò)查詢 __all_virtual_tenant_parameter_stat 表獲取配置項(xiàng)的值。
int ObShowResolver::resolve(const ParseNode &parse_tree)
{......case T_SHOW_PARAMETERS: {......if (params_.show_seed_) {char local_ip[OB_MAX_SERVER_ADDR_SIZE] = "";if (OB_UNLIKELY(true != GCONF.self_addr_.ip_to_string(local_ip, sizeof(local_ip)))) {ret = OB_CONVERT_ERROR;} else {GEN_SQL_STEP_1(ObShowSqlSet::SHOW_PARAMETERS_SEED);GEN_SQL_STEP_2(ObShowSqlSet::SHOW_PARAMETERS_SEED, REAL_NAME(OB_SYS_DATABASE_NAME, OB_ORA_SYS_SCHEMA_NAME), REAL_NAME(OB_ALL_VIRTUAL_TENANT_PARAMETER_STAT_TNAME, OB_ALL_VIRTUAL_TENANT_PARAMETER_STAT_ORA_TNAME),local_ip, GCONF.self_addr_.get_port());}} else if (OB_SYS_TENANT_ID == show_tenant_id) {GEN_SQL_STEP_1(ObShowSqlSet::SHOW_PARAMETERS);GEN_SQL_STEP_2(ObShowSqlSet::SHOW_PARAMETERS,REAL_NAME(OB_SYS_DATABASE_NAME, OB_ORA_SYS_SCHEMA_NAME),REAL_NAME(OB_ALL_VIRTUAL_TENANT_PARAMETER_STAT_TNAME, OB_ALL_VIRTUAL_TENANT_PARAMETER_STAT_ORA_TNAME),show_tenant_id);} else {GEN_SQL_STEP_1(ObShowSqlSet::SHOW_PARAMETERS);GEN_SQL_STEP_2(ObShowSqlSet::SHOW_PARAMETERS,REAL_NAME(OB_SYS_DATABASE_NAME, OB_ORA_SYS_SCHEMA_NAME),REAL_NAME(OB_ALL_VIRTUAL_TENANT_PARAMETER_STAT_TNAME, OB_ALL_VIRTUAL_TENANT_PARAMETER_STAT_ORA_TNAME),show_tenant_id);}
}// 最終查詢的虛擬表為 __all_virtual_tenant_parameter_stat
const char *const OB_ALL_VIRTUAL_TENANT_PARAMETER_STAT_TNAME = "__all_virtual_tenant_parameter_stat";
查詢?cè)撎摂M表需要先初始化一個(gè) ObAllVirtualTenantParameterStat 對(duì)象,然后調(diào)用 inner_open() 函數(shù),再循環(huán)調(diào)用 inner_get_next_row() 函數(shù)遍歷虛擬表。
- ObVirtualTableIterator::get_next_row(common::ObNewRow*&)
- ObAllVirtualTenantParameterStat::inner_get_next_row(common::ObNewRow*&)
其中 inner_sys_get_next_row() 函數(shù)獲取集群配置項(xiàng),inner_tenant_get_next_row() 函數(shù)獲取租戶配置項(xiàng),最后根據(jù)查詢條件進(jìn)行篩選,返回滿足條件的配置項(xiàng)。
int ObAllVirtualTenantParameterStat::inner_get_next_row(ObNewRow *&row)
{int ret = OB_SUCCESS;if (OB_UNLIKELY(!inited_)) {ret = OB_NOT_INIT;SERVER_LOG(WARN, "not inited", K(inited_), KR(ret));} else if (show_seed_) {ret = inner_seed_get_next_row(row);} else {if (OB_SUCC(inner_sys_get_next_row(row))) {} else if (OB_ITER_END == ret) {ret = inner_tenant_get_next_row(row);}}return ret;
}
- ObAllVirtualTenantParameterStat::inner_sys_get_next_row(common::ObNewRow*&)
從 GCONF 中獲取集群配置項(xiàng)。
int ObAllVirtualTenantParameterStat::inner_sys_get_next_row(common::ObNewRow *&row)
{/*cluster parameter does not belong to any tenant*/return fill_row_(row, sys_iter_, GCONF.get_container(), NULL);
}// 集群配置項(xiàng)迭代器也就是 GCONF 中一個(gè) hashmap 的迭代器sys_iter_ = GCONF.get_container().begin();
- ObAllVirtualTenantParameterStat::inner_tenant_get_next_row(common::ObNewRow *&row)
從 tenant_config_中獲取租戶配置項(xiàng)。
int ObAllVirtualTenantParameterStat::inner_tenant_get_next_row(common::ObNewRow *&row)
{// 租戶配置項(xiàng)迭代器也就是 tenant_config_ 中一個(gè) hashmap 的迭代器if (cur_tenant_idx_ < 0 // first come-in// current tenant is over|| (tenant_config_.is_valid() && tenant_iter_ == tenant_config_->get_container().end())) {// find next valid tenantwhile (OB_SUCC(ret) && ++cur_tenant_idx_ < tenant_id_list_.count()) {uint64_t tenant_id = tenant_id_list_.at(cur_tenant_idx_);tenant_config_.set_config(TENANT_CONF(tenant_id));if (tenant_config_.is_valid()) {tenant_iter_ = tenant_config_->get_container().begin();......}......} else {const uint64_t tenant_id = tenant_id_list_.at(cur_tenant_idx_);if (OB_FAIL(fill_row_(row,tenant_iter_,tenant_config_->get_container(),&tenant_id))) {SERVER_LOG(WARN, "fill row fail", KR(ret), K(tenant_id), K(tenant_config_->get_tenant_id()),K(cur_tenant_idx_), K(tenant_id_list_));}
內(nèi)部獲取
集群配置項(xiàng)
因?yàn)榕渲庙?xiàng)重載了操作符 operator &(),所以集群配置項(xiàng)直接通過(guò) GCONF.xxx 的形式訪問(wèn)即可。
#include "share/config/ob_server_config.h"GCONF.enable_sql_audit
在代碼中有需要的地方,可以用“if (GCONF.enable_xxx)”來(lái)控制分支的走向,或者用“GCONF.xxx_time”來(lái)進(jìn)行時(shí)間的計(jì)算,這樣就可以把配置項(xiàng)使用起來(lái)了。
租戶配置項(xiàng)
訪問(wèn)租戶配置項(xiàng)需要先調(diào)用 OTC_MGR.read_tenant_config() 函數(shù)獲取租戶的 config,然后從 config 中獲取指定的配置項(xiàng)。以租戶配置項(xiàng) max_stale_time_for_weak_consistency 為例,為其封裝一個(gè)取值函數(shù)。
- ObWeakReadUtil::max_stale_time_for_weak_consistency(const uint64_t tenant_id, int64_t ignore_warn)
如果獲取租戶 config 成功,則從 config 中獲取配置項(xiàng)的值,否則返回配置項(xiàng)的默認(rèn)值并打印日志。
int64_t ObWeakReadUtil::max_stale_time_for_weak_consistency(const uint64_t tenant_id, int64_t ignore_warn)
{int64_t max_stale_time = 0;OTC_MGR.read_tenant_config(tenant_id,oceanbase::omt::ObTenantConfigMgr::default_fallback_tenant_id(),/* success */ [&max_stale_time](const omt::ObTenantConfig &config) mutable {max_stale_time = config.max_stale_time_for_weak_consistency;},/* failure */ [tenant_id, ignore_warn, &max_stale_time]() mutable {max_stale_time = DEFAULT_MAX_STALE_TIME_FOR_WEAK_CONSISTENCY;if (IGNORE_TENANT_EXIST_WARN != ignore_warn && REACH_TIME_INTERVAL(1 * 1000 * 1000L)) {TRANS_LOG_RET(WARN, OB_ERR_UNEXPECTED, "tenant not exist when get max stale time for weak consistency,"" use default max stale time instead",K(tenant_id), K(max_stale_time), K(lbt()));}});return max_stale_time;
}
- ObTenantConfigMgr::read_tenant_config()
從 config_map_中獲取 tenant_id 對(duì)應(yīng)的config,成功則調(diào)用 SuccessFunctor,失敗則調(diào)用 FailureFunctor。
int ObTenantConfigMgr::read_tenant_config(const uint64_t tenant_id,const uint64_t fallback_tenant_id,const SuccessFunctor &on_success,const FailureFunctor &on_failure) const
{int ret = OB_SUCCESS;ObTenantConfig *config = nullptr;DRWLock::RDLockGuard guard(rwlock_);if (OB_FAIL(config_map_.get_refactored(ObTenantID(tenant_id), config))) {if (fallback_tenant_id > 0 && OB_INVALID_ID != fallback_tenant_id) {if (OB_FAIL(config_map_.get_refactored(ObTenantID(fallback_tenant_id), config))) {LOG_WARN("failed to get tenant config", K(fallback_tenant_id), K(ret), K(lbt()));}} else {LOG_WARN("failed to get tenant config", K(tenant_id), K(ret));}}if (OB_SUCC(ret) && OB_NOT_NULL(config)) {on_success(*config);} else {on_failure();LOG_WARN("fail read tenant config", K(tenant_id), K(ret));}return ret;
}
在代碼中修改配置項(xiàng)
外部修改
修改集群配置項(xiàng)
系統(tǒng)租戶執(zhí)行修改集群配置項(xiàng)命令時(shí),內(nèi)部會(huì)向 __all_sys_parameter 表中插入一條記錄(該表只記錄增量數(shù)據(jù)),實(shí)際執(zhí)行的是以下sql命令。
select config_version, zone, svr_type, svr_ip, svr_port, name, data_type, value, info, section, scope, source, edit_level from __all_sys_parameter
INSERT INTO __all_sys_parameter (zone, svr_type, svr_ip, svr_port, name, data_type, value, info, config_version, gmt_modified, section, scope, source, edit_level) VALUES ('', 'observer', 'ANY', 0, 'cpu_count', 'varchar', '9', '', 1689734424757370, usec_to_time(1689734424757370), 'OBSERVER', 'CLUSTER', 'DEFAULT', 'DYNAMIC_EFFECTIVE') ON DUPLICATE KEY UPDATE data_type = 'varchar', value = '9', info = '', config_version = 1689734424757370, gmt_modified = usec_to_time(1689734424757370), section = 'OBSERVER', scope = 'CLUSTER', source = 'DEFAULT', edit_level = 'DYNAMIC_EFFECTIVE'
select config_version, zone, svr_type, svr_ip, svr_port, name, data_type, value, info, section, scope, source, edit_level from __all_sys_parameter
select config_version, zone, svr_type, svr_ip, svr_port, name, data_type, value, info, section, scope, source, edit_level from __all_sys_parameter
將修改后的值插入內(nèi)部表之后,修改配置項(xiàng)命令就執(zhí)行完成了。之后內(nèi)部會(huì)將表中的增量數(shù)據(jù)刷新到各節(jié)點(diǎn)的本地?cái)?shù)據(jù)結(jié)構(gòu)中,此時(shí)才算真正完成了集群配置項(xiàng)更新。
修改租戶配置項(xiàng)
執(zhí)行租戶配置項(xiàng)修改命令后,內(nèi)部會(huì)執(zhí)行兩條sql,先往 __tenant_parameter 內(nèi)部表中插入一行數(shù)據(jù)(該表只記錄增量數(shù)據(jù)),然后往 __all_rootservice_event_history 內(nèi)部表中插入一條修改記錄。
INSERT INTO __tenant_parameter (tenant_id, zone, svr_type, svr_ip, svr_port, name, data_type, value, info, config_version, gmt_modified, section, scope, source, edit_level) VALUES (1004, '', 'observer', 'ANY', 0, 'max_stale_time_for_weak_consistency', 'varchar', '7s', '', 1689732907024711, usec_to_time(1689732907024711), 'TENANT', 'TENANT', 'DEFAULT', 'DYNAMIC_EFFECTIVE') ON DUPLICATE KEY UPDATE data_type = 'varchar', value = '7s', info = '', config_version = 1689732907024711, gmt_modified = usec_to_time(1689732907024711), section = 'TENANT', scope = 'TENANT', source = 'DEFAULT', edit_level = 'DYNAMIC_EFFECTIVE'
INSERT INTO __all_rootservice_event_history (gmt_create, module, event, name1, value1, name2, value2, rs_svr_ip, rs_svr_port) VALUES (usec_to_time(1689732907031822), 'root_service', 'admin_set_config', 'ret', 0, 'arg', '{items:[{name:"max_stale_time_for_weak_consistency", value:"7s", comment:"", zone:"", server:"0.0.0.0:0", tenant_name:"", exec_tenant_id:1004, tenant_ids:[1004]}], is_inner:false}', '127.0.0.1', 23401
同樣的,修改內(nèi)部表之后SQL命令就會(huì)返回,之后再由后臺(tái)線程刷新各節(jié)點(diǎn)的租戶配置項(xiàng)。
內(nèi)部更新(同步機(jī)制)
內(nèi)部主動(dòng)更新配置項(xiàng),就是把 __tenant_parameter 和 __all_sys_parameter 內(nèi)部表中的增量配置項(xiàng)同步到本地的過(guò)程。
集群配置項(xiàng)同步
- ObLeaseStateMgr::start_heartbeat()
后臺(tái)任務(wù) hb_ 每2s執(zhí)行一次。
int ObLeaseStateMgr::start_heartbeat()
{int ret = OB_SUCCESS;if (!inited_) {ret = OB_NOT_INIT;LOG_WARN("not init", K(ret));} else {const bool repeat = false;if (OB_FAIL(hb_timer_.schedule(hb_, DELAY_TIME, repeat))) {LOG_WARN("schedule failed", LITERAL_K(DELAY_TIME), K(repeat), K(ret));}}return ret;
}static const int64_t DELAY_TIME = 2 * 1000 * 1000;//2s
- ObLeaseStateMgr::HeartBeat::runTimerTask()
......
- ObHeartBeatProcess::do_heartbeat_event(oceanbase::share::ObLeaseResponse const&)
- ObHeartBeatProcess::ObZoneLeaseInfoUpdateTask::runTimerTask()
......
- ObConfigManager::got_version(long, bool)
- ObConfigManager::UpdateTask::runTimerTask()
該函數(shù)是刷新配置項(xiàng)的后臺(tái)定時(shí)任務(wù),會(huì)調(diào)用 update_local() 函數(shù),將內(nèi)部表中的數(shù)據(jù)同步到本地配置項(xiàng)中。
void ObConfigManager::UpdateTask::runTimerTask()
{......} else if (update_local_) {config_mgr_->current_version_ = version;if (OB_FAIL(config_mgr_->system_config_.clear())) {// just print log, ignore retLOG_WARN("Clear system config map failed", K(ret));} else {// do nothing}if (OB_FAIL(config_mgr_->update_local(version))) {LOG_WARN("Update local config failed", K(ret));// recovery current_version_config_mgr_->current_version_ = old_current_version;// retry update local config in 1s laterif (OB_FAIL(TG_SCHEDULE(lib::TGDefIDs::CONFIG_MGR, *this, 1000 * 1000L, false))) {LOG_WARN("Reschedule update local config failed", K(ret));}
- ObConfigManager::update_local(int64_t expected_version)
該函數(shù)主要做了以下操作:
- sql_client_retry_weak.read():從 __all_sys_parameter 內(nèi)部表中讀取增量配置項(xiàng);
- system_config_.update():將配置項(xiàng)的新值更新到本地;
- reload_config():重新加載和校驗(yàn)配置項(xiàng);
- dump2file():將配置項(xiàng)同步到 observer.config.bin 文件中;
int ObConfigManager::update_local(int64_t expected_version)
{int ret = OB_SUCCESS;if (OB_ISNULL(sql_proxy_)) {ret = OB_NOT_INIT;LOG_WARN("sql proxy is null", K(ret));} else {ObSQLClientRetryWeak sql_client_retry_weak(sql_proxy_);SMART_VAR(ObMySQLProxy::MySQLResult, result) {int64_t start = ObTimeUtility::current_time();const char *sqlstr = "select config_version, zone, svr_type, svr_ip, svr_port, name, ""data_type, value, info, section, scope, source, edit_level ""from __all_sys_parameter";if (OB_FAIL(sql_client_retry_weak.read(result, sqlstr))) {LOG_WARN("read config from __all_sys_parameter failed", K(sqlstr), K(ret));} else if (OB_FAIL(system_config_.update(result))) {LOG_WARN("failed to load system config", K(ret));......if (OB_SUCC(ret)) {if ('\0' == dump_path_[0]) {ret = OB_NOT_INIT;LOG_ERROR("Dump path doesn't set, stop read config", K(ret));} else if (OB_FAIL(server_config_.read_config())) {LOG_ERROR("Read server config failed", K(ret));} else if (OB_FAIL(reload_config())) {LOG_WARN("Reload configuration failed", K(ret));} else {DRWLock::RDLockGuard guard(OTC_MGR.rwlock_); // need protect tenant config because it will also serialize tenant configif (OB_FAIL(dump2file())) {LOG_WARN("Dump to file failed", K_(dump_path), K(ret));......
租戶配置項(xiàng)同步
- ObLeaseStateMgr::HeartBeat::runTimerTask()
......
- ObTenantConfig::got_version(long, bool)
- ObTenantConfig::TenantConfigUpdateTask::runTimerTask()
租戶配置項(xiàng)同樣有一個(gè)后臺(tái)定時(shí)任務(wù),只要配置項(xiàng)的最新版本大于本地版本,就會(huì)觸發(fā)更新操作。
void ObTenantConfig::TenantConfigUpdateTask::runTimerTask()
{int ret = OB_SUCCESS;if (OB_ISNULL(config_mgr_)) {ret = OB_NOT_INIT;LOG_WARN("invalid argument", K_(config_mgr), K(ret));} else if (OB_ISNULL(tenant_config_)){ret = OB_NOT_INIT;LOG_WARN("invalid argument", K_(tenant_config), K(ret));} else {const int64_t saved_current_version = tenant_config_->current_version_;const int64_t version = version_;THIS_WORKER.set_timeout_ts(INT64_MAX);if (tenant_config_->current_version_ >= version) {ret = OB_ALREADY_DONE;} else if (update_local_) {tenant_config_->current_version_ = version;if (OB_FAIL(tenant_config_->system_config_.clear())) {LOG_WARN("Clear system config map failed", K(ret));} else if (OB_FAIL(config_mgr_->update_local(tenant_config_->tenant_id_, version))) {LOG_WARN("ObTenantConfigMgr update_local failed", K(ret), K(tenant_config_));} else {config_mgr_->notify_tenant_config_changed(tenant_config_->tenant_id_);}
- ObTenantConfigMgr::update_local(uint64_t tenant_id, int64_t expected_version)
查詢 __tenant_parameter 內(nèi)部表獲取當(dāng)前租戶的 config,然后將新的數(shù)據(jù)更新到 config中。
int ObTenantConfigMgr::update_local(uint64_t tenant_id, int64_t expected_version)
{SMART_VAR(ObMySQLProxy::MySQLResult, result) {if (OB_FAIL(sql.assign_fmt("select config_version, zone, svr_type, svr_ip, svr_port, name, ""data_type, value, info, section, scope, source, edit_level ""from %s where tenant_id = '%lu'", OB_TENANT_PARAMETER_TNAME, tenant_id))) {} else if (OB_FAIL(sql_client_retry_weak.read(result, exec_tenant_id, sql.ptr()))) {LOG_WARN("read config from __tenant_parameter failed",KR(ret), K(tenant_id), K(exec_tenant_id), K(sql));} else {DRWLock::WRLockGuard guard(rwlock_);ret = config_map_.get_refactored(ObTenantID(tenant_id), config);if (OB_FAIL(ret)) {LOG_ERROR("failed to get tenant config", K(tenant_id), K(ret));} else {ret = config->update_local(expected_version, result);}
- ObTenantConfig::update_local()
調(diào)用 system_config_.update() 函數(shù)將配置項(xiàng)更新到本地,然后調(diào)用 dump2file() 將配置項(xiàng)持久化到文件中。
int ObTenantConfig::update_local(int64_t expected_version, ObMySQLProxy::MySQLResult &result,bool save2file /* = true */)
{int ret = OB_SUCCESS;if (OB_FAIL(system_config_.update(result))) {LOG_WARN("failed to load system config", K(ret));if (OB_SUCC(ret)) {if (OB_FAIL(read_config())) {LOG_ERROR("Read tenant config failed", K_(tenant_id), K(ret));} else if (save2file && OB_FAIL(config_mgr_->dump2file())) {LOG_WARN("Dump to file failed", K(ret));
小結(jié)
新增配置項(xiàng)并不復(fù)雜,代碼中已經(jīng)實(shí)現(xiàn)了成熟的訪問(wèn)和同步機(jī)制,只需要使用合適的宏并填上一些參數(shù)就可以定義新配置項(xiàng)了,而后續(xù)如何使用這個(gè)配置項(xiàng)才是實(shí)現(xiàn)新功能的關(guān)鍵。在修改配置項(xiàng)的過(guò)程中,實(shí)際上還會(huì)進(jìn)行一些合法性檢查,這部分會(huì)在后面的文章中與系統(tǒng)變量一起進(jìn)行說(shuō)明。
本專題下一篇文章是關(guān)于“系統(tǒng)變量的定義和源碼解析”,系統(tǒng)變量的機(jī)制有別于配置項(xiàng),還有全局級(jí)和會(huì)話級(jí)的區(qū)分,感興趣的同學(xué)歡迎繼續(xù)關(guān)注。
參考資料
- 配置項(xiàng)總覽
- OceanBase源碼