aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Ostroukhov <eostroukhov@google.com>2024-04-09 10:12:03 -0700
committerCopybara-Service <copybara-worker@google.com>2024-04-09 10:14:18 -0700
commit33f56657d213bbc9d4c68a20b8ca581c42a46483 (patch)
tree95d3d90f3ad6e8e769f39259f7096413d8d88554
parenta2ec6005586e768534f395840d8ff64236a38441 (diff)
downloadgrpc-grpc-33f56657d213bbc9d4c68a20b8ca581c42a46483.tar.gz
[Samples] Health checking example (#36235)
Closes #36235 PiperOrigin-RevId: 623204716
-rw-r--r--examples/cpp/health/BUILD42
-rw-r--r--examples/cpp/health/CMakeLists.txt98
-rw-r--r--examples/cpp/health/README.md6
-rw-r--r--examples/cpp/health/health_client.cc140
-rw-r--r--examples/cpp/health/health_server.cc100
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;
+}