命名服務(wù)
命名服務(wù)是用將易記的名稱(chēng)(通常是由人類(lèi)可讀的名稱(chēng))映射到特定的網(wǎng)絡(luò)資源或服務(wù)叫搁。它為用戶提供了一種方便的方式來(lái)訪問(wèn)網(wǎng)絡(luò)資源枉氮,而無(wú)需記住資源的物理地址或復(fù)雜的網(wǎng)絡(luò)標(biāo)識(shí)符雷滚。
BRPC NamingService
在brpc中西剥,使用NamingService框架來(lái)管理命名服務(wù)初斑,同時(shí)集成負(fù)載均衡策略宪肖。
在當(dāng)前BRPC版本(1.7.0)中已經(jīng)內(nèi)置了多種命名服務(wù)實(shí)現(xiàn)其中包括:BNS疯搅、DNS濒生、File、consul等方式幔欧。
而zookeeper也是一種比較常見(jiàn)罪治、常用的命名服務(wù)工具丽声,以下是通過(guò)zookeeper搭建命名服務(wù)并集成到BRPC的NamingService框架中。
zookeeper命名服務(wù)的實(shí)現(xiàn)
第一步:初始化連接ZK服務(wù)觉义,設(shè)置命名服務(wù)名稱(chēng)節(jié)點(diǎn)
int Connect() {
int ret = 0;
// 設(shè)置 ZK 日志級(jí)別
zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
// 完成zk初始化雁社,獲取zk句柄,連接ZK服務(wù)
zk_handle_ = zookeeper_init(zk_config_.GetServers().c_str(), zk_watcher_g,
zk_config_.GetTimeout(), 0, this, 0);
if (NULL == zk_handle_) {
LOG(ERROR) << "Error when connecting to zookeeper servers...";
return -1;
}
// 完成zk驗(yàn)證
if (!zk_config_.GetUserAuth().empty()) {
ret = zoo_add_auth(zk_handle_, "digest", zk_config_.GetUserAuth().c_str(),
zk_config_.GetUserAuth().length(), NULL, NULL);
if (ret) {
LOG(ERROR) << "error" << ret << " for verification.";
return -2;
}
}
// 設(shè)置命名服務(wù)ZK節(jié)點(diǎn)路徑 /naming_service_example/naming_service1
// 用于watch服務(wù)信息變化
data_dir_ = zk_config_.GetDataDir();
return 0;
};
第二步:添加Watcher 持續(xù)監(jiān)控命名服務(wù)下的節(jié)點(diǎn)變化
/**
* AddWatcher 添加 命名服務(wù) 節(jié)點(diǎn)監(jiān)控
* **/
void AddWatcher() {
int ret = zoo_get_children(zk_handle_, data_dir_.c_str(), 1, &children_names_);
if (ret != ZOK || children_names_.count <= 0) {
LOG(INFO) << "default zookeeper data parser data is empty." << children_names_.count;
}
// 處理命名服務(wù)節(jié)點(diǎn)下的內(nèi)容信息
parser_children_names();
runing_ = true;
watcher_thread_ = std::thread(&ZookeeperClient::do_parser_loop, this);
};
第三步:持續(xù)添加節(jié)點(diǎn)變化watcher
由于zookeeper的注冊(cè)event是一次性的晒骇,因此霉撵,watcher在接收到上次注冊(cè)的event以后就會(huì)失效,為了持續(xù)監(jiān)聽(tīng)zookeeper服務(wù)節(jié)點(diǎn)變化洪囤,每次watcher失效以后需要重新注冊(cè)watcher事件徒坡,因?yàn)槊?wù)一直監(jiān)聽(tīng)命名服務(wù)節(jié)點(diǎn)下的變化,因此可以通過(guò)zoo_get_children方法進(jìn)行watcher的重新注冊(cè)的功能
// 可通過(guò) 設(shè)置zoo_get_children的第三個(gè)參數(shù) watch 為非0 達(dá)到重新注冊(cè)的目的瘤缩,注冊(cè)的回調(diào)函數(shù)為zookeeper_init 傳入的 zk_watcher_g 方法
ZOOAPI int zoo_get_children(zhandle_t *zh, const char *path, int watch,
struct String_vector *strings)
static void zk_watcher_g(zhandle_t* zh, int type, int state, const char* path,
void* watcherCtx) {
ZookeeperClient *self = (ZookeeperClient *)watcherCtx;
if (type == ZOO_CHILD_EVENT) {
std::lock_guard<std::mutex> lock(self->mutex_);
zoo_get_children(zh, path, 1, &self->children_names_);
self->is_changed_ = true;
} else if (ZOO_SESSION_EVENT == type) {
if (ZOO_CONNECTED_STATE == state) {
LOG(INFO) << "Connected to ZooKeeper.";
} else if (ZOO_EXPIRED_SESSION_STATE == state) {
LOG(INFO) << "ZooKeeper session expired.";
// TODO: Handle session expiration
// do reconect
for (int i = 0; i < 10; i++) {
if (0 == self->Connect()) {
break;
}
LOG(WARNING) << "Do zookeeper reconnect, times:" << i;
sleep(5);
}
}
}
};
zookeeper命名服務(wù)集成到BRPC
命名服務(wù)集成到BRPC 的NamingService框架只需要集成NamingService然后重寫(xiě)父類(lèi)方法即可喇完,最好通過(guò)注冊(cè)的方式,集成到框架中
第一步:繼承brpc::NamingService 類(lèi)
class ZooKeeperNamingService : public brpc::PeriodicNamingService {}
第二步:重寫(xiě)父類(lèi)方法實(shí)現(xiàn)自定義處理邏輯
// 持續(xù)獲取命名服務(wù)節(jié)點(diǎn)下的服務(wù)器列表剥啤,根據(jù)各自的業(yè)務(wù)需求實(shí)現(xiàn)列表獲取邏輯
// 列表通過(guò) zk watcher 持續(xù)監(jiān)聽(tīng)變化更新
int GetServers(const char *service_name, std::vector<brpc::ServerNode> *servers) override;
// 服務(wù)列表更新時(shí)間間隔锦溪,也就是框架調(diào)用GetServers方法的時(shí)間間隔
int GetNamingServiceAccessIntervalMs() const override;
void Describe(std::ostream &os, const brpc::DescribeOptions &) const override;
// 生成 命名服務(wù)對(duì)象
NamingService *New() const override;
void Destroy() override;
第三步:集成到BRPC的NamingService框架
// 設(shè)置ZK連接信息
ZKNamingService::ZKConfig zk_config("127.0.0.1:2181", "",
"test", "ns_test", 1000);
// 初始化內(nèi)部ZK連接信息
ZKNamingService::ZooKeeperNamingService zkns(zk_config, 2000);
// 將命名服務(wù)注冊(cè)到 NamingService框架,該步驟只是將命名服務(wù)類(lèi)注冊(cè)到框架中府怯,并沒(méi)有實(shí)際執(zhí)行刻诊,
// 在channel.Init()時(shí)通過(guò)brpc::NamingService* ZooKeeperNamingService::New();方法重新生成對(duì)象并執(zhí)行相關(guān)邏輯
brpc::NamingServiceExtension()->RegisterOrDie("zk", &zkns);
brpc::Channel channel;
// Initialize the channel, NULL means using default options.
brpc::ChannelOptions options;
options.timeout_ms = 1000 /*milliseconds*/;
options.max_retry = 1;
// 使用命名服務(wù)
if (channel.Init("zk://ns_test", "rr", &options) != 0) {
LOG(ERROR) << "Fail to initialize channel";
return -1;
}