2021/05/08 C++とGRPC

May 2021 · 3 minute read

こんにちは。RockinWoolです。最近外付けHDDの挙動がおかしかったので、もう一つ買ってきました。これで完璧。
さて、今回はGRPC! と行きたいところです。ただ、そもそものC++周りの知識が全然無かったので、ひたすらそれを書いていこうかなという感じです。

まずは今回の目的であるgRPCの導入と、gRPCを使用する際に必要になるprotobufが使えるようにならないといけない。なので、そのあたりの環境構築からはじめる.参考2
最初にcmakeのインストールを行う

export MY_INSTALL_DIR=$HOME/.local
wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.19.6/cmake-3.19.6-Linux-x86_64.sh
sh cmake-linux.sh -- --skip-license --prefix=$MY_INSTALL_DIR
rm cmake-linux.sh
cmake --version

次にgRPCのインストールを行う。まずはパス通しから

export MY_INSTALL_DIR=$HOME/.local
mkdir -p $MY_INSTALL_DIR
export PATH="$PATH:$MY_INSTALL_DIR/bin"

.~bashrcにも追記する

export PATH=$PATH:$HOME/.local/bin

そして関連ツールのインストール

sudo apt-get install build-essential autoconf libtool pkg-config libssl-dev

そしてgRPCをクローンしてきてひたすらmakeする.
ただし普通にビルドするとPCに負荷がかかり過ぎてフリーズすることがあった。そのため、cgroupで制約を課しながら、システムに余裕を持たせて実行すると良い。\

sudo apt install cgroup-tools

cgroup-toolsをインストールしたら、/etc/cgconfig.confを作成して、以下のように入力する

group g1{
  memory{
    memory.limit_in_bytes=2G;
  }
}

最後に設定を反映させたターミナルを起動する

sudo cgconfigparser -l /etc/cgconfig.conf
sudo cgexec -g memory:g1 gnome-terminal
su $一般ユーザー

ターミナルが起動したら作業ディレクトリまで移動したのち、以下のコマンドを実行してビルドを行う

export MY_INSTALL_DIR=$HOME/.local
git clone --recurse-submodules -b v1.35.0 https://github.com/grpc/grpc
cd grpc
mkdir -p cmake/build
pushd cmake/build
cmake -DgRPC_INSTALL=ON \
    -DgRPC_BUILD_TESTS=OFF \
    -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \
    ../..
make
make install
popd
mkdir -p third_party/abseil-cpp/cmake/build
pushd third_party/abseil-cpp/cmake/build
cmake -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \
    -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \
    ../..
make
make install
popd

最後になぜかパーミッションがおかしい.pcファイルたちを移動しつつ権限を変更する

sudo cp ~/.local/lib/pkgconfig/*.pc /usr/local/lib/pkgconfig/
sudo chmod 664 /usr/local/lib/pkgconfig/*

それとPKG_CONFIG_PATHを指定しておかないと.pcファイルを読んでくれないので、これも指定する

export PKG_CONFIG_PATH=~/.local/lib/pkgconfig/

いちおうbashrcにも書いておく。

PKG_CONFIG_PATH=~/.local/lib/pkgconfig/

ここまでで、ようやくgrpcの作成環境が整えることができた。次はプログラミングである。ディレクトリ構成を以下のようにする

(root) /
    proto /  (.protoファイルを置くためのディレクトリ)
    server /   (C++で書かれたserverのソースを置くためのディレクトリ)
    client /    (Pythonで書かれたclientのソースを置くディレクトリ)

このprotoディレクトリ内にrandom_walker.protoを用意する

syntax = "proto3";
package random_walker;
service MyRandomWalker {
  rpc Update (NumSteps) returns (Empty) {}
  rpc GetPosition (Empty) returns (Position) {}
}
message Empty {
}
message NumSteps {
  int32 n = 1;
}
message Position {
  int32 x = 1;
}

これを以下のコマンドでcppとhに変換する

protoc -I ../proto --cpp_out=. ../proto/random_walker.proto
protoc -I ../proto --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ../proto/random_walker.proto

これで生成されるproto/random_walker.grpc.pb.hを見ると、134-140行目に関数の定義がされている

class Service : public ::grpc::Service {
  public:
    Service();
    virtual ~Service();
    virtual ::grpc::Status Update(::grpc::ServerContext* context, const ::random_walker::NumSteps* request, ::random_walker::Empty* response);
    virtual ::grpc::Status GetPosition(::grpc::ServerContext* context, const ::random_walker::Empty* request, ::random_walker::Position* response);
};