Difference between revisions of "How to create a C++ gRPC application"

From epsciwiki
Jump to navigation Jump to search
 
(35 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<font size="+2">
+
<font size="+1">
  
 
: '''Setup some environmental variables (assuming bash)'''
 
: '''Setup some environmental variables (assuming bash)'''
Line 25: Line 25:
 
mkdir -p cmake/build
 
mkdir -p cmake/build
 
cd cmake/build
 
cd cmake/build
 +
rm *
 +
rm -r CMakeFiles
 
cmake -DCMAKE_PREFIX_PATH=$GRPC_INSTALL_DIR -DBUILD_SHARED_LIBS=ON ../..
 
cmake -DCMAKE_PREFIX_PATH=$GRPC_INSTALL_DIR -DBUILD_SHARED_LIBS=ON ../..
 
make -j 4
 
make -j 4
Line 30: Line 32:
 
</blockquote>
 
</blockquote>
  
: '''Rename a few files and directories, from helloworld to loadBalancerControl '''
+
 
 +
=== Now that it compiles, as an example, implement ERSAP backend reassembler communication of fifo fill percentage to load-balancer control plane ===
 +
<br>
 +
 
 +
: '''Rename a few files and directories, from helloworld to loadBalancerControl (or whatever you want)'''
  
 
<blockquote>
 
<blockquote>
Line 36: Line 42:
 
cd <my_gRPC_dir>/ejfat
 
cd <my_gRPC_dir>/ejfat
  
mv protos/helloworld.proto protos/loadBalancerControl.proto.
+
mv protos/helloworld.proto protos/loadBalancerControl.proto
 
mv cpp/helloworld cpp/loadBalancerControl
 
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
 +
cd cpp/loadBalancerControl
 +
touch lbcontrol.cc
 +
touch lbcontrol.h
 +
</pre>
 +
</blockquote>
 +
 +
: '''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.'''
 +
 +
<blockquote>
 +
<pre>
 +
// 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 {
 +
  string  name = 1;            // name of backend implementation
 +
  int32  bufferCount = 2;    // number of backend's buffers or fifo entries
 +
  int32  bufferSize  = 3;    // size in bytes of each buffer or fifo entry
 +
  int32  fillPercent = 4;    // % of fifo entries that are filled with unprocessed data
 +
  float  pidError    = 5;    // PID loop error term in percentage of fifo entries
 +
}
 +
</pre>
 +
</blockquote>
 +
 +
: '''The strategy at this point is to create a library and header file to link against when creating code.  Use both the client and server files (cpp/loadBalancerControl/lbcontrol_server.cc & lbcontrol_client.cc) as a base and create lbcontrol.h and lbcontrol.cc. These files need to implement the API using the messages defined in the previous step. While not laid out explicitly here, all the files are available as an example.'''
 +
 +
 +
 +
: '''Next modify several lines in the  cpp/loadBalancerControl/CMakefile.txt in order to reflect file/directory name changes. Get the proto file path correct.'''
 +
 +
<blockquote>
 +
<pre>
 +
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 LB control client & server
 +
foreach(_target
 +
 +
</pre>
 +
</blockquote>
 +
 +
==='''Following are changes needed to make a library and to install that lib along with the new header file. ===
 +
*  '''If other include files and libraries need to be compiled with and linked against, you'll need to make changes yourself. Perhaps create a cpp/cmake/Modules/FindXX.cmake file so cmake can find things.'''
 +
* '''In this case, however, all we need do is add lines to create another library (ejfatGrpcLib) and also add lines so "make install' will put the lib and includes where we can use them.'''
  
  
cd cpp/helloworld
+
<blockquote>
 +
<pre>
 +
add_library(ejfatGrpcLib
 +
  lbcontrol.cc
 +
  ${hw_grpc_srcs}
 +
  ${hw_grpc_hdrs}
 +
  ${hw_proto_srcs}
 +
  ${hw_proto_hdrs})
 +
 
 +
target_link_libraries(ejfatGrpcLib
 +
  ${_REFLECTION}
 +
  ${_GRPC_GRPCPP}
 +
  ${_PROTOBUF_LIBPROTOBUF})
 +
 
 +
 
 +
# Targets LB control client & server
 +
foreach(_target
 +
  lbcontrol_client lbcontrol_server)
 +
  add_executable(${_target} "${_target}.cc")
 +
  target_link_libraries(${_target}
 +
    hw_grpc_proto
 +
    ${_REFLECTION}
 +
    ${_GRPC_GRPCPP}
 +
    ${_PROTOBUF_LIBPROTOBUF})
 +
endforeach()
 +
 
 +
install(TARGETS ejfatGrpcLib LIBRARY DESTINATION  "$ENV{GRPC_INSTALL_DIR}/lib")
 +
install(FILES ${HEADER_FILES} DESTINATION  "$ENV{GRPC_INSTALL_DIR}/include")
 +
</pre>
 +
</blockquote>
 +
 
 +
 
 +
 
 +
: '''Now recompile'''
 +
<blockquote>
 +
<pre>
 +
cd cpp/loadBalancerControl
 +
rm -fr cmake
 
mkdir -p cmake/build
 
mkdir -p cmake/build
 
cd cmake/build
 
cd cmake/build
 
cmake -DCMAKE_PREFIX_PATH=$GRPC_INSTALL_DIR -DBUILD_SHARED_LIBS=ON ../..
 
cmake -DCMAKE_PREFIX_PATH=$GRPC_INSTALL_DIR -DBUILD_SHARED_LIBS=ON ../..
 
make -j 4
 
make -j 4
 +
make install
 
</pre>
 
</pre>
 
</blockquote>
 
</blockquote>
  
=== The application in this case is the reporting to the control plane of the fill level of an ERSAP backend reassembler's fifo. ===
+
: '''For an example of a client and server communicating with the defined protobuf message, try running both server and client:'''
 +
 
 +
<blockquote>
 +
<pre>
 +
# Run the server first
 +
cd cpp/loadBalancerControl/cmake/build
 +
./lbcontrol_server
 +
 
 +
# then run the client
 +
./lbcontrol_client
 +
 
 +
# You should see some relevant client printout
 +
</pre>
 +
</blockquote>
 +
 
 +
: '''For your own application to use the newly defined gRPC communication, in your cmake file, link against the libejfatGrpcLIb.so  library in $ENV(GRPC_INSTALL_DIR}/lib using the include file $ENV{GRPC_INSTALL_DIR}/include/lbcontrol.h . For a good example, look at the ejfat git repository (ersap branch). Look at the CMakeLists.txt file along with the cmake/Modules/FindGRPC.cmake file.'''
 +
 
 +
 
 +
 
  
 
</font>
 
</font>

Latest revision as of 15:54, 6 January 2023

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
rm *
rm -r CMakeFiles
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
cd cpp/loadBalancerControl
touch lbcontrol.cc
touch lbcontrol.h
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 {
  string  name = 1;            // name of backend implementation
  int32   bufferCount = 2;     // number of backend's buffers or fifo entries
  int32   bufferSize  = 3;     // size in bytes of each buffer or fifo entry
  int32   fillPercent = 4;     // % of fifo entries that are filled with unprocessed data
  float   pidError    = 5;     // PID loop error term in percentage of fifo entries
}
The strategy at this point is to create a library and header file to link against when creating code. Use both the client and server files (cpp/loadBalancerControl/lbcontrol_server.cc & lbcontrol_client.cc) as a base and create lbcontrol.h and lbcontrol.cc. These files need to implement the API using the messages defined in the previous step. While not laid out explicitly here, all the files are available as an example.


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 LB control client & server
foreach(_target

Following are changes needed to make a library and to install that lib along with the new header file.

  • If other include files and libraries need to be compiled with and linked against, you'll need to make changes yourself. Perhaps create a cpp/cmake/Modules/FindXX.cmake file so cmake can find things.
  • In this case, however, all we need do is add lines to create another library (ejfatGrpcLib) and also add lines so "make install' will put the lib and includes where we can use them.


add_library(ejfatGrpcLib
  lbcontrol.cc
  ${hw_grpc_srcs}
  ${hw_grpc_hdrs}
  ${hw_proto_srcs}
  ${hw_proto_hdrs})

target_link_libraries(ejfatGrpcLib
  ${_REFLECTION}
  ${_GRPC_GRPCPP}
  ${_PROTOBUF_LIBPROTOBUF})
  

# Targets LB control client & server
foreach(_target
  lbcontrol_client lbcontrol_server)
  add_executable(${_target} "${_target}.cc")
  target_link_libraries(${_target}
    hw_grpc_proto
    ${_REFLECTION}
    ${_GRPC_GRPCPP}
    ${_PROTOBUF_LIBPROTOBUF})
endforeach()

install(TARGETS ejfatGrpcLib LIBRARY DESTINATION  "$ENV{GRPC_INSTALL_DIR}/lib")
install(FILES ${HEADER_FILES} DESTINATION  "$ENV{GRPC_INSTALL_DIR}/include")


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
make install
For an example of a client and server communicating with the defined protobuf message, try 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
For your own application to use the newly defined gRPC communication, in your cmake file, link against the libejfatGrpcLIb.so library in $ENV(GRPC_INSTALL_DIR}/lib using the include file $ENV{GRPC_INSTALL_DIR}/include/lbcontrol.h . For a good example, look at the ejfat git repository (ersap branch). Look at the CMakeLists.txt file along with the cmake/Modules/FindGRPC.cmake file.