gRPC LB policy

pull/6924/head
David Garcia Quintas 9 years ago
parent fa9b7c1bc6
commit 3fb8f7360b
  1. 3
      BUILD
  2. 38
      Makefile
  3. 1
      binding.gyp
  4. 14
      build.yaml
  5. 1
      config.m4
  6. 1
      gRPC.podspec
  7. 1
      grpc.gemspec
  8. 1
      package.xml
  9. 5
      src/core/ext/client_config/lb_policy.c
  10. 13
      src/core/ext/client_config/lb_policy.h
  11. 836
      src/core/ext/lb_policy/grpclb/grpclb.c
  12. 44
      src/core/ext/lb_policy/grpclb/grpclb.h
  13. 77
      src/core/ext/lb_policy/grpclb/load_balancer_api.c
  14. 16
      src/core/ext/lb_policy/grpclb/load_balancer_api.h
  15. 4
      src/core/plugin_registry/grpc_plugin_registry.c
  16. 4
      src/core/plugin_registry/grpc_unsecure_plugin_registry.c
  17. 1
      src/python/grpcio/grpc_core_dependencies.py
  18. 670
      test/core/client_config/grpclb_test.c
  19. 9
      test/core/end2end/cq_verifier.c
  20. 3
      test/core/end2end/cq_verifier.h
  21. 97
      tools/codegen/core/gen_grpclb_test_response.py
  22. 1
      tools/doxygen/Doxyfile.core.internal
  23. 19
      tools/run_tests/sources_and_headers.json
  24. 21
      tools/run_tests/tests.json
  25. 27
      vsprojects/buildtests_c.sln
  26. 2
      vsprojects/vcxproj/grpc/grpc.vcxproj
  27. 3
      vsprojects/vcxproj/grpc/grpc.vcxproj.filters
  28. 2
      vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
  29. 3
      vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
  30. 199
      vsprojects/vcxproj/test/grpclb_test/grpclb_test.vcxproj
  31. 21
      vsprojects/vcxproj/test/grpclb_test/grpclb_test.vcxproj.filters

@ -467,6 +467,7 @@ cc_library(
"src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
"src/core/ext/transport/chttp2/client/insecure/channel_create.c",
"src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
"src/core/ext/lb_policy/grpclb/grpclb.c",
"src/core/ext/lb_policy/grpclb/load_balancer_api.c",
"src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
"src/core/ext/lb_policy/pick_first/pick_first.c",
@ -1141,6 +1142,7 @@ cc_library(
"src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
"src/core/ext/load_reporting/load_reporting.c",
"src/core/ext/load_reporting/load_reporting_filter.c",
"src/core/ext/lb_policy/grpclb/grpclb.c",
"src/core/ext/lb_policy/grpclb/load_balancer_api.c",
"src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
"src/core/ext/lb_policy/pick_first/pick_first.c",
@ -1943,6 +1945,7 @@ objc_library(
"src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
"src/core/ext/transport/chttp2/client/insecure/channel_create.c",
"src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
"src/core/ext/lb_policy/grpclb/grpclb.c",
"src/core/ext/lb_policy/grpclb/load_balancer_api.c",
"src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
"src/core/ext/lb_policy/pick_first/pick_first.c",

@ -946,6 +946,7 @@ grpc_jwt_verifier_test: $(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test
grpc_print_google_default_creds_token: $(BINDIR)/$(CONFIG)/grpc_print_google_default_creds_token
grpc_security_connector_test: $(BINDIR)/$(CONFIG)/grpc_security_connector_test
grpc_verify_jwt: $(BINDIR)/$(CONFIG)/grpc_verify_jwt
grpclb_test: $(BINDIR)/$(CONFIG)/grpclb_test
hpack_parser_fuzzer_test: $(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test
hpack_parser_test: $(BINDIR)/$(CONFIG)/hpack_parser_test
hpack_table_test: $(BINDIR)/$(CONFIG)/hpack_table_test
@ -1277,6 +1278,7 @@ buildtests_c: privatelibs_c \
$(BINDIR)/$(CONFIG)/grpc_json_token_test \
$(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test \
$(BINDIR)/$(CONFIG)/grpc_security_connector_test \
$(BINDIR)/$(CONFIG)/grpclb_test \
$(BINDIR)/$(CONFIG)/hpack_parser_test \
$(BINDIR)/$(CONFIG)/hpack_table_test \
$(BINDIR)/$(CONFIG)/http_parser_test \
@ -1579,6 +1581,8 @@ test_c: buildtests_c
$(Q) $(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test || ( echo test grpc_jwt_verifier_test failed ; exit 1 )
$(E) "[RUN] Testing grpc_security_connector_test"
$(Q) $(BINDIR)/$(CONFIG)/grpc_security_connector_test || ( echo test grpc_security_connector_test failed ; exit 1 )
$(E) "[RUN] Testing grpclb_test"
$(Q) $(BINDIR)/$(CONFIG)/grpclb_test || ( echo test grpclb_test failed ; exit 1 )
$(E) "[RUN] Testing hpack_parser_test"
$(Q) $(BINDIR)/$(CONFIG)/hpack_parser_test || ( echo test hpack_parser_test failed ; exit 1 )
$(E) "[RUN] Testing hpack_table_test"
@ -2662,6 +2666,7 @@ LIBGRPC_SRC = \
src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
src/core/ext/transport/chttp2/client/insecure/channel_create.c \
src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
src/core/ext/lb_policy/grpclb/grpclb.c \
src/core/ext/lb_policy/grpclb/load_balancer_api.c \
src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
third_party/nanopb/pb_common.c \
@ -3240,6 +3245,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/resolver/sockaddr/sockaddr_resolver.c \
src/core/ext/load_reporting/load_reporting.c \
src/core/ext/load_reporting/load_reporting_filter.c \
src/core/ext/lb_policy/grpclb/grpclb.c \
src/core/ext/lb_policy/grpclb/load_balancer_api.c \
src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
third_party/nanopb/pb_common.c \
@ -8470,6 +8476,38 @@ endif
endif
GRPCLB_TEST_SRC = \
test/core/client_config/grpclb_test.c \
GRPCLB_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPCLB_TEST_SRC))))
ifeq ($(NO_SECURE),true)
# You can't build secure targets if you don't have OpenSSL.
$(BINDIR)/$(CONFIG)/grpclb_test: openssl_dep_error
else
$(BINDIR)/$(CONFIG)/grpclb_test: $(GRPCLB_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LD) $(LDFLAGS) $(GRPCLB_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpclb_test
endif
$(OBJDIR)/$(CONFIG)/test/core/client_config/grpclb_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
deps_grpclb_test: $(GRPCLB_TEST_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(GRPCLB_TEST_OBJS:.o=.dep)
endif
endif
HPACK_PARSER_FUZZER_TEST_SRC = \
test/core/transport/chttp2/hpack_parser_fuzzer_test.c \

@ -722,6 +722,7 @@
'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
'src/core/ext/lb_policy/grpclb/grpclb.c',
'src/core/ext/lb_policy/grpclb/load_balancer_api.c',
'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
'third_party/nanopb/pb_common.c',

@ -377,8 +377,10 @@ filegroups:
- src/core/ext/lb_policy/grpclb/load_balancer_api.h
- src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
src:
- src/core/ext/lb_policy/grpclb/grpclb.c
- src/core/ext/lb_policy/grpclb/load_balancer_api.c
- src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
plugin: grpc_lb_policy_grpclb
uses:
- grpc_base
- grpc_client_config
@ -796,6 +798,7 @@ libs:
- grpc_lb_policy_grpclb
- grpc_lb_policy_pick_first
- grpc_lb_policy_round_robin
- grpc_lb_policy_grpclb
- grpc_resolver_dns_native
- grpc_resolver_sockaddr
- grpc_load_reporting
@ -893,6 +896,7 @@ libs:
- grpc_lb_policy_grpclb
- grpc_lb_policy_pick_first
- grpc_lb_policy_round_robin
- grpc_lb_policy_grpclb
- census
generate_plugin_registry: true
secure: false
@ -1815,6 +1819,16 @@ targets:
- grpc
- gpr_test_util
- gpr
- name: grpclb_test
build: test
language: c
src:
- test/core/client_config/grpclb_test.c
deps:
- grpc_test_util
- grpc
- gpr_test_util
- gpr
- name: hpack_parser_fuzzer_test
build: fuzzer
language: c

@ -241,6 +241,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
src/core/ext/transport/chttp2/client/insecure/channel_create.c \
src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
src/core/ext/lb_policy/grpclb/grpclb.c \
src/core/ext/lb_policy/grpclb/load_balancer_api.c \
src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
third_party/nanopb/pb_common.c \

@ -504,6 +504,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
'src/core/ext/lb_policy/grpclb/grpclb.c',
'src/core/ext/lb_policy/grpclb/load_balancer_api.c',
'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
'third_party/nanopb/pb_common.c',

@ -483,6 +483,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c )
s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create.c )
s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c )
s.files += %w( src/core/ext/lb_policy/grpclb/grpclb.c )
s.files += %w( src/core/ext/lb_policy/grpclb/load_balancer_api.c )
s.files += %w( src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c )
s.files += %w( third_party/nanopb/pb_common.c )

@ -490,6 +490,7 @@
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create.c" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c" role="src" />
<file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/grpclb.c" role="src" />
<file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/load_balancer_api.c" role="src" />
<file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c" role="src" />
<file baseinstalldir="/" name="third_party/nanopb/pb_common.c" role="src" />

@ -60,8 +60,9 @@ static gpr_atm ref_mutate(grpc_lb_policy *c, gpr_atm delta,
: gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta);
#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"LB_POLICY: %p % 12s 0x%08x -> 0x%08x [%s]", c, purpose, old_val,
old_val + delta, reason);
"LB_POLICY: 0x%" PRIxPTR " %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR
" [%s]",
(intptr_t)c, purpose, old_val, old_val + delta, reason);
#endif
return old_val;
}

@ -73,15 +73,17 @@ struct grpc_lb_policy_vtable {
void (*ping_one)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
grpc_closure *closure);
/** try to enter a READY connectivity state */
/** Try to enter a READY connectivity state */
void (*exit_idle)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
/** check the current connectivity of the lb_policy */
/** Check the current connectivity */
grpc_connectivity_state (*check_connectivity)(grpc_exec_ctx *exec_ctx,
grpc_lb_policy *policy);
/** call notify when the connectivity state of a channel changes from *state.
Updates *state with the new state of the policy */
Updates *state with the new state of the policy. Calling with a NULL \a
state cancels the subscription.
*/
void (*notify_on_state_change)(grpc_exec_ctx *exec_ctx,
grpc_lb_policy *policy,
grpc_connectivity_state *state,
@ -124,7 +126,7 @@ void grpc_lb_policy_init(grpc_lb_policy *policy,
/** Given initial metadata in \a initial_metadata, find an appropriate
target for this rpc, and 'return' it by calling \a on_complete after setting
\a target.
Picking can be asynchronous. Any IO should be done under \a pollset. */
Picking can be asynchronous. Any IO should be done under \a pollent. */
int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
grpc_polling_entity *pollent,
grpc_metadata_batch *initial_metadata,
@ -146,8 +148,11 @@ void grpc_lb_policy_cancel_picks(grpc_exec_ctx *exec_ctx,
uint32_t initial_metadata_flags_mask,
uint32_t initial_metadata_flags_eq);
/** Try to enter a READY connectivity state */
void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
/* Call notify when the connectivity state of a channel changes from \a *state.
* Updates \a *state with the new state of the policy */
void grpc_lb_policy_notify_on_state_change(grpc_exec_ctx *exec_ctx,
grpc_lb_policy *policy,
grpc_connectivity_state *state,

@ -0,0 +1,836 @@
/*
*
* Copyright 2016, 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/ext/lb_policy/grpclb/grpclb.h"
#include "src/core/ext/client_config/client_channel_factory.h"
#include "src/core/ext/client_config/lb_policy_registry.h"
#include "src/core/ext/client_config/parse_address.h"
#include "src/core/ext/lb_policy/grpclb/load_balancer_api.h"
#include "src/core/lib/iomgr/sockaddr_utils.h"
#include "src/core/lib/support/string.h"
#include "src/core/lib/surface/call.h"
#include "src/core/lib/surface/channel.h"
#include <string.h>
#include <grpc/byte_buffer_reader.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/string_util.h>
int grpc_lb_glb_trace = 0;
typedef struct wrapped_rr_closure_arg {
grpc_closure *wrapped_closure;
grpc_lb_policy *rr_policy;
} wrapped_rr_closure_arg;
/* The \a on_complete closure passed as part of the pick requires keeping a
* reference to its associated round robin instance. We wrap this closure in
* order to unref the round robin instance upon its invocation */
static void wrapped_rr_closure(grpc_exec_ctx *exec_ctx, void *arg,
bool success) {
wrapped_rr_closure_arg *wc = arg;
if (wc->rr_policy != NULL) {
if (grpc_lb_glb_trace) {
gpr_log(GPR_INFO, "Unreffing RR %p", wc->rr_policy);
}
GRPC_LB_POLICY_UNREF(exec_ctx, wc->rr_policy, "wrapped_rr_closure");
}
if (wc->wrapped_closure != NULL) {
grpc_exec_ctx_enqueue(exec_ctx, wc->wrapped_closure, success, NULL);
}
gpr_free(wc);
}
typedef struct pending_pick {
struct pending_pick *next;
grpc_polling_entity *pollent;
grpc_metadata_batch *initial_metadata;
uint32_t initial_metadata_flags;
grpc_connected_subchannel **target;
grpc_closure *wrapped_on_complete;
wrapped_rr_closure_arg *wrapped_on_complete_arg;
} pending_pick;
typedef struct pending_ping {
struct pending_ping *next;
grpc_closure *wrapped_notify;
wrapped_rr_closure_arg *wrapped_notify_arg;
} pending_ping;
typedef struct glb_lb_policy glb_lb_policy;
#define MAX_LBCD_OPS_LEN 6
typedef struct lb_client_data {
gpr_mu mu;
grpc_closure md_sent;
grpc_closure md_rcvd;
grpc_closure req_sent;
grpc_closure res_rcvd;
grpc_closure close_sent;
grpc_closure srv_status_rcvd;
grpc_call *c;
gpr_timespec deadline;
grpc_metadata_array initial_metadata_recv;
grpc_metadata_array trailing_metadata_recv;
grpc_byte_buffer *request_payload;
grpc_byte_buffer *response_payload;
grpc_status_code status;
char *status_details;
size_t status_details_capacity;
glb_lb_policy *p;
} lb_client_data;
/* Keeps track and reacts to changes in connectivity of the RR instance */
typedef struct rr_connectivity_data {
grpc_closure on_change;
grpc_connectivity_state state;
glb_lb_policy *p;
} rr_connectivity_data;
struct glb_lb_policy {
/** base policy: must be first */
grpc_lb_policy base;
/** mutex protecting remaining members */
gpr_mu mu;
grpc_client_channel_factory *cc_factory;
/** for communicating with the LB server */
grpc_channel *lb_server_channel;
/** the RR policy to use of the backend servers returned by the LB server */
grpc_lb_policy *rr_policy;
bool started_picking;
/** our connectivity state tracker */
grpc_connectivity_state_tracker state_tracker;
grpc_grpclb_serverlist *serverlist;
/** list of picks that are waiting on connectivity */
pending_pick *pending_picks;
/** list of pings that are waiting on connectivity */
pending_ping *pending_pings;
/** data associated with the communication with the LB server */
lb_client_data *lbcd;
/** for tracking of the RR connectivity */
rr_connectivity_data *rr_connectivity;
};
static void rr_handover(grpc_exec_ctx *exec_ctx, glb_lb_policy *p);
static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
bool iomgr_success) {
rr_connectivity_data *rrcd = arg;
if (!iomgr_success) {
gpr_free(rrcd);
return;
}
glb_lb_policy *p = rrcd->p;
const grpc_connectivity_state new_state = p->rr_connectivity->state;
if (new_state == GRPC_CHANNEL_SHUTDOWN && p->serverlist != NULL) {
/* a RR policy is shutting down but there's a serverlist available ->
* perform a handover */
rr_handover(exec_ctx, p);
} else {
grpc_connectivity_state_set(exec_ctx, &p->state_tracker, new_state,
"rr_connectivity_changed");
/* resubscribe */
grpc_lb_policy_notify_on_state_change(exec_ctx, p->rr_policy,
&p->rr_connectivity->state,
&p->rr_connectivity->on_change);
}
}
static void add_pending_pick(pending_pick **root, grpc_polling_entity *pollent,
grpc_metadata_batch *initial_metadata,
uint32_t initial_metadata_flags,
grpc_connected_subchannel **target,
grpc_closure *on_complete) {
pending_pick *pp = gpr_malloc(sizeof(*pp));
memset(pp, 0, sizeof(pending_pick));
pp->wrapped_on_complete_arg = gpr_malloc(sizeof(wrapped_rr_closure_arg));
memset(pp->wrapped_on_complete_arg, 0, sizeof(wrapped_rr_closure_arg));
pp->next = *root;
pp->pollent = pollent;
pp->target = target;
pp->initial_metadata = initial_metadata;
pp->initial_metadata_flags = initial_metadata_flags;
pp->wrapped_on_complete =
grpc_closure_create(wrapped_rr_closure, pp->wrapped_on_complete_arg);
pp->wrapped_on_complete_arg->wrapped_closure = on_complete;
*root = pp;
}
static void add_pending_ping(pending_ping **root, grpc_closure *notify) {
pending_ping *pping = gpr_malloc(sizeof(*pping));
memset(pping, 0, sizeof(pending_ping));
pping->wrapped_notify_arg = gpr_malloc(sizeof(wrapped_rr_closure_arg));
memset(pping->wrapped_notify_arg, 0, sizeof(wrapped_rr_closure_arg));
pping->next = *root;
pping->wrapped_notify =
grpc_closure_create(wrapped_rr_closure, pping->wrapped_notify_arg);
pping->wrapped_notify_arg->wrapped_closure = notify;
*root = pping;
}
static void lb_client_data_destroy(lb_client_data *lbcd);
static void md_sent_cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
lb_client_data *lbcd = arg;
GPR_ASSERT(lbcd->c);
grpc_call_error error;
grpc_op ops[1];
memset(ops, 0, sizeof(ops));
grpc_op *op = ops;
op->op = GRPC_OP_RECV_INITIAL_METADATA;
op->data.recv_initial_metadata = &lbcd->initial_metadata_recv;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch_and_execute(exec_ctx, lbcd->c, ops,
(size_t)(op - ops), &lbcd->md_rcvd);
GPR_ASSERT(GRPC_CALL_OK == error);
}
static void md_recv_cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
lb_client_data *lbcd = arg;
GPR_ASSERT(lbcd->c);
grpc_call_error error;
grpc_op ops[1];
memset(ops, 0, sizeof(ops));
grpc_op *op = ops;
op->op = GRPC_OP_SEND_MESSAGE;
op->data.send_message = lbcd->request_payload;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch_and_execute(
exec_ctx, lbcd->c, ops, (size_t)(op - ops), &lbcd->req_sent);
GPR_ASSERT(GRPC_CALL_OK == error);
}
static void req_sent_cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
lb_client_data *lbcd = arg;
grpc_call_error error;
grpc_op ops[1];
memset(ops, 0, sizeof(ops));
grpc_op *op = ops;
op->op = GRPC_OP_RECV_MESSAGE;
op->data.recv_message = &lbcd->response_payload;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch_and_execute(
exec_ctx, lbcd->c, ops, (size_t)(op - ops), &lbcd->res_rcvd);
GPR_ASSERT(GRPC_CALL_OK == error);
}
static void res_rcvd_cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
/* look inside lbcd->response_payload, ideally to send it back as the
* serverlist. */
lb_client_data *lbcd = arg;
grpc_op ops[2];
memset(ops, 0, sizeof(ops));
grpc_op *op = ops;
if (lbcd->response_payload) {
grpc_byte_buffer_reader bbr;
grpc_byte_buffer_reader_init(&bbr, lbcd->response_payload);
gpr_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
grpc_byte_buffer_destroy(lbcd->response_payload);
grpc_grpclb_serverlist *serverlist =
grpc_grpclb_response_parse_serverlist(response_slice);
if (serverlist) {
gpr_slice_unref(response_slice);
if (grpc_lb_glb_trace) {
gpr_log(GPR_INFO, "Serverlist with %zu servers received",
serverlist->num_servers);
}
/* update serverlist */
if (serverlist->num_servers > 0) {
if (grpc_grpclb_serverlist_equals(lbcd->p->serverlist, serverlist)) {
gpr_log(GPR_INFO,
"Incoming server list identical to current, ignoring.");
} else {
if (lbcd->p->serverlist != NULL) {
grpc_grpclb_destroy_serverlist(lbcd->p->serverlist);
}
lbcd->p->serverlist = serverlist;
}
}
if (lbcd->p->rr_policy == NULL) {
/* initial "handover", in this case from a null RR policy, meaning it'll
* just create the first one */
rr_handover(exec_ctx, lbcd->p);
} else {
/* unref the RR policy, eventually leading to its substitution with a
* new one constructed from the received serverlist (see
* rr_connectivity_changed) */
GRPC_LB_POLICY_UNREF(exec_ctx, lbcd->p->rr_policy,
"serverlist_received");
}
/* listen for a potential serverlist update */
op->op = GRPC_OP_RECV_MESSAGE;
op->data.recv_message = &lbcd->response_payload;
op->flags = 0;
op->reserved = NULL;
op++;
const grpc_call_error error = grpc_call_start_batch_and_execute(
exec_ctx, lbcd->c, ops, (size_t)(op - ops),
&lbcd->res_rcvd); /* loop */
GPR_ASSERT(GRPC_CALL_OK == error);
return;
} else {
gpr_log(GPR_ERROR, "Invalid LB response received: '%s'",
gpr_dump_slice(response_slice, GPR_DUMP_ASCII));
gpr_slice_unref(response_slice);
/* Disconnect from server returning invalid response. */
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
op->flags = 0;
op->reserved = NULL;
op++;
grpc_call_error error = grpc_call_start_batch_and_execute(
exec_ctx, lbcd->c, ops, (size_t)(op - ops), &lbcd->close_sent);
GPR_ASSERT(GRPC_CALL_OK == error);
}
}
/* empty payload: call cancelled by server. Cleanups happening in
* srv_status_rcvd_cb */
}
static void close_sent_cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
if (grpc_lb_glb_trace) {
gpr_log(GPR_INFO,
"Close from LB client sent. Waiting from server status now");
}
}
static void srv_status_rcvd_cb(grpc_exec_ctx *exec_ctx, void *arg,
bool success) {
lb_client_data *lbcd = arg;
glb_lb_policy *p = lbcd->p;
if (grpc_lb_glb_trace) {
gpr_log(
GPR_INFO,
"status from lb server received. Status = %d, Details = '%s', Capaticy "
"= %zu",
lbcd->status, lbcd->status_details, lbcd->status_details_capacity);
}
grpc_call_destroy(lbcd->c);
grpc_channel_destroy(lbcd->p->lb_server_channel);
lbcd->p->lb_server_channel = NULL;
lb_client_data_destroy(lbcd);
p->lbcd = NULL;
}
static lb_client_data *lb_client_data_create(glb_lb_policy *p) {
lb_client_data *lbcd = gpr_malloc(sizeof(lb_client_data));
memset(lbcd, 0, sizeof(lb_client_data));
gpr_mu_init(&lbcd->mu);
grpc_closure_init(&lbcd->md_sent, md_sent_cb, lbcd);
grpc_closure_init(&lbcd->md_rcvd, md_recv_cb, lbcd);
grpc_closure_init(&lbcd->req_sent, req_sent_cb, lbcd);
grpc_closure_init(&lbcd->res_rcvd, res_rcvd_cb, lbcd);
grpc_closure_init(&lbcd->close_sent, close_sent_cb, lbcd);
grpc_closure_init(&lbcd->srv_status_rcvd, srv_status_rcvd_cb, lbcd);
/* TODO(dgq): get the deadline from the client/user instead of fabricating
* one
* here. Make it a policy arg? */
lbcd->deadline = gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(3, GPR_TIMESPAN));
lbcd->c = grpc_channel_create_pollset_set_call(
p->lb_server_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
p->base.interested_parties, "/BalanceLoad",
NULL, /* FIXME(dgq): which "host" value to use? */
lbcd->deadline, NULL);
grpc_metadata_array_init(&lbcd->initial_metadata_recv);
grpc_metadata_array_init(&lbcd->trailing_metadata_recv);
grpc_grpclb_request *request = grpc_grpclb_request_create(
"load.balanced.service.name"); /* FIXME(dgq): get the name of the load
balanced service from above. */
gpr_slice request_payload_slice = grpc_grpclb_request_encode(request);
lbcd->request_payload =
grpc_raw_byte_buffer_create(&request_payload_slice, 1);
gpr_slice_unref(request_payload_slice);
grpc_grpclb_request_destroy(request);
lbcd->status_details = NULL;
lbcd->status_details_capacity = 0;
lbcd->p = p;
return lbcd;
}
static void lb_client_data_destroy(lb_client_data *lbcd) {
grpc_metadata_array_destroy(&lbcd->initial_metadata_recv);
grpc_metadata_array_destroy(&lbcd->trailing_metadata_recv);
grpc_byte_buffer_destroy(lbcd->request_payload);
gpr_free(lbcd->status_details);
gpr_mu_destroy(&lbcd->mu);
gpr_free(lbcd);
}
static void glb_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
glb_lb_policy *p = (glb_lb_policy *)pol;
GPR_ASSERT(p->pending_picks == NULL);
GPR_ASSERT(p->pending_pings == NULL);
grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker);
if (p->serverlist != NULL) {
grpc_grpclb_destroy_serverlist(p->serverlist);
}
gpr_mu_destroy(&p->mu);
gpr_free(p);
}
static void glb_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
glb_lb_policy *p = (glb_lb_policy *)pol;
gpr_mu_lock(&p->mu);
pending_pick *pp = p->pending_picks;
p->pending_picks = NULL;
pending_ping *pping = p->pending_pings;
p->pending_pings = NULL;
gpr_mu_unlock(&p->mu);
while (pp != NULL) {
pending_pick *next = pp->next;
*pp->target = NULL;
grpc_exec_ctx_enqueue(exec_ctx, pp->wrapped_on_complete, true, NULL);
gpr_free(pp);
pp = next;
}
while (pping != NULL) {
pending_ping *next = pping->next;
grpc_exec_ctx_enqueue(exec_ctx, pping->wrapped_notify, true, NULL);
pping = next;
}
if (p->rr_policy) {
/* unsubscribe */
grpc_lb_policy_notify_on_state_change(exec_ctx, p->rr_policy, NULL,
&p->rr_connectivity->on_change);
GRPC_LB_POLICY_UNREF(exec_ctx, p->rr_policy, "glb_shutdown");
}
grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
GRPC_CHANNEL_SHUTDOWN, "glb_shutdown");
}
static void glb_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
grpc_connected_subchannel **target) {
glb_lb_policy *p = (glb_lb_policy *)pol;
gpr_mu_lock(&p->mu);
pending_pick *pp = p->pending_picks;
p->pending_picks = NULL;
while (pp != NULL) {
pending_pick *next = pp->next;
if (pp->target == target) {
grpc_polling_entity_del_from_pollset_set(exec_ctx, pp->pollent,
p->base.interested_parties);
*target = NULL;
grpc_exec_ctx_enqueue(exec_ctx, pp->wrapped_on_complete, false, NULL);
gpr_free(pp);
} else {
pp->next = p->pending_picks;
p->pending_picks = pp;
}
pp = next;
}
gpr_mu_unlock(&p->mu);
}
static void glb_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
uint32_t initial_metadata_flags_mask,
uint32_t initial_metadata_flags_eq) {
glb_lb_policy *p = (glb_lb_policy *)pol;
gpr_mu_lock(&p->mu);
if (p->lbcd != NULL) {
/* cancel the call to the load balancer service, if any */
grpc_call_cancel(p->lbcd->c, NULL);
}
pending_pick *pp = p->pending_picks;
p->pending_picks = NULL;
while (pp != NULL) {
pending_pick *next = pp->next;
if ((pp->initial_metadata_flags & initial_metadata_flags_mask) ==
initial_metadata_flags_eq) {
grpc_polling_entity_del_from_pollset_set(exec_ctx, pp->pollent,
p->base.interested_parties);
grpc_exec_ctx_enqueue(exec_ctx, pp->wrapped_on_complete, false, NULL);
gpr_free(pp);
} else {
pp->next = p->pending_picks;
p->pending_picks = pp;
}
pp = next;
}
gpr_mu_unlock(&p->mu);
}
static void query_for_backends(grpc_exec_ctx *exec_ctx, glb_lb_policy *p) {
GPR_ASSERT(p->lb_server_channel != NULL);
p->lbcd = lb_client_data_create(p);
grpc_call_error error;
grpc_op ops[1];
memset(ops, 0, sizeof(ops));
grpc_op *op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch_and_execute(
exec_ctx, p->lbcd->c, ops, (size_t)(op - ops), &p->lbcd->md_sent);
GPR_ASSERT(GRPC_CALL_OK == error);
op = ops;
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata =
&p->lbcd->trailing_metadata_recv;
op->data.recv_status_on_client.status = &p->lbcd->status;
op->data.recv_status_on_client.status_details = &p->lbcd->status_details;
op->data.recv_status_on_client.status_details_capacity =
&p->lbcd->status_details_capacity;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch_and_execute(
exec_ctx, p->lbcd->c, ops, (size_t)(op - ops), &p->lbcd->srv_status_rcvd);
GPR_ASSERT(GRPC_CALL_OK == error);
}
static grpc_lb_policy *create_rr(grpc_exec_ctx *exec_ctx,
const grpc_grpclb_serverlist *serverlist,
glb_lb_policy *p) {
/* TODO(dgq): support mixed ip version */
GPR_ASSERT(serverlist != NULL && serverlist->num_servers > 0);
char **host_ports = gpr_malloc(sizeof(char *) * serverlist->num_servers);
for (size_t i = 0; i < serverlist->num_servers; ++i) {
gpr_join_host_port(&host_ports[i], serverlist->servers[i]->ip_address,
serverlist->servers[i]->port);
}
size_t uri_path_len;
char *concat_ipports = gpr_strjoin_sep(
(const char **)host_ports, serverlist->num_servers, ",", &uri_path_len);
grpc_lb_policy_args args;
args.client_channel_factory = p->cc_factory;
args.addresses = gpr_malloc(sizeof(grpc_resolved_addresses));
args.addresses->naddrs = serverlist->num_servers;
args.addresses->addrs =
gpr_malloc(sizeof(grpc_resolved_address) * args.addresses->naddrs);
size_t out_addrs_idx = 0;
for (size_t i = 0; i < serverlist->num_servers; ++i) {
grpc_uri uri;
struct sockaddr_storage sa;
size_t sa_len;
uri.path = host_ports[i];
if (parse_ipv4(&uri, &sa, &sa_len)) { /* TODO(dgq): add support for ipv6 */
memcpy(args.addresses->addrs[out_addrs_idx].addr, &sa, sa_len);
args.addresses->addrs[out_addrs_idx].len = sa_len;
++out_addrs_idx;
} else {
gpr_log(GPR_ERROR, "Invalid LB service address '%s', ignoring.",
host_ports[i]);
}
}
grpc_lb_policy *rr = grpc_lb_policy_create(exec_ctx, "round_robin", &args);
gpr_free(concat_ipports);
for (size_t i = 0; i < serverlist->num_servers; i++) {
gpr_free(host_ports[i]);
}
gpr_free(host_ports);
gpr_free(args.addresses->addrs);
gpr_free(args.addresses);
return rr;
}
static void start_picking(grpc_exec_ctx *exec_ctx, glb_lb_policy *p) {
p->started_picking = true;
query_for_backends(exec_ctx, p);
}
static void rr_handover(grpc_exec_ctx *exec_ctx, glb_lb_policy *p) {
p->rr_policy = create_rr(exec_ctx, p->serverlist, p);
if (grpc_lb_glb_trace) {
gpr_log(GPR_INFO, "Created RR policy (0x%" PRIxPTR ")",
(intptr_t)p->rr_policy);
}
GPR_ASSERT(p->rr_policy != NULL);
p->rr_connectivity->state =
grpc_lb_policy_check_connectivity(exec_ctx, p->rr_policy);
grpc_lb_policy_notify_on_state_change(exec_ctx, p->rr_policy,
&p->rr_connectivity->state,
&p->rr_connectivity->on_change);
grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
p->rr_connectivity->state, "rr_handover");
grpc_lb_policy_exit_idle(exec_ctx, p->rr_policy);
/* flush pending ops */
pending_pick *pp;
while ((pp = p->pending_picks)) {
p->pending_picks = pp->next;
GRPC_LB_POLICY_REF(p->rr_policy, "rr_handover_pending_pick");
pp->wrapped_on_complete_arg->rr_policy = p->rr_policy;
if (grpc_lb_glb_trace) {
gpr_log(GPR_INFO, "Pending pick about to PICK from 0x%"PRIxPTR"", (intptr_t)p->rr_policy);
}
grpc_lb_policy_pick(exec_ctx, p->rr_policy, pp->pollent,
pp->initial_metadata, pp->initial_metadata_flags,
pp->target, pp->wrapped_on_complete);
gpr_free(pp);
}
pending_ping *pping;
while ((pping = p->pending_pings)) {
p->pending_pings = pping->next;
GRPC_LB_POLICY_REF(p->rr_policy, "rr_handover_pending_ping");
pping->wrapped_notify_arg->rr_policy = p->rr_policy;
if (grpc_lb_glb_trace) {
gpr_log(GPR_INFO, "Pending ping about to PING from 0x%"PRIxPTR"", (intptr_t)p->rr_policy);
}
grpc_lb_policy_ping_one(exec_ctx, p->rr_policy, pping->wrapped_notify);
gpr_free(pping);
}
}
static void glb_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
glb_lb_policy *p = (glb_lb_policy *)pol;
gpr_mu_lock(&p->mu);
if (!p->started_picking) {
start_picking(exec_ctx, p);
}
gpr_mu_unlock(&p->mu);
}
static int glb_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
grpc_polling_entity *pollent,
grpc_metadata_batch *initial_metadata,
uint32_t initial_metadata_flags,
grpc_connected_subchannel **target,
grpc_closure *on_complete) {
glb_lb_policy *p = (glb_lb_policy *)pol;
gpr_mu_lock(&p->mu);
int r;
if (p->rr_policy != NULL) {
if (grpc_lb_glb_trace) {
gpr_log(GPR_INFO, "about to PICK from 0x%"PRIxPTR"", (intptr_t)p->rr_policy);
}
GRPC_LB_POLICY_REF(p->rr_policy, "rr_pick");
wrapped_rr_closure_arg *warg = gpr_malloc(sizeof(wrapped_rr_closure_arg));
warg->rr_policy = p->rr_policy;
warg->wrapped_closure = on_complete;
grpc_closure *wrapped_on_complete =
grpc_closure_create(wrapped_rr_closure, warg);
r = grpc_lb_policy_pick(exec_ctx, p->rr_policy, pollent, initial_metadata,
initial_metadata_flags, target,
wrapped_on_complete);
if (r != 0) {
/* the call to grpc_lb_policy_pick has been sychronous. Invoke a neutered
* wrapped closure */
warg->wrapped_closure = NULL;
grpc_exec_ctx_enqueue(exec_ctx, wrapped_on_complete, false, NULL);
}
} else {
grpc_polling_entity_add_to_pollset_set(exec_ctx, pollent,
p->base.interested_parties);
add_pending_pick(&p->pending_picks, pollent, initial_metadata,
initial_metadata_flags, target, on_complete);
if (!p->started_picking) {
start_picking(exec_ctx, p);
}
r = 0;
}
gpr_mu_unlock(&p->mu);
return r;
}
static grpc_connectivity_state glb_check_connectivity(grpc_exec_ctx *exec_ctx,
grpc_lb_policy *pol) {
glb_lb_policy *p = (glb_lb_policy *)pol;
grpc_connectivity_state st;
gpr_mu_lock(&p->mu);
st = grpc_connectivity_state_check(&p->state_tracker);
gpr_mu_unlock(&p->mu);
return st;
}
static void glb_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
grpc_closure *closure) {
glb_lb_policy *p = (glb_lb_policy *)pol;
gpr_mu_lock(&p->mu);
if (p->rr_policy) {
grpc_lb_policy_ping_one(exec_ctx, p->rr_policy, closure);
} else {
add_pending_ping(&p->pending_pings, closure);
if (!p->started_picking) {
start_picking(exec_ctx, p);
}
}
gpr_mu_unlock(&p->mu);
}
static void glb_notify_on_state_change(grpc_exec_ctx *exec_ctx,
grpc_lb_policy *pol,
grpc_connectivity_state *current,
grpc_closure *notify) {
glb_lb_policy *p = (glb_lb_policy *)pol;
gpr_mu_lock(&p->mu);
grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker,
current, notify);
gpr_mu_unlock(&p->mu);
}
static const grpc_lb_policy_vtable glb_lb_policy_vtable = {
glb_destroy, glb_shutdown, glb_pick,
glb_cancel_pick, glb_cancel_picks, glb_ping_one,
glb_exit_idle, glb_check_connectivity, glb_notify_on_state_change};
static void glb_factory_ref(grpc_lb_policy_factory *factory) {}
static void glb_factory_unref(grpc_lb_policy_factory *factory) {}
static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
grpc_lb_policy_factory *factory,
grpc_lb_policy_args *args) {
glb_lb_policy *p = gpr_malloc(sizeof(*p));
memset(p, 0, sizeof(*p));
/* all input addresses in args->addresses come from a resolver that claims
* they are LB services.
*
* Create a client channel over them to communicate with a LB service */
p->cc_factory = args->client_channel_factory;
GPR_ASSERT(p->cc_factory != NULL);
if (args->addresses->naddrs == 0) {
return NULL;
}
/* construct a target from the args->addresses, in the form
* ipvX://ip1:port1,ip2:port2,...
* TODO(dgq): support mixed ip version */
char **addr_strs = gpr_malloc(sizeof(char *) * args->addresses->naddrs);
addr_strs[0] =
grpc_sockaddr_to_uri((const struct sockaddr *)&args->addresses->addrs[0]);
for (size_t i = 1; i < args->addresses->naddrs; i++) {
GPR_ASSERT(grpc_sockaddr_to_string(
&addr_strs[i],
(const struct sockaddr *)&args->addresses->addrs[i],
true) == 0);
}
size_t uri_path_len;
char *target_uri_str = gpr_strjoin_sep(
(const char **)addr_strs, args->addresses->naddrs, ",", &uri_path_len);
/* will pick using pick_first */
p->lb_server_channel = grpc_client_channel_factory_create_channel(
exec_ctx, p->cc_factory, target_uri_str,
GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, NULL);
gpr_free(target_uri_str);
for (size_t i = 0; i < args->addresses->naddrs; i++) {
gpr_free(addr_strs[i]);
}
gpr_free(addr_strs);
if (p->lb_server_channel == NULL) {
gpr_free(p);
return NULL;
}
rr_connectivity_data *rr_connectivity =
gpr_malloc(sizeof(rr_connectivity_data));
memset(rr_connectivity, 0, sizeof(rr_connectivity_data));
grpc_closure_init(&rr_connectivity->on_change, rr_connectivity_changed,
rr_connectivity);
rr_connectivity->p = p;
p->rr_connectivity = rr_connectivity;
grpc_lb_policy_init(&p->base, &glb_lb_policy_vtable);
gpr_mu_init(&p->mu);
grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, "grpclb");
return &p->base;
}
static const grpc_lb_policy_factory_vtable glb_factory_vtable = {
glb_factory_ref, glb_factory_unref, glb_create, "grpclb"};
static grpc_lb_policy_factory glb_lb_policy_factory = {&glb_factory_vtable};
grpc_lb_policy_factory *grpc_glb_lb_factory_create() {
return &glb_lb_policy_factory;
}
/* Plugin registration */
void grpc_lb_policy_grpclb_init() {
grpc_register_lb_policy(grpc_glb_lb_factory_create());
grpc_register_tracer("glb", &grpc_lb_glb_trace);
}
void grpc_lb_policy_grpclb_shutdown() {}

@ -0,0 +1,44 @@
/*
*
* Copyright 2016, 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_CORE_EXT_LB_POLICY_GRPCLB_GRPCLB_H
#define GRPC_CORE_EXT_LB_POLICY_GRPCLB_GRPCLB_H
#include "src/core/ext/client_config/lb_policy_factory.h"
/** Returns a load balancing factory for the glb policy, which tries to connect
* to a load balancing server to decide the next successfully connected
* subchannel to pick. */
grpc_lb_policy_factory *grpc_glb_lb_factory_create();
#endif /* GRPC_CORE_EXT_LB_POLICY_GRPCLB_GRPCLB_H */

@ -56,6 +56,7 @@ static bool decode_serverlist(pb_istream_t *stream, const pb_field_t *field,
dec_arg->num_servers++;
} else { /* second pass */
grpc_grpclb_server *server = gpr_malloc(sizeof(grpc_grpclb_server));
memset(server, 0, sizeof(grpc_grpclb_server));
GPR_ASSERT(dec_arg->num_servers > 0);
if (dec_arg->i == 0) { /* first iteration of second pass */
dec_arg->servers =
@ -147,6 +148,7 @@ grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
}
grpc_grpclb_serverlist *sl = gpr_malloc(sizeof(grpc_grpclb_serverlist));
memset(sl, 0, sizeof(*sl));
sl->num_servers = arg.num_servers;
sl->servers = arg.servers;
if (res->server_list.has_expiration_interval) {
@ -167,6 +169,81 @@ void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist) {
gpr_free(serverlist);
}
grpc_grpclb_serverlist *grpc_grpclb_serverlist_copy(
const grpc_grpclb_serverlist *sl) {
grpc_grpclb_serverlist *copy = gpr_malloc(sizeof(grpc_grpclb_serverlist));
memset(copy, 0, sizeof(grpc_grpclb_serverlist));
copy->num_servers = sl->num_servers;
memcpy(&copy->expiration_interval, &sl->expiration_interval,
sizeof(grpc_grpclb_duration));
copy->servers = gpr_malloc(sizeof(grpc_grpclb_server*) * sl->num_servers);
for (size_t i = 0; i < sl->num_servers; i++) {
copy->servers[i] = gpr_malloc(sizeof(grpc_grpclb_server));
memcpy(copy->servers[i], sl->servers[i], sizeof(grpc_grpclb_server));
}
return copy;
}
bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist *lhs,
const grpc_grpclb_serverlist *rhs) {
if ((lhs == NULL) || (rhs == NULL)) {
return false;
}
if (lhs->num_servers != rhs->num_servers) {
return false;
}
if (grpc_grpclb_duration_compare(&lhs->expiration_interval,
&rhs->expiration_interval) != 0) {
return false;
}
for (size_t i = 0; i < lhs->num_servers; i++) {
if (!grpc_grpclb_server_equals(lhs->servers[i], rhs->servers[i])) {
return false;
}
}
return true;
}
bool grpc_grpclb_server_equals(const grpc_grpclb_server *lhs,
const grpc_grpclb_server *rhs) {
return memcmp(lhs, rhs, sizeof(grpc_grpclb_server)) == 0;
}
int grpc_grpclb_duration_compare(const grpc_grpclb_duration *lhs,
const grpc_grpclb_duration *rhs) {
GPR_ASSERT(lhs && rhs);
if (lhs->has_seconds && rhs->has_seconds) {
if (lhs->seconds < rhs->seconds) {
return -1;
}
if (lhs->seconds > rhs->seconds) {
return 1;
}
goto compare_nanos;
}
if (lhs->has_seconds + rhs->has_seconds == 0) {
goto compare_nanos;
}
// only lhs or rhs have seconds
return rhs->has_seconds ? 1 : -1;
compare_nanos:
if (lhs->has_nanos && rhs->has_nanos) {
if (lhs->nanos < rhs->nanos) {
return -1;
}
if (lhs->nanos > rhs->nanos) {
return 1;
}
return 0;
}
if (lhs->has_nanos + rhs->has_nanos == 0) {
return 0;
}
// only lhs or rhs have nanos
return rhs->has_nanos ? 1 : -1;
}
void grpc_grpclb_response_destroy(grpc_grpclb_response *response) {
gpr_free(response);
}

@ -68,6 +68,11 @@ void grpc_grpclb_request_destroy(grpc_grpclb_request *request);
* grpc_grpclb_response */
grpc_grpclb_response *grpc_grpclb_response_parse(gpr_slice encoded_response);
/** Return a copy of \a sl. The caller is responsible for calling \a
* grpc_grpclb_destroy_serverlist on the returned copy. */
grpc_grpclb_serverlist *grpc_grpclb_serverlist_copy(
const grpc_grpclb_serverlist *sl);
/** Destroy \a serverlist */
void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist);
@ -75,6 +80,17 @@ void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist);
grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
gpr_slice encoded_response);
bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist *lhs,
const grpc_grpclb_serverlist *rhs);
bool grpc_grpclb_server_equals(const grpc_grpclb_server *lhs,
const grpc_grpclb_server *rhs);
/** Compare \a lhs against \a rhs and return 0 if \a lhs and \a rhs are equal,
* < 0 if \a lhs represents a duration shorter than \a rhs and > 0 otherwise */
int grpc_grpclb_duration_compare(const grpc_grpclb_duration *lhs,
const grpc_grpclb_duration *rhs);
/** Destroy \a response */
void grpc_grpclb_response_destroy(grpc_grpclb_response *response);

@ -37,6 +37,8 @@ extern void grpc_chttp2_plugin_init(void);
extern void grpc_chttp2_plugin_shutdown(void);
extern void grpc_client_config_init(void);
extern void grpc_client_config_shutdown(void);
extern void grpc_lb_policy_grpclb_init(void);
extern void grpc_lb_policy_grpclb_shutdown(void);
extern void grpc_lb_policy_pick_first_init(void);
extern void grpc_lb_policy_pick_first_shutdown(void);
extern void grpc_lb_policy_round_robin_init(void);
@ -55,6 +57,8 @@ void grpc_register_built_in_plugins(void) {
grpc_chttp2_plugin_shutdown);
grpc_register_plugin(grpc_client_config_init,
grpc_client_config_shutdown);
grpc_register_plugin(grpc_lb_policy_grpclb_init,
grpc_lb_policy_grpclb_shutdown);
grpc_register_plugin(grpc_lb_policy_pick_first_init,
grpc_lb_policy_pick_first_shutdown);
grpc_register_plugin(grpc_lb_policy_round_robin_init,

@ -43,6 +43,8 @@ extern void grpc_resolver_sockaddr_init(void);
extern void grpc_resolver_sockaddr_shutdown(void);
extern void grpc_load_reporting_plugin_init(void);
extern void grpc_load_reporting_plugin_shutdown(void);
extern void grpc_lb_policy_grpclb_init(void);
extern void grpc_lb_policy_grpclb_shutdown(void);
extern void grpc_lb_policy_pick_first_init(void);
extern void grpc_lb_policy_pick_first_shutdown(void);
extern void grpc_lb_policy_round_robin_init(void);
@ -61,6 +63,8 @@ void grpc_register_built_in_plugins(void) {
grpc_resolver_sockaddr_shutdown);
grpc_register_plugin(grpc_load_reporting_plugin_init,
grpc_load_reporting_plugin_shutdown);
grpc_register_plugin(grpc_lb_policy_grpclb_init,
grpc_lb_policy_grpclb_shutdown);
grpc_register_plugin(grpc_lb_policy_pick_first_init,
grpc_lb_policy_pick_first_shutdown);
grpc_register_plugin(grpc_lb_policy_round_robin_init,

@ -235,6 +235,7 @@ CORE_SOURCE_FILES = [
'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
'src/core/ext/lb_policy/grpclb/grpclb.c',
'src/core/ext/lb_policy/grpclb/load_balancer_api.c',
'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
'third_party/nanopb/pb_common.c',

@ -0,0 +1,670 @@
/*
*
* Copyright 2016, 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 <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <tgmath.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
#include <grpc/support/thd.h>
#include <grpc/support/time.h>
#include "src/core/ext/client_config/client_channel.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/support/string.h"
#include "src/core/lib/support/tmpfile.h"
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/surface/server.h"
#include "test/core/end2end/cq_verifier.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
#define NUM_BACKENDS 4
typedef struct client_fixture {
grpc_channel *client;
char *server_uri;
grpc_completion_queue *cq;
} client_fixture;
typedef struct server_fixture {
grpc_server *server;
grpc_call *server_call;
grpc_completion_queue *cq;
char *servers_hostport;
int port;
gpr_thd_id tid;
int num_calls_serviced;
} server_fixture;
typedef struct test_fixture {
server_fixture lb_server;
server_fixture lb_backends[NUM_BACKENDS];
client_fixture client;
int lb_server_update_delay_ms;
} test_fixture;
static gpr_timespec n_seconds_time(int n) {
return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
}
static void *tag(intptr_t t) { return (void *)t; }
static gpr_slice build_response_payload_slice(const char *host, int *ports,
size_t nports) {
/*
server_list {
servers {
ip_address: "127.0.0.1"
port: ...
load_balance_token: "token..."
}
...
} */
char **hostports_vec = gpr_malloc(sizeof(char *) * nports);
for (size_t i = 0; i < nports; i++) {
gpr_join_host_port(&hostports_vec[i], "127.0.0.1", ports[i]);
}
char *hostports_str =
gpr_strjoin_sep((const char **)hostports_vec, nports, " ", NULL);
gpr_log(GPR_INFO, "generating response for %s", hostports_str);
char *output_fname;
FILE *tmpfd = gpr_tmpfile("grpclb_test", &output_fname);
fclose(tmpfd);
char *cmdline;
gpr_asprintf(&cmdline,
"./tools/codegen/core/gen_grpclb_test_response.py --lb_proto "
"src/proto/grpc/lb/v1/load_balancer.proto %s "
"--output %s --quiet",
hostports_str, output_fname);
GPR_ASSERT(system(cmdline) == 0);
FILE *f = fopen(output_fname, "rb");
fseek(f, 0, SEEK_END);
const size_t fsize = (size_t)ftell(f);
rewind(f);
char *serialized_response = gpr_malloc(fsize);
GPR_ASSERT(fread(serialized_response, fsize, 1, f) == 1);
fclose(f);
gpr_free(output_fname);
gpr_free(cmdline);
for (size_t i = 0; i < nports; i++) {
gpr_free(hostports_vec[i]);
}
gpr_free(hostports_vec);
gpr_free(hostports_str);
const gpr_slice response_slice =
gpr_slice_from_copied_buffer(serialized_response, fsize);
gpr_free(serialized_response);
return response_slice;
}
static void drain_cq(grpc_completion_queue *cq) {
grpc_event ev;
do {
ev = grpc_completion_queue_next(cq, n_seconds_time(5), NULL);
} while (ev.type != GRPC_QUEUE_SHUTDOWN);
}
static void sleep_ms(int delay_ms) {
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_millis(delay_ms, GPR_TIMESPAN)));
}
static void start_lb_server(server_fixture *sf, int *ports, size_t nports,
int update_delay_ms) {
grpc_call *s;
cq_verifier *cqv = cq_verifier_create(sf->cq);
grpc_op ops[6];
grpc_op *op;
grpc_metadata_array request_metadata_recv;
grpc_call_details call_details;
grpc_call_error error;
int was_cancelled = 2;
grpc_byte_buffer *request_payload_recv;
grpc_byte_buffer *response_payload;
memset(ops, 0, sizeof(ops));
grpc_metadata_array_init(&request_metadata_recv);
grpc_call_details_init(&call_details);
error = grpc_server_request_call(sf->server, &s, &call_details,
&request_metadata_recv, sf->cq, sf->cq,
tag(200));
GPR_ASSERT(GRPC_CALL_OK == error);
gpr_log(GPR_INFO, "LB Server[%s] up", sf->servers_hostport);
cq_expect_completion(cqv, tag(200), 1);
cq_verify(cqv);
gpr_log(GPR_INFO, "LB Server[%s] after tag 200", sf->servers_hostport);
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
op->data.recv_close_on_server.cancelled = &was_cancelled;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(201), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
gpr_log(GPR_INFO, "LB Server[%s] after tag 201", sf->servers_hostport);
/* receive request for backends */
op = ops;
op->op = GRPC_OP_RECV_MESSAGE;
op->data.recv_message = &request_payload_recv;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(202), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
cq_expect_completion(cqv, tag(202), 1);
cq_verify(cqv);
gpr_log(GPR_INFO, "LB Server[%s] after RECV_MSG", sf->servers_hostport);
// TODO(dgq): validate request.
grpc_byte_buffer_destroy(request_payload_recv);
gpr_slice response_payload_slice;
for (int i = 0; i < 2; i++) {
if (i == 0) {
// First half of the ports.
response_payload_slice =
build_response_payload_slice("127.0.0.1", ports, nports / 2);
} else {
// Second half of the ports.
sleep_ms(update_delay_ms);
response_payload_slice = build_response_payload_slice(
"127.0.0.1", ports + (nports / 2), (nports + 1) / 2 /* ceil */);
}
response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1);
op = ops;
op->op = GRPC_OP_SEND_MESSAGE;
op->data.send_message = response_payload;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(203), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
cq_expect_completion(cqv, tag(203), 1);
cq_verify(cqv);
gpr_log(GPR_INFO, "LB Server[%s] after SEND_MESSAGE, iter %d",
sf->servers_hostport, i);
grpc_byte_buffer_destroy(response_payload);
gpr_slice_unref(response_payload_slice);
}
gpr_log(GPR_INFO, "LB Server[%s] shutting down", sf->servers_hostport);
op = ops;
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op->data.send_status_from_server.trailing_metadata_count = 0;
op->data.send_status_from_server.status = GRPC_STATUS_OK;
op->data.send_status_from_server.status_details = "xyz";
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(204), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
cq_expect_completion(cqv, tag(201), 1);
cq_expect_completion(cqv, tag(204), 1);
cq_verify(cqv);
gpr_log(GPR_INFO, "LB Server[%s] after tag 204. All done. LB server out",
sf->servers_hostport);
grpc_call_destroy(s);
cq_verifier_destroy(cqv);
grpc_metadata_array_destroy(&request_metadata_recv);
grpc_call_details_destroy(&call_details);
}
static void start_backend_server(server_fixture *sf) {
grpc_call *s;
cq_verifier *cqv;
grpc_op ops[6];
grpc_op *op;
grpc_metadata_array request_metadata_recv;
grpc_call_details call_details;
grpc_call_error error;
int was_cancelled;
grpc_byte_buffer *request_payload_recv;
grpc_byte_buffer *response_payload;
grpc_event ev;
while (true) {
memset(ops, 0, sizeof(ops));
cqv = cq_verifier_create(sf->cq);
was_cancelled = 2;
grpc_metadata_array_init(&request_metadata_recv);
grpc_call_details_init(&call_details);
error = grpc_server_request_call(sf->server, &s, &call_details,
&request_metadata_recv, sf->cq, sf->cq,
tag(100));
GPR_ASSERT(GRPC_CALL_OK == error);
gpr_log(GPR_INFO, "Server[%s] up", sf->servers_hostport);
ev = grpc_completion_queue_next(sf->cq, n_seconds_time(60), NULL);
if (!ev.success) {
gpr_log(GPR_INFO, "Server[%s] being torn down", sf->servers_hostport);
cq_verifier_destroy(cqv);
grpc_metadata_array_destroy(&request_metadata_recv);
grpc_call_details_destroy(&call_details);
return;
}
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
gpr_log(GPR_INFO, "Server[%s] after tag 100", sf->servers_hostport);
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
op->data.recv_close_on_server.cancelled = &was_cancelled;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(101), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
gpr_log(GPR_INFO, "Server[%s] after tag 101", sf->servers_hostport);
bool exit = false;
gpr_slice response_payload_slice =
gpr_slice_from_copied_string("hello you");
while (!exit) {
op = ops;
op->op = GRPC_OP_RECV_MESSAGE;
op->data.recv_message = &request_payload_recv;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
ev = grpc_completion_queue_next(sf->cq, n_seconds_time(3), NULL);
if (ev.type == GRPC_OP_COMPLETE && ev.success) {
GPR_ASSERT(ev.tag = tag(102));
if (request_payload_recv == NULL) {
exit = true;
gpr_log(GPR_INFO,
"Server[%s] recv \"close\" from client, exiting. Call #%d",
sf->servers_hostport, sf->num_calls_serviced);
}
} else {
gpr_log(GPR_INFO, "Server[%s] forced to shutdown. Call #%d",
sf->servers_hostport, sf->num_calls_serviced);
exit = true;
}
gpr_log(GPR_INFO, "Server[%s] after tag 102. Call #%d",
sf->servers_hostport, sf->num_calls_serviced);
if (!exit) {
response_payload =
grpc_raw_byte_buffer_create(&response_payload_slice, 1);
op = ops;
op->op = GRPC_OP_SEND_MESSAGE;
op->data.send_message = response_payload;
op->flags = 0;
op->reserved = NULL;
op++;
error =
grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
ev = grpc_completion_queue_next(sf->cq, n_seconds_time(3), NULL);
if (ev.type == GRPC_OP_COMPLETE && ev.success) {
GPR_ASSERT(ev.tag = tag(103));
} else {
gpr_log(GPR_INFO, "Server[%s] forced to shutdown. Call #%d",
sf->servers_hostport, sf->num_calls_serviced);
exit = true;
}
gpr_log(GPR_INFO, "Server[%s] after tag 103. Call #%d",
sf->servers_hostport, sf->num_calls_serviced);
grpc_byte_buffer_destroy(response_payload);
}
grpc_byte_buffer_destroy(request_payload_recv);
}
++sf->num_calls_serviced;
gpr_log(GPR_INFO, "Server[%s] OUT OF THE LOOP", sf->servers_hostport);
gpr_slice_unref(response_payload_slice);
op = ops;
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op->data.send_status_from_server.trailing_metadata_count = 0;
op->data.send_status_from_server.status = GRPC_STATUS_OK;
op->data.send_status_from_server.status_details = "Backend server out a-ok";
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
cq_expect_completion(cqv, tag(101), 1);
cq_expect_completion(cqv, tag(104), 1);
cq_verify(cqv);
gpr_log(GPR_INFO, "Server[%s] DONE. After servicing %d calls",
sf->servers_hostport, sf->num_calls_serviced);
grpc_call_destroy(s);
cq_verifier_destroy(cqv);
grpc_metadata_array_destroy(&request_metadata_recv);
grpc_call_details_destroy(&call_details);
}
}
static void perform_request(client_fixture *cf) {
grpc_call *c;
cq_verifier *cqv = cq_verifier_create(cf->cq);
grpc_op ops[6];
grpc_op *op;
grpc_metadata_array initial_metadata_recv;
grpc_metadata_array trailing_metadata_recv;
grpc_status_code status;
grpc_call_error error;
char *details = NULL;
size_t details_capacity = 0;
grpc_byte_buffer *request_payload;
grpc_byte_buffer *response_payload_recv;
int i;
memset(ops, 0, sizeof(ops));
gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
c = grpc_channel_create_call(cf->client, NULL, GRPC_PROPAGATE_DEFAULTS,
cf->cq, "/foo", "foo.test.google.fr:1234",
n_seconds_time(1000), NULL);
gpr_log(GPR_INFO, "Call 0x%" PRIxPTR " created", (intptr_t)c);
GPR_ASSERT(c);
char *peer;
grpc_metadata_array_init(&initial_metadata_recv);
grpc_metadata_array_init(&trailing_metadata_recv);
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_INITIAL_METADATA;
op->data.recv_initial_metadata = &initial_metadata_recv;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
op->data.recv_status_on_client.status = &status;
op->data.recv_status_on_client.status_details = &details;
op->data.recv_status_on_client.status_details_capacity = &details_capacity;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
for (i = 0; i < 4; i++) {
request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1);
op = ops;
op->op = GRPC_OP_SEND_MESSAGE;
op->data.send_message = request_payload;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_MESSAGE;
op->data.recv_message = &response_payload_recv;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(2), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
peer = grpc_call_get_peer(c);
cq_expect_completion(cqv, tag(2), 1);
cq_verify(cqv);
gpr_free(peer);
grpc_byte_buffer_destroy(request_payload);
grpc_byte_buffer_destroy(response_payload_recv);
}
gpr_slice_unref(request_payload_slice);
op = ops;
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
cq_expect_completion(cqv, tag(1), 1);
cq_expect_completion(cqv, tag(3), 1);
cq_verify(cqv);
peer = grpc_call_get_peer(c);
gpr_log(GPR_INFO, "Client DONE WITH SERVER %s ", peer);
gpr_free(peer);
grpc_call_destroy(c);
cq_verify_empty_timeout(cqv, 1);
cq_verifier_destroy(cqv);
grpc_metadata_array_destroy(&initial_metadata_recv);
grpc_metadata_array_destroy(&trailing_metadata_recv);
gpr_free(details);
}
static void setup_client(const char *server_hostport, client_fixture *cf) {
cf->cq = grpc_completion_queue_create(NULL);
cf->server_uri = gpr_strdup(server_hostport);
cf->client = grpc_insecure_channel_create(cf->server_uri, NULL, NULL);
}
static void teardown_client(client_fixture *cf) {
grpc_completion_queue_shutdown(cf->cq);
drain_cq(cf->cq);
grpc_completion_queue_destroy(cf->cq);
cf->cq = NULL;
grpc_channel_destroy(cf->client);
cf->client = NULL;
gpr_free(cf->server_uri);
}
static void setup_server(const char *host, server_fixture *sf) {
int assigned_port;
sf->cq = grpc_completion_queue_create(NULL);
char *colon_idx = strchr(host, ':');
if (colon_idx) {
char *port_str = colon_idx + 1;
sf->port = atoi(port_str);
sf->servers_hostport = gpr_strdup(host);
} else {
sf->port = grpc_pick_unused_port_or_die();
gpr_join_host_port(&sf->servers_hostport, host, sf->port);
}
sf->server = grpc_server_create(NULL, NULL);
grpc_server_register_completion_queue(sf->server, sf->cq, NULL);
GPR_ASSERT((assigned_port = grpc_server_add_insecure_http2_port(
sf->server, sf->servers_hostport)) > 0);
GPR_ASSERT(sf->port == assigned_port);
grpc_server_start(sf->server);
}
static void teardown_server(server_fixture *sf) {
if (!sf->server) return;
gpr_log(GPR_INFO, "Server[%s] shutting down", sf->servers_hostport);
grpc_server_shutdown_and_notify(sf->server, sf->cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(
sf->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
.type == GRPC_OP_COMPLETE);
grpc_server_destroy(sf->server);
gpr_thd_join(sf->tid);
sf->server = NULL;
grpc_completion_queue_shutdown(sf->cq);
drain_cq(sf->cq);
grpc_completion_queue_destroy(sf->cq);
gpr_log(GPR_INFO, "Server[%s] bye bye", sf->servers_hostport);
gpr_free(sf->servers_hostport);
}
static void fork_backend_server(void *arg) {
server_fixture *sf = arg;
start_backend_server(sf);
}
static void fork_lb_server(void *arg) {
test_fixture *tf = arg;
int ports[NUM_BACKENDS];
for (int i = 0; i < NUM_BACKENDS; i++) {
ports[i] = tf->lb_backends[i].port;
}
start_lb_server(&tf->lb_server, ports, NUM_BACKENDS,
tf->lb_server_update_delay_ms);
}
static void setup_test_fixture(test_fixture *tf,
int lb_server_update_delay_ms) {
tf->lb_server_update_delay_ms = lb_server_update_delay_ms;
gpr_thd_options options = gpr_thd_options_default();
gpr_thd_options_set_joinable(&options);
for (int i = 0; i < NUM_BACKENDS; ++i) {
setup_server("127.0.0.1", &tf->lb_backends[i]);
gpr_thd_new(&tf->lb_backends[i].tid, fork_backend_server,
&tf->lb_backends[i], &options);
}
setup_server("127.0.0.1", &tf->lb_server);
gpr_thd_new(&tf->lb_server.tid, fork_lb_server, &tf->lb_server, &options);
char *server_uri;
gpr_asprintf(&server_uri, "ipv4:%s?lb_policy=grpclb&lb_enabled=1",
tf->lb_server.servers_hostport);
setup_client(server_uri, &tf->client);
gpr_free(server_uri);
}
static void teardown_test_fixture(test_fixture *tf) {
teardown_client(&tf->client);
for (int i = 0; i < NUM_BACKENDS; ++i) {
teardown_server(&tf->lb_backends[i]);
}
teardown_server(&tf->lb_server);
}
// The LB server will send two updates: batch 1 and batch 2. Each batch contains
// two addresses, both of a valid and running backend server. Batch 1 is readily
// available and provided as soon as the client establishes the streaming call.
// Batch 2 is sent after a delay of \a lb_server_update_delay_ms milliseconds.
static test_fixture test_update(int lb_server_update_delay_ms) {
test_fixture tf;
memset(&tf, 0, sizeof(tf));
setup_test_fixture(&tf, lb_server_update_delay_ms);
perform_request(
&tf.client); // "consumes" 1st backend server of 1st serverlist
perform_request(
&tf.client); // "consumes" 2nd backend server of 1st serverlist
perform_request(
&tf.client); // "consumes" 1st backend server of 2nd serverlist
perform_request(
&tf.client); // "consumes" 2nd backend server of 2nd serverlist
teardown_test_fixture(&tf);
return tf;
}
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
grpc_init();
test_fixture tf_result;
// Clients take a bit over one second to complete a call (the last part of the
// call sleeps for 1 second while verifying the client's completion queue is
// empty). Therefore:
//
// If the LB server waits 800ms before sending an update, it will arrive
// before the first client request is done, skipping the second server from
// batch 1 altogether: the 2nd client request will go to the 1st server of
// batch 2 (ie, the third one out of the four total servers).
tf_result = test_update(800);
GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced == 1);
GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced == 0);
GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced == 2);
GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 1);
// If the LB server waits 1500ms, the update arrives after having picked the
// 2nd server from batch 1 but before the next pick for the first server of
// batch 2. All server are used.
tf_result = test_update(1500);
GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced == 1);
GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced == 1);
GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced == 1);
GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 1);
// If the LB server waits >= 2000ms, the update arrives after the first two
// request are done and the third pick is performed, which returns, in RR
// fashion, the 1st server of the 1st update. Therefore, the second server of
// batch 1 is hit twice, whereas the first server of batch 2 is never hit.
tf_result = test_update(2000);
GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced == 2);
GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced == 1);
GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced == 1);
GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 0);
grpc_shutdown();
return 0;
}

@ -258,9 +258,10 @@ void cq_verify(cq_verifier *v) {
gpr_strvec_destroy(&have_tags);
}
void cq_verify_empty(cq_verifier *v) {
gpr_timespec deadline = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_seconds(1, GPR_TIMESPAN));
void cq_verify_empty_timeout(cq_verifier *v, int timeout_sec) {
gpr_timespec deadline =
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_seconds(timeout_sec, GPR_TIMESPAN));
grpc_event ev;
GPR_ASSERT(v->expect.next == &v->expect && "expectation queue must be empty");
@ -274,6 +275,8 @@ void cq_verify_empty(cq_verifier *v) {
}
}
void cq_verify_empty(cq_verifier *v) { cq_verify_empty_timeout(v, 1); }
static expectation *add(cq_verifier *v, grpc_completion_type type, void *tag) {
expectation *e = gpr_malloc(sizeof(expectation));
e->type = type;

@ -55,6 +55,9 @@ void cq_verify(cq_verifier *v);
/* ensure that the completion queue is empty */
void cq_verify_empty(cq_verifier *v);
/* ensure that the completion queue is empty, waiting up to \a timeout secs. */
void cq_verify_empty_timeout(cq_verifier *v, int timeout_sec);
/* Various expectation matchers
Any functions taking ... expect a NULL terminated list of key/value pairs
(each pair using two parameter slots) of metadata that MUST be present in

@ -0,0 +1,97 @@
#!/usr/bin/env python2.7
# Copyright 2015-2016, 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.
from __future__ import print_function
import argparse
import subprocess
import sys
import os.path
import sys
import tempfile
import importlib
# Example: tools/codegen/core/gen_grpclb_test_response.py \
# --lb_proto src/proto/grpc/lb/v1/load_balancer.proto \
# 127.0.0.1:1234 10.0.0.1:4321
# 1) Compile src/proto/grpc/lb/v1/load_balancer.proto to a temp location
parser = argparse.ArgumentParser()
parser.add_argument('--lb_proto', required=True)
parser.add_argument('-e', '--expiration_interval_secs', type=int)
parser.add_argument('-o', '--output')
parser.add_argument('-q', '--quiet', default=False, action='store_true')
parser.add_argument('ipports', nargs='+')
args = parser.parse_args()
if not os.path.isfile(args.lb_proto):
print("ERROR: file '{}' cannot be accessed (not found, no permissions, etc.)"
.format(args.lb_proto), file=sys.stderr)
sys.exit(1)
proto_dirname = os.path.dirname(args.lb_proto)
output_dir = tempfile.mkdtemp()
protoc_cmd = 'protoc -I{} --python_out={} {}'.format(
proto_dirname, output_dir, args.lb_proto)
with tempfile.TemporaryFile() as stderr_tmpfile:
if subprocess.call(protoc_cmd, stderr=stderr_tmpfile, shell=True) != 0:
stderr_tmpfile.seek(0)
print("ERROR: while running '{}': {}".
format(protoc_cmd, stderr_tmpfile.read()))
sys.exit(2)
# 2) import the output .py file.
module_name = os.path.splitext(os.path.basename(args.lb_proto))[0] + '_pb2'
sys.path.append(output_dir)
pb_module = importlib.import_module(module_name)
# 3) Generate!
lb_response = pb_module.LoadBalanceResponse()
if args.expiration_interval_secs:
lb_response.server_list.expiration_interval.seconds = \
args.expiration_interval_secs
for ipport in args.ipports:
ip, port = ipport.split(':')
server = lb_response.server_list.servers.add()
server.ip_address = ip
server.port = int(port)
server.load_balance_token = b'token{}'.format(port)
serialized_bytes = lb_response.SerializeToString()
serialized_hex = ''.join('\\x{:02x}'.format(ord(c)) for c in serialized_bytes)
if args.output:
with open(args.output, 'w') as f:
f.write(serialized_bytes)
if not args.quiet:
print(str(lb_response))
print(serialized_hex)

@ -1100,6 +1100,7 @@ src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
src/core/ext/transport/chttp2/client/insecure/channel_create.c \
src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
src/core/ext/lb_policy/grpclb/grpclb.c \
src/core/ext/lb_policy/grpclb/load_balancer_api.c \
src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
third_party/nanopb/pb_common.c \

@ -944,6 +944,22 @@
"third_party": false,
"type": "target"
},
{
"deps": [
"gpr",
"gpr_test_util",
"grpc",
"grpc_test_util"
],
"headers": [],
"language": "c",
"name": "grpclb_test",
"src": [
"test/core/client_config/grpclb_test.c"
],
"third_party": false,
"type": "target"
},
{
"deps": [
"gpr",
@ -4152,6 +4168,7 @@
"gpr",
"grpc_base",
"grpc_lb_policy_grpclb",
"grpc_lb_policy_grpclb",
"grpc_lb_policy_pick_first",
"grpc_lb_policy_round_robin",
"grpc_load_reporting",
@ -4246,6 +4263,7 @@
"gpr",
"grpc_base",
"grpc_lb_policy_grpclb",
"grpc_lb_policy_grpclb",
"grpc_lb_policy_pick_first",
"grpc_lb_policy_round_robin",
"grpc_load_reporting",
@ -6048,6 +6066,7 @@
"language": "c",
"name": "grpc_lb_policy_grpclb",
"src": [
"src/core/ext/lb_policy/grpclb/grpclb.c",
"src/core/ext/lb_policy/grpclb/load_balancer_api.c",
"src/core/ext/lb_policy/grpclb/load_balancer_api.h",
"src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",

@ -1079,6 +1079,27 @@
"windows"
]
},
{
"args": [],
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"flaky": false,
"gtest": false,
"language": "c",
"name": "grpclb_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
]
},
{
"args": [],
"ci_platforms": [

@ -643,6 +643,17 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc_verify_jwt", "vcxproj\
{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpclb_test", "vcxproj\test\grpclb_test\grpclb_test.vcxproj", "{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}"
ProjectSection(myProperties) = preProject
lib = "False"
EndProjectSection
ProjectSection(ProjectDependencies) = postProject
{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_census_nosec_test", "vcxproj\test/end2end/fixtures\h2_census_nosec_test\h2_census_nosec_test.vcxproj", "{A8039D43-910E-4248-2A22-74366E8C4DCD}"
ProjectSection(myProperties) = preProject
lib = "False"
@ -2455,6 +2466,22 @@ Global
{02FAC25F-5FF6-34A0-00AE-B82BFBA851A9}.Release-DLL|Win32.Build.0 = Release|Win32
{02FAC25F-5FF6-34A0-00AE-B82BFBA851A9}.Release-DLL|x64.ActiveCfg = Release|x64
{02FAC25F-5FF6-34A0-00AE-B82BFBA851A9}.Release-DLL|x64.Build.0 = Release|x64
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Debug|Win32.ActiveCfg = Debug|Win32
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Debug|x64.ActiveCfg = Debug|x64
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Release|Win32.ActiveCfg = Release|Win32
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Release|x64.ActiveCfg = Release|x64
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Debug|Win32.Build.0 = Debug|Win32
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Debug|x64.Build.0 = Debug|x64
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Release|Win32.Build.0 = Release|Win32
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Release|x64.Build.0 = Release|x64
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Debug-DLL|Win32.Build.0 = Debug|Win32
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Debug-DLL|x64.ActiveCfg = Debug|x64
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Debug-DLL|x64.Build.0 = Debug|x64
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Release-DLL|Win32.ActiveCfg = Release|Win32
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Release-DLL|Win32.Build.0 = Release|Win32
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Release-DLL|x64.ActiveCfg = Release|x64
{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}.Release-DLL|x64.Build.0 = Release|x64
{A8039D43-910E-4248-2A22-74366E8C4DCD}.Debug|Win32.ActiveCfg = Debug|Win32
{A8039D43-910E-4248-2A22-74366E8C4DCD}.Debug|x64.ActiveCfg = Debug|x64
{A8039D43-910E-4248-2A22-74366E8C4DCD}.Release|Win32.ActiveCfg = Release|Win32

@ -770,6 +770,8 @@
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create_posix.c">
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.c">
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.c">
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.c">

@ -478,6 +478,9 @@
<ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create_posix.c">
<Filter>src\core\ext\transport\chttp2\client\insecure</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.c">
<Filter>src\core\ext\lb_policy\grpclb</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.c">
<Filter>src\core\ext\lb_policy\grpclb</Filter>
</ClCompile>

@ -688,6 +688,8 @@
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.c">
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.c">
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.c">
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.c">

@ -406,6 +406,9 @@
<ClCompile Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.c">
<Filter>src\core\ext\load_reporting</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.c">
<Filter>src\core\ext\lb_policy\grpclb</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.c">
<Filter>src\core\ext\lb_policy\grpclb</Filter>
</ClCompile>

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}</ProjectGuid>
<IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
<IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
<PlatformToolset>v100</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
<PlatformToolset>v110</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(SolutionDir)\..\vsprojects\global.props" />
<Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
<Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
<Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<TargetName>grpclb_test</TargetName>
<Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
<Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
<Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
<Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<TargetName>grpclb_test</TargetName>
<Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
<Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
<Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
<Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="$(SolutionDir)\..\test\core\client_config\grpclb_test.c">
</ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
<Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
<Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
<Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
<Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
</Target>
</Project>

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="$(SolutionDir)\..\test\core\client_config\grpclb_test.c">
<Filter>test\core\client_config</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="test">
<UniqueIdentifier>{e0ba55a2-37d9-5029-6f4e-64f097307340}</UniqueIdentifier>
</Filter>
<Filter Include="test\core">
<UniqueIdentifier>{6d1d02a2-d635-142d-16d4-45bd59f5c83a}</UniqueIdentifier>
</Filter>
<Filter Include="test\core\client_config">
<UniqueIdentifier>{356b8663-f1cf-f0df-39d0-d2de958699d0}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
Loading…
Cancel
Save