commit
bd0d1bd5d9
19 changed files with 1499 additions and 34 deletions
@ -0,0 +1,59 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
/** Support zookeeper as alternative name system in addition to DNS
|
||||
* Zookeeper name in gRPC is represented as a URI: |
||||
* zookeeper://host:port/path/service/instance
|
||||
* |
||||
* Where zookeeper is the name system scheme |
||||
* host:port is the address of a zookeeper server |
||||
* /path/service/instance is the zookeeper name to be resolved |
||||
* |
||||
* Refer doc/naming.md for more details |
||||
*/ |
||||
|
||||
#ifndef GRPC_GRPC_ZOOKEEPER_H |
||||
#define GRPC_GRPC_ZOOKEEPER_H |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/** Register zookeeper name resolver in grpc */ |
||||
void grpc_zookeeper_register(); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* GRPC_GRPC_ZOOKEEPER_H */ |
@ -0,0 +1,501 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/client_config/resolvers/zookeeper_resolver.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include <grpc/grpc_zookeeper.h> |
||||
#include <zookeeper/zookeeper.h> |
||||
|
||||
#include "src/core/client_config/lb_policies/pick_first.h" |
||||
#include "src/core/client_config/resolver_registry.h" |
||||
#include "src/core/iomgr/resolve_address.h" |
||||
#include "src/core/support/string.h" |
||||
#include "src/core/json/json.h" |
||||
|
||||
/** Zookeeper session expiration time in milliseconds */ |
||||
#define GRPC_ZOOKEEPER_SESSION_TIMEOUT 15000 |
||||
|
||||
typedef struct { |
||||
/** base class: must be first */ |
||||
grpc_resolver base; |
||||
/** refcount */ |
||||
gpr_refcount refs; |
||||
/** name to resolve */ |
||||
char *name; |
||||
/** subchannel factory */ |
||||
grpc_subchannel_factory *subchannel_factory; |
||||
/** load balancing policy factory */ |
||||
grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, |
||||
size_t num_subchannels); |
||||
|
||||
/** mutex guarding the rest of the state */ |
||||
gpr_mu mu; |
||||
/** are we currently resolving? */ |
||||
int resolving; |
||||
/** which version of resolved_config have we published? */ |
||||
int published_version; |
||||
/** which version of resolved_config is current? */ |
||||
int resolved_version; |
||||
/** pending next completion, or NULL */ |
||||
grpc_iomgr_closure *next_completion; |
||||
/** target config address for next completion */ |
||||
grpc_client_config **target_config; |
||||
/** current (fully resolved) config */ |
||||
grpc_client_config *resolved_config; |
||||
|
||||
/** zookeeper handle */ |
||||
zhandle_t *zookeeper_handle; |
||||
/** zookeeper resolved addresses */ |
||||
grpc_resolved_addresses *resolved_addrs; |
||||
/** total number of addresses to be resolved */ |
||||
int resolved_total; |
||||
/** number of addresses resolved */ |
||||
int resolved_num; |
||||
} zookeeper_resolver; |
||||
|
||||
static void zookeeper_destroy(grpc_resolver *r); |
||||
|
||||
static void zookeeper_start_resolving_locked(zookeeper_resolver *r); |
||||
static void zookeeper_maybe_finish_next_locked(zookeeper_resolver *r); |
||||
|
||||
static void zookeeper_shutdown(grpc_resolver *r); |
||||
static void zookeeper_channel_saw_error(grpc_resolver *r, |
||||
struct sockaddr *failing_address, |
||||
int failing_address_len); |
||||
static void zookeeper_next(grpc_resolver *r, grpc_client_config **target_config, |
||||
grpc_iomgr_closure *on_complete); |
||||
|
||||
static const grpc_resolver_vtable zookeeper_resolver_vtable = { |
||||
zookeeper_destroy, zookeeper_shutdown, zookeeper_channel_saw_error, |
||||
zookeeper_next}; |
||||
|
||||
static void zookeeper_shutdown(grpc_resolver *resolver) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)resolver; |
||||
gpr_mu_lock(&r->mu); |
||||
if (r->next_completion != NULL) { |
||||
*r->target_config = NULL; |
||||
grpc_iomgr_add_callback(r->next_completion); |
||||
r->next_completion = NULL; |
||||
} |
||||
zookeeper_close(r->zookeeper_handle); |
||||
gpr_mu_unlock(&r->mu); |
||||
} |
||||
|
||||
static void zookeeper_channel_saw_error(grpc_resolver *resolver, |
||||
struct sockaddr *sa, int len) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)resolver; |
||||
gpr_mu_lock(&r->mu); |
||||
if (r->resolving == 0) { |
||||
zookeeper_start_resolving_locked(r); |
||||
} |
||||
gpr_mu_unlock(&r->mu); |
||||
} |
||||
|
||||
static void zookeeper_next(grpc_resolver *resolver, |
||||
grpc_client_config **target_config, |
||||
grpc_iomgr_closure *on_complete) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)resolver; |
||||
gpr_mu_lock(&r->mu); |
||||
GPR_ASSERT(r->next_completion == NULL); |
||||
r->next_completion = on_complete; |
||||
r->target_config = target_config; |
||||
if (r->resolved_version == 0 && r->resolving == 0) { |
||||
zookeeper_start_resolving_locked(r); |
||||
} else { |
||||
zookeeper_maybe_finish_next_locked(r); |
||||
} |
||||
gpr_mu_unlock(&r->mu); |
||||
} |
||||
|
||||
/** Zookeeper global watcher for connection management
|
||||
TODO: better connection management besides logs */ |
||||
static void zookeeper_global_watcher(zhandle_t *zookeeper_handle, int type, |
||||
int state, const char *path, |
||||
void *watcher_ctx) { |
||||
if (type == ZOO_SESSION_EVENT) { |
||||
if (state == ZOO_EXPIRED_SESSION_STATE) { |
||||
gpr_log(GPR_ERROR, "Zookeeper session expired"); |
||||
} else if (state == ZOO_AUTH_FAILED_STATE) { |
||||
gpr_log(GPR_ERROR, "Zookeeper authentication failed"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** Zookeeper watcher triggered by changes to watched nodes
|
||||
Once triggered, it tries to resolve again to get updated addresses */ |
||||
static void zookeeper_watcher(zhandle_t *zookeeper_handle, int type, int state, |
||||
const char *path, void *watcher_ctx) { |
||||
if (watcher_ctx != NULL) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)watcher_ctx; |
||||
if (state == ZOO_CONNECTED_STATE) { |
||||
gpr_mu_lock(&r->mu); |
||||
if (r->resolving == 0) { |
||||
zookeeper_start_resolving_locked(r); |
||||
} |
||||
gpr_mu_unlock(&r->mu); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** Callback function after getting all resolved addresses
|
||||
Creates a subchannel for each address */ |
||||
static void zookeeper_on_resolved(void *arg, |
||||
grpc_resolved_addresses *addresses) { |
||||
zookeeper_resolver *r = arg; |
||||
grpc_client_config *config = NULL; |
||||
grpc_subchannel **subchannels; |
||||
grpc_subchannel_args args; |
||||
grpc_lb_policy *lb_policy; |
||||
size_t i; |
||||
if (addresses != NULL) { |
||||
config = grpc_client_config_create(); |
||||
subchannels = gpr_malloc(sizeof(grpc_subchannel *) * addresses->naddrs); |
||||
for (i = 0; i < addresses->naddrs; i++) { |
||||
memset(&args, 0, sizeof(args)); |
||||
args.addr = (struct sockaddr *)(addresses->addrs[i].addr); |
||||
args.addr_len = addresses->addrs[i].len; |
||||
subchannels[i] = grpc_subchannel_factory_create_subchannel( |
||||
r->subchannel_factory, &args); |
||||
} |
||||
lb_policy = r->lb_policy_factory(subchannels, addresses->naddrs); |
||||
grpc_client_config_set_lb_policy(config, lb_policy); |
||||
GRPC_LB_POLICY_UNREF(lb_policy, "construction"); |
||||
grpc_resolved_addresses_destroy(addresses); |
||||
gpr_free(subchannels); |
||||
} |
||||
gpr_mu_lock(&r->mu); |
||||
GPR_ASSERT(r->resolving == 1); |
||||
r->resolving = 0; |
||||
if (r->resolved_config != NULL) { |
||||
grpc_client_config_unref(r->resolved_config); |
||||
} |
||||
r->resolved_config = config; |
||||
r->resolved_version++; |
||||
zookeeper_maybe_finish_next_locked(r); |
||||
gpr_mu_unlock(&r->mu); |
||||
|
||||
GRPC_RESOLVER_UNREF(&r->base, "zookeeper-resolving"); |
||||
} |
||||
|
||||
/** Callback function for each DNS resolved address */ |
||||
static void zookeeper_dns_resolved(void *arg, |
||||
grpc_resolved_addresses *addresses) { |
||||
size_t i; |
||||
zookeeper_resolver *r = arg; |
||||
int resolve_done = 0; |
||||
|
||||
gpr_mu_lock(&r->mu); |
||||
r->resolved_num++; |
||||
r->resolved_addrs->addrs = |
||||
gpr_realloc(r->resolved_addrs->addrs, |
||||
sizeof(grpc_resolved_address) * |
||||
(r->resolved_addrs->naddrs + addresses->naddrs)); |
||||
for (i = 0; i < addresses->naddrs; i++) { |
||||
memcpy(r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].addr, |
||||
addresses->addrs[i].addr, addresses->addrs[i].len); |
||||
r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].len = |
||||
addresses->addrs[i].len; |
||||
} |
||||
|
||||
r->resolved_addrs->naddrs += addresses->naddrs; |
||||
grpc_resolved_addresses_destroy(addresses); |
||||
|
||||
/** Wait for all addresses to be resolved */ |
||||
resolve_done = (r->resolved_num == r->resolved_total); |
||||
gpr_mu_unlock(&r->mu); |
||||
if (resolve_done) { |
||||
zookeeper_on_resolved(r, r->resolved_addrs); |
||||
} |
||||
} |
||||
|
||||
/** Parses JSON format address of a zookeeper node */ |
||||
static char *zookeeper_parse_address(const char *value, int value_len) { |
||||
grpc_json *json; |
||||
grpc_json *cur; |
||||
const char *host; |
||||
const char *port; |
||||
char* buffer; |
||||
char *address = NULL; |
||||
|
||||
buffer = gpr_malloc(value_len); |
||||
memcpy(buffer, value, value_len); |
||||
json = grpc_json_parse_string_with_len(buffer, value_len); |
||||
if (json != NULL) { |
||||
host = NULL; |
||||
port = NULL; |
||||
for (cur = json->child; cur != NULL; cur = cur->next) { |
||||
if (!strcmp(cur->key, "host")) { |
||||
host = cur->value; |
||||
if (port != NULL) { |
||||
break; |
||||
} |
||||
} else if (!strcmp(cur->key, "port")) { |
||||
port = cur->value; |
||||
if (host != NULL) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
if (host != NULL && port != NULL) { |
||||
gpr_asprintf(&address, "%s:%s", host, port); |
||||
} |
||||
grpc_json_destroy(json); |
||||
} |
||||
gpr_free(buffer); |
||||
|
||||
return address; |
||||
} |
||||
|
||||
static void zookeeper_get_children_node_completion(int rc, const char *value, |
||||
int value_len, |
||||
const struct Stat *stat, |
||||
const void *arg) { |
||||
char *address = NULL; |
||||
zookeeper_resolver *r = (zookeeper_resolver *)arg; |
||||
int resolve_done = 0; |
||||
|
||||
if (rc != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting a child node of %s", r->name); |
||||
return; |
||||
} |
||||
|
||||
address = zookeeper_parse_address(value, value_len); |
||||
if (address != NULL) { |
||||
/** Further resolves address by DNS */ |
||||
grpc_resolve_address(address, NULL, zookeeper_dns_resolved, r); |
||||
gpr_free(address); |
||||
} else { |
||||
gpr_log(GPR_ERROR, "Error in resolving a child node of %s", r->name); |
||||
gpr_mu_lock(&r->mu); |
||||
r->resolved_total--; |
||||
resolve_done = (r->resolved_num == r->resolved_total); |
||||
gpr_mu_unlock(&r->mu); |
||||
if (resolve_done) { |
||||
zookeeper_on_resolved(r, r->resolved_addrs); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_get_children_completion( |
||||
int rc, const struct String_vector *children, const void *arg) { |
||||
char *path; |
||||
int status; |
||||
int i; |
||||
zookeeper_resolver *r = (zookeeper_resolver *)arg; |
||||
|
||||
if (rc != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name); |
||||
return; |
||||
} |
||||
|
||||
if (children->count == 0) { |
||||
gpr_log(GPR_ERROR, "Error in resolving zookeeper address %s", r->name); |
||||
return; |
||||
} |
||||
|
||||
r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); |
||||
r->resolved_addrs->addrs = NULL; |
||||
r->resolved_addrs->naddrs = 0; |
||||
r->resolved_total = children->count; |
||||
|
||||
/** TODO: Replace expensive heap allocation with stack
|
||||
if we can get maximum length of zookeeper path */ |
||||
for (i = 0; i < children->count; i++) { |
||||
gpr_asprintf(&path, "%s/%s", r->name, children->data[i]); |
||||
status = zoo_awget(r->zookeeper_handle, path, zookeeper_watcher, r, |
||||
zookeeper_get_children_node_completion, r); |
||||
gpr_free(path); |
||||
if (status != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", path); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_get_node_completion(int rc, const char *value, |
||||
int value_len, |
||||
const struct Stat *stat, |
||||
const void *arg) { |
||||
int status; |
||||
char *address = NULL; |
||||
zookeeper_resolver *r = (zookeeper_resolver *)arg; |
||||
r->resolved_addrs = NULL; |
||||
r->resolved_total = 0; |
||||
r->resolved_num = 0; |
||||
|
||||
if (rc != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name); |
||||
return; |
||||
} |
||||
|
||||
/** If zookeeper node of path r->name does not have address
|
||||
(i.e. service node), get its children */ |
||||
address = zookeeper_parse_address(value, value_len); |
||||
if (address != NULL) { |
||||
r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); |
||||
r->resolved_addrs->addrs = NULL; |
||||
r->resolved_addrs->naddrs = 0; |
||||
r->resolved_total = 1; |
||||
/** Further resolves address by DNS */ |
||||
grpc_resolve_address(address, NULL, zookeeper_dns_resolved, r); |
||||
gpr_free(address); |
||||
return; |
||||
} |
||||
|
||||
status = zoo_awget_children(r->zookeeper_handle, r->name, zookeeper_watcher, |
||||
r, zookeeper_get_children_completion, r); |
||||
if (status != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name); |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_resolve_address(zookeeper_resolver *r) { |
||||
int status; |
||||
status = zoo_awget(r->zookeeper_handle, r->name, zookeeper_watcher, r, |
||||
zookeeper_get_node_completion, r); |
||||
if (status != 0) { |
||||
gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name); |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_start_resolving_locked(zookeeper_resolver *r) { |
||||
GRPC_RESOLVER_REF(&r->base, "zookeeper-resolving"); |
||||
GPR_ASSERT(r->resolving == 0); |
||||
r->resolving = 1; |
||||
zookeeper_resolve_address(r); |
||||
} |
||||
|
||||
static void zookeeper_maybe_finish_next_locked(zookeeper_resolver *r) { |
||||
if (r->next_completion != NULL && |
||||
r->resolved_version != r->published_version) { |
||||
*r->target_config = r->resolved_config; |
||||
if (r->resolved_config != NULL) { |
||||
grpc_client_config_ref(r->resolved_config); |
||||
} |
||||
grpc_iomgr_add_callback(r->next_completion); |
||||
r->next_completion = NULL; |
||||
r->published_version = r->resolved_version; |
||||
} |
||||
} |
||||
|
||||
static void zookeeper_destroy(grpc_resolver *gr) { |
||||
zookeeper_resolver *r = (zookeeper_resolver *)gr; |
||||
gpr_mu_destroy(&r->mu); |
||||
if (r->resolved_config != NULL) { |
||||
grpc_client_config_unref(r->resolved_config); |
||||
} |
||||
grpc_subchannel_factory_unref(r->subchannel_factory); |
||||
gpr_free(r->name); |
||||
gpr_free(r); |
||||
} |
||||
|
||||
static grpc_resolver *zookeeper_create( |
||||
grpc_uri *uri, |
||||
grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, |
||||
size_t num_subchannels), |
||||
grpc_subchannel_factory *subchannel_factory) { |
||||
zookeeper_resolver *r; |
||||
size_t length; |
||||
char *path = uri->path; |
||||
|
||||
if (0 == strcmp(uri->authority, "")) { |
||||
gpr_log(GPR_ERROR, "No authority specified in zookeeper uri"); |
||||
return NULL; |
||||
} |
||||
|
||||
/** Removes the trailing slash if exists */ |
||||
length = strlen(path); |
||||
if (length > 1 && path[length - 1] == '/') { |
||||
path[length - 1] = 0; |
||||
} |
||||
|
||||
r = gpr_malloc(sizeof(zookeeper_resolver)); |
||||
memset(r, 0, sizeof(*r)); |
||||
gpr_ref_init(&r->refs, 1); |
||||
gpr_mu_init(&r->mu); |
||||
grpc_resolver_init(&r->base, &zookeeper_resolver_vtable); |
||||
r->name = gpr_strdup(path); |
||||
|
||||
r->subchannel_factory = subchannel_factory; |
||||
r->lb_policy_factory = lb_policy_factory; |
||||
grpc_subchannel_factory_ref(subchannel_factory); |
||||
|
||||
/** Initializes zookeeper client */ |
||||
zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); |
||||
r->zookeeper_handle = zookeeper_init(uri->authority, zookeeper_global_watcher, |
||||
GRPC_ZOOKEEPER_SESSION_TIMEOUT, 0, 0, 0); |
||||
if (r->zookeeper_handle == NULL) { |
||||
gpr_log(GPR_ERROR, "Unable to connect to zookeeper server"); |
||||
return NULL; |
||||
} |
||||
|
||||
return &r->base; |
||||
} |
||||
|
||||
static void zookeeper_plugin_init() { |
||||
grpc_register_resolver_type("zookeeper", |
||||
grpc_zookeeper_resolver_factory_create()); |
||||
} |
||||
|
||||
void grpc_zookeeper_register() { |
||||
grpc_register_plugin(zookeeper_plugin_init, NULL); |
||||
} |
||||
|
||||
/*
|
||||
* FACTORY |
||||
*/ |
||||
|
||||
static void zookeeper_factory_ref(grpc_resolver_factory *factory) {} |
||||
|
||||
static void zookeeper_factory_unref(grpc_resolver_factory *factory) {} |
||||
|
||||
static grpc_resolver *zookeeper_factory_create_resolver( |
||||
grpc_resolver_factory *factory, grpc_uri *uri, |
||||
grpc_subchannel_factory *subchannel_factory) { |
||||
return zookeeper_create(uri, grpc_create_pick_first_lb_policy, |
||||
subchannel_factory); |
||||
} |
||||
|
||||
static const grpc_resolver_factory_vtable zookeeper_factory_vtable = { |
||||
zookeeper_factory_ref, zookeeper_factory_unref, |
||||
zookeeper_factory_create_resolver}; |
||||
static grpc_resolver_factory zookeeper_resolver_factory = { |
||||
&zookeeper_factory_vtable}; |
||||
|
||||
grpc_resolver_factory *grpc_zookeeper_resolver_factory_create() { |
||||
return &zookeeper_resolver_factory; |
||||
} |
@ -0,0 +1,42 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H |
||||
#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H |
||||
|
||||
#include "src/core/client_config/resolver_factory.h" |
||||
|
||||
/** Create a zookeeper resolver factory */ |
||||
grpc_resolver_factory *grpc_zookeeper_resolver_factory_create(void); |
||||
|
||||
#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H */ |
@ -0,0 +1,43 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
/* This is just a compilation test, to see if we have Zookeeper C client
|
||||
library installed. */ |
||||
|
||||
#include <stdlib.h> |
||||
#include <zookeeper/zookeeper.h> |
||||
|
||||
int main() { |
||||
zookeeper_init(NULL, NULL, 0, 0, 0, 0); |
||||
return 0; |
||||
} |
@ -0,0 +1,223 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include "test/core/util/test_config.h" |
||||
#include "test/core/util/port.h" |
||||
#include "test/cpp/util/echo.grpc.pb.h" |
||||
#include "src/core/support/env.h" |
||||
#include <grpc++/channel_arguments.h> |
||||
#include <grpc++/channel_interface.h> |
||||
#include <grpc++/client_context.h> |
||||
#include <grpc++/create_channel.h> |
||||
#include <grpc++/credentials.h> |
||||
#include <grpc++/server.h> |
||||
#include <grpc++/server_builder.h> |
||||
#include <grpc++/server_context.h> |
||||
#include <grpc++/server_credentials.h> |
||||
#include <grpc++/status.h> |
||||
#include <gtest/gtest.h> |
||||
#include <grpc/grpc.h> |
||||
#include <grpc/grpc_zookeeper.h> |
||||
#include <zookeeper/zookeeper.h> |
||||
|
||||
using grpc::cpp::test::util::EchoRequest; |
||||
using grpc::cpp::test::util::EchoResponse; |
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
|
||||
class ZookeeperTestServiceImpl |
||||
: public ::grpc::cpp::test::util::TestService::Service { |
||||
public: |
||||
Status Echo(ServerContext* context, const EchoRequest* request, |
||||
EchoResponse* response) GRPC_OVERRIDE { |
||||
response->set_message(request->message()); |
||||
return Status::OK; |
||||
} |
||||
}; |
||||
|
||||
class ZookeeperTest : public ::testing::Test { |
||||
protected: |
||||
ZookeeperTest() {} |
||||
|
||||
void SetUp() GRPC_OVERRIDE { |
||||
SetUpZookeeper(); |
||||
|
||||
// Sets up two servers
|
||||
int port1 = grpc_pick_unused_port_or_die(); |
||||
server1_ = SetUpServer(port1); |
||||
|
||||
int port2 = grpc_pick_unused_port_or_die(); |
||||
server2_ = SetUpServer(port2); |
||||
|
||||
// Registers service /test in zookeeper
|
||||
RegisterService("/test", "test"); |
||||
|
||||
// Registers service instance /test/1 in zookeeper
|
||||
string value = |
||||
"{\"host\":\"localhost\",\"port\":\"" + to_string(port1) + "\"}"; |
||||
RegisterService("/test/1", value); |
||||
|
||||
// Registers service instance /test/2 in zookeeper
|
||||
value = |
||||
"{\"host\":\"localhost\",\"port\":\"" + to_string(port2) + "\"}"; |
||||
RegisterService("/test/2", value); |
||||
} |
||||
|
||||
// Requires zookeeper server running
|
||||
void SetUpZookeeper() { |
||||
// Finds zookeeper server address in environment
|
||||
// Default is localhost:2181
|
||||
zookeeper_address_ = "localhost:2181"; |
||||
char* addr = gpr_getenv("GRPC_ZOOKEEPER_SERVER_TEST"); |
||||
if (addr != NULL) { |
||||
string addr_str(addr); |
||||
zookeeper_address_ = addr_str; |
||||
gpr_free(addr); |
||||
} |
||||
gpr_log(GPR_DEBUG, zookeeper_address_.c_str()); |
||||
|
||||
// Connects to zookeeper server
|
||||
zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); |
||||
zookeeper_handle_ = |
||||
zookeeper_init(zookeeper_address_.c_str(), NULL, 15000, 0, 0, 0); |
||||
GPR_ASSERT(zookeeper_handle_ != NULL); |
||||
|
||||
// Registers zookeeper name resolver in grpc
|
||||
grpc_zookeeper_register(); |
||||
} |
||||
|
||||
std::unique_ptr<Server> SetUpServer(const int port) { |
||||
string server_address = "localhost:" + to_string(port); |
||||
|
||||
ServerBuilder builder; |
||||
builder.AddListeningPort(server_address, InsecureServerCredentials()); |
||||
builder.RegisterService(&service_); |
||||
std::unique_ptr<Server> server = builder.BuildAndStart(); |
||||
return server; |
||||
} |
||||
|
||||
void RegisterService(const string& name, const string& value) { |
||||
char* path = (char*)gpr_malloc(name.size()); |
||||
|
||||
int status = zoo_exists(zookeeper_handle_, name.c_str(), 0, NULL); |
||||
if (status == ZNONODE) { |
||||
status = |
||||
zoo_create(zookeeper_handle_, name.c_str(), value.c_str(), |
||||
value.size(), &ZOO_OPEN_ACL_UNSAFE, 0, path, name.size()); |
||||
} else { |
||||
status = zoo_set(zookeeper_handle_, name.c_str(), value.c_str(), |
||||
value.size(), -1); |
||||
} |
||||
gpr_free(path); |
||||
GPR_ASSERT(status == 0); |
||||
} |
||||
|
||||
void DeleteService(const string& name) { |
||||
int status = zoo_delete(zookeeper_handle_, name.c_str(), -1); |
||||
GPR_ASSERT(status == 0); |
||||
} |
||||
|
||||
void ChangeZookeeperState() { |
||||
server1_->Shutdown(); |
||||
DeleteService("/test/1"); |
||||
} |
||||
|
||||
void TearDown() GRPC_OVERRIDE { |
||||
server1_->Shutdown(); |
||||
server2_->Shutdown(); |
||||
zookeeper_close(zookeeper_handle_); |
||||
} |
||||
|
||||
void ResetStub() { |
||||
string target = "zookeeper://" + zookeeper_address_ + "/test"; |
||||
channel_ = CreateChannel(target, InsecureCredentials(), ChannelArguments()); |
||||
stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel_)); |
||||
} |
||||
|
||||
string to_string(const int number) { |
||||
std::stringstream strs; |
||||
strs << number; |
||||
return strs.str(); |
||||
} |
||||
|
||||
std::shared_ptr<ChannelInterface> channel_; |
||||
std::unique_ptr<grpc::cpp::test::util::TestService::Stub> stub_; |
||||
std::unique_ptr<Server> server1_; |
||||
std::unique_ptr<Server> server2_; |
||||
ZookeeperTestServiceImpl service_; |
||||
zhandle_t* zookeeper_handle_; |
||||
string zookeeper_address_; |
||||
}; |
||||
|
||||
// Tests zookeeper state change between two RPCs
|
||||
// TODO(ctiller): leaked objects in this test
|
||||
TEST_F(ZookeeperTest, ZookeeperStateChangeTwoRpc) { |
||||
ResetStub(); |
||||
|
||||
// First RPC
|
||||
EchoRequest request1; |
||||
EchoResponse response1; |
||||
ClientContext context1; |
||||
context1.set_authority("test"); |
||||
request1.set_message("Hello"); |
||||
Status s1 = stub_->Echo(&context1, request1, &response1); |
||||
EXPECT_EQ(response1.message(), request1.message()); |
||||
EXPECT_TRUE(s1.ok()); |
||||
|
||||
// Zookeeper state changes
|
||||
gpr_log(GPR_DEBUG, "Zookeeper state change");
|
||||
ChangeZookeeperState(); |
||||
// Waits for re-resolving addresses
|
||||
// TODO(ctiller): RPC will probably fail if not waiting
|
||||
sleep(1); |
||||
|
||||
// Second RPC
|
||||
EchoRequest request2; |
||||
EchoResponse response2; |
||||
ClientContext context2; |
||||
context2.set_authority("test"); |
||||
request2.set_message("World"); |
||||
Status s2 = stub_->Echo(&context2, request2, &response2); |
||||
EXPECT_EQ(response2.message(), request2.message()); |
||||
EXPECT_TRUE(s2.ok()); |
||||
} |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc_test_init(argc, argv); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue