implement gRPC demo
This commit is contained in:
parent
59cb8c23e4
commit
5521d510ad
7 changed files with 428 additions and 0 deletions
86
CMakeLists.txt
Normal file
86
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
project(gRPCTest C CXX)
|
||||||
|
|
||||||
|
# Options to build certain parts of the application
|
||||||
|
option(BUILD_CLIENT "Build the gRPC client." ON)
|
||||||
|
option(BUILD_SERVER "Build the gRPC server." ON)
|
||||||
|
|
||||||
|
# If you do not have gRPC isntalled through a package manager, you can set the custom install location here
|
||||||
|
# so CMake can find the libraries
|
||||||
|
set(GRPC_INSTALL_LOCATION "" CACHE PATH "Custom gRPC installation location.")
|
||||||
|
cmake_path(ABSOLUTE_PATH GRPC_INSTALL_LOCATION)
|
||||||
|
|
||||||
|
# set required C++ version
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||||
|
add_compile_options(-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wconversion-null
|
||||||
|
-Wmissing-declarations
|
||||||
|
-Woverlength-strings)
|
||||||
|
add_compile_options(
|
||||||
|
-Wpointer-arith
|
||||||
|
-Wunused-local-typedefs
|
||||||
|
-Wunused-result
|
||||||
|
-Wvarargs
|
||||||
|
-Wvla
|
||||||
|
-Wwrite-strings
|
||||||
|
-Wformat-security
|
||||||
|
-Wundef)
|
||||||
|
add_compile_options(-O2)
|
||||||
|
|
||||||
|
# Find the protoc protobuffer compiler
|
||||||
|
find_program(_PROTOBUF_PROTOC protoc)
|
||||||
|
|
||||||
|
# Find the required gRPC libraries
|
||||||
|
# Since we have the option to link dynamically, we have to be explicit
|
||||||
|
find_library(
|
||||||
|
_PROTOBUF_LIBPROTOBUF
|
||||||
|
protobuf
|
||||||
|
HINTS ${GRPC_INSTALL_LOCATION}/lib/
|
||||||
|
REQUIRED
|
||||||
|
)
|
||||||
|
find_library(
|
||||||
|
_GRPC_GRPCPP_REFLECTION
|
||||||
|
grpc++_reflection
|
||||||
|
HINTS ${GRPC_INSTALL_LOCATION}/lib/
|
||||||
|
REQUIRED
|
||||||
|
)
|
||||||
|
find_library(
|
||||||
|
_GRPC_GRPCPP
|
||||||
|
grpc++
|
||||||
|
HINTS ${GRPC_INSTALL_LOCATION}/lib/
|
||||||
|
REQUIRED
|
||||||
|
)
|
||||||
|
find_library(
|
||||||
|
_GRPC_GRPC
|
||||||
|
grpc
|
||||||
|
HINTS ${GRPC_INSTALL_LOCATION}/lib/
|
||||||
|
REQUIRED
|
||||||
|
)
|
||||||
|
find_library(
|
||||||
|
_GRPC_GPR
|
||||||
|
gpr
|
||||||
|
HINTS ${GRPC_INSTALL_LOCATION}/lib/
|
||||||
|
REQUIRED
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure the location of the gRPC protoc plugin
|
||||||
|
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
|
||||||
|
|
||||||
|
# gRPC uses the Abseil package
|
||||||
|
find_package(absl REQUIRED)
|
||||||
|
|
||||||
|
# Include the protobuffers
|
||||||
|
add_subdirectory(buffers)
|
||||||
|
|
||||||
|
# Include the client
|
||||||
|
if(BUILD_CLIENT)
|
||||||
|
add_subdirectory(client)
|
||||||
|
endif(BUILD_CLIENT)
|
||||||
|
|
||||||
|
# Include the controller server
|
||||||
|
if(BUILD_SERVER)
|
||||||
|
add_subdirectory(controller)
|
||||||
|
endif(BUILD_SERVER)
|
||||||
77
buffers/CMakeLists.txt
Normal file
77
buffers/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
# Generate the source files from the protobuf files.
|
||||||
|
# These will be bundled into a statically linked library and
|
||||||
|
# linked into the applications.
|
||||||
|
|
||||||
|
# library name
|
||||||
|
set(PROTOBUFFER_LIB "grpc_proto_buffers")
|
||||||
|
set(PROTOBUFFER_LIB ${PROTOBUFFER_LIB} PARENT_SCOPE)
|
||||||
|
|
||||||
|
# protoc needs the folder in which all the buffer files reside
|
||||||
|
# in case there is an import
|
||||||
|
set(protobuf_src_folder ".")
|
||||||
|
cmake_path(ABSOLUTE_PATH protobuf_src_folder)
|
||||||
|
|
||||||
|
# Grab all the protobuffer files
|
||||||
|
file(
|
||||||
|
GLOB BUFFER_SRCS
|
||||||
|
"*.proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize the lists of generated files
|
||||||
|
set(proto_srcs)
|
||||||
|
set(proto_hdrs)
|
||||||
|
set(grpc_srcs)
|
||||||
|
set(grpc_hdrs)
|
||||||
|
|
||||||
|
# Generate all headers and buffer sources
|
||||||
|
foreach(buffer_src ${BUFFER_SRCS})
|
||||||
|
# Get the name stem of the file
|
||||||
|
cmake_path(GET buffer_src STEM buffer_name)
|
||||||
|
|
||||||
|
# Set the names for all generated files
|
||||||
|
set(buffer_proto_src "${CMAKE_CURRENT_BINARY_DIR}/${buffer_name}.pb.cc")
|
||||||
|
set(buffer_proto_hdr "${CMAKE_CURRENT_BINARY_DIR}/${buffer_name}.pb.h")
|
||||||
|
set(buffer_grpc_src "${CMAKE_CURRENT_BINARY_DIR}/${buffer_name}.grpc.pb.cc")
|
||||||
|
set(buffer_grpc_hdr "${CMAKE_CURRENT_BINARY_DIR}/${buffer_name}.grpc.pb.h")
|
||||||
|
|
||||||
|
# Generate
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${buffer_proto_src}" "${buffer_proto_hdr}" "${buffer_grpc_src}" "${buffer_grpc_hdr}"
|
||||||
|
COMMAND ${_PROTOBUF_PROTOC}
|
||||||
|
ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
|
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
|
-I "${protobuf_src_folder}"
|
||||||
|
--plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
|
||||||
|
"${buffer_src}"
|
||||||
|
DEPENDS "${buffer_src}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the files to the list of generated files
|
||||||
|
list(APPEND proto_srcs ${buffer_proto_src})
|
||||||
|
list(APPEND proto_hdrs ${buffer_proto_hdr})
|
||||||
|
list(APPEND grpc_srcs ${buffer_grpc_src})
|
||||||
|
list(APPEND grpc_hdrs ${buffer_grpc_hdr})
|
||||||
|
endforeach(buffer_src)
|
||||||
|
|
||||||
|
# Include the generated headers
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
# Build the library
|
||||||
|
add_library(
|
||||||
|
${PROTOBUFFER_LIB}
|
||||||
|
${proto_srcs}
|
||||||
|
${proto_hdrs}
|
||||||
|
${grpc_srcs}
|
||||||
|
${grpc_hdrs}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
${PROTOBUFFER_LIB}
|
||||||
|
absl::check
|
||||||
|
${_GRPC_GRPCPP_REFLECTION}
|
||||||
|
${_GRPC_GRPCPP}
|
||||||
|
${_PROTOBUF_LIBPROTOBUF}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make the headers available to other parts of the program
|
||||||
|
set(PROTOBUFFER_HDRS_DIR "${CMAKE_CURRENT_BINARY_DIR}" PARENT_SCOPE)
|
||||||
11
buffers/hello_world.proto
Normal file
11
buffers/hello_world.proto
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
service Greeter {
|
||||||
|
rpc SayHello (HelloRequest) returns (HelloReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelloRequest {
|
||||||
|
required string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelloReply {
|
||||||
|
required string message = 1;
|
||||||
|
}
|
||||||
27
client/CMakeLists.txt
Normal file
27
client/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
# Include the headers generated by the protobuffers
|
||||||
|
include_directories(${PROTOBUFFER_HDRS_DIR}/)
|
||||||
|
|
||||||
|
file(
|
||||||
|
GLOB CLIENT_SRCS
|
||||||
|
"*.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the main executable
|
||||||
|
add_executable(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
${CLIENT_SRCS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
${PROTOBUFFER_LIB}
|
||||||
|
absl::check
|
||||||
|
absl::flags
|
||||||
|
absl::flags_parse
|
||||||
|
absl::log
|
||||||
|
${_GRPC_GRPC}
|
||||||
|
${_GRPC_GRPCPP}
|
||||||
|
${_GRPC_GRPCPP_REFLECTION}
|
||||||
|
${_PROTOBUF_LIBPROTOBUF}
|
||||||
|
${_GRPC_GPR}
|
||||||
|
)
|
||||||
92
client/greeter_client.cpp
Normal file
92
client/greeter_client.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2015 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 "hello_world.pb.h"
|
||||||
|
|
||||||
|
#include <grpcpp/grpcpp.h>
|
||||||
|
|
||||||
|
#ifdef BAZEL_BUILD
|
||||||
|
#include "examples/protos/helloworld.grpc.pb.h"
|
||||||
|
#else
|
||||||
|
#include "hello_world.grpc.pb.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ABSL_FLAG(std::string, target, "localhost:50051", "Server address");
|
||||||
|
|
||||||
|
using grpc::Channel;
|
||||||
|
using grpc::ClientContext;
|
||||||
|
using grpc::Status;
|
||||||
|
|
||||||
|
class GreeterClient {
|
||||||
|
public:
|
||||||
|
GreeterClient(std::shared_ptr<Channel> channel)
|
||||||
|
: stub_(Greeter::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.
|
||||||
|
Status status = stub_->SayHello(&context, request, &reply);
|
||||||
|
|
||||||
|
// Act upon its status.
|
||||||
|
if (status.ok()) {
|
||||||
|
return reply.message();
|
||||||
|
} else {
|
||||||
|
std::cout << status.error_code() << ": " << status.error_message()
|
||||||
|
<< std::endl;
|
||||||
|
return "RPC failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Greeter::Stub> 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()).
|
||||||
|
GreeterClient greeter(
|
||||||
|
grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
|
||||||
|
std::string user("world");
|
||||||
|
std::string reply = greeter.SayHello(user);
|
||||||
|
std::cout << "Greeter received: " << reply << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
28
controller/CMakeLists.txt
Normal file
28
controller/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
# Include the headers generated by the protobuffers
|
||||||
|
include_directories(${PROTOBUFFER_HDRS_DIR})
|
||||||
|
|
||||||
|
file(
|
||||||
|
GLOB CONTROLLER_SRCS
|
||||||
|
"*.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Add the main executable
|
||||||
|
add_executable(
|
||||||
|
${PROJECT_NAME}_controller
|
||||||
|
${CONTROLLER_SRCS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}_controller
|
||||||
|
${PROTOBUFFER_LIB}
|
||||||
|
absl::check
|
||||||
|
absl::flags
|
||||||
|
absl::flags_parse
|
||||||
|
absl::log
|
||||||
|
${_GRPC_GRPC}
|
||||||
|
${_GRPC_GRPCPP}
|
||||||
|
${_GRPC_GRPCPP_REFLECTION}
|
||||||
|
${_PROTOBUF_LIBPROTOBUF}
|
||||||
|
${_GRPC_GPR}
|
||||||
|
)
|
||||||
107
controller/greeter_server.cpp
Normal file
107
controller/greeter_server.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2015 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 "hello_world.pb.h"
|
||||||
|
|
||||||
|
#include <grpcpp/server_context.h>
|
||||||
|
#include <grpcpp/support/server_callback.h>
|
||||||
|
#include <grpcpp/support/status.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 "hello_world.grpc.pb.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void RunServer(uint16_t port);
|
||||||
|
|
||||||
|
using grpc::Server;
|
||||||
|
using grpc::ServerBuilder;
|
||||||
|
|
||||||
|
ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");
|
||||||
|
|
||||||
|
// This is the implementation of the actual service;
|
||||||
|
// We elect to implement this as a callback server (preferred implementation)
|
||||||
|
class GreeterServiceImpl final : public Greeter::CallbackService {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// We only reply with a single message back, thus we use a unary reactor
|
||||||
|
grpc::ServerUnaryReactor *
|
||||||
|
SayHello([[maybe_unused]] grpc::CallbackServerContext *context,
|
||||||
|
const HelloRequest *request, HelloReply *reply) override {
|
||||||
|
|
||||||
|
// To be able to handle more fancy things, we elect to implement our own
|
||||||
|
// reactor
|
||||||
|
class HelloReactor : public grpc::ServerUnaryReactor {
|
||||||
|
public:
|
||||||
|
HelloReactor(const HelloRequest &request, const std::string &prefix,
|
||||||
|
HelloReply *reply) {
|
||||||
|
reply->set_message(prefix + request.name());
|
||||||
|
Finish(grpc::Status::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OnDone() override {
|
||||||
|
std::cout << "RPC completed." << std::endl;
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnCancel() override { std::cerr << "RPC cancelled." << std::endl; }
|
||||||
|
};
|
||||||
|
|
||||||
|
return new HelloReactor(*request, "Hello ", reply);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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());
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue