mirror of https://github.com/grpc/grpc.git
commit
74a207509a
3895 changed files with 106339 additions and 198245 deletions
@ -0,0 +1,2 @@ |
||||
# Security tests will contain fake secrets |
||||
test/core/security/** |
@ -0,0 +1,35 @@ |
||||
# this file is auto-generated |
||||
*protoc_lib_deps.py |
||||
|
||||
# no need to format protoc generated files |
||||
*_pb2*.py |
||||
|
||||
# no need to format build-yaml generated files |
||||
*.gen.py |
||||
|
||||
# generated files from a template |
||||
*test/cpp/naming/resolver_component_tests_runner.py |
||||
|
||||
# No BUILD, .bzl files |
||||
*BUILD |
||||
*.bzl |
||||
*.bazelrc |
||||
|
||||
# No other languages |
||||
*.bat |
||||
*.c |
||||
*.c++ |
||||
*.cc |
||||
*.css |
||||
*.go |
||||
*.h |
||||
*.html |
||||
*.json |
||||
*.md |
||||
*.objc |
||||
*.php |
||||
*.proto |
||||
*.rb |
||||
*.sh |
||||
*.xml |
||||
*.yaml |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,63 @@ |
||||
#!/bin/bash |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
# Script to upload github archives for bazel dependencies to GCS, creating a reliable mirror link. |
||||
# Archives are copied to "grpc-bazel-mirror" GCS bucket (https://console.cloud.google.com/storage/browser/grpc-bazel-mirror?project=grpc-testing) |
||||
# and will by downloadable with the https://storage.googleapis.com/grpc-bazel-mirror/ prefix. |
||||
# |
||||
# This script should be run each time bazel dependencies are updated. |
||||
|
||||
set -e |
||||
|
||||
cd $(dirname $0)/.. |
||||
|
||||
# Create a temp directory to hold the versioned tarball, |
||||
# and clean it up when the script exits. |
||||
tmpdir="$(mktemp -d)" |
||||
function cleanup { |
||||
rm -rf "$tmpdir" |
||||
} |
||||
trap cleanup EXIT |
||||
|
||||
function upload { |
||||
local file="$1" |
||||
|
||||
echo "Downloading https://${file}" |
||||
curl -L --fail --output "${tmpdir}/archive" "https://${file}" |
||||
|
||||
echo "Uploading https://${file} to https://storage.googleapis.com/grpc-bazel-mirror/${file}" |
||||
gsutil cp -n "${tmpdir}/archive" "gs://grpc-bazel-mirror/${file}" # "-n" will skip existing files |
||||
|
||||
rm -rf "${tmpdir}/archive" |
||||
} |
||||
|
||||
# How to check that all mirror URLs work: |
||||
# 1. clean $HOME/.cache/bazel |
||||
# 2. bazel clean --expunge |
||||
# 3. bazel sync (failed downloads will print warnings) |
||||
|
||||
# A specific link can be upload manually by running e.g. |
||||
# upload "github.com/google/boringssl/archive/1c2769383f027befac5b75b6cedd25daf3bf4dcf.tar.gz" |
||||
|
||||
# bazel binaries used by the tools/bazel wrapper script |
||||
upload github.com/bazelbuild/bazel/releases/download/1.0.0/bazel-1.0.0-linux-x86_64 |
||||
upload github.com/bazelbuild/bazel/releases/download/1.0.0/bazel-1.0.0-darwin-x86_64 |
||||
upload github.com/bazelbuild/bazel/releases/download/1.0.0/bazel-1.0.0-windows-x86_64.exe |
||||
|
||||
# Collect the github archives to mirror from grpc_deps.bzl |
||||
grep -o '"https://github.com/[^"]*"' bazel/grpc_deps.bzl | sed 's/^"https:\/\///' | sed 's/"$//' | while read -r line ; do |
||||
echo "Updating mirror for ${line}" |
||||
upload "${line}" |
||||
done |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,263 @@ |
||||
'#1': This file describes the list of targets and dependencies. |
||||
'#2': It is used among other things to generate all of our project files. |
||||
'#3': Please refer to the templates directory for more information. |
||||
settings: |
||||
'#01': The public version number of the library. |
||||
'#02': === |
||||
'#03': Please update the 'g_stands_for' field periodically with a new g word |
||||
'#04': not listed in doc/g_stands_for.md - and update that document to list the |
||||
'#05': new word. When doing so, please also update BUILD. |
||||
'#06': === |
||||
'#07': Master always has a "-dev" suffix |
||||
'#08': Use "-preN" suffixes to identify pre-release versions |
||||
'#09': Per-language overrides are possible with (eg) ruby_version tag here |
||||
'#10': See the expand_version.py for all the quirks here |
||||
core_version: 10.0.0 |
||||
csharp_major_version: 2 |
||||
g_stands_for: gradius |
||||
version: 1.30.0-dev |
||||
targets: |
||||
- name: check_epollexclusive |
||||
build: tool |
||||
language: c |
||||
src: |
||||
- test/build/check_epollexclusive.c |
||||
deps: |
||||
- grpc |
||||
- gpr |
||||
- name: gen_hpack_tables |
||||
build: tool |
||||
language: c++ |
||||
src: |
||||
- tools/codegen/core/gen_hpack_tables.cc |
||||
deps: |
||||
- grpc |
||||
- gpr |
||||
uses_polling: false |
||||
- name: gen_legal_metadata_characters |
||||
build: tool |
||||
language: c++ |
||||
src: |
||||
- tools/codegen/core/gen_legal_metadata_characters.cc |
||||
deps: [] |
||||
- name: gen_percent_encoding_tables |
||||
build: tool |
||||
language: c++ |
||||
src: |
||||
- tools/codegen/core/gen_percent_encoding_tables.cc |
||||
deps: [] |
||||
uses_polling: false |
||||
vspackages: |
||||
- linkage: static |
||||
name: grpc.dependencies.zlib |
||||
props: false |
||||
redist: true |
||||
version: 1.2.8.10 |
||||
- linkage: static |
||||
name: grpc.dependencies.openssl |
||||
props: true |
||||
redist: true |
||||
version: 1.0.204.1 |
||||
- name: gflags |
||||
props: false |
||||
redist: false |
||||
version: 2.1.2.1 |
||||
- name: gtest |
||||
props: false |
||||
redist: false |
||||
version: 1.7.0.1 |
||||
configs: |
||||
asan: |
||||
CC: clang |
||||
CPPFLAGS: -O0 -fsanitize-coverage=edge,trace-pc-guard -fsanitize=address -fno-omit-frame-pointer |
||||
-Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS |
||||
CXX: clang++ |
||||
LD: clang++ |
||||
LDFLAGS: -fsanitize=address |
||||
LDXX: clang++ |
||||
compile_the_world: true |
||||
test_environ: |
||||
ASAN_OPTIONS: detect_leaks=1:color=always |
||||
LSAN_OPTIONS: suppressions=test/core/util/lsan_suppressions.txt:report_objects=1 |
||||
asan-noleaks: |
||||
CC: clang |
||||
CPPFLAGS: -O0 -fsanitize-coverage=edge,trace-pc-guard -fsanitize=address -fno-omit-frame-pointer |
||||
-Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS |
||||
CXX: clang++ |
||||
LD: clang++ |
||||
LDFLAGS: fsanitize=address |
||||
LDXX: clang++ |
||||
compile_the_world: true |
||||
test_environ: |
||||
ASAN_OPTIONS: detect_leaks=0:color=always |
||||
asan-trace-cmp: |
||||
CC: clang |
||||
CPPFLAGS: -O0 -fsanitize-coverage=edge,trace-pc-guard -fsanitize-coverage=trace-cmp |
||||
-fsanitize=address -fno-omit-frame-pointer -Wno-unused-command-line-argument |
||||
-DGPR_NO_DIRECT_SYSCALLS |
||||
CXX: clang++ |
||||
LD: clang++ |
||||
LDFLAGS: -fsanitize=address |
||||
LDXX: clang++ |
||||
compile_the_world: true |
||||
test_environ: |
||||
ASAN_OPTIONS: detect_leaks=1:color=always |
||||
LSAN_OPTIONS: suppressions=test/core/util/lsan_suppressions.txt:report_objects=1 |
||||
basicprof: |
||||
CPPFLAGS: -O2 -DGRPC_BASIC_PROFILER -DGRPC_TIMERS_RDTSC |
||||
DEFINES: NDEBUG |
||||
c++-compat: |
||||
CFLAGS: -Wc++-compat |
||||
CPPFLAGS: -O0 |
||||
DEFINES: _DEBUG DEBUG |
||||
counters: |
||||
CPPFLAGS: -O2 -DGPR_LOW_LEVEL_COUNTERS |
||||
DEFINES: NDEBUG |
||||
counters_with_memory_counter: |
||||
CPPFLAGS: -O2 -DGPR_LOW_LEVEL_COUNTERS -DGPR_WRAP_MEMORY_COUNTER |
||||
DEFINES: NDEBUG |
||||
LDFLAGS: -Wl,--wrap=malloc -Wl,--wrap=calloc -Wl,--wrap=realloc -Wl,--wrap=free |
||||
dbg: |
||||
CPPFLAGS: -O0 |
||||
DEFINES: _DEBUG DEBUG |
||||
gcov: |
||||
CC: gcc |
||||
CPPFLAGS: -O0 -fprofile-arcs -ftest-coverage -Wno-return-type |
||||
CXX: g++ |
||||
DEFINES: _DEBUG DEBUG GPR_GCOV |
||||
LD: gcc |
||||
LDFLAGS: -fprofile-arcs -ftest-coverage -rdynamic -lstdc++ |
||||
LDXX: g++ |
||||
helgrind: |
||||
CPPFLAGS: -O0 |
||||
DEFINES: _DEBUG DEBUG |
||||
LDFLAGS: -rdynamic |
||||
valgrind: --tool=helgrind |
||||
lto: |
||||
CPPFLAGS: -O2 |
||||
DEFINES: NDEBUG |
||||
memcheck: |
||||
CPPFLAGS: -O0 |
||||
DEFINES: _DEBUG DEBUG |
||||
LDFLAGS: -rdynamic |
||||
valgrind: --tool=memcheck --leak-check=full |
||||
msan: |
||||
CC: clang |
||||
CPPFLAGS: -O0 -stdlib=libc++ -fsanitize-coverage=edge,trace-pc-guard -fsanitize=memory |
||||
-fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor -fno-omit-frame-pointer |
||||
-DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1 -Wno-unused-command-line-argument |
||||
-fPIE -pie -DGPR_NO_DIRECT_SYSCALLS |
||||
CXX: clang++ |
||||
DEFINES: NDEBUG |
||||
LD: clang++ |
||||
LDFLAGS: -stdlib=libc++ -fsanitize=memory -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1 |
||||
-fPIE -pie $(if $(JENKINS_BUILD),-Wl$(comma)-Ttext-segment=0x7e0000000000,) |
||||
LDXX: clang++ |
||||
compile_the_world: true |
||||
test_environ: |
||||
MSAN_OPTIONS: poison_in_dtor=1 |
||||
mutrace: |
||||
CPPFLAGS: -O3 -fno-omit-frame-pointer |
||||
DEFINES: NDEBUG |
||||
LDFLAGS: -rdynamic |
||||
noexcept: |
||||
CPPFLAGS: -O2 -Wframe-larger-than=16384 |
||||
CXXFLAGS: -fno-exceptions |
||||
DEFINES: NDEBUG |
||||
opt: |
||||
CPPFLAGS: -O2 -Wframe-larger-than=16384 |
||||
DEFINES: NDEBUG |
||||
stapprof: |
||||
CPPFLAGS: -O2 -DGRPC_STAP_PROFILER |
||||
DEFINES: NDEBUG |
||||
tsan: |
||||
CC: clang |
||||
CPPFLAGS: -O0 -fsanitize=thread -fno-omit-frame-pointer -Wno-unused-command-line-argument |
||||
-DGPR_NO_DIRECT_SYSCALLS |
||||
CXX: clang++ |
||||
DEFINES: GRPC_TSAN |
||||
LD: clang++ |
||||
LDFLAGS: -fsanitize=thread |
||||
LDXX: clang++ |
||||
compile_the_world: true |
||||
test_environ: |
||||
TSAN_OPTIONS: suppressions=test/core/util/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1 |
||||
ubsan: |
||||
CC: clang |
||||
CPPFLAGS: -O0 -stdlib=libc++ -fsanitize-coverage=edge,trace-pc-guard -fsanitize=undefined |
||||
-fno-omit-frame-pointer -Wno-unused-command-line-argument -Wvarargs |
||||
CXX: clang++ |
||||
DEFINES: NDEBUG GRPC_UBSAN |
||||
LD: clang++ |
||||
LDFLAGS: -stdlib=libc++ -fsanitize=undefined,unsigned-integer-overflow |
||||
LDXX: clang++ |
||||
compile_the_world: true |
||||
test_environ: |
||||
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1:suppressions=test/core/util/ubsan_suppressions.txt |
||||
defaults: |
||||
ares: |
||||
CFLAGS: -g |
||||
CPPFLAGS: -Ithird_party/cares -Ithird_party/cares/cares -fvisibility=hidden -D_GNU_SOURCE |
||||
$(if $(subst Darwin,,$(SYSTEM)),,-Ithird_party/cares/config_darwin) $(if $(subst |
||||
FreeBSD,,$(SYSTEM)),,-Ithird_party/cares/config_freebsd) $(if $(subst Linux,,$(SYSTEM)),,-Ithird_party/cares/config_linux) |
||||
$(if $(subst OpenBSD,,$(SYSTEM)),,-Ithird_party/cares/config_openbsd) -DWIN32_LEAN_AND_MEAN |
||||
-D_HAS_EXCEPTIONS=0 -DNOMINMAX $(if $(subst MINGW32,,$(SYSTEM)),-DHAVE_CONFIG_H,) |
||||
benchmark: |
||||
CPPFLAGS: -Ithird_party/benchmark/include -DHAVE_POSIX_REGEX |
||||
boringssl: |
||||
CFLAGS: -g |
||||
CPPFLAGS: -Ithird_party/boringssl-with-bazel/src/include -fvisibility=hidden -DOPENSSL_NO_ASM |
||||
-D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX |
||||
CXXFLAGS: -fno-exceptions |
||||
global: |
||||
CFLAGS: -g |
||||
COREFLAGS: -fno-exceptions |
||||
CPPFLAGS: -g -Wall -Wextra -DOSATOMIC_USE_INLINED=1 -Ithird_party/abseil-cpp -Ithird_party/upb |
||||
-Isrc/core/ext/upb-generated |
||||
LDFLAGS: -g |
||||
zlib: |
||||
CFLAGS: -fvisibility=hidden |
||||
php_config_m4: |
||||
deps: |
||||
- grpc |
||||
- address_sorting |
||||
- boringssl |
||||
- z |
||||
headers: |
||||
- src/php/ext/grpc/byte_buffer.h |
||||
- src/php/ext/grpc/call.h |
||||
- src/php/ext/grpc/call_credentials.h |
||||
- src/php/ext/grpc/channel.h |
||||
- src/php/ext/grpc/channel_credentials.h |
||||
- src/php/ext/grpc/completion_queue.h |
||||
- src/php/ext/grpc/php7_wrapper.h |
||||
- src/php/ext/grpc/php_grpc.h |
||||
- src/php/ext/grpc/server.h |
||||
- src/php/ext/grpc/server_credentials.h |
||||
- src/php/ext/grpc/timeval.h |
||||
- src/php/ext/grpc/version.h |
||||
src: |
||||
- src/php/ext/grpc/byte_buffer.c |
||||
- src/php/ext/grpc/call.c |
||||
- src/php/ext/grpc/call_credentials.c |
||||
- src/php/ext/grpc/channel.c |
||||
- src/php/ext/grpc/channel_credentials.c |
||||
- src/php/ext/grpc/completion_queue.c |
||||
- src/php/ext/grpc/php_grpc.c |
||||
- src/php/ext/grpc/server.c |
||||
- src/php/ext/grpc/server_credentials.c |
||||
- src/php/ext/grpc/timeval.c |
||||
python_dependencies: |
||||
deps: |
||||
- grpc |
||||
- address_sorting |
||||
- ares |
||||
- boringssl |
||||
- z |
||||
ruby_gem: |
||||
deps: |
||||
- grpc |
||||
- address_sorting |
||||
- ares |
||||
- boringssl |
||||
- z |
@ -0,0 +1,3 @@ |
||||
dl.field-list > dt { |
||||
word-break: keep-all !important; |
||||
} |
@ -0,0 +1,132 @@ |
||||
gRPC AsyncIO API |
||||
================ |
||||
|
||||
.. module:: grpc.experimental.aio |
||||
|
||||
Overview |
||||
-------- |
||||
|
||||
gRPC AsyncIO API is the **new version** of gRPC Python whose architecture is |
||||
tailored to AsyncIO. Underlying, it utilizes the same C-extension, gRPC C-Core, |
||||
as existing stack, and it replaces all gRPC IO operations with methods provided |
||||
by the AsyncIO library. |
||||
|
||||
This stack currently is under active development. Feel free to offer |
||||
suggestions by opening issues on our GitHub repo `grpc/grpc <https://github.com/grpc/grpc>`_. |
||||
|
||||
The design doc can be found here as `gRFC <https://github.com/grpc/proposal/pull/155>`_. |
||||
|
||||
|
||||
Caveats |
||||
------- |
||||
|
||||
gRPC Async API objects may only be used on the thread on which they were |
||||
created. AsyncIO doesn't provide thread safety for most of its APIs. |
||||
|
||||
|
||||
Module Contents |
||||
--------------- |
||||
|
||||
Enable AsyncIO in gRPC |
||||
^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. function:: init_grpc_aio |
||||
|
||||
Enable AsyncIO for gRPC Python. |
||||
|
||||
This function is idempotent and it should be invoked before creation of |
||||
AsyncIO stack objects. Otherwise, the application might deadlock. |
||||
|
||||
This function configurates the gRPC C-Core to invoke AsyncIO methods for IO |
||||
operations (e.g., socket read, write). The configuration applies to the |
||||
entire process. |
||||
|
||||
After invoking this function, making blocking function calls in coroutines |
||||
or in the thread running event loop will block the event loop, potentially |
||||
starving all RPCs in the process. Refer to the Python language |
||||
documentation on AsyncIO for more details (`running-blocking-code <https://docs.python.org/3/library/asyncio-dev.html#running-blocking-code>`_). |
||||
|
||||
|
||||
Create Channel |
||||
^^^^^^^^^^^^^^ |
||||
|
||||
Channels are the abstraction of clients, where most of networking logic |
||||
happens, for example, managing one or more underlying connections, name |
||||
resolution, load balancing, flow control, etc.. If you are using ProtoBuf, |
||||
Channel objects works best when further encapsulate into stub objects, then the |
||||
application can invoke remote functions as if they are local functions. |
||||
|
||||
.. autofunction:: insecure_channel |
||||
.. autofunction:: secure_channel |
||||
|
||||
|
||||
Channel Object |
||||
^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: Channel |
||||
|
||||
|
||||
Create Server |
||||
^^^^^^^^^^^^^ |
||||
|
||||
.. autofunction:: server |
||||
|
||||
|
||||
Server Object |
||||
^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: Server |
||||
|
||||
|
||||
gRPC Exceptions |
||||
^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoexception:: BaseError |
||||
.. autoexception:: UsageError |
||||
.. autoexception:: AbortError |
||||
.. autoexception:: InternalError |
||||
.. autoexception:: AioRpcError |
||||
|
||||
|
||||
Shared Context |
||||
^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: RpcContext |
||||
|
||||
|
||||
Client-Side Context |
||||
^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: Call |
||||
.. autoclass:: UnaryUnaryCall |
||||
.. autoclass:: UnaryStreamCall |
||||
.. autoclass:: StreamUnaryCall |
||||
.. autoclass:: StreamStreamCall |
||||
|
||||
|
||||
Server-Side Context |
||||
^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: ServicerContext |
||||
|
||||
|
||||
Client-Side Interceptor |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: ClientCallDetails |
||||
.. autoclass:: InterceptedUnaryUnaryCall |
||||
.. autoclass:: UnaryUnaryClientInterceptor |
||||
|
||||
.. Service-Side Context |
||||
.. ^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. .. autoclass:: ServicerContext |
||||
|
||||
|
||||
Multi-Callable Interfaces |
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ |
||||
|
||||
.. autoclass:: UnaryUnaryMultiCallable |
||||
.. autoclass:: UnaryStreamMultiCallable() |
||||
.. autoclass:: StreamUnaryMultiCallable() |
||||
.. autoclass:: StreamStreamMultiCallable() |
@ -0,0 +1,293 @@ |
||||
# xDS (Load-Balancing) Interop Test Case Descriptions |
||||
|
||||
Client and server use [test.proto](../src/proto/grpc/testing/test.proto). |
||||
|
||||
## Server |
||||
|
||||
The code for the xDS test server can be found at: |
||||
[Java](https://github.com/grpc/grpc-java/blob/master/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java) (other language implementations are in progress). |
||||
|
||||
Server should accept these arguments: |
||||
|
||||
* --port=PORT |
||||
* The port the server will run on. |
||||
|
||||
## Client |
||||
|
||||
The base behavior of the xDS test client is to send a constant QPS of unary |
||||
messages and record the remote-peer distribution of the responses. Further, the |
||||
client must expose an implementation of the `LoadBalancerStatsService` gRPC |
||||
service to allow the test driver to validate the load balancing behavior for a |
||||
particular test case (see below for more details). |
||||
|
||||
The code for the xDS test client can be at: |
||||
[Java](https://github.com/grpc/grpc-java/blob/master/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestClient.java) (other language implementations are in progress). |
||||
|
||||
Clients should accept these arguments: |
||||
|
||||
* --fail_on_failed_rpcs=BOOL |
||||
* If true, the client should exit with a non-zero return code if any RPCs |
||||
fail. Default is false. |
||||
* --num_channels=CHANNELS |
||||
* The number of channels to create to the server. |
||||
* --qps=QPS |
||||
* The QPS per channel. |
||||
* --server=HOSTNAME:PORT |
||||
* The server host to connect to. For example, "localhost:8080" |
||||
* --stats_port=PORT |
||||
* The port for to expose the client's `LoadBalancerStatsService` |
||||
implementation. |
||||
|
||||
## Test Driver |
||||
|
||||
Note that, unlike our other interop tests, neither the client nor the server has |
||||
any notion of which of the following test scenarios is under test. Instead, a |
||||
separate test driver is responsible for configuring the load balancer and the |
||||
server backends, running the client, and then querying the client's |
||||
`LoadBalancerStatsService` to validate load balancer behavior for each of the |
||||
tests described below. |
||||
|
||||
## LoadBalancerStatsService |
||||
|
||||
The service is defined as: |
||||
|
||||
``` |
||||
message LoadBalancerStatsRequest { |
||||
// Request stats for the next num_rpcs sent by client. |
||||
int32 num_rpcs = 1; |
||||
// If num_rpcs have not completed within timeout_sec, return partial results. |
||||
int32 timeout_sec = 2; |
||||
} |
||||
|
||||
message LoadBalancerStatsResponse { |
||||
// The number of completed RPCs for each peer. |
||||
map<string, int32> rpcs_by_peer = 1; |
||||
// The number of RPCs that failed to record a remote peer. |
||||
int32 num_failures = 2; |
||||
} |
||||
|
||||
service LoadBalancerStatsService { |
||||
// Gets the backend distribution for RPCs sent by a test client. |
||||
rpc GetClientStats(LoadBalancerStatsRequest) |
||||
returns (LoadBalancerStatsResponse) {} |
||||
} |
||||
``` |
||||
|
||||
Note that the `LoadBalancerStatsResponse` contains the remote peer distribution |
||||
of the next `num_rpcs` *sent* by the client after receiving the |
||||
`LoadBalancerStatsRequest`. It is important that the remote peer distribution be |
||||
recorded for a block of consecutive outgoing RPCs, to validate the intended |
||||
distribution from the load balancer, rather than just looking at the next |
||||
`num_rpcs` responses received from backends, as different backends may respond |
||||
at different rates. |
||||
|
||||
## Test Cases |
||||
|
||||
### ping_pong |
||||
|
||||
This test verifies that every backend receives traffic. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
1. --fail_on_failed_rpc=true |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. 4 backends are created in a single managed instance group (MIG). |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC |
||||
|
||||
### round_robin |
||||
|
||||
This test verifies that RPCs are evenly routed according to an unweighted round |
||||
robin policy. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
1. --fail_on_failed_rpc=true |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. 4 backends are created in a single MIG. |
||||
|
||||
Test driver asserts that: |
||||
|
||||
1. Once all backends receive at least one RPC, the following 100 RPCs are |
||||
evenly distributed across the 4 backends. |
||||
|
||||
### backends_restart |
||||
|
||||
This test verifies that the load balancer will resume sending traffic to a set |
||||
of backends that is stopped and then resumed. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. 4 backends are created in a single MIG. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC. |
||||
|
||||
The test driver records the peer distribution for a subsequent block of 100 RPCs |
||||
then stops the backends. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. No RPCs from the client are successful. |
||||
|
||||
The test driver resumes the backends. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. Once all backends receive at least one RPC, the distribution for a block of |
||||
100 RPCs is the same as the distribution recorded prior to restart. |
||||
|
||||
### secondary_locality_gets_requests_on_primary_failure |
||||
|
||||
This test verifies that backends in a secondary locality receive traffic when |
||||
all backends in the primary locality fail. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. The primary MIG with 2 backends in the same zone as the client |
||||
1. The secondary MIG with 2 backends in a different zone |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the primary locality receive at least 1 RPC. |
||||
1. No backends in the secondary locality receive RPCs. |
||||
|
||||
The test driver stops the backends in the primary locality. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the secondary locality receive at least 1 RPC. |
||||
|
||||
The test driver resumes the backends in the primary locality. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the primary locality receive at least 1 RPC. |
||||
1. No backends in the secondary locality receive RPCs. |
||||
|
||||
### secondary_locality_gets_no_requests_on_partial_primary_failure |
||||
|
||||
This test verifies that backends in a failover locality do not receive traffic |
||||
when at least one of the backends in the primary locality remain healthy. |
||||
|
||||
**Note:** Future TD features may change the expected behavior and require |
||||
changes to this test case. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. The primary MIG with 2 backends in the same zone as the client |
||||
1. The secondary MIG with 2 backends in a different zone |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the primary locality receive at least 1 RPC. |
||||
1. No backends in the secondary locality receive RPCs. |
||||
|
||||
The test driver stops one of the backends in the primary locality. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in the primary locality receive at least 1 RPC. |
||||
1. No backends in the secondary locality receive RPCs. |
||||
|
||||
### new_instance_group_receives_traffic |
||||
|
||||
This test verifies that new instance groups added to a backend service in the |
||||
same zone receive traffic. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
1. --fail_on_failed_rpc=true |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. One MIG with two backends, using rate balancing mode. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC. |
||||
|
||||
The test driver adds a new MIG with two backends in the same zone. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends in each MIG receive at least one RPC. |
||||
|
||||
### remove_instance_group |
||||
|
||||
This test verifies that a remaining instance group can successfully serve RPCs |
||||
after removal of another instance group in the same zone. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. Two MIGs with two backends each, using rate balancing mode. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC. |
||||
|
||||
The test driver removes one MIG. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All RPCs are directed to the two remaining backends (no RPC failures). |
||||
|
||||
### change_backend_service |
||||
|
||||
This test verifies that the backend service can be replaced and traffic routed |
||||
to the new backends. |
||||
|
||||
Client parameters: |
||||
|
||||
1. --num_channels=1 |
||||
1. --qps=10 |
||||
1. --fail_on_failed_rpc=true |
||||
|
||||
Load balancer configuration: |
||||
|
||||
1. One MIG with two backends |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All backends receive at least one RPC. |
||||
|
||||
The test driver creates a new backend service containing a MIG with two backends |
||||
and changes the TD URL map to point to this new backend service. |
||||
|
||||
Test driver asserts: |
||||
|
||||
1. All RPCs are directed to the new backend service. |
||||
|
@ -1,44 +1,13 @@ |
||||
# gRPC in 3 minutes (C++) |
||||
# gRPC C++ Examples |
||||
|
||||
## Installation |
||||
- **[Hello World][]!** Eager to run your first gRPC example? You'll find |
||||
instructions for building gRPC and running a simple "Hello World" app in [Quick Start][]. |
||||
- **[Route Guide][].** For a basic tutorial on gRPC see [gRPC Basics][]. |
||||
|
||||
To install gRPC on your system, follow the instructions to build from source |
||||
[here](../../BUILDING.md). This also installs the protocol buffer compiler |
||||
`protoc` (if you don't have it already), and the C++ gRPC plugin for `protoc`. |
||||
For information about the other examples in this directory, see their respective |
||||
README files. |
||||
|
||||
## Hello C++ gRPC! |
||||
|
||||
Here's how to build and run the C++ implementation of the [Hello |
||||
World](../protos/helloworld.proto) example used in [Getting started](..). |
||||
|
||||
### Client and server implementations |
||||
|
||||
The client implementation is at [greeter_client.cc](helloworld/greeter_client.cc). |
||||
|
||||
The server implementation is at [greeter_server.cc](helloworld/greeter_server.cc). |
||||
|
||||
### Try it! |
||||
Build client and server: |
||||
|
||||
```sh |
||||
$ make |
||||
``` |
||||
|
||||
Run the server, which will listen on port 50051: |
||||
|
||||
```sh |
||||
$ ./greeter_server |
||||
``` |
||||
|
||||
Run the client (in a different terminal): |
||||
|
||||
```sh |
||||
$ ./greeter_client |
||||
``` |
||||
|
||||
If things go smoothly, you will see the "Greeter received: Hello world" in the |
||||
client side output. |
||||
|
||||
## Tutorial |
||||
|
||||
You can find a more detailed tutorial in [gRPC Basics: C++](cpptutorial.md) |
||||
[gRPC Basics]: https://grpc.io/docs/tutorials/basic/cpp |
||||
[Hello World]: helloworld |
||||
[Quick Start]: https://grpc.io/docs/quickstart/cpp |
||||
[Route Guide]: route_guide |
||||
|
@ -1,488 +0,0 @@ |
||||
# gRPC Basics: C++ |
||||
|
||||
This tutorial provides a basic C++ programmer's introduction to working with |
||||
gRPC. By walking through this example you'll learn how to: |
||||
|
||||
- Define a service in a `.proto` file. |
||||
- Generate server and client code using the protocol buffer compiler. |
||||
- Use the C++ gRPC API to write a simple client and server for your service. |
||||
|
||||
It assumes that you are familiar with |
||||
[protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). |
||||
Note that the example in this tutorial uses the proto3 version of the protocol |
||||
buffers language, which is currently in alpha release: you can find out more in |
||||
the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) |
||||
and see the [release notes](https://github.com/google/protobuf/releases) for the |
||||
new version in the protocol buffers Github repository. |
||||
|
||||
## Why use gRPC? |
||||
|
||||
Our example is a simple route mapping application that lets clients get |
||||
information about features on their route, create a summary of their route, and |
||||
exchange route information such as traffic updates with the server and other |
||||
clients. |
||||
|
||||
With gRPC we can define our service once in a `.proto` file and implement clients |
||||
and servers in any of gRPC's supported languages, which in turn can be run in |
||||
environments ranging from servers inside Google to your own tablet - all the |
||||
complexity of communication between different languages and environments is |
||||
handled for you by gRPC. We also get all the advantages of working with protocol |
||||
buffers, including efficient serialization, a simple IDL, and easy interface |
||||
updating. |
||||
|
||||
## Example code and setup |
||||
|
||||
The example code for our tutorial is in [examples/cpp/route_guide](route_guide). |
||||
You also should have the relevant tools installed to generate the server and |
||||
client interface code - if you don't already, follow the setup instructions in |
||||
[BUILDING.md](../../BUILDING.md). |
||||
|
||||
## Defining the service |
||||
|
||||
Our first step is to define the gRPC *service* and the method *request* and |
||||
*response* types using |
||||
[protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). |
||||
You can see the complete `.proto` file in |
||||
[`examples/protos/route_guide.proto`](../protos/route_guide.proto). |
||||
|
||||
To define a service, you specify a named `service` in your `.proto` file: |
||||
|
||||
```protobuf |
||||
service RouteGuide { |
||||
... |
||||
} |
||||
``` |
||||
|
||||
Then you define `rpc` methods inside your service definition, specifying their |
||||
request and response types. gRPC lets you define four kinds of service method, |
||||
all of which are used in the `RouteGuide` service: |
||||
|
||||
- A *simple RPC* where the client sends a request to the server using the stub |
||||
and waits for a response to come back, just like a normal function call. |
||||
|
||||
```protobuf |
||||
// Obtains the feature at a given position. |
||||
rpc GetFeature(Point) returns (Feature) {} |
||||
``` |
||||
|
||||
- A *server-side streaming RPC* where the client sends a request to the server |
||||
and gets a stream to read a sequence of messages back. The client reads from |
||||
the returned stream until there are no more messages. As you can see in our |
||||
example, you specify a server-side streaming method by placing the `stream` |
||||
keyword before the *response* type. |
||||
|
||||
```protobuf |
||||
// Obtains the Features available within the given Rectangle. Results are |
||||
// streamed rather than returned at once (e.g. in a response message with a |
||||
// repeated field), as the rectangle may cover a large area and contain a |
||||
// huge number of features. |
||||
rpc ListFeatures(Rectangle) returns (stream Feature) {} |
||||
``` |
||||
|
||||
- A *client-side streaming RPC* where the client writes a sequence of messages |
||||
and sends them to the server, again using a provided stream. Once the client |
||||
has finished writing the messages, it waits for the server to read them all |
||||
and return its response. You specify a client-side streaming method by placing |
||||
the `stream` keyword before the *request* type. |
||||
|
||||
```protobuf |
||||
// Accepts a stream of Points on a route being traversed, returning a |
||||
// RouteSummary when traversal is completed. |
||||
rpc RecordRoute(stream Point) returns (RouteSummary) {} |
||||
``` |
||||
|
||||
- A *bidirectional streaming RPC* where both sides send a sequence of messages |
||||
using a read-write stream. The two streams operate independently, so clients |
||||
and servers can read and write in whatever order they like: for example, the |
||||
server could wait to receive all the client messages before writing its |
||||
responses, or it could alternately read a message then write a message, or |
||||
some other combination of reads and writes. The order of messages in each |
||||
stream is preserved. You specify this type of method by placing the `stream` |
||||
keyword before both the request and the response. |
||||
|
||||
```protobuf |
||||
// Accepts a stream of RouteNotes sent while a route is being traversed, |
||||
// while receiving other RouteNotes (e.g. from other users). |
||||
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} |
||||
``` |
||||
|
||||
Our `.proto` file also contains protocol buffer message type definitions for all |
||||
the request and response types used in our service methods - for example, here's |
||||
the `Point` message type: |
||||
|
||||
```protobuf |
||||
// Points are represented as latitude-longitude pairs in the E7 representation |
||||
// (degrees multiplied by 10**7 and rounded to the nearest integer). |
||||
// Latitudes should be in the range +/- 90 degrees and longitude should be in |
||||
// the range +/- 180 degrees (inclusive). |
||||
message Point { |
||||
int32 latitude = 1; |
||||
int32 longitude = 2; |
||||
} |
||||
``` |
||||
|
||||
## Generating client and server code |
||||
|
||||
Next we need to generate the gRPC client and server interfaces from our `.proto` |
||||
service definition. We do this using the protocol buffer compiler `protoc` with |
||||
a special gRPC C++ plugin. |
||||
|
||||
For simplicity, we've provided a [Makefile](route_guide/Makefile) that runs |
||||
`protoc` for you with the appropriate plugin, input, and output (if you want to |
||||
run this yourself, make sure you've installed protoc and followed the gRPC code |
||||
[installation instructions](../../BUILDING.md) first): |
||||
|
||||
```shell |
||||
$ make route_guide.grpc.pb.cc route_guide.pb.cc |
||||
``` |
||||
|
||||
which actually runs: |
||||
|
||||
```shell |
||||
$ protoc -I ../../protos --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ../../protos/route_guide.proto |
||||
$ protoc -I ../../protos --cpp_out=. ../../protos/route_guide.proto |
||||
``` |
||||
|
||||
Running this command generates the following files in your current directory: |
||||
- `route_guide.pb.h`, the header which declares your generated message classes |
||||
- `route_guide.pb.cc`, which contains the implementation of your message classes |
||||
- `route_guide.grpc.pb.h`, the header which declares your generated service |
||||
classes |
||||
- `route_guide.grpc.pb.cc`, which contains the implementation of your service |
||||
classes |
||||
|
||||
These contain: |
||||
- All the protocol buffer code to populate, serialize, and retrieve our request |
||||
and response message types |
||||
- A class called `RouteGuide` that contains |
||||
- a remote interface type (or *stub*) for clients to call with the methods |
||||
defined in the `RouteGuide` service. |
||||
- two abstract interfaces for servers to implement, also with the methods |
||||
defined in the `RouteGuide` service. |
||||
|
||||
|
||||
<a name="server"></a> |
||||
## Creating the server |
||||
|
||||
First let's look at how we create a `RouteGuide` server. If you're only |
||||
interested in creating gRPC clients, you can skip this section and go straight |
||||
to [Creating the client](#client) (though you might find it interesting |
||||
anyway!). |
||||
|
||||
There are two parts to making our `RouteGuide` service do its job: |
||||
- Implementing the service interface generated from our service definition: |
||||
doing the actual "work" of our service. |
||||
- Running a gRPC server to listen for requests from clients and return the |
||||
service responses. |
||||
|
||||
You can find our example `RouteGuide` server in |
||||
[route_guide/route_guide_server.cc](route_guide/route_guide_server.cc). Let's |
||||
take a closer look at how it works. |
||||
|
||||
### Implementing RouteGuide |
||||
|
||||
As you can see, our server has a `RouteGuideImpl` class that implements the |
||||
generated `RouteGuide::Service` interface: |
||||
|
||||
```cpp |
||||
class RouteGuideImpl final : public RouteGuide::Service { |
||||
... |
||||
} |
||||
``` |
||||
In this case we're implementing the *synchronous* version of `RouteGuide`, which |
||||
provides our default gRPC server behaviour. It's also possible to implement an |
||||
asynchronous interface, `RouteGuide::AsyncService`, which allows you to further |
||||
customize your server's threading behaviour, though we won't look at this in |
||||
this tutorial. |
||||
|
||||
`RouteGuideImpl` implements all our service methods. Let's look at the simplest |
||||
type first, `GetFeature`, which just gets a `Point` from the client and returns |
||||
the corresponding feature information from its database in a `Feature`. |
||||
|
||||
```cpp |
||||
Status GetFeature(ServerContext* context, const Point* point, |
||||
Feature* feature) override { |
||||
feature->set_name(GetFeatureName(*point, feature_list_)); |
||||
feature->mutable_location()->CopyFrom(*point); |
||||
return Status::OK; |
||||
} |
||||
``` |
||||
|
||||
The method is passed a context object for the RPC, the client's `Point` protocol |
||||
buffer request, and a `Feature` protocol buffer to fill in with the response |
||||
information. In the method we populate the `Feature` with the appropriate |
||||
information, and then `return` with an `OK` status to tell gRPC that we've |
||||
finished dealing with the RPC and that the `Feature` can be returned to the |
||||
client. |
||||
|
||||
Now let's look at something a bit more complicated - a streaming RPC. |
||||
`ListFeatures` is a server-side streaming RPC, so we need to send back multiple |
||||
`Feature`s to our client. |
||||
|
||||
```cpp |
||||
Status ListFeatures(ServerContext* context, const Rectangle* rectangle, |
||||
ServerWriter<Feature>* writer) override { |
||||
auto lo = rectangle->lo(); |
||||
auto hi = rectangle->hi(); |
||||
long left = std::min(lo.longitude(), hi.longitude()); |
||||
long right = std::max(lo.longitude(), hi.longitude()); |
||||
long top = std::max(lo.latitude(), hi.latitude()); |
||||
long bottom = std::min(lo.latitude(), hi.latitude()); |
||||
for (const Feature& f : feature_list_) { |
||||
if (f.location().longitude() >= left && |
||||
f.location().longitude() <= right && |
||||
f.location().latitude() >= bottom && |
||||
f.location().latitude() <= top) { |
||||
writer->Write(f); |
||||
} |
||||
} |
||||
return Status::OK; |
||||
} |
||||
``` |
||||
|
||||
As you can see, instead of getting simple request and response objects in our |
||||
method parameters, this time we get a request object (the `Rectangle` in which |
||||
our client wants to find `Feature`s) and a special `ServerWriter` object. In the |
||||
method, we populate as many `Feature` objects as we need to return, writing them |
||||
to the `ServerWriter` using its `Write()` method. Finally, as in our simple RPC, |
||||
we `return Status::OK` to tell gRPC that we've finished writing responses. |
||||
|
||||
If you look at the client-side streaming method `RecordRoute` you'll see it's |
||||
quite similar, except this time we get a `ServerReader` instead of a request |
||||
object and a single response. We use the `ServerReader`s `Read()` method to |
||||
repeatedly read in our client's requests to a request object (in this case a |
||||
`Point`) until there are no more messages: the server needs to check the return |
||||
value of `Read()` after each call. If `true`, the stream is still good and it |
||||
can continue reading; if `false` the message stream has ended. |
||||
|
||||
```cpp |
||||
while (stream->Read(&point)) { |
||||
...//process client input |
||||
} |
||||
``` |
||||
Finally, let's look at our bidirectional streaming RPC `RouteChat()`. |
||||
|
||||
```cpp |
||||
Status RouteChat(ServerContext* context, |
||||
ServerReaderWriter<RouteNote, RouteNote>* stream) override { |
||||
std::vector<RouteNote> received_notes; |
||||
RouteNote note; |
||||
while (stream->Read(¬e)) { |
||||
for (const RouteNote& n : received_notes) { |
||||
if (n.location().latitude() == note.location().latitude() && |
||||
n.location().longitude() == note.location().longitude()) { |
||||
stream->Write(n); |
||||
} |
||||
} |
||||
received_notes.push_back(note); |
||||
} |
||||
|
||||
return Status::OK; |
||||
} |
||||
``` |
||||
|
||||
This time we get a `ServerReaderWriter` that can be used to read *and* write |
||||
messages. The syntax for reading and writing here is exactly the same as for our |
||||
client-streaming and server-streaming methods. Although each side will always |
||||
get the other's messages in the order they were written, both the client and |
||||
server can read and write in any order — the streams operate completely |
||||
independently. |
||||
|
||||
### Starting the server |
||||
|
||||
Once we've implemented all our methods, we also need to start up a gRPC server |
||||
so that clients can actually use our service. The following snippet shows how we |
||||
do this for our `RouteGuide` service: |
||||
|
||||
```cpp |
||||
void RunServer(const std::string& db_path) { |
||||
std::string server_address("0.0.0.0:50051"); |
||||
RouteGuideImpl service(db_path); |
||||
|
||||
ServerBuilder builder; |
||||
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); |
||||
builder.RegisterService(&service); |
||||
std::unique_ptr<Server> server(builder.BuildAndStart()); |
||||
std::cout << "Server listening on " << server_address << std::endl; |
||||
server->Wait(); |
||||
} |
||||
``` |
||||
As you can see, we build and start our server using a `ServerBuilder`. To do this, we: |
||||
|
||||
1. Create an instance of our service implementation class `RouteGuideImpl`. |
||||
1. Create an instance of the factory `ServerBuilder` class. |
||||
1. Specify the address and port we want to use to listen for client requests |
||||
using the builder's `AddListeningPort()` method. |
||||
1. Register our service implementation with the builder. |
||||
1. Call `BuildAndStart()` on the builder to create and start an RPC server for |
||||
our service. |
||||
1. Call `Wait()` on the server to do a blocking wait until process is killed or |
||||
`Shutdown()` is called. |
||||
|
||||
<a name="client"></a> |
||||
## Creating the client |
||||
|
||||
In this section, we'll look at creating a C++ client for our `RouteGuide` |
||||
service. You can see our complete example client code in |
||||
[route_guide/route_guide_client.cc](route_guide/route_guide_client.cc). |
||||
|
||||
### Creating a stub |
||||
|
||||
To call service methods, we first need to create a *stub*. |
||||
|
||||
First we need to create a gRPC *channel* for our stub, specifying the server |
||||
address and port we want to connect to without SSL: |
||||
|
||||
```cpp |
||||
grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); |
||||
``` |
||||
|
||||
Now we can use the channel to create our stub using the `NewStub` method |
||||
provided in the `RouteGuide` class we generated from our `.proto`. |
||||
|
||||
```cpp |
||||
public: |
||||
RouteGuideClient(std::shared_ptr<Channel> channel, const std::string& db) |
||||
: stub_(RouteGuide::NewStub(channel)) { |
||||
... |
||||
} |
||||
``` |
||||
|
||||
### Calling service methods |
||||
|
||||
Now let's look at how we call our service methods. Note that in this tutorial |
||||
we're calling the *blocking/synchronous* versions of each method: this means |
||||
that the RPC call waits for the server to respond, and will either return a |
||||
response or raise an exception. |
||||
|
||||
#### Simple RPC |
||||
|
||||
Calling the simple RPC `GetFeature` is nearly as straightforward as calling a |
||||
local method. |
||||
|
||||
```cpp |
||||
Point point; |
||||
Feature feature; |
||||
point = MakePoint(409146138, -746188906); |
||||
GetOneFeature(point, &feature); |
||||
|
||||
... |
||||
|
||||
bool GetOneFeature(const Point& point, Feature* feature) { |
||||
ClientContext context; |
||||
Status status = stub_->GetFeature(&context, point, feature); |
||||
... |
||||
} |
||||
``` |
||||
|
||||
As you can see, we create and populate a request protocol buffer object (in our |
||||
case `Point`), and create a response protocol buffer object for the server to |
||||
fill in. We also create a `ClientContext` object for our call - you can |
||||
optionally set RPC configuration values on this object, such as deadlines, |
||||
though for now we'll use the default settings. Note that you cannot reuse this |
||||
object between calls. Finally, we call the method on the stub, passing it the |
||||
context, request, and response. If the method returns `OK`, then we can read the |
||||
response information from the server from our response object. |
||||
|
||||
```cpp |
||||
std::cout << "Found feature called " << feature->name() << " at " |
||||
<< feature->location().latitude()/kCoordFactor_ << ", " |
||||
<< feature->location().longitude()/kCoordFactor_ << std::endl; |
||||
``` |
||||
|
||||
#### Streaming RPCs |
||||
|
||||
Now let's look at our streaming methods. If you've already read [Creating the |
||||
server](#server) some of this may look very familiar - streaming RPCs are |
||||
implemented in a similar way on both sides. Here's where we call the server-side |
||||
streaming method `ListFeatures`, which returns a stream of geographical |
||||
`Feature`s: |
||||
|
||||
```cpp |
||||
std::unique_ptr<ClientReader<Feature> > reader( |
||||
stub_->ListFeatures(&context, rect)); |
||||
while (reader->Read(&feature)) { |
||||
std::cout << "Found feature called " |
||||
<< feature.name() << " at " |
||||
<< feature.location().latitude()/kCoordFactor_ << ", " |
||||
<< feature.location().longitude()/kCoordFactor_ << std::endl; |
||||
} |
||||
Status status = reader->Finish(); |
||||
``` |
||||
|
||||
Instead of passing the method a context, request, and response, we pass it a |
||||
context and request and get a `ClientReader` object back. The client can use the |
||||
`ClientReader` to read the server's responses. We use the `ClientReader`s |
||||
`Read()` method to repeatedly read in the server's responses to a response |
||||
protocol buffer object (in this case a `Feature`) until there are no more |
||||
messages: the client needs to check the return value of `Read()` after each |
||||
call. If `true`, the stream is still good and it can continue reading; if |
||||
`false` the message stream has ended. Finally, we call `Finish()` on the stream |
||||
to complete the call and get our RPC status. |
||||
|
||||
The client-side streaming method `RecordRoute` is similar, except there we pass |
||||
the method a context and response object and get back a `ClientWriter`. |
||||
|
||||
```cpp |
||||
std::unique_ptr<ClientWriter<Point> > writer( |
||||
stub_->RecordRoute(&context, &stats)); |
||||
for (int i = 0; i < kPoints; i++) { |
||||
const Feature& f = feature_list_[feature_distribution(generator)]; |
||||
std::cout << "Visiting point " |
||||
<< f.location().latitude()/kCoordFactor_ << ", " |
||||
<< f.location().longitude()/kCoordFactor_ << std::endl; |
||||
if (!writer->Write(f.location())) { |
||||
// Broken stream. |
||||
break; |
||||
} |
||||
std::this_thread::sleep_for(std::chrono::milliseconds( |
||||
delay_distribution(generator))); |
||||
} |
||||
writer->WritesDone(); |
||||
Status status = writer->Finish(); |
||||
if (status.IsOk()) { |
||||
std::cout << "Finished trip with " << stats.point_count() << " points\n" |
||||
<< "Passed " << stats.feature_count() << " features\n" |
||||
<< "Travelled " << stats.distance() << " meters\n" |
||||
<< "It took " << stats.elapsed_time() << " seconds" |
||||
<< std::endl; |
||||
} else { |
||||
std::cout << "RecordRoute rpc failed." << std::endl; |
||||
} |
||||
``` |
||||
|
||||
Once we've finished writing our client's requests to the stream using `Write()`, |
||||
we need to call `WritesDone()` on the stream to let gRPC know that we've |
||||
finished writing, then `Finish()` to complete the call and get our RPC status. |
||||
If the status is `OK`, our response object that we initially passed to |
||||
`RecordRoute()` will be populated with the server's response. |
||||
|
||||
Finally, let's look at our bidirectional streaming RPC `RouteChat()`. In this |
||||
case, we just pass a context to the method and get back a `ClientReaderWriter`, |
||||
which we can use to both write and read messages. |
||||
|
||||
```cpp |
||||
std::shared_ptr<ClientReaderWriter<RouteNote, RouteNote> > stream( |
||||
stub_->RouteChat(&context)); |
||||
``` |
||||
|
||||
The syntax for reading and writing here is exactly the same as for our |
||||
client-streaming and server-streaming methods. Although each side will always |
||||
get the other's messages in the order they were written, both the client and |
||||
server can read and write in any order — the streams operate completely |
||||
independently. |
||||
|
||||
## Try it out! |
||||
|
||||
Build client and server: |
||||
```shell |
||||
$ make |
||||
``` |
||||
Run the server, which will listen on port 50051: |
||||
```shell |
||||
$ ./route_guide_server |
||||
``` |
||||
Run the client (in a different terminal): |
||||
```shell |
||||
$ ./route_guide_client |
||||
``` |
@ -1,260 +1,6 @@ |
||||
# gRPC C++ Hello World Tutorial |
||||
|
||||
### Install gRPC |
||||
Make sure you have installed gRPC on your system. Follow the |
||||
[BUILDING.md](../../../BUILDING.md) instructions. |
||||
|
||||
### Get the tutorial source code |
||||
|
||||
The example code for this and our other examples lives in the `examples` |
||||
directory. Clone this repository to your local machine by running the |
||||
following command: |
||||
|
||||
|
||||
```sh |
||||
$ git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc |
||||
``` |
||||
|
||||
Change your current directory to examples/cpp/helloworld |
||||
|
||||
```sh |
||||
$ cd examples/cpp/helloworld/ |
||||
``` |
||||
|
||||
### Defining a service |
||||
|
||||
The first step in creating our example is to define a *service*: an RPC |
||||
service specifies the methods that can be called remotely with their parameters |
||||
and return types. As you saw in the |
||||
[overview](#protocolbuffers) above, gRPC does this using [protocol |
||||
buffers](https://developers.google.com/protocol-buffers/docs/overview). We |
||||
use the protocol buffers interface definition language (IDL) to define our |
||||
service methods, and define the parameters and return |
||||
types as protocol buffer message types. Both the client and the |
||||
server use interface code generated from the service definition. |
||||
|
||||
Here's our example service definition, defined using protocol buffers IDL in |
||||
[helloworld.proto](../../protos/helloworld.proto). The `Greeter` |
||||
service has one method, `SayHello`, that lets the server receive a single |
||||
`HelloRequest` |
||||
message from the remote client containing the user's name, then send back |
||||
a greeting in a single `HelloReply`. This is the simplest type of RPC you |
||||
can specify in gRPC - we'll look at some other types later in this document. |
||||
|
||||
```protobuf |
||||
syntax = "proto3"; |
||||
|
||||
option java_package = "ex.grpc"; |
||||
|
||||
package helloworld; |
||||
|
||||
// The greeting service definition. |
||||
service Greeter { |
||||
// Sends a greeting |
||||
rpc SayHello (HelloRequest) returns (HelloReply) {} |
||||
} |
||||
|
||||
// The request message containing the user's name. |
||||
message HelloRequest { |
||||
string name = 1; |
||||
} |
||||
|
||||
// The response message containing the greetings |
||||
message HelloReply { |
||||
string message = 1; |
||||
} |
||||
|
||||
``` |
||||
|
||||
<a name="generating"></a> |
||||
### Generating gRPC code |
||||
|
||||
Once we've defined our service, we use the protocol buffer compiler |
||||
`protoc` to generate the special client and server code we need to create |
||||
our application. The generated code contains both stub code for clients to |
||||
use and an abstract interface for servers to implement, both with the method |
||||
defined in our `Greeting` service. |
||||
|
||||
To generate the client and server side interfaces: |
||||
|
||||
```sh |
||||
$ make helloworld.grpc.pb.cc helloworld.pb.cc |
||||
``` |
||||
Which internally invokes the proto-compiler as: |
||||
|
||||
```sh |
||||
$ protoc -I ../../protos/ --grpc_out=. --plugin=protoc-gen-grpc=grpc_cpp_plugin ../../protos/helloworld.proto |
||||
$ protoc -I ../../protos/ --cpp_out=. ../../protos/helloworld.proto |
||||
``` |
||||
|
||||
### Writing a client |
||||
|
||||
- Create a channel. A channel is a logical connection to an endpoint. A gRPC |
||||
channel can be created with the target address, credentials to use and |
||||
arguments as follows |
||||
|
||||
```cpp |
||||
auto channel = CreateChannel("localhost:50051", InsecureChannelCredentials()); |
||||
``` |
||||
|
||||
- Create a stub. A stub implements the rpc methods of a service and in the |
||||
generated code, a method is provided to create a stub with a channel: |
||||
|
||||
```cpp |
||||
auto stub = helloworld::Greeter::NewStub(channel); |
||||
``` |
||||
|
||||
- Make a unary rpc, with `ClientContext` and request/response proto messages. |
||||
|
||||
```cpp |
||||
ClientContext context; |
||||
HelloRequest request; |
||||
request.set_name("hello"); |
||||
HelloReply reply; |
||||
Status status = stub->SayHello(&context, request, &reply); |
||||
``` |
||||
|
||||
- Check returned status and response. |
||||
|
||||
```cpp |
||||
if (status.ok()) { |
||||
// check reply.message() |
||||
} else { |
||||
// rpc failed. |
||||
} |
||||
``` |
||||
|
||||
For a working example, refer to [greeter_client.cc](greeter_client.cc). |
||||
|
||||
### Writing a server |
||||
|
||||
- Implement the service interface |
||||
|
||||
```cpp |
||||
class GreeterServiceImpl final : public Greeter::Service { |
||||
Status SayHello(ServerContext* context, const HelloRequest* request, |
||||
HelloReply* reply) override { |
||||
std::string prefix("Hello "); |
||||
reply->set_message(prefix + request->name()); |
||||
return Status::OK; |
||||
} |
||||
}; |
||||
|
||||
``` |
||||
|
||||
- Build a server exporting the service |
||||
|
||||
```cpp |
||||
GreeterServiceImpl service; |
||||
ServerBuilder builder; |
||||
builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials()); |
||||
builder.RegisterService(&service); |
||||
std::unique_ptr<Server> server(builder.BuildAndStart()); |
||||
``` |
||||
|
||||
For a working example, refer to [greeter_server.cc](greeter_server.cc). |
||||
|
||||
### Writing asynchronous client and server |
||||
|
||||
gRPC uses `CompletionQueue` API for asynchronous operations. The basic work flow |
||||
is |
||||
- bind a `CompletionQueue` to a rpc call |
||||
- do something like a read or write, present with a unique `void*` tag |
||||
- call `CompletionQueue::Next` to wait for operations to complete. If a tag |
||||
appears, it indicates that the corresponding operation is complete. |
||||
|
||||
#### Async client |
||||
|
||||
The channel and stub creation code is the same as the sync client. |
||||
|
||||
- Initiate the rpc and create a handle for the rpc. Bind the rpc to a |
||||
`CompletionQueue`. |
||||
|
||||
```cpp |
||||
CompletionQueue cq; |
||||
auto rpc = stub->AsyncSayHello(&context, request, &cq); |
||||
``` |
||||
|
||||
- Ask for reply and final status, with a unique tag |
||||
|
||||
```cpp |
||||
Status status; |
||||
rpc->Finish(&reply, &status, (void*)1); |
||||
``` |
||||
|
||||
- Wait for the completion queue to return the next tag. The reply and status are |
||||
ready once the tag passed into the corresponding `Finish()` call is returned. |
||||
|
||||
```cpp |
||||
void* got_tag; |
||||
bool ok = false; |
||||
cq.Next(&got_tag, &ok); |
||||
if (ok && got_tag == (void*)1) { |
||||
// check reply and status |
||||
} |
||||
``` |
||||
|
||||
For a working example, refer to [greeter_async_client.cc](greeter_async_client.cc). |
||||
|
||||
#### Async server |
||||
|
||||
The server implementation requests a rpc call with a tag and then wait for the |
||||
completion queue to return the tag. The basic flow is |
||||
|
||||
- Build a server exporting the async service |
||||
|
||||
```cpp |
||||
helloworld::Greeter::AsyncService service; |
||||
ServerBuilder builder; |
||||
builder.AddListeningPort("0.0.0.0:50051", InsecureServerCredentials()); |
||||
builder.RegisterService(&service); |
||||
auto cq = builder.AddCompletionQueue(); |
||||
auto server = builder.BuildAndStart(); |
||||
``` |
||||
|
||||
- Request one rpc |
||||
|
||||
```cpp |
||||
ServerContext context; |
||||
HelloRequest request; |
||||
ServerAsyncResponseWriter<HelloReply> responder; |
||||
service.RequestSayHello(&context, &request, &responder, &cq, &cq, (void*)1); |
||||
``` |
||||
|
||||
- Wait for the completion queue to return the tag. The context, request and |
||||
responder are ready once the tag is retrieved. |
||||
|
||||
```cpp |
||||
HelloReply reply; |
||||
Status status; |
||||
void* got_tag; |
||||
bool ok = false; |
||||
cq.Next(&got_tag, &ok); |
||||
if (ok && got_tag == (void*)1) { |
||||
// set reply and status |
||||
responder.Finish(reply, status, (void*)2); |
||||
} |
||||
``` |
||||
|
||||
- Wait for the completion queue to return the tag. The rpc is finished when the |
||||
tag is back. |
||||
|
||||
```cpp |
||||
void* got_tag; |
||||
bool ok = false; |
||||
cq.Next(&got_tag, &ok); |
||||
if (ok && got_tag == (void*)2) { |
||||
// clean up |
||||
} |
||||
``` |
||||
|
||||
To handle multiple rpcs, the async server creates an object `CallData` to |
||||
maintain the state of each rpc and use the address of it as the unique tag. For |
||||
simplicity the server only uses one completion queue for all events, and runs a |
||||
main loop in `HandleRpcs` to query the queue. |
||||
|
||||
For a working example, refer to [greeter_async_server.cc](greeter_async_server.cc). |
||||
|
||||
|
||||
# gRPC C++ Hello World Example |
||||
|
||||
You can find a complete set of instructions for building gRPC and running the |
||||
Hello World app in the [C++ Quick Start][]. |
||||
|
||||
[C++ Quick Start]: https://grpc.io/docs/quickstart/cpp |
||||
|
@ -1,116 +1,129 @@ |
||||
# Copyright 2018 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
# cmake "superbuild" file for C++ helloworld example. |
||||
# This build file demonstrates how to build the helloworld project |
||||
# and all its dependencies in a single cmake build (hence "superbuild") |
||||
# that is easy to build and maintain. |
||||
# cmake's ExternalProject_Add() is used to import all the sub-projects, |
||||
# including the "helloworld" project itself. |
||||
# See https://blog.kitware.com/cmake-superbuilds-git-submodules/ |
||||
|
||||
cmake_minimum_required(VERSION 3.5.1) |
||||
|
||||
# Project |
||||
project(HelloWorld-SuperBuild C CXX) |
||||
|
||||
include(ExternalProject) |
||||
|
||||
# Builds c-ares project from the git submodule. |
||||
# Note: For all external projects, instead of using checked-out code, one could |
||||
# specify GIT_REPOSITORY and GIT_TAG to have cmake download the dependency directly, |
||||
# without needing to add a submodule to your project. |
||||
ExternalProject_Add(c-ares |
||||
PREFIX c-ares |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../third_party/cares/cares" |
||||
CMAKE_CACHE_ARGS |
||||
-DCARES_SHARED:BOOL=OFF |
||||
-DCARES_STATIC:BOOL=ON |
||||
-DCARES_STATIC_PIC:BOOL=ON |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares |
||||
) |
||||
|
||||
# Builds protobuf project from the git submodule. |
||||
ExternalProject_Add(protobuf |
||||
PREFIX protobuf |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../third_party/protobuf/cmake" |
||||
CMAKE_CACHE_ARGS |
||||
-Dprotobuf_BUILD_TESTS:BOOL=OFF |
||||
-Dprotobuf_WITH_ZLIB:BOOL=OFF |
||||
-Dprotobuf_MSVC_STATIC_RUNTIME:BOOL=OFF |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/protobuf |
||||
) |
||||
|
||||
# Builds zlib project from the git submodule. |
||||
ExternalProject_Add(zlib |
||||
PREFIX zlib |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../third_party/zlib" |
||||
CMAKE_CACHE_ARGS |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/zlib |
||||
) |
||||
|
||||
# the location where protobuf-config.cmake will be installed varies by platform |
||||
if (WIN32) |
||||
set(_FINDPACKAGE_PROTOBUF_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}/protobuf/cmake") |
||||
else() |
||||
set(_FINDPACKAGE_PROTOBUF_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}/protobuf/lib/cmake/protobuf") |
||||
endif() |
||||
|
||||
# if OPENSSL_ROOT_DIR is set, propagate that hint path to the external projects with OpenSSL dependency. |
||||
set(_CMAKE_ARGS_OPENSSL_ROOT_DIR "") |
||||
if (OPENSSL_ROOT_DIR) |
||||
set(_CMAKE_ARGS_OPENSSL_ROOT_DIR "-DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR}") |
||||
endif() |
||||
|
||||
# Builds gRPC based on locally checked-out sources and set arguments so that all the dependencies |
||||
# are correctly located. |
||||
ExternalProject_Add(grpc |
||||
PREFIX grpc |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../.." |
||||
CMAKE_CACHE_ARGS |
||||
-DgRPC_INSTALL:BOOL=ON |
||||
-DgRPC_BUILD_TESTS:BOOL=OFF |
||||
-DgRPC_PROTOBUF_PROVIDER:STRING=package |
||||
-DgRPC_PROTOBUF_PACKAGE_TYPE:STRING=CONFIG |
||||
-DProtobuf_DIR:PATH=${_FINDPACKAGE_PROTOBUF_CONFIG_DIR} |
||||
-DgRPC_ZLIB_PROVIDER:STRING=package |
||||
-DZLIB_ROOT:STRING=${CMAKE_CURRENT_BINARY_DIR}/zlib |
||||
-DgRPC_CARES_PROVIDER:STRING=package |
||||
-Dc-ares_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares/lib/cmake/c-ares |
||||
-DgRPC_SSL_PROVIDER:STRING=package |
||||
${_CMAKE_ARGS_OPENSSL_ROOT_DIR} |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/grpc |
||||
DEPENDS c-ares protobuf zlib |
||||
) |
||||
|
||||
# Build the helloworld projects itself using a CMakeLists.txt that assumes all the dependencies |
||||
# have already been installed. |
||||
# Even though helloworld is not really an "external project" from perspective of this build, |
||||
# we are still importing it using ExternalProject_Add because that allows us to use find_package() |
||||
# to locate all the dependencies (if we were building helloworld directly in this build we, |
||||
# we would have needed to manually import the libraries as opposed to reusing targets exported by |
||||
# gRPC and protobuf). |
||||
ExternalProject_Add(helloworld |
||||
PREFIX helloworld |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." |
||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/helloworld" |
||||
INSTALL_COMMAND "" |
||||
CMAKE_CACHE_ARGS |
||||
-DProtobuf_DIR:PATH=${_FINDPACKAGE_PROTOBUF_CONFIG_DIR} |
||||
-Dc-ares_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares/lib/cmake/c-ares |
||||
-DZLIB_ROOT:STRING=${CMAKE_CURRENT_BINARY_DIR}/zlib |
||||
${_CMAKE_ARGS_OPENSSL_ROOT_DIR} |
||||
-DgRPC_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/grpc/lib/cmake/grpc |
||||
DEPENDS protobuf grpc |
||||
) |
||||
# Copyright 2018 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
# cmake "superbuild" file for C++ helloworld example. |
||||
# This build file demonstrates how to build the helloworld project |
||||
# and all its dependencies in a single cmake build (hence "superbuild") |
||||
# that is easy to build and maintain. |
||||
# cmake's ExternalProject_Add() is used to import all the sub-projects, |
||||
# including the "helloworld" project itself. |
||||
# See https://blog.kitware.com/cmake-superbuilds-git-submodules/ |
||||
|
||||
cmake_minimum_required(VERSION 3.5.1) |
||||
|
||||
# Project |
||||
project(HelloWorld-SuperBuild C CXX) |
||||
|
||||
include(ExternalProject) |
||||
|
||||
# Note: For all external projects, instead of using checked-out code, one could |
||||
# specify GIT_REPOSITORY and GIT_TAG to have cmake download the dependency directly, |
||||
# without needing to add a submodule to your project. |
||||
|
||||
# Builds absl project from the git submodule. |
||||
ExternalProject_Add(absl |
||||
PREFIX absl |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../third_party/abseil-cpp" |
||||
CMAKE_CACHE_ARGS |
||||
-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/absl |
||||
) |
||||
|
||||
# Builds c-ares project from the git submodule. |
||||
ExternalProject_Add(c-ares |
||||
PREFIX c-ares |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../third_party/cares/cares" |
||||
CMAKE_CACHE_ARGS |
||||
-DCARES_SHARED:BOOL=OFF |
||||
-DCARES_STATIC:BOOL=ON |
||||
-DCARES_STATIC_PIC:BOOL=ON |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares |
||||
) |
||||
|
||||
# Builds protobuf project from the git submodule. |
||||
ExternalProject_Add(protobuf |
||||
PREFIX protobuf |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../third_party/protobuf/cmake" |
||||
CMAKE_CACHE_ARGS |
||||
-Dprotobuf_BUILD_TESTS:BOOL=OFF |
||||
-Dprotobuf_WITH_ZLIB:BOOL=OFF |
||||
-Dprotobuf_MSVC_STATIC_RUNTIME:BOOL=OFF |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/protobuf |
||||
) |
||||
|
||||
# Builds zlib project from the git submodule. |
||||
ExternalProject_Add(zlib |
||||
PREFIX zlib |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../third_party/zlib" |
||||
CMAKE_CACHE_ARGS |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/zlib |
||||
) |
||||
|
||||
# the location where protobuf-config.cmake will be installed varies by platform |
||||
if (WIN32) |
||||
set(_FINDPACKAGE_PROTOBUF_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}/protobuf/cmake") |
||||
else() |
||||
set(_FINDPACKAGE_PROTOBUF_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}/protobuf/lib/cmake/protobuf") |
||||
endif() |
||||
|
||||
# if OPENSSL_ROOT_DIR is set, propagate that hint path to the external projects with OpenSSL dependency. |
||||
set(_CMAKE_ARGS_OPENSSL_ROOT_DIR "") |
||||
if (OPENSSL_ROOT_DIR) |
||||
set(_CMAKE_ARGS_OPENSSL_ROOT_DIR "-DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR}") |
||||
endif() |
||||
|
||||
# Builds gRPC based on locally checked-out sources and set arguments so that all the dependencies |
||||
# are correctly located. |
||||
ExternalProject_Add(grpc |
||||
PREFIX grpc |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../.." |
||||
CMAKE_CACHE_ARGS |
||||
-DgRPC_INSTALL:BOOL=ON |
||||
-DgRPC_BUILD_TESTS:BOOL=OFF |
||||
-DgRPC_PROTOBUF_PROVIDER:STRING=package |
||||
-DgRPC_PROTOBUF_PACKAGE_TYPE:STRING=CONFIG |
||||
-DProtobuf_DIR:PATH=${_FINDPACKAGE_PROTOBUF_CONFIG_DIR} |
||||
-DgRPC_ZLIB_PROVIDER:STRING=package |
||||
-DZLIB_ROOT:STRING=${CMAKE_CURRENT_BINARY_DIR}/zlib |
||||
-DgRPC_ABSL_PROVIDER:STRING=package |
||||
-Dabsl_DIR:STRING=${CMAKE_CURRENT_BINARY_DIR}/absl/lib/cmake/absl |
||||
-DgRPC_CARES_PROVIDER:STRING=package |
||||
-Dc-ares_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares/lib/cmake/c-ares |
||||
-DgRPC_SSL_PROVIDER:STRING=package |
||||
${_CMAKE_ARGS_OPENSSL_ROOT_DIR} |
||||
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/grpc |
||||
DEPENDS c-ares protobuf zlib absl |
||||
) |
||||
|
||||
# Build the helloworld projects itself using a CMakeLists.txt that assumes all the dependencies |
||||
# have already been installed. |
||||
# Even though helloworld is not really an "external project" from perspective of this build, |
||||
# we are still importing it using ExternalProject_Add because that allows us to use find_package() |
||||
# to locate all the dependencies (if we were building helloworld directly in this build we, |
||||
# we would have needed to manually import the libraries as opposed to reusing targets exported by |
||||
# gRPC and protobuf). |
||||
ExternalProject_Add(helloworld |
||||
PREFIX helloworld |
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." |
||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/helloworld" |
||||
INSTALL_COMMAND "" |
||||
CMAKE_CACHE_ARGS |
||||
-DProtobuf_DIR:PATH=${_FINDPACKAGE_PROTOBUF_CONFIG_DIR} |
||||
-Dc-ares_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/c-ares/lib/cmake/c-ares |
||||
-DZLIB_ROOT:STRING=${CMAKE_CURRENT_BINARY_DIR}/zlib |
||||
-Dabsl_DIR:STRING=${CMAKE_CURRENT_BINARY_DIR}/absl/lib/cmake/absl |
||||
${_CMAKE_ARGS_OPENSSL_ROOT_DIR} |
||||
-DgRPC_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}/grpc/lib/cmake/grpc |
||||
DEPENDS protobuf grpc |
||||
) |
||||
|
@ -0,0 +1,49 @@ |
||||
# Copyright 2020 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
load("@grpc_python_dependencies//:requirements.bzl", "requirement") |
||||
|
||||
py_binary( |
||||
name = "alts_server", |
||||
srcs = [ |
||||
"alts_server.py", |
||||
"demo_pb2.py", |
||||
"demo_pb2_grpc.py", |
||||
"server.py", |
||||
], |
||||
main = "alts_server.py", |
||||
python_version = "PY3", |
||||
srcs_version = "PY2AND3", |
||||
deps = [ |
||||
"//src/python/grpcio/grpc:grpcio", |
||||
], |
||||
) |
||||
|
||||
py_binary( |
||||
name = "alts_client", |
||||
srcs = [ |
||||
"alts_client.py", |
||||
"client.py", |
||||
"demo_pb2.py", |
||||
"demo_pb2_grpc.py", |
||||
], |
||||
main = "alts_client.py", |
||||
python_version = "PY3", |
||||
srcs_version = "PY2AND3", |
||||
deps = [ |
||||
"//src/python/grpcio/grpc:grpcio", |
||||
], |
||||
) |
@ -0,0 +1,39 @@ |
||||
# Copyright 2020 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""The example of using ALTS credentials to setup gRPC client. |
||||
|
||||
The example would only successfully run in GCP environment.""" |
||||
|
||||
import grpc |
||||
|
||||
import demo_pb2_grpc |
||||
from client import (bidirectional_streaming_method, client_streaming_method, |
||||
server_streaming_method, simple_method) |
||||
|
||||
SERVER_ADDRESS = "localhost:23333" |
||||
|
||||
|
||||
def main(): |
||||
with grpc.secure_channel( |
||||
SERVER_ADDRESS, |
||||
credentials=grpc.alts_channel_credentials()) as channel: |
||||
stub = demo_pb2_grpc.GRPCDemoStub(channel) |
||||
simple_method(stub) |
||||
client_streaming_method(stub) |
||||
server_streaming_method(stub) |
||||
bidirectional_streaming_method(stub) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,39 @@ |
||||
# Copyright 2020 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""The example of using ALTS credentials to setup gRPC server in python. |
||||
|
||||
The example would only successfully run in GCP environment.""" |
||||
|
||||
from concurrent import futures |
||||
|
||||
import grpc |
||||
|
||||
import demo_pb2_grpc |
||||
from server import DemoServer |
||||
|
||||
SERVER_ADDRESS = 'localhost:23333' |
||||
|
||||
|
||||
def main(): |
||||
svr = grpc.server(futures.ThreadPoolExecutor()) |
||||
demo_pb2_grpc.add_GRPCDemoServicer_to_server(DemoServer(), svr) |
||||
svr.add_secure_port(SERVER_ADDRESS, |
||||
server_credentials=grpc.alts_server_credentials()) |
||||
print("------------------start Python GRPC server with ALTS encryption") |
||||
svr.start() |
||||
svr.wait_for_termination() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
main() |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue