From ec2a7668e3057529e3256129550d606273ab06d4 Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov Date: Fri, 19 May 2023 14:42:48 -0700 Subject: [PATCH] [examples] Add CPP multiplex example (#33187) --- examples/cpp/multiplex/BUILD | 43 ++++++++ examples/cpp/multiplex/CMakeLists.txt | 82 +++++++++++++++ examples/cpp/multiplex/README.md | 7 ++ examples/cpp/multiplex/multiplex_client.cc | 106 +++++++++++++++++++ examples/cpp/multiplex/multiplex_server.cc | 113 +++++++++++++++++++++ 5 files changed, 351 insertions(+) create mode 100644 examples/cpp/multiplex/BUILD create mode 100644 examples/cpp/multiplex/CMakeLists.txt create mode 100644 examples/cpp/multiplex/README.md create mode 100644 examples/cpp/multiplex/multiplex_client.cc create mode 100644 examples/cpp/multiplex/multiplex_server.cc diff --git a/examples/cpp/multiplex/BUILD b/examples/cpp/multiplex/BUILD new file mode 100644 index 00000000000..24c90d363bc --- /dev/null +++ b/examples/cpp/multiplex/BUILD @@ -0,0 +1,43 @@ +# Copyright 2023 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. + +licenses(["notice"]) + +cc_binary( + name = "multiplex_client", + srcs = ["multiplex_client.cc"], + defines = ["BAZEL_BUILD"], + deps = [ + "//:grpc++", + "//examples/protos:helloworld_cc_grpc", + "//examples/protos:route_guide", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + ], +) + +cc_binary( + name = "multiplex_server", + srcs = ["multiplex_server.cc"], + defines = ["BAZEL_BUILD"], + deps = [ + "//:grpc++", + "//:grpc++_reflection", + "//examples/protos:helloworld_cc_grpc", + "//examples/protos:route_guide", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + "@com_google_absl//absl/strings:str_format", + ], +) diff --git a/examples/cpp/multiplex/CMakeLists.txt b/examples/cpp/multiplex/CMakeLists.txt new file mode 100644 index 00000000000..d0b97a37f26 --- /dev/null +++ b/examples/cpp/multiplex/CMakeLists.txt @@ -0,0 +1,82 @@ +# Copyright 2023 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 build file for C++ helloworld example. +# Assumes protobuf and gRPC have been installed using cmake. +# See cmake_externalproject/CMakeLists.txt for all-in-one cmake build +# that automatically builds all the dependencies before building helloworld. + +cmake_minimum_required(VERSION 3.8) + +project(Multiplex C CXX) + +include(../cmake/common.cmake) + +# Proto file +get_filename_component(hw_proto "../../protos/helloworld.proto" ABSOLUTE) +get_filename_component(hw_proto_path "${hw_proto}" PATH) +get_filename_component(rg_proto "../../protos/route_guide.proto" ABSOLUTE) +get_filename_component(rg_proto_path "${rg_proto}" PATH) + +# Generated sources +set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.pb.cc") +set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.pb.h") +set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.cc") +set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.h") +set(rg_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/route_guide.pb.cc") +set(rg_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/route_guide.pb.h") +set(rg_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/route_guide.grpc.pb.cc") +set(rg_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/route_guide.grpc.pb.h") +add_custom_command( + OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}" "${hw_grpc_hdrs}" + "${rg_proto_srcs}" "${rg_proto_hdrs}" "${rg_grpc_srcs}" "${rg_grpc_hdrs}" + COMMAND ${_PROTOBUF_PROTOC} + ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" + --cpp_out "${CMAKE_CURRENT_BINARY_DIR}" + -I "${rg_proto_path}" + --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" + "${hw_proto}" "${rg_proto}" + DEPENDS "${hw_proto}" "${rg_proto}") + +# Include generated *.pb.h files +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + +# example_grpc_proto +add_library(example_grpc_proto + ${hw_grpc_srcs} + ${hw_grpc_hdrs} + ${hw_proto_srcs} + ${hw_proto_hdrs} + ${rg_grpc_srcs} + ${rg_grpc_hdrs} + ${rg_proto_srcs} + ${rg_proto_hdrs} +) +target_link_libraries( + example_grpc_proto + ${_REFLECTION} + ${_GRPC_GRPCPP} + ${_PROTOBUF_LIBPROTOBUF}) + +# Targets multiplex_(client|server) +foreach(_target multiplex_client multiplex_server) + add_executable(${_target} "${_target}.cc") + target_link_libraries(${_target} + example_grpc_proto + absl::flags + absl::flags_parse + ${_REFLECTION} + ${_GRPC_GRPCPP} + ${_PROTOBUF_LIBPROTOBUF}) +endforeach() diff --git a/examples/cpp/multiplex/README.md b/examples/cpp/multiplex/README.md new file mode 100644 index 00000000000..fb8ee2c73b0 --- /dev/null +++ b/examples/cpp/multiplex/README.md @@ -0,0 +1,7 @@ +# gRPC C++ Multiplexing Example + +This example shows how to use a single connection to send multiple concurrent +asynchronous requests to different services. It also demonstrates multiple +sharing the same server connection. + +[C++ Quick Start]: https://grpc.io/docs/languages/cpp/quickstart diff --git a/examples/cpp/multiplex/multiplex_client.cc b/examples/cpp/multiplex/multiplex_client.cc new file mode 100644 index 00000000000..085d1334a8c --- /dev/null +++ b/examples/cpp/multiplex/multiplex_client.cc @@ -0,0 +1,106 @@ +/* + * + * Copyright 2023 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. + * + */ +#include +#include +#include +#include +#include +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" + +#include + +#ifdef BAZEL_BUILD +#include "examples/protos/helloworld.grpc.pb.h" +#include "examples/protos/route_guide.grpc.pb.h" +#else +#include "helloworld.grpc.pb.h" +#include "route_guide.grpc.pb.h" +#endif + +ABSL_FLAG(std::string, target, "localhost:50051", "Server address"); + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; + +int main(int argc, char** argv) { + absl::ParseCommandLine(argc, argv); + // Instantiate the client. It requires a channel, out of which the actual RPCs + // are created. This channel models a connection to an endpoint specified by + // the argument "--target=" which is the only expected argument. + std::string target_str = absl::GetFlag(FLAGS_target); + // We indicate that the channel isn't authenticated (use of + // InsecureChannelCredentials()). + auto channel = + grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()); + + std::mutex mu; + std::condition_variable cv; + int done_count = 0; + + // Callbacks will be called on background threads + std::unique_lock lock(mu); + + ClientContext hello_context; + helloworld::HelloRequest hello_request; + helloworld::HelloReply hello_response; + Status hello_status; + + ClientContext feature_context; + routeguide::Point feature_request; + routeguide::Feature feature_response; + Status feature_status; + // Request to a Greeter service + hello_request.set_name("user"); + helloworld::Greeter::NewStub(channel)->async()->SayHello( + &hello_context, &hello_request, &hello_response, [&](Status status) { + std::lock_guard lock(mu); + done_count++; + hello_status = std::move(status); + cv.notify_all(); + }); + // Request to a RouteGuide service + feature_request.set_latitude(50); + feature_request.set_longitude(100); + routeguide::RouteGuide::NewStub(channel)->async()->GetFeature( + &feature_context, &feature_request, &feature_response, + [&](Status status) { + std::lock_guard lock(mu); + done_count++; + feature_status = std::move(status); + cv.notify_all(); + }); + // Wait for both requests to finish + cv.wait(lock, [&]() { return done_count == 2; }); + if (hello_status.ok()) { + std::cout << "Greeter received: " << hello_response.message() << std::endl; + } else { + std::cerr << "Greeter failed: " << hello_status.error_message() + << std::endl; + } + if (feature_status.ok()) { + std::cout << "Found feature: " << feature_response.name() << std::endl; + } else { + std::cerr << "Getting feature failed: " << feature_status.error_message() + << std::endl; + } + return 0; +} diff --git a/examples/cpp/multiplex/multiplex_server.cc b/examples/cpp/multiplex/multiplex_server.cc new file mode 100644 index 00000000000..e4cdc54ec1f --- /dev/null +++ b/examples/cpp/multiplex/multiplex_server.cc @@ -0,0 +1,113 @@ +/* + * + * Copyright 2023 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. + * + */ + +#include +#include +#include +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/strings/str_format.h" + +#include +#include +#include + +#ifdef BAZEL_BUILD +#include "examples/protos/helloworld.grpc.pb.h" +#include "examples/protos/route_guide.grpc.pb.h" +#else +#include "helloworld.grpc.pb.h" +#include "route_guide.grpc.pb.h" +#endif + +ABSL_FLAG(uint16_t, port, 50051, "Server port for the service"); + +using grpc::CallbackServerContext; +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerUnaryReactor; +using grpc::Status; +using helloworld::Greeter; +using helloworld::HelloReply; +using helloworld::HelloRequest; +using routeguide::RouteGuide; + +// Logic and data behind the server's behavior. +class GreeterServiceImpl final : public Greeter::CallbackService { + ServerUnaryReactor* SayHello(CallbackServerContext* context, + const HelloRequest* request, + HelloReply* reply) override { + std::string prefix("Hello "); + reply->set_message(prefix + request->name()); + + ServerUnaryReactor* reactor = context->DefaultReactor(); + reactor->Finish(Status::OK); + return reactor; + } +}; + +class RouteGuideImpl final : public RouteGuide::CallbackService { + public: + ServerUnaryReactor* GetFeature(CallbackServerContext* context, + const routeguide::Point* request, + routeguide::Feature* response) override { + std::string key = + absl::StrFormat("%d:%d", request->latitude(), request->longitude()); + response->set_name(absl::StrFormat("Feature: latitude: %d, longitude: %d", + request->latitude(), + request->longitude())); + *response->mutable_location() = *request; + ServerUnaryReactor* reactor = context->DefaultReactor(); + reactor->Finish(Status::OK); + return reactor; + } + + private: + std::map features_db_; +}; + +void RunServer(uint16_t port) { + std::string server_address = absl::StrFormat("0.0.0.0:%d", port); + GreeterServiceImpl greeter; + RouteGuideImpl route_guide; + + grpc::EnableDefaultHealthCheckService(true); + grpc::reflection::InitProtoReflectionServerBuilderPlugin(); + ServerBuilder builder; + // Listen on the given address without any authentication mechanism. + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + // Register "greeter" as the instance through which we'll communicate with + // clients. In this case it corresponds to an *synchronous* greeter. + builder.RegisterService(&greeter); + builder.RegisterService(&route_guide); + // Finally assemble the server. + std::unique_ptr server(builder.BuildAndStart()); + std::cout << "Server listening on " << server_address << std::endl; + + // Wait for the server to shutdown. Note that some other thread must be + // responsible for shutting down the server for this call to ever return. + server->Wait(); +} + +int main(int argc, char** argv) { + absl::ParseCommandLine(argc, argv); + RunServer(absl::GetFlag(FLAGS_port)); + return 0; +}