How to create a C++ gRPC application

From epsciwiki
Revision as of 19:30, 19 December 2022 by Timmer (talk | contribs)
Jump to navigation Jump to search

Setup some environmental variables (assuming bash)
export GRPC_INSTALL_DIR=/daqfs/gRPC/installation
export PATH="$GRPC_INSTALL_DIR/bin:$PATH"
export LD_LIBRARY_PATH="$GRPC_INSTALL_DIR/lib:$LD_LIBRARY_PATH"
Start by copying the hello world example and compiling it (official instructions here). The compilation steps differ slightly between examples.
cd <my_gRPC_dir>
mkdir ejfat
cd ejfat
mkdir cpp protos

cp /daqfs/gRPC/grpc/examples/protos/helloworld.proto protos/.
cp -r /daqfs/gRPC/grpc/examples/cpp/cmake cpp/.
cp -r /daqfs/gRPC/grpc/examples/cpp/helloworld cpp/.

cd cpp/helloworld
mkdir -p cmake/build
cd cmake/build
cmake -DCMAKE_PREFIX_PATH=$GRPC_INSTALL_DIR -DBUILD_SHARED_LIBS=ON ../..
make -j 4


Now that it compiles, as an example, implement ERSAP backend reassembler communication of fifo fill percentage to load-balancer control plane


Rename a few files and directories, from helloworld to loadBalancerControl (or whatever you want)
cd <my_gRPC_dir>/ejfat

mv protos/helloworld.proto protos/loadBalancerControl.proto
mv cpp/helloworld cpp/loadBalancerControl
mv cpp/loadBalancerControl/greeter_server.cc cpp/loadBalancerControl/lbcontrol_server.cc
mv cpp/loadBalancerControl/greeter_client.cc cpp/loadBalancerControl/lbcontrol_client.cc
Start by modifying loadBalancerControl.proto to define the message and the communication API. Make it look like the following and don't worry about option and package statements.
// The ERSAP backend state reporting service definition.
service BackendState {
  // Sends a request to get the backend's state
  rpc GetState (StateRequest) returns (StateReply) {}
}

// The get-state request message containing the LB control plane's name.
message StateRequest {
  string name = 1;
}

// The response message containing the backend's current state
message StateReply {
  int32  bufferCount = 1;     // number of backend's buffers or fifo entries
  int32  bufferSize  = 2;     // size in bytes of each buffer or fifo entry
  int32  fillPercent = 3;     // % of fifo entries that are filled with unprocessed data
  int32  pidError    = 4;     // PID loop error term in percentage of fifo entries
}
Next modify both the client and server files: cpp/loadBalancerControl/lbcontrol_server.cc and lbcontrol_client.cc. These files need to implement the API using the messages defined in the previous step.


Next modify several lines in the cpp/loadBalancerControl/CMakefile.txt in order to reflect file/directory name changes. Get the proto file path correct.
project(LoadBalancerControl C CXX)

include(../cmake/common.cmake)

# Proto file
get_filename_component(hw_proto "../../protos/loadBalancerControl.proto" ABSOLUTE)
get_filename_component(hw_proto_path "${hw_proto}" PATH)

# Generated sources
set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/loadBalancerControl.pb.cc")
set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/loadBalancerControl.pb.h")
set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/loadBalancerControl.grpc.pb.cc")
set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/loadBalancerControl.grpc.pb.h")

.
.
.

# Targets greeter_[async_](client|server)
foreach(_target
  lbcontrol_client lbcontrol_server)

If other include files and libraries need to be compiled with and linked against, you'll need to make changes. Following are changes needed to include code using the ET system.

  • Copy the cmake/Modules/FindET.cmake file from github's ejfat repository (ersap branch) into the cpp/cmake/Modules directory so cmake can find things.
  • Set the "CODA" environmental variable (used by FindEt.cmake) which should point to a CODA installation directory with ET headers and shared library. (If you do things differently, you'll need to modify FindET.cmake).
  • Finally you'll need to edit the cpp/loadBalancerControl/CMakefile.txt file so it can use FindET.cmake to find the ET includes and libs. Seen below, it fits right where the ellipsis is in the previous listing of the same file.


# ET includes and libs
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules)

# Finding ET libs and includes involve looking at CODA install directory
if (DEFINED ENV{CODA})
    message(STATUS "CODA = " $ENV{CODA})
else()
    message(FATAL_ERROR "Set the \"CODA\" env var so that ET libs and includes can be found!")
endif()

find_package(ET)
if (NOT ET_FOUND)
    message( FATAL_ERROR "Et cannot be found" )
endif()

# Include generated *.pb.h files
include_directories("${CMAKE_CURRENT_BINARY_DIR}" "${ET_INCLUDE_DIRS}")

# 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
  ${ET_LIBRARY}
  ${_REFLECTION}
  ${_GRPC_GRPCPP}
  ${_PROTOBUF_LIBPROTOBUF})


Now recompile
cd cpp/loadBalancerControl
rm -fr cmake
mkdir -p cmake/build
cd cmake/build
cmake -DCMAKE_PREFIX_PATH=$GRPC_INSTALL_DIR -DBUILD_SHARED_LIBS=ON ../..
make -j 4
The application in this case is the reporting to the control plane of the fill level of an ERSAP backend reassembler's fifo. Try it out by running both server and client.
# Run the server first
cd cpp/loadBalancerControl/cmake/build
./lbcontrol_server

# then run the client
./lbcontrol-client

# You should see some relevant client printout