diff options
author | Eugene Ostroukhov <eostroukhov@google.com> | 2024-04-09 10:12:03 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2024-04-09 10:14:18 -0700 |
commit | 33f56657d213bbc9d4c68a20b8ca581c42a46483 (patch) | |
tree | 95d3d90f3ad6e8e769f39259f7096413d8d88554 | |
parent | a2ec6005586e768534f395840d8ff64236a38441 (diff) | |
download | grpc-grpc-33f56657d213bbc9d4c68a20b8ca581c42a46483.tar.gz |
[Samples] Health checking example (#36235)
Closes #36235
PiperOrigin-RevId: 623204716
-rw-r--r-- | examples/cpp/health/BUILD | 42 | ||||
-rw-r--r-- | examples/cpp/health/CMakeLists.txt | 98 | ||||
-rw-r--r-- | examples/cpp/health/README.md | 6 | ||||
-rw-r--r-- | examples/cpp/health/health_client.cc | 140 | ||||
-rw-r--r-- | examples/cpp/health/health_server.cc | 100 |
5 files changed, 386 insertions, 0 deletions
diff --git a/examples/cpp/health/BUILD b/examples/cpp/health/BUILD new file mode 100644 index 0000000000..2192936989 --- /dev/null +++ b/examples/cpp/health/BUILD @@ -0,0 +1,42 @@ +# Copyright 2024 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 = "health_client", + srcs = ["health_client.cc"], + defines = ["BAZEL_BUILD"], + deps = [ + "//:grpc++", + "//examples/protos:helloworld_cc_grpc", + "//src/proto/grpc/health/v1:health_proto", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + ], +) + +cc_binary( + name = "health_server", + srcs = ["health_server.cc"], + defines = ["BAZEL_BUILD"], + deps = [ + "//:grpc++", + "//:grpc++_reflection", + "//examples/protos:helloworld_cc_grpc", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + "@com_google_absl//absl/strings:str_format", + ], +) diff --git a/examples/cpp/health/CMakeLists.txt b/examples/cpp/health/CMakeLists.txt new file mode 100644 index 0000000000..963e82f0a3 --- /dev/null +++ b/examples/cpp/health/CMakeLists.txt @@ -0,0 +1,98 @@ +# Copyright 2024 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(HelloWorld 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(health_proto "../../../src/proto/grpc/health/v1/health.proto" ABSOLUTE) +get_filename_component(health_proto_path "${health_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") +add_custom_command( + OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}" "${hw_grpc_hdrs}" + COMMAND ${_PROTOBUF_PROTOC} + ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" + --cpp_out "${CMAKE_CURRENT_BINARY_DIR}" + -I "${hw_proto_path}" + --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" + "${hw_proto}" + DEPENDS "${hw_proto}") + +# Health protos +set(health_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/health.pb.cc") +set(health_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/health.pb.h") +set(health_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/health.grpc.pb.cc") +set(health_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/health.grpc.pb.h") +add_custom_command( + OUTPUT "${health_proto_srcs}" "${health_proto_hdrs}" "${health_grpc_srcs}" "${health_grpc_hdrs}" + COMMAND ${_PROTOBUF_PROTOC} + ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" + --cpp_out "${CMAKE_CURRENT_BINARY_DIR}" + -I "${health_proto_path}" + --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" + "${health_proto}" + DEPENDS "${health_proto}") + +# Include generated *.pb.h files +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + +# hw_grpc_proto +add_library(hw_grpc_proto + ${hw_grpc_srcs} + ${hw_grpc_hdrs} + ${hw_proto_srcs} + ${hw_proto_hdrs}) +target_link_libraries(hw_grpc_proto + ${_REFLECTION} + ${_GRPC_GRPCPP} + ${_PROTOBUF_LIBPROTOBUF}) + +#health_grpc_proto +add_library(health_grpc_proto + ${health_grpc_srcs} + ${health_grpc_hdrs} + ${health_proto_srcs} + ${health_proto_hdrs}) +target_link_libraries(health_grpc_proto + ${_PROTOBUF_LIBPROTOBUF}) + + +# Targets greeter_[async_](client|server) +foreach(_target + health_client health_server) + add_executable(${_target} "${_target}.cc") + target_link_libraries(${_target} + hw_grpc_proto + health_grpc_proto + absl::flags + absl::flags_parse + ${_REFLECTION} + ${_GRPC_GRPCPP} + ${_PROTOBUF_LIBPROTOBUF}) +endforeach() diff --git a/examples/cpp/health/README.md b/examples/cpp/health/README.md new file mode 100644 index 0000000000..6e65d73db4 --- /dev/null +++ b/examples/cpp/health/README.md @@ -0,0 +1,6 @@ +# gRPC C++ Health Check Example + +You can find a complete set of instructions for building gRPC and running the +example in the [C++ Quick Start][]. + +[C++ Quick Start]: https://grpc.io/docs/languages/cpp/quickstart diff --git a/examples/cpp/health/health_client.cc b/examples/cpp/health/health_client.cc new file mode 100644 index 0000000000..488dbdf4d3 --- /dev/null +++ b/examples/cpp/health/health_client.cc @@ -0,0 +1,140 @@ +/* + * + * Copyright 2024 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 <condition_variable> +#include <iostream> +#include <memory> +#include <mutex> +#include <string> + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" + +#include <grpcpp/grpcpp.h> + +#ifdef BAZEL_BUILD +#include "examples/protos/helloworld.grpc.pb.h" + +#include "src/proto/grpc/health/v1/health.grpc.pb.h" +#else +#include "health.grpc.pb.h" +#include "helloworld.grpc.pb.h" +#endif + +ABSL_FLAG(std::string, target, "localhost:50051", "Server address"); + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; +using grpc::health::v1::Health; +using grpc::health::v1::HealthCheckRequest; +using grpc::health::v1::HealthCheckResponse; +using helloworld::Greeter; +using helloworld::HelloReply; +using helloworld::HelloRequest; + +class GreeterClient { + public: + GreeterClient(std::shared_ptr<Channel> channel) + : stub_(Greeter::NewStub(channel)), + health_stub_(Health::NewStub(channel)) {} + + // Assembles the client's payload, sends it and presents the response back + // from the server. + std::string SayHello(const std::string& user) { + // Data we are sending to the server. + HelloRequest request; + request.set_name(user); + + // Container for the data we expect from the server. + HelloReply reply; + + // Context for the client. It could be used to convey extra information to + // the server and/or tweak certain RPC behaviors. + ClientContext context; + + // The actual RPC. + std::mutex mu; + std::condition_variable cv; + bool done = false; + Status status; + stub_->async()->SayHello(&context, &request, &reply, + [&mu, &cv, &done, &status](Status s) { + status = std::move(s); + std::lock_guard<std::mutex> lock(mu); + done = true; + cv.notify_one(); + }); + + std::unique_lock<std::mutex> lock(mu); + while (!done) { + cv.wait(lock); + } + + // Act upon its status. + if (status.ok()) { + return reply.message(); + } else { + std::cout << status.error_code() << ": " << status.error_message() + << std::endl; + return "RPC failed"; + } + } + + void CheckHealth(const std::string& message) { + ClientContext context; + HealthCheckResponse response; + Status status = health_stub_->Check( + &context, HealthCheckRequest::default_instance(), &response); + if (!status.ok()) { + std::cerr << "Failed to check service health: " << status.error_code() + << ": " << status.error_message() << "\n"; + return; + } + std::cout << message << ": " << response.DebugString(); + } + + private: + std::unique_ptr<Greeter::Stub> stub_; + std::unique_ptr<Health::Stub> health_stub_; +}; + +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()). + grpc::ChannelArguments args; + args.SetServiceConfigJSON( + "{\"healthCheckConfig\": " + "{\"serviceName\": \"\"}}"); + GreeterClient greeter(grpc::CreateCustomChannel( + target_str, grpc::InsecureChannelCredentials(), args)); + std::string user = "world"; + greeter.CheckHealth("Before call"); + std::string reply = greeter.SayHello(user); + std::cout << "Greeter received: " << reply << std::endl; + greeter.CheckHealth("After call"); + reply = greeter.SayHello(user); + std::cout << "Greeter received: " << reply << std::endl; + greeter.CheckHealth("After second call"); + return 0; +} diff --git a/examples/cpp/health/health_server.cc b/examples/cpp/health/health_server.cc new file mode 100644 index 0000000000..ac78435ea1 --- /dev/null +++ b/examples/cpp/health/health_server.cc @@ -0,0 +1,100 @@ +/* + * + * Copyright 2024 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 <iostream> +#include <memory> +#include <string> + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/strings/str_format.h" + +#include <grpcpp/ext/proto_server_reflection_plugin.h> +#include <grpcpp/grpcpp.h> +#include <grpcpp/health_check_service_interface.h> + +#ifdef BAZEL_BUILD +#include "examples/protos/helloworld.grpc.pb.h" +#else +#include "helloworld.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; + +// Logic and data behind the server's behavior. +class GreeterServiceImpl final : public Greeter::CallbackService { + public: + void set_health_check_service( + grpc::HealthCheckServiceInterface* health_check_service) { + health_check_service_ = health_check_service; + } + + private: + 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); + // Goes down, then up + is_serving_ = !is_serving_; + health_check_service_->SetServingStatus(is_serving_); + return reactor; + } + + grpc::HealthCheckServiceInterface* health_check_service_ = nullptr; + bool is_serving_ = true; +}; + +void RunServer(uint16_t port) { + std::string server_address = absl::StrFormat("0.0.0.0:%d", port); + GreeterServiceImpl service; + + grpc::EnableDefaultHealthCheckService(true); + grpc::reflection::InitProtoReflectionServerBuilderPlugin(); + ServerBuilder builder; + // Listen on the given address without any authentication mechanism. + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + // Register "service" as the instance through which we'll communicate with + // clients. In this case it corresponds to an *synchronous* service. + builder.RegisterService(&service); + // Finally assemble the server. + std::unique_ptr<Server> server(builder.BuildAndStart()); + service.set_health_check_service(server->GetHealthCheckService()); + 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; +} |