Commit e784526a authored by jamesge's avatar jamesge

Support multiple discovery nodes

parent 12e73e1d
...@@ -48,12 +48,32 @@ DEFINE_int32(discovery_reregister_threshold, 3, "The renew error threshold beyon ...@@ -48,12 +48,32 @@ DEFINE_int32(discovery_reregister_threshold, 3, "The renew error threshold beyon
static pthread_once_t s_init_discovery_channel_once = PTHREAD_ONCE_INIT; static pthread_once_t s_init_discovery_channel_once = PTHREAD_ONCE_INIT;
static Channel* s_discovery_channel = NULL; static Channel* s_discovery_channel = NULL;
int ParseNodesResult(const butil::IOBuf& buf, std::string* server_addr) { static int ListDiscoveryNodes(const char* discovery_api_addr, std::string* servers) {
Channel api_channel;
ChannelOptions channel_options;
channel_options.protocol = PROTOCOL_HTTP;
channel_options.timeout_ms = FLAGS_discovery_timeout_ms;
channel_options.connect_timeout_ms = FLAGS_discovery_timeout_ms / 3;
if (api_channel.Init(discovery_api_addr, "", &channel_options) != 0) {
LOG(FATAL) << "Fail to init channel to " << discovery_api_addr;
return -1;
}
Controller cntl;
cntl.http_request().uri() = discovery_api_addr;
api_channel.CallMethod(NULL, &cntl, NULL, NULL, NULL);
if (cntl.Failed()) {
LOG(FATAL) << "Fail to access " << cntl.http_request().uri()
<< ": " << cntl.ErrorText();
return -1;
}
servers->assign("list://");
const std::string response = cntl.response_attachment().to_string();
BUTIL_RAPIDJSON_NAMESPACE::Document d; BUTIL_RAPIDJSON_NAMESPACE::Document d;
const std::string response = buf.to_string();
d.Parse(response.c_str()); d.Parse(response.c_str());
if (!d.IsObject()) { if (!d.IsObject()) {
LOG(ERROR) << "Fail to parse " << buf << " as json object"; LOG(ERROR) << "Fail to parse " << response << " as json object";
return -1; return -1;
} }
auto itr = d.FindMember("data"); auto itr = d.FindMember("data");
...@@ -77,50 +97,39 @@ int ParseNodesResult(const butil::IOBuf& buf, std::string* server_addr) { ...@@ -77,50 +97,39 @@ int ParseNodesResult(const butil::IOBuf& buf, std::string* server_addr) {
itr_status->value.GetUint() != 0) { itr_status->value.GetUint() != 0) {
continue; continue;
} }
server_addr->assign(itr_addr->value.GetString(), servers->push_back(',');
itr_addr->value.GetStringLength()); servers->append(itr_addr->value.GetString(),
// Currently, we just use the first successful result itr_addr->value.GetStringLength());
break;
} }
return 0; return 0;
} }
static void InitDiscoveryChannel() { static void NewDiscoveryChannel() {
Channel api_channel; // NOTE: Newly added discovery server is NOT detected until this server
// is restarted. The reasons for this design is that NS cluster rarely
// changes. Although we could detect new discovery servers by implmenenting
// a NamingService, however which is too heavy for solving such a rare case.
std::string discovery_servers;
if (ListDiscoveryNodes(FLAGS_discovery_api_addr.c_str(), &discovery_servers) != 0) {
LOG(ERROR) << "Fail to get discovery nodes from " << FLAGS_discovery_api_addr;
return;
}
ChannelOptions channel_options; ChannelOptions channel_options;
channel_options.protocol = PROTOCOL_HTTP; channel_options.protocol = PROTOCOL_HTTP;
channel_options.timeout_ms = FLAGS_discovery_timeout_ms; channel_options.timeout_ms = FLAGS_discovery_timeout_ms;
channel_options.connect_timeout_ms = FLAGS_discovery_timeout_ms / 3; channel_options.connect_timeout_ms = FLAGS_discovery_timeout_ms / 3;
if (api_channel.Init(FLAGS_discovery_api_addr.c_str(), "", &channel_options) != 0) {
LOG(FATAL) << "Fail to init channel to " << FLAGS_discovery_api_addr;
return;
}
Controller cntl;
cntl.http_request().uri() = FLAGS_discovery_api_addr;
api_channel.CallMethod(NULL, &cntl, NULL, NULL, NULL);
if (cntl.Failed()) {
LOG(FATAL) << "Fail to access " << cntl.http_request().uri()
<< ": " << cntl.ErrorText();
return;
}
std::string discovery_addr;
if (ParseNodesResult(cntl.response_attachment(), &discovery_addr) != 0) {
LOG(FATAL) << "Fail to parse nodes result from discovery api server";
return;
}
s_discovery_channel = new Channel; s_discovery_channel = new Channel;
if (s_discovery_channel->Init(discovery_addr.c_str(), "", &channel_options) != 0) { if (s_discovery_channel->Init(discovery_servers.c_str(), "rr", &channel_options) != 0) {
LOG(FATAL) << "Fail to init channel to " << discovery_addr; LOG(ERROR) << "Fail to init channel to " << discovery_servers;
return; return;
} }
} }
Channel* GetDiscoveryChannel() { inline Channel* GetOrNewDiscoveryChannel() {
pthread_once(&s_init_discovery_channel_once, InitDiscoveryChannel); pthread_once(&s_init_discovery_channel_once, NewDiscoveryChannel);
return s_discovery_channel; return s_discovery_channel;
} }
bool DiscoveryRegisterParam::IsValid() const { bool DiscoveryRegisterParam::IsValid() const {
return !appid.empty() && !hostname.empty() && !addrs.empty() && return !appid.empty() && !hostname.empty() && !addrs.empty() &&
!env.empty() && !zone.empty() && !version.empty(); !env.empty() && !zone.empty() && !version.empty();
...@@ -138,7 +147,7 @@ DiscoveryClient::~DiscoveryClient() { ...@@ -138,7 +147,7 @@ DiscoveryClient::~DiscoveryClient() {
} }
} }
int ParseCommonResult(const butil::IOBuf& buf, std::string* error_text) { static int ParseCommonResult(const butil::IOBuf& buf, std::string* error_text) {
const std::string s = buf.to_string(); const std::string s = buf.to_string();
BUTIL_RAPIDJSON_NAMESPACE::Document d; BUTIL_RAPIDJSON_NAMESPACE::Document d;
d.Parse(s.c_str()); d.Parse(s.c_str());
...@@ -161,6 +170,17 @@ int ParseCommonResult(const butil::IOBuf& buf, std::string* error_text) { ...@@ -161,6 +170,17 @@ int ParseCommonResult(const butil::IOBuf& buf, std::string* error_text) {
} }
int DiscoveryClient::DoRenew() const { int DiscoveryClient::DoRenew() const {
// May create short connections which are OK.
ChannelOptions channel_options;
channel_options.protocol = PROTOCOL_HTTP;
channel_options.timeout_ms = FLAGS_discovery_timeout_ms;
channel_options.connect_timeout_ms = FLAGS_discovery_timeout_ms / 3;
Channel chan;
if (chan.Init(_current_discovery_server, &channel_options) != 0) {
LOG(FATAL) << "Fail to init channel to " << _current_discovery_server;
return -1;
}
Controller cntl; Controller cntl;
cntl.http_request().set_method(HTTP_METHOD_POST); cntl.http_request().set_method(HTTP_METHOD_POST);
cntl.http_request().uri() = "/discovery/renew"; cntl.http_request().uri() = "/discovery/renew";
...@@ -172,7 +192,7 @@ int DiscoveryClient::DoRenew() const { ...@@ -172,7 +192,7 @@ int DiscoveryClient::DoRenew() const {
<< "&region=" << _params.region << "&region=" << _params.region
<< "&zone=" << _params.zone; << "&zone=" << _params.zone;
os.move_to(cntl.request_attachment()); os.move_to(cntl.request_attachment());
GetDiscoveryChannel()->CallMethod(NULL, &cntl, NULL, NULL, NULL); chan.CallMethod(NULL, &cntl, NULL, NULL, NULL);
if (cntl.Failed()) { if (cntl.Failed()) {
LOG(ERROR) << "Fail to post /discovery/renew: " << cntl.ErrorText(); LOG(ERROR) << "Fail to post /discovery/renew: " << cntl.ErrorText();
return -1; return -1;
...@@ -219,15 +239,15 @@ void* DiscoveryClient::PeriodicRenew(void* arg) { ...@@ -219,15 +239,15 @@ void* DiscoveryClient::PeriodicRenew(void* arg) {
return NULL; return NULL;
} }
int DiscoveryClient::Register(const DiscoveryRegisterParam& req) { int DiscoveryClient::Register(const DiscoveryRegisterParam& params) {
if (_registered.load(butil::memory_order_relaxed) || if (_registered.load(butil::memory_order_relaxed) ||
_registered.exchange(true, butil::memory_order_release)) { _registered.exchange(true, butil::memory_order_release)) {
return 0; return 0;
} }
if (!req.IsValid()) { if (!params.IsValid()) {
return -1; return -1;
} }
_params = req; _params = params;
if (DoRegister() != 0) { if (DoRegister() != 0) {
return -1; return -1;
...@@ -239,7 +259,12 @@ int DiscoveryClient::Register(const DiscoveryRegisterParam& req) { ...@@ -239,7 +259,12 @@ int DiscoveryClient::Register(const DiscoveryRegisterParam& req) {
return 0; return 0;
} }
int DiscoveryClient::DoRegister() const { int DiscoveryClient::DoRegister() {
Channel* chan = GetOrNewDiscoveryChannel();
if (NULL == chan) {
LOG(ERROR) << "Fail to create discovery channel";
return -1;
}
Controller cntl; Controller cntl;
cntl.http_request().set_method(HTTP_METHOD_POST); cntl.http_request().set_method(HTTP_METHOD_POST);
cntl.http_request().uri() = "/discovery/register"; cntl.http_request().uri() = "/discovery/register";
...@@ -255,7 +280,7 @@ int DiscoveryClient::DoRegister() const { ...@@ -255,7 +280,7 @@ int DiscoveryClient::DoRegister() const {
<< "&version=" << _params.version << "&version=" << _params.version
<< "&metadata=" << _params.metadata; << "&metadata=" << _params.metadata;
os.move_to(cntl.request_attachment()); os.move_to(cntl.request_attachment());
GetDiscoveryChannel()->CallMethod(NULL, &cntl, NULL, NULL, NULL); chan->CallMethod(NULL, &cntl, NULL, NULL, NULL);
if (cntl.Failed()) { if (cntl.Failed()) {
LOG(ERROR) << "Fail to register " << _params.appid << ": " << cntl.ErrorText(); LOG(ERROR) << "Fail to register " << _params.appid << ": " << cntl.ErrorText();
return -1; return -1;
...@@ -266,10 +291,22 @@ int DiscoveryClient::DoRegister() const { ...@@ -266,10 +291,22 @@ int DiscoveryClient::DoRegister() const {
<< ": " << error_text; << ": " << error_text;
return -1; return -1;
} }
_current_discovery_server = cntl.remote_side();
return 0; return 0;
} }
int DiscoveryClient::DoCancel() const { int DiscoveryClient::DoCancel() const {
// May create short connections which are OK.
ChannelOptions channel_options;
channel_options.protocol = PROTOCOL_HTTP;
channel_options.timeout_ms = FLAGS_discovery_timeout_ms;
channel_options.connect_timeout_ms = FLAGS_discovery_timeout_ms / 3;
Channel chan;
if (chan.Init(_current_discovery_server, &channel_options) != 0) {
LOG(FATAL) << "Fail to init channel to " << _current_discovery_server;
return -1;
}
Controller cntl; Controller cntl;
cntl.http_request().set_method(HTTP_METHOD_POST); cntl.http_request().set_method(HTTP_METHOD_POST);
cntl.http_request().uri() = "/discovery/cancel"; cntl.http_request().uri() = "/discovery/cancel";
...@@ -281,7 +318,7 @@ int DiscoveryClient::DoCancel() const { ...@@ -281,7 +318,7 @@ int DiscoveryClient::DoCancel() const {
<< "&region=" << _params.region << "&region=" << _params.region
<< "&zone=" << _params.zone; << "&zone=" << _params.zone;
os.move_to(cntl.request_attachment()); os.move_to(cntl.request_attachment());
GetDiscoveryChannel()->CallMethod(NULL, &cntl, NULL, NULL, NULL); chan.CallMethod(NULL, &cntl, NULL, NULL, NULL);
if (cntl.Failed()) { if (cntl.Failed()) {
LOG(ERROR) << "Fail to post /discovery/cancel: " << cntl.ErrorText(); LOG(ERROR) << "Fail to post /discovery/cancel: " << cntl.ErrorText();
return -1; return -1;
...@@ -305,12 +342,16 @@ int DiscoveryNamingService::GetServers(const char* service_name, ...@@ -305,12 +342,16 @@ int DiscoveryNamingService::GetServers(const char* service_name,
LOG_ONCE(ERROR) << "Invalid parameters"; LOG_ONCE(ERROR) << "Invalid parameters";
return -1; return -1;
} }
Channel* chan = GetOrNewDiscoveryChannel();
if (NULL == chan) {
LOG(ERROR) << "Fail to create discovery channel";
}
servers->clear(); servers->clear();
Controller cntl; Controller cntl;
cntl.http_request().uri() = butil::string_printf( cntl.http_request().uri() = butil::string_printf(
"/discovery/fetchs?appid=%s&env=%s&status=%s", service_name, "/discovery/fetchs?appid=%s&env=%s&status=%s", service_name,
FLAGS_discovery_env.c_str(), FLAGS_discovery_status.c_str()); FLAGS_discovery_env.c_str(), FLAGS_discovery_status.c_str());
GetDiscoveryChannel()->CallMethod(NULL, &cntl, NULL, NULL, NULL); chan->CallMethod(NULL, &cntl, NULL, NULL, NULL);
if (cntl.Failed()) { if (cntl.Failed()) {
LOG(ERROR) << "Fail to get /discovery/fetchs: " << cntl.ErrorText(); LOG(ERROR) << "Fail to get /discovery/fetchs: " << cntl.ErrorText();
return -1; return -1;
......
...@@ -44,24 +44,28 @@ struct DiscoveryRegisterParam { ...@@ -44,24 +44,28 @@ struct DiscoveryRegisterParam {
// ONE DiscoveryClient corresponds to ONE service instance. // ONE DiscoveryClient corresponds to ONE service instance.
// If your program has multiple service instances to register, // If your program has multiple service instances to register,
// you need multiple DiscoveryClient. // you need multiple DiscoveryClient.
// Note: Unregister is automatically called in dtor. // Note: Cancel to the server is automatically called in dtor.
class DiscoveryClient { class DiscoveryClient {
public: public:
DiscoveryClient(); DiscoveryClient();
~DiscoveryClient(); ~DiscoveryClient();
// Initialize this client.
// Returns 0 on success.
// NOTE: Calling more than once does nothing and returns 0.
int Register(const DiscoveryRegisterParam& req); int Register(const DiscoveryRegisterParam& req);
private: private:
static void* PeriodicRenew(void* arg); static void* PeriodicRenew(void* arg);
int DoCancel() const; int DoCancel() const;
int DoRegister() const; int DoRegister();
int DoRenew() const; int DoRenew() const;
private: private:
bthread_t _th; bthread_t _th;
butil::atomic<bool> _registered; butil::atomic<bool> _registered;
DiscoveryRegisterParam _params; DiscoveryRegisterParam _params;
butil::EndPoint _current_discovery_server;
}; };
class DiscoveryNamingService : public PeriodicNamingService { class DiscoveryNamingService : public PeriodicNamingService {
......
...@@ -46,9 +46,6 @@ DECLARE_string(discovery_api_addr); ...@@ -46,9 +46,6 @@ DECLARE_string(discovery_api_addr);
DECLARE_string(discovery_env); DECLARE_string(discovery_env);
DECLARE_int32(discovery_renew_interval_s); DECLARE_int32(discovery_renew_interval_s);
// Defined in discovery_naming_service.cpp
int ParseNodesResult(const butil::IOBuf& buf, std::string* server_addr);
} // policy } // policy
} // brpc } // brpc
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment