|
|
|
@ -49,109 +49,168 @@ |
|
|
|
|
#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 |
|
|
|
|
#define GRPC_DNS_RECONNECT_JITTER 0.2 |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
/** base class: must be first */ |
|
|
|
|
grpc_resolver base; |
|
|
|
|
/** DNS server to use (if not system default) */ |
|
|
|
|
char* dns_server; |
|
|
|
|
/** name to resolve (usually the same as target_name) */ |
|
|
|
|
char* name_to_resolve; |
|
|
|
|
/** default port to use */ |
|
|
|
|
char* default_port; |
|
|
|
|
/** channel args. */ |
|
|
|
|
grpc_channel_args* channel_args; |
|
|
|
|
/** whether to request the service config */ |
|
|
|
|
bool request_service_config; |
|
|
|
|
/** pollset_set to drive the name resolution process */ |
|
|
|
|
grpc_pollset_set* interested_parties; |
|
|
|
|
|
|
|
|
|
/** Closures used by the combiner */ |
|
|
|
|
grpc_closure dns_ares_on_next_resolution_timer_closure; |
|
|
|
|
grpc_closure dns_ares_on_resolved_closure; |
|
|
|
|
|
|
|
|
|
/** Combiner guarding the rest of the state */ |
|
|
|
|
grpc_combiner* combiner; |
|
|
|
|
/** are we currently resolving? */ |
|
|
|
|
bool resolving; |
|
|
|
|
/** the pending resolving request */ |
|
|
|
|
grpc_ares_request* pending_request; |
|
|
|
|
/** which version of the result have we published? */ |
|
|
|
|
int published_version; |
|
|
|
|
/** which version of the result is current? */ |
|
|
|
|
int resolved_version; |
|
|
|
|
/** pending next completion, or NULL */ |
|
|
|
|
grpc_closure* next_completion; |
|
|
|
|
/** target result address for next completion */ |
|
|
|
|
grpc_channel_args** target_result; |
|
|
|
|
/** current (fully resolved) result */ |
|
|
|
|
grpc_channel_args* resolved_result; |
|
|
|
|
/** next resolution timer */ |
|
|
|
|
bool have_next_resolution_timer; |
|
|
|
|
grpc_timer next_resolution_timer; |
|
|
|
|
/** retry backoff state */ |
|
|
|
|
grpc_core::ManualConstructor<grpc_core::BackOff> backoff; |
|
|
|
|
/** min resolution period. Max one resolution will happen per period */ |
|
|
|
|
grpc_millis min_time_between_resolutions; |
|
|
|
|
/** when was the last resolution? -1 if no resolution has happened yet */ |
|
|
|
|
grpc_millis last_resolution_timestamp; |
|
|
|
|
/** currently resolving addresses */ |
|
|
|
|
grpc_lb_addresses* lb_addresses; |
|
|
|
|
/** currently resolving service config */ |
|
|
|
|
char* service_config_json; |
|
|
|
|
} ares_dns_resolver; |
|
|
|
|
|
|
|
|
|
static void dns_ares_destroy(grpc_resolver* r); |
|
|
|
|
|
|
|
|
|
static void dns_ares_start_resolving_locked(ares_dns_resolver* r); |
|
|
|
|
static void dns_ares_maybe_start_resolving_locked(ares_dns_resolver* r); |
|
|
|
|
static void dns_ares_maybe_finish_next_locked(ares_dns_resolver* r); |
|
|
|
|
|
|
|
|
|
static void dns_ares_shutdown_locked(grpc_resolver* r); |
|
|
|
|
static void dns_ares_channel_saw_error_locked(grpc_resolver* r); |
|
|
|
|
static void dns_ares_next_locked(grpc_resolver* r, |
|
|
|
|
grpc_channel_args** target_result, |
|
|
|
|
grpc_closure* on_complete); |
|
|
|
|
|
|
|
|
|
static const grpc_resolver_vtable dns_ares_resolver_vtable = { |
|
|
|
|
dns_ares_destroy, dns_ares_shutdown_locked, |
|
|
|
|
dns_ares_channel_saw_error_locked, dns_ares_next_locked}; |
|
|
|
|
|
|
|
|
|
static void dns_ares_shutdown_locked(grpc_resolver* resolver) { |
|
|
|
|
ares_dns_resolver* r = (ares_dns_resolver*)resolver; |
|
|
|
|
if (r->have_next_resolution_timer) { |
|
|
|
|
grpc_timer_cancel(&r->next_resolution_timer); |
|
|
|
|
namespace grpc_core { |
|
|
|
|
|
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
const char kDefaultPort[] = "https"; |
|
|
|
|
|
|
|
|
|
class AresDnsResolver : public Resolver { |
|
|
|
|
public: |
|
|
|
|
explicit AresDnsResolver(const ResolverArgs& args); |
|
|
|
|
|
|
|
|
|
void NextLocked(grpc_channel_args** result, |
|
|
|
|
grpc_closure* on_complete) override; |
|
|
|
|
|
|
|
|
|
void RequestReresolutionLocked() override; |
|
|
|
|
|
|
|
|
|
void ShutdownLocked() override; |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
virtual ~AresDnsResolver(); |
|
|
|
|
|
|
|
|
|
void MaybeStartResolvingLocked(); |
|
|
|
|
void StartResolvingLocked(); |
|
|
|
|
void MaybeFinishNextLocked(); |
|
|
|
|
|
|
|
|
|
static void OnNextResolutionLocked(void* arg, grpc_error* error); |
|
|
|
|
static void OnResolvedLocked(void* arg, grpc_error* error); |
|
|
|
|
|
|
|
|
|
/// DNS server to use (if not system default)
|
|
|
|
|
char* dns_server_; |
|
|
|
|
/// name to resolve (usually the same as target_name)
|
|
|
|
|
char* name_to_resolve_; |
|
|
|
|
/// channel args
|
|
|
|
|
grpc_channel_args* channel_args_; |
|
|
|
|
/// whether to request the service config
|
|
|
|
|
bool request_service_config_; |
|
|
|
|
/// pollset_set to drive the name resolution process
|
|
|
|
|
grpc_pollset_set* interested_parties_; |
|
|
|
|
/// closures used by the combiner
|
|
|
|
|
grpc_closure on_next_resolution_; |
|
|
|
|
grpc_closure on_resolved_; |
|
|
|
|
/// are we currently resolving?
|
|
|
|
|
bool resolving_ = false; |
|
|
|
|
/// the pending resolving request
|
|
|
|
|
grpc_ares_request* pending_request_ = nullptr; |
|
|
|
|
/// which version of the result have we published?
|
|
|
|
|
int published_version_ = 0; |
|
|
|
|
/// which version of the result is current?
|
|
|
|
|
int resolved_version_ = 0; |
|
|
|
|
/// pending next completion, or NULL
|
|
|
|
|
grpc_closure* next_completion_ = nullptr; |
|
|
|
|
/// target result address for next completion
|
|
|
|
|
grpc_channel_args** target_result_ = nullptr; |
|
|
|
|
/// current (fully resolved) result
|
|
|
|
|
grpc_channel_args* resolved_result_ = nullptr; |
|
|
|
|
/// next resolution timer
|
|
|
|
|
bool have_next_resolution_timer_ = false; |
|
|
|
|
grpc_timer next_resolution_timer_; |
|
|
|
|
/// min interval between DNS requests
|
|
|
|
|
grpc_millis min_time_between_resolutions_; |
|
|
|
|
/// timestamp of last DNS request
|
|
|
|
|
grpc_millis last_resolution_timestamp_ = -1; |
|
|
|
|
/// retry backoff state
|
|
|
|
|
BackOff backoff_; |
|
|
|
|
/// currently resolving addresses
|
|
|
|
|
grpc_lb_addresses* lb_addresses_ = nullptr; |
|
|
|
|
/// currently resolving service config
|
|
|
|
|
char* service_config_json_ = nullptr; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
AresDnsResolver::AresDnsResolver(const ResolverArgs& args) |
|
|
|
|
: Resolver(args.combiner), |
|
|
|
|
backoff_( |
|
|
|
|
BackOff::Options() |
|
|
|
|
.set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * |
|
|
|
|
1000) |
|
|
|
|
.set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER) |
|
|
|
|
.set_jitter(GRPC_DNS_RECONNECT_JITTER) |
|
|
|
|
.set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) { |
|
|
|
|
// Get name to resolve from URI path.
|
|
|
|
|
const char* path = args.uri->path; |
|
|
|
|
if (path[0] == '/') ++path; |
|
|
|
|
name_to_resolve_ = gpr_strdup(path); |
|
|
|
|
// Get DNS server from URI authority.
|
|
|
|
|
if (0 != strcmp(args.uri->authority, "")) { |
|
|
|
|
dns_server_ = gpr_strdup(args.uri->authority); |
|
|
|
|
} |
|
|
|
|
channel_args_ = grpc_channel_args_copy(args.args); |
|
|
|
|
const grpc_arg* arg = grpc_channel_args_find( |
|
|
|
|
channel_args_, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION); |
|
|
|
|
request_service_config_ = !grpc_channel_arg_get_integer( |
|
|
|
|
arg, (grpc_integer_options){false, false, true}); |
|
|
|
|
arg = grpc_channel_args_find(channel_args_, |
|
|
|
|
GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS); |
|
|
|
|
min_time_between_resolutions_ = |
|
|
|
|
grpc_channel_arg_get_integer(arg, {1000, 0, INT_MAX}); |
|
|
|
|
interested_parties_ = grpc_pollset_set_create(); |
|
|
|
|
if (args.pollset_set != nullptr) { |
|
|
|
|
grpc_pollset_set_add_pollset_set(interested_parties_, args.pollset_set); |
|
|
|
|
} |
|
|
|
|
GRPC_CLOSURE_INIT(&on_next_resolution_, OnNextResolutionLocked, this, |
|
|
|
|
grpc_combiner_scheduler(combiner())); |
|
|
|
|
GRPC_CLOSURE_INIT(&on_resolved_, OnResolvedLocked, this, |
|
|
|
|
grpc_combiner_scheduler(combiner())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
AresDnsResolver::~AresDnsResolver() { |
|
|
|
|
gpr_log(GPR_DEBUG, "destroying AresDnsResolver"); |
|
|
|
|
if (resolved_result_ != nullptr) { |
|
|
|
|
grpc_channel_args_destroy(resolved_result_); |
|
|
|
|
} |
|
|
|
|
if (r->pending_request != nullptr) { |
|
|
|
|
grpc_cancel_ares_request(r->pending_request); |
|
|
|
|
grpc_pollset_set_destroy(interested_parties_); |
|
|
|
|
gpr_free(dns_server_); |
|
|
|
|
gpr_free(name_to_resolve_); |
|
|
|
|
grpc_channel_args_destroy(channel_args_); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void AresDnsResolver::NextLocked(grpc_channel_args** target_result, |
|
|
|
|
grpc_closure* on_complete) { |
|
|
|
|
gpr_log(GPR_DEBUG, "AresDnsResolver::NextLocked() is called."); |
|
|
|
|
GPR_ASSERT(next_completion_ == nullptr); |
|
|
|
|
next_completion_ = on_complete; |
|
|
|
|
target_result_ = target_result; |
|
|
|
|
if (resolved_version_ == 0 && !resolving_) { |
|
|
|
|
MaybeStartResolvingLocked(); |
|
|
|
|
} else { |
|
|
|
|
MaybeFinishNextLocked(); |
|
|
|
|
} |
|
|
|
|
if (r->next_completion != nullptr) { |
|
|
|
|
*r->target_result = nullptr; |
|
|
|
|
GRPC_CLOSURE_SCHED(r->next_completion, GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"Resolver Shutdown")); |
|
|
|
|
r->next_completion = nullptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void AresDnsResolver::RequestReresolutionLocked() { |
|
|
|
|
if (!resolving_) { |
|
|
|
|
MaybeStartResolvingLocked(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void dns_ares_channel_saw_error_locked(grpc_resolver* resolver) { |
|
|
|
|
ares_dns_resolver* r = (ares_dns_resolver*)resolver; |
|
|
|
|
if (!r->resolving) { |
|
|
|
|
dns_ares_maybe_start_resolving_locked(r); |
|
|
|
|
void AresDnsResolver::ShutdownLocked() { |
|
|
|
|
if (have_next_resolution_timer_) { |
|
|
|
|
grpc_timer_cancel(&next_resolution_timer_); |
|
|
|
|
} |
|
|
|
|
if (pending_request_ != nullptr) { |
|
|
|
|
grpc_cancel_ares_request(pending_request_); |
|
|
|
|
} |
|
|
|
|
if (next_completion_ != nullptr) { |
|
|
|
|
*target_result_ = nullptr; |
|
|
|
|
GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"Resolver Shutdown")); |
|
|
|
|
next_completion_ = nullptr; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void dns_ares_on_next_resolution_timer_locked(void* arg, |
|
|
|
|
grpc_error* error) { |
|
|
|
|
ares_dns_resolver* r = (ares_dns_resolver*)arg; |
|
|
|
|
r->have_next_resolution_timer = false; |
|
|
|
|
void AresDnsResolver::OnNextResolutionLocked(void* arg, grpc_error* error) { |
|
|
|
|
AresDnsResolver* r = static_cast<AresDnsResolver*>(arg); |
|
|
|
|
r->have_next_resolution_timer_ = false; |
|
|
|
|
if (error == GRPC_ERROR_NONE) { |
|
|
|
|
if (!r->resolving) { |
|
|
|
|
dns_ares_start_resolving_locked(r); |
|
|
|
|
if (!r->resolving_) { |
|
|
|
|
r->StartResolvingLocked(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
GRPC_RESOLVER_UNREF(&r->base, "next_resolution_timer"); |
|
|
|
|
r->Unref(DEBUG_LOCATION, "next_resolution_timer"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool value_in_json_array(grpc_json* array, const char* value) { |
|
|
|
|
bool ValueInJsonArray(grpc_json* array, const char* value) { |
|
|
|
|
for (grpc_json* entry = array->child; entry != nullptr; entry = entry->next) { |
|
|
|
|
if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) { |
|
|
|
|
return true; |
|
|
|
@ -160,7 +219,7 @@ static bool value_in_json_array(grpc_json* array, const char* value) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static char* choose_service_config(char* service_config_choice_json) { |
|
|
|
|
char* ChooseServiceConfig(char* service_config_choice_json) { |
|
|
|
|
grpc_json* choices_json = grpc_json_parse_string(service_config_choice_json); |
|
|
|
|
if (choices_json == nullptr || choices_json->type != GRPC_JSON_ARRAY) { |
|
|
|
|
gpr_log(GPR_ERROR, "cannot parse service config JSON string"); |
|
|
|
@ -178,8 +237,7 @@ static char* choose_service_config(char* service_config_choice_json) { |
|
|
|
|
field = field->next) { |
|
|
|
|
// Check client language, if specified.
|
|
|
|
|
if (strcmp(field->key, "clientLanguage") == 0) { |
|
|
|
|
if (field->type != GRPC_JSON_ARRAY || |
|
|
|
|
!value_in_json_array(field, "c++")) { |
|
|
|
|
if (field->type != GRPC_JSON_ARRAY || !ValueInJsonArray(field, "c++")) { |
|
|
|
|
service_config_json = nullptr; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
@ -188,7 +246,7 @@ static char* choose_service_config(char* service_config_choice_json) { |
|
|
|
|
if (strcmp(field->key, "clientHostname") == 0) { |
|
|
|
|
char* hostname = grpc_gethostname(); |
|
|
|
|
if (hostname == nullptr || field->type != GRPC_JSON_ARRAY || |
|
|
|
|
!value_in_json_array(field, hostname)) { |
|
|
|
|
!ValueInJsonArray(field, hostname)) { |
|
|
|
|
service_config_json = nullptr; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
@ -223,24 +281,24 @@ static char* choose_service_config(char* service_config_choice_json) { |
|
|
|
|
return service_config; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void dns_ares_on_resolved_locked(void* arg, grpc_error* error) { |
|
|
|
|
ares_dns_resolver* r = (ares_dns_resolver*)arg; |
|
|
|
|
void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) { |
|
|
|
|
AresDnsResolver* r = static_cast<AresDnsResolver*>(arg); |
|
|
|
|
grpc_channel_args* result = nullptr; |
|
|
|
|
GPR_ASSERT(r->resolving); |
|
|
|
|
r->resolving = false; |
|
|
|
|
r->pending_request = nullptr; |
|
|
|
|
if (r->lb_addresses != nullptr) { |
|
|
|
|
GPR_ASSERT(r->resolving_); |
|
|
|
|
r->resolving_ = false; |
|
|
|
|
r->pending_request_ = nullptr; |
|
|
|
|
if (r->lb_addresses_ != nullptr) { |
|
|
|
|
static const char* args_to_remove[2]; |
|
|
|
|
size_t num_args_to_remove = 0; |
|
|
|
|
grpc_arg new_args[3]; |
|
|
|
|
size_t num_args_to_add = 0; |
|
|
|
|
new_args[num_args_to_add++] = |
|
|
|
|
grpc_lb_addresses_create_channel_arg(r->lb_addresses); |
|
|
|
|
grpc_lb_addresses_create_channel_arg(r->lb_addresses_); |
|
|
|
|
grpc_service_config* service_config = nullptr; |
|
|
|
|
char* service_config_string = nullptr; |
|
|
|
|
if (r->service_config_json != nullptr) { |
|
|
|
|
service_config_string = choose_service_config(r->service_config_json); |
|
|
|
|
gpr_free(r->service_config_json); |
|
|
|
|
if (r->service_config_json_ != nullptr) { |
|
|
|
|
service_config_string = ChooseServiceConfig(r->service_config_json_); |
|
|
|
|
gpr_free(r->service_config_json_); |
|
|
|
|
if (service_config_string != nullptr) { |
|
|
|
|
gpr_log(GPR_INFO, "selected service config choice: %s", |
|
|
|
|
service_config_string); |
|
|
|
@ -260,221 +318,150 @@ static void dns_ares_on_resolved_locked(void* arg, grpc_error* error) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
result = grpc_channel_args_copy_and_add_and_remove( |
|
|
|
|
r->channel_args, args_to_remove, num_args_to_remove, new_args, |
|
|
|
|
r->channel_args_, args_to_remove, num_args_to_remove, new_args, |
|
|
|
|
num_args_to_add); |
|
|
|
|
if (service_config != nullptr) grpc_service_config_destroy(service_config); |
|
|
|
|
gpr_free(service_config_string); |
|
|
|
|
grpc_lb_addresses_destroy(r->lb_addresses); |
|
|
|
|
grpc_lb_addresses_destroy(r->lb_addresses_); |
|
|
|
|
// Reset backoff state so that we start from the beginning when the
|
|
|
|
|
// next request gets triggered.
|
|
|
|
|
r->backoff->Reset(); |
|
|
|
|
r->backoff_.Reset(); |
|
|
|
|
} else { |
|
|
|
|
const char* msg = grpc_error_string(error); |
|
|
|
|
gpr_log(GPR_DEBUG, "dns resolution failed: %s", msg); |
|
|
|
|
grpc_millis next_try = r->backoff->NextAttemptTime(); |
|
|
|
|
grpc_millis timeout = next_try - grpc_core::ExecCtx::Get()->Now(); |
|
|
|
|
grpc_millis next_try = r->backoff_.NextAttemptTime(); |
|
|
|
|
grpc_millis timeout = next_try - ExecCtx::Get()->Now(); |
|
|
|
|
gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", |
|
|
|
|
grpc_error_string(error)); |
|
|
|
|
GPR_ASSERT(!r->have_next_resolution_timer); |
|
|
|
|
r->have_next_resolution_timer = true; |
|
|
|
|
GRPC_RESOLVER_REF(&r->base, "next_resolution_timer"); |
|
|
|
|
GPR_ASSERT(!r->have_next_resolution_timer_); |
|
|
|
|
r->have_next_resolution_timer_ = true; |
|
|
|
|
// TODO(roth): We currently deal with this ref manually. Once the
|
|
|
|
|
// new closure API is done, find a way to track this ref with the timer
|
|
|
|
|
// callback as part of the type system.
|
|
|
|
|
RefCountedPtr<Resolver> self = r->Ref(DEBUG_LOCATION, "retry-timer"); |
|
|
|
|
self.release(); |
|
|
|
|
if (timeout > 0) { |
|
|
|
|
gpr_log(GPR_DEBUG, "retrying in %" PRIdPTR " milliseconds", timeout); |
|
|
|
|
} else { |
|
|
|
|
gpr_log(GPR_DEBUG, "retrying immediately"); |
|
|
|
|
} |
|
|
|
|
grpc_timer_init(&r->next_resolution_timer, next_try, |
|
|
|
|
&r->dns_ares_on_next_resolution_timer_closure); |
|
|
|
|
grpc_timer_init(&r->next_resolution_timer_, next_try, |
|
|
|
|
&r->on_next_resolution_); |
|
|
|
|
} |
|
|
|
|
if (r->resolved_result != nullptr) { |
|
|
|
|
grpc_channel_args_destroy(r->resolved_result); |
|
|
|
|
} |
|
|
|
|
r->resolved_result = result; |
|
|
|
|
r->last_resolution_timestamp = grpc_core::ExecCtx::Get()->Now(); |
|
|
|
|
r->resolved_version++; |
|
|
|
|
dns_ares_maybe_finish_next_locked(r); |
|
|
|
|
GRPC_RESOLVER_UNREF(&r->base, "dns-resolving"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void dns_ares_next_locked(grpc_resolver* resolver, |
|
|
|
|
grpc_channel_args** target_result, |
|
|
|
|
grpc_closure* on_complete) { |
|
|
|
|
gpr_log(GPR_DEBUG, "dns_ares_next is called."); |
|
|
|
|
ares_dns_resolver* r = (ares_dns_resolver*)resolver; |
|
|
|
|
GPR_ASSERT(!r->next_completion); |
|
|
|
|
r->next_completion = on_complete; |
|
|
|
|
r->target_result = target_result; |
|
|
|
|
if (r->resolved_version == 0 && !r->resolving) { |
|
|
|
|
dns_ares_maybe_start_resolving_locked(r); |
|
|
|
|
} else { |
|
|
|
|
dns_ares_maybe_finish_next_locked(r); |
|
|
|
|
if (r->resolved_result_ != nullptr) { |
|
|
|
|
grpc_channel_args_destroy(r->resolved_result_); |
|
|
|
|
} |
|
|
|
|
r->resolved_result_ = result; |
|
|
|
|
++r->resolved_version_; |
|
|
|
|
r->MaybeFinishNextLocked(); |
|
|
|
|
r->Unref(DEBUG_LOCATION, "dns-resolving"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void dns_ares_start_resolving_locked(ares_dns_resolver* r) { |
|
|
|
|
GRPC_RESOLVER_REF(&r->base, "dns-resolving"); |
|
|
|
|
GPR_ASSERT(!r->resolving); |
|
|
|
|
r->resolving = true; |
|
|
|
|
r->lb_addresses = nullptr; |
|
|
|
|
r->service_config_json = nullptr; |
|
|
|
|
r->pending_request = grpc_dns_lookup_ares( |
|
|
|
|
r->dns_server, r->name_to_resolve, r->default_port, r->interested_parties, |
|
|
|
|
&r->dns_ares_on_resolved_closure, &r->lb_addresses, |
|
|
|
|
true /* check_grpclb */, |
|
|
|
|
r->request_service_config ? &r->service_config_json : nullptr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void dns_ares_maybe_finish_next_locked(ares_dns_resolver* r) { |
|
|
|
|
if (r->next_completion != nullptr && |
|
|
|
|
r->resolved_version != r->published_version) { |
|
|
|
|
*r->target_result = r->resolved_result == nullptr |
|
|
|
|
? nullptr |
|
|
|
|
: grpc_channel_args_copy(r->resolved_result); |
|
|
|
|
gpr_log(GPR_DEBUG, "dns_ares_maybe_finish_next_locked"); |
|
|
|
|
GRPC_CLOSURE_SCHED(r->next_completion, GRPC_ERROR_NONE); |
|
|
|
|
r->next_completion = nullptr; |
|
|
|
|
r->published_version = r->resolved_version; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void dns_ares_maybe_start_resolving_locked(ares_dns_resolver* r) { |
|
|
|
|
if (r->last_resolution_timestamp >= 0) { |
|
|
|
|
void AresDnsResolver::MaybeStartResolvingLocked() { |
|
|
|
|
if (last_resolution_timestamp_ >= 0) { |
|
|
|
|
const grpc_millis earliest_next_resolution = |
|
|
|
|
r->last_resolution_timestamp + r->min_time_between_resolutions; |
|
|
|
|
last_resolution_timestamp_ + min_time_between_resolutions_; |
|
|
|
|
const grpc_millis ms_until_next_resolution = |
|
|
|
|
earliest_next_resolution - grpc_core::ExecCtx::Get()->Now(); |
|
|
|
|
if (ms_until_next_resolution > 0) { |
|
|
|
|
const grpc_millis last_resolution_ago = |
|
|
|
|
grpc_core::ExecCtx::Get()->Now() - r->last_resolution_timestamp; |
|
|
|
|
grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_; |
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"In cooldown from last resolution (from %" PRIdPTR |
|
|
|
|
" ms ago). Will resolve again in %" PRIdPTR " ms", |
|
|
|
|
last_resolution_ago, ms_until_next_resolution); |
|
|
|
|
if (!r->have_next_resolution_timer) { |
|
|
|
|
r->have_next_resolution_timer = true; |
|
|
|
|
GRPC_RESOLVER_REF(&r->base, "next_resolution_timer_cooldown"); |
|
|
|
|
grpc_timer_init(&r->next_resolution_timer, ms_until_next_resolution, |
|
|
|
|
&r->dns_ares_on_next_resolution_timer_closure); |
|
|
|
|
if (!have_next_resolution_timer_) { |
|
|
|
|
have_next_resolution_timer_ = true; |
|
|
|
|
// TODO(roth): We currently deal with this ref manually. Once the
|
|
|
|
|
// new closure API is done, find a way to track this ref with the timer
|
|
|
|
|
// callback as part of the type system.
|
|
|
|
|
RefCountedPtr<Resolver> self = |
|
|
|
|
Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown"); |
|
|
|
|
self.release(); |
|
|
|
|
grpc_timer_init(&next_resolution_timer_, ms_until_next_resolution, |
|
|
|
|
&on_next_resolution_); |
|
|
|
|
} |
|
|
|
|
// TODO(dgq): remove the following two lines once Pick First stops
|
|
|
|
|
// discarding subchannels after selecting.
|
|
|
|
|
++r->resolved_version; |
|
|
|
|
dns_ares_maybe_finish_next_locked(r); |
|
|
|
|
++resolved_version_; |
|
|
|
|
MaybeFinishNextLocked(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
dns_ares_start_resolving_locked(r); |
|
|
|
|
StartResolvingLocked(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void dns_ares_destroy(grpc_resolver* gr) { |
|
|
|
|
gpr_log(GPR_DEBUG, "dns_ares_destroy"); |
|
|
|
|
ares_dns_resolver* r = (ares_dns_resolver*)gr; |
|
|
|
|
if (r->resolved_result != nullptr) { |
|
|
|
|
grpc_channel_args_destroy(r->resolved_result); |
|
|
|
|
} |
|
|
|
|
grpc_pollset_set_destroy(r->interested_parties); |
|
|
|
|
gpr_free(r->dns_server); |
|
|
|
|
gpr_free(r->name_to_resolve); |
|
|
|
|
gpr_free(r->default_port); |
|
|
|
|
grpc_channel_args_destroy(r->channel_args); |
|
|
|
|
gpr_free(r); |
|
|
|
|
void AresDnsResolver::StartResolvingLocked() { |
|
|
|
|
// TODO(roth): We currently deal with this ref manually. Once the
|
|
|
|
|
// new closure API is done, find a way to track this ref with the timer
|
|
|
|
|
// callback as part of the type system.
|
|
|
|
|
RefCountedPtr<Resolver> self = Ref(DEBUG_LOCATION, "dns-resolving"); |
|
|
|
|
self.release(); |
|
|
|
|
GPR_ASSERT(!resolving_); |
|
|
|
|
resolving_ = true; |
|
|
|
|
lb_addresses_ = nullptr; |
|
|
|
|
service_config_json_ = nullptr; |
|
|
|
|
pending_request_ = grpc_dns_lookup_ares( |
|
|
|
|
dns_server_, name_to_resolve_, kDefaultPort, interested_parties_, |
|
|
|
|
&on_resolved_, &lb_addresses_, true /* check_grpclb */, |
|
|
|
|
request_service_config_ ? &service_config_json_ : nullptr); |
|
|
|
|
last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static grpc_resolver* dns_ares_create(grpc_resolver_args* args, |
|
|
|
|
const char* default_port) { |
|
|
|
|
/* Get name from args. */ |
|
|
|
|
const char* path = args->uri->path; |
|
|
|
|
if (path[0] == '/') ++path; |
|
|
|
|
/* Create resolver. */ |
|
|
|
|
ares_dns_resolver* r = |
|
|
|
|
(ares_dns_resolver*)gpr_zalloc(sizeof(ares_dns_resolver)); |
|
|
|
|
grpc_resolver_init(&r->base, &dns_ares_resolver_vtable, args->combiner); |
|
|
|
|
if (0 != strcmp(args->uri->authority, "")) { |
|
|
|
|
r->dns_server = gpr_strdup(args->uri->authority); |
|
|
|
|
} |
|
|
|
|
r->name_to_resolve = gpr_strdup(path); |
|
|
|
|
r->default_port = gpr_strdup(default_port); |
|
|
|
|
r->channel_args = grpc_channel_args_copy(args->args); |
|
|
|
|
const grpc_arg* arg = grpc_channel_args_find( |
|
|
|
|
r->channel_args, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION); |
|
|
|
|
r->request_service_config = !grpc_channel_arg_get_integer( |
|
|
|
|
arg, (grpc_integer_options){false, false, true}); |
|
|
|
|
r->interested_parties = grpc_pollset_set_create(); |
|
|
|
|
if (args->pollset_set != nullptr) { |
|
|
|
|
grpc_pollset_set_add_pollset_set(r->interested_parties, args->pollset_set); |
|
|
|
|
void AresDnsResolver::MaybeFinishNextLocked() { |
|
|
|
|
if (next_completion_ != nullptr && resolved_version_ != published_version_) { |
|
|
|
|
*target_result_ = resolved_result_ == nullptr |
|
|
|
|
? nullptr |
|
|
|
|
: grpc_channel_args_copy(resolved_result_); |
|
|
|
|
gpr_log(GPR_DEBUG, "AresDnsResolver::MaybeFinishNextLocked()"); |
|
|
|
|
GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE); |
|
|
|
|
next_completion_ = nullptr; |
|
|
|
|
published_version_ = resolved_version_; |
|
|
|
|
} |
|
|
|
|
grpc_core::BackOff::Options backoff_options; |
|
|
|
|
backoff_options |
|
|
|
|
.set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000) |
|
|
|
|
.set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER) |
|
|
|
|
.set_jitter(GRPC_DNS_RECONNECT_JITTER) |
|
|
|
|
.set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); |
|
|
|
|
r->backoff.Init(grpc_core::BackOff(backoff_options)); |
|
|
|
|
GRPC_CLOSURE_INIT(&r->dns_ares_on_next_resolution_timer_closure, |
|
|
|
|
dns_ares_on_next_resolution_timer_locked, r, |
|
|
|
|
grpc_combiner_scheduler(r->base.combiner)); |
|
|
|
|
GRPC_CLOSURE_INIT(&r->dns_ares_on_resolved_closure, |
|
|
|
|
dns_ares_on_resolved_locked, r, |
|
|
|
|
grpc_combiner_scheduler(r->base.combiner)); |
|
|
|
|
const grpc_arg* period_arg = grpc_channel_args_find( |
|
|
|
|
args->args, GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS); |
|
|
|
|
r->min_time_between_resolutions = |
|
|
|
|
grpc_channel_arg_get_integer(period_arg, {1000, 0, INT_MAX}); |
|
|
|
|
r->last_resolution_timestamp = -1; |
|
|
|
|
return &r->base; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* FACTORY |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static void dns_ares_factory_ref(grpc_resolver_factory* factory) {} |
|
|
|
|
//
|
|
|
|
|
// Factory
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static void dns_ares_factory_unref(grpc_resolver_factory* factory) {} |
|
|
|
|
|
|
|
|
|
static grpc_resolver* dns_factory_create_resolver( |
|
|
|
|
grpc_resolver_factory* factory, grpc_resolver_args* args) { |
|
|
|
|
return dns_ares_create(args, "https"); |
|
|
|
|
} |
|
|
|
|
class AresDnsResolverFactory : public ResolverFactory { |
|
|
|
|
public: |
|
|
|
|
OrphanablePtr<Resolver> CreateResolver( |
|
|
|
|
const ResolverArgs& args) const override { |
|
|
|
|
return OrphanablePtr<Resolver>(New<AresDnsResolver>(args)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static char* dns_ares_factory_get_default_host_name( |
|
|
|
|
grpc_resolver_factory* factory, grpc_uri* uri) { |
|
|
|
|
const char* path = uri->path; |
|
|
|
|
if (path[0] == '/') ++path; |
|
|
|
|
return gpr_strdup(path); |
|
|
|
|
} |
|
|
|
|
const char* scheme() const override { return "dns"; } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static const grpc_resolver_factory_vtable dns_ares_factory_vtable = { |
|
|
|
|
dns_ares_factory_ref, dns_ares_factory_unref, dns_factory_create_resolver, |
|
|
|
|
dns_ares_factory_get_default_host_name, "dns"}; |
|
|
|
|
static grpc_resolver_factory dns_resolver_factory = {&dns_ares_factory_vtable}; |
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
static grpc_resolver_factory* dns_ares_resolver_factory_create() { |
|
|
|
|
return &dns_resolver_factory; |
|
|
|
|
} |
|
|
|
|
} // namespace grpc_core
|
|
|
|
|
|
|
|
|
|
void grpc_resolver_dns_ares_init(void) { |
|
|
|
|
char* resolver = gpr_getenv("GRPC_DNS_RESOLVER"); |
|
|
|
|
void grpc_resolver_dns_ares_init() { |
|
|
|
|
char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER"); |
|
|
|
|
/* TODO(zyc): Turn on c-ares based resolver by default after the address
|
|
|
|
|
sorter and the CNAME support are added. */ |
|
|
|
|
if (resolver != nullptr && gpr_stricmp(resolver, "ares") == 0) { |
|
|
|
|
if (resolver_env != nullptr && gpr_stricmp(resolver_env, "ares") == 0) { |
|
|
|
|
grpc_error* error = grpc_ares_init(); |
|
|
|
|
if (error != GRPC_ERROR_NONE) { |
|
|
|
|
GRPC_LOG_IF_ERROR("ares_library_init() failed", error); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
grpc_resolve_address = grpc_resolve_address_ares; |
|
|
|
|
grpc_register_resolver_type(dns_ares_resolver_factory_create()); |
|
|
|
|
grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( |
|
|
|
|
grpc_core::UniquePtr<grpc_core::ResolverFactory>( |
|
|
|
|
grpc_core::New<grpc_core::AresDnsResolverFactory>())); |
|
|
|
|
} |
|
|
|
|
gpr_free(resolver); |
|
|
|
|
gpr_free(resolver_env); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_resolver_dns_ares_shutdown(void) { |
|
|
|
|
char* resolver = gpr_getenv("GRPC_DNS_RESOLVER"); |
|
|
|
|
if (resolver != nullptr && gpr_stricmp(resolver, "ares") == 0) { |
|
|
|
|
void grpc_resolver_dns_ares_shutdown() { |
|
|
|
|
char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER"); |
|
|
|
|
if (resolver_env != nullptr && gpr_stricmp(resolver_env, "ares") == 0) { |
|
|
|
|
grpc_ares_cleanup(); |
|
|
|
|
} |
|
|
|
|
gpr_free(resolver); |
|
|
|
|
gpr_free(resolver_env); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#else /* GRPC_ARES == 1 && !defined(GRPC_UV) */ |
|
|
|
|