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