NestJSとC++間でgRPC（③ C++でgRPCサーバを作成）

# はじめに

### 最終目標
NestJSプログラムとC++プログラムにそれぞれgRPCモジュールを実装して相互通信させることを最終目標とし、目標達成に至るまでのプロセスを段階的に記します。
C++側は公式サンプルにあるCMakeによるコンパイルではなく、Makefileでコンパイルできるようにします。

### 今回の目標
C++でgRPCのサーバプログラムを作成します。
公式のサンプルにはCMakeでコンパイルする方法しかありませんが、ここではMakefileでコンパイルする方法を試します。

### 環境
NestJSプログラム、C++プログラム共に同一のAlmaLinux8.8上で動作するものとします。

# ファイル構成
```cpp_server```フォルダは前回作成した```grpc_api```と同階層に配置します。
各ファイルについては後ほど説明します。
```
./grpc_api
./cpp_server
    ├ main.cpp
    ├ server.cpp
    ├ server.h
    ├ libs.mk
    └ Makefile
```
# C++でgRPCサーバプログラムを実装
下記の通り実装します。
前回作成したProtocol Buffersのライブラリの、```UsersService```クラスを継承して```CppServerServiceImpl```クラスを作成し、```GetUser()```と```SetUser()```をオーバーライド実装します。
またメンバに、```id```をキー、```name```を値とした```userList```連想配列を持たせ、この連想配列に対し、```GetUser()```で取得、```SetUser()```で登録するようにします。
```server.h
#include <string>
#include <map>
#include <grpcpp/grpcpp.h>
#include <user.grpc.pb.h>

using namespace grpc;
using namespace user;

class CppServerServiceImpl final : public UsersService::Service {
public:
	static void RunServer(uint16_t port);

private:
	CppServerServiceImpl();
	virtual ~CppServerServiceImpl();
	Status GetUser(ServerContext* context, const UserById* request, User* reply) override;
	Status SetUser(ServerContext* context, const User* request, Result* reply) override;
	std::map<int, std::string> userList;
};
```

```server.cpp
#include <iostream>
#include <memory>
#include <string>

#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <user.grpc.pb.h>
#include "server.h"

using namespace grpc;
using namespace user;

CppServerServiceImpl::CppServerServiceImpl() {
	userList[1] = "User1";
	userList[2] = "User2";
	userList[3] = "User3";
	userList[4] = "User4";
}

CppServerServiceImpl::~CppServerServiceImpl() {}

Status CppServerServiceImpl::GetUser(ServerContext* context, const UserById* request, User* reply) {
	int id = request->id();
	std::cout << "Received GetUser(id=" << id << ")" << std::endl;
	std::string name = "";
	Status status;
	if(userList.find(request->id()) != userList.end()){
		name = userList[id];
		status = Status::OK;
	}
	else {
		name = "Not Found";
		status = Status::CANCELLED;
	}
	std::cout << "Reply User (name=" << name << ")" << std::endl;
	reply->set_name(name);
	return status;
}

Status CppServerServiceImpl::SetUser(ServerContext* context, const User* request, Result* reply) {
	userList[request->id()] = request->name();
	std::cout << "Request SetUser(id=" << request->id() << ",name=" << request->name() << ")" << std::endl;
	reply->set_message("Success");
	return Status::OK;
}

void CppServerServiceImpl::RunServer(uint16_t port) {
  std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
  CppServerServiceImpl service;

  grpc::EnableDefaultHealthCheckService(true);
  grpc::reflection::InitProtoReflectionServerBuilderPlugin();
  ServerBuilder builder;
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  builder.RegisterService(&service);
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  server->Wait();
}
```
```CppServerServiceImpl::RunServer()```内の```server->Wait()```は処理をブロックします。
明示的に終了処理を実装したい場合は```server->Wait()```ではなく別途ループ処理などを実装し、終了時にループを抜ければ```unique_ptr```のスコープを外れるとき終了します。
もしくは```server```をクラスメンバにし、終了時は別スレッドから```server->Shutdown()```を実行することで```server->Wait()```のブロックが解除されます。

```main.cpp
#include <absl/flags/flag.h>
#include <absl/flags/parse.h>

#include "server.h"

ABSL_FLAG(uint16_t, port, 5001, "Server port for the service");

int main(int argc, char** argv) {
	absl::ParseCommandLine(argc, argv);
	CppServerServiceImpl::RunServer(absl::GetFlag(FLAGS_port));
	return 0;
}
```
# Makefileの作成
### インクルードパス
gRPCはホームディレクトリの```.local```にインストールされているので、その```include```フォルダと、前回作成したProtocol Buffersのライブラリのヘッダファイルがある```grpc_api/build```をインクルードパスに指定します。
```
INCLUDES := -I../.local/include -I../grpc_api/build
```
### ライブラリパス＆ライブラリリンク
ライブラリパスはProtocol Buffersのライブラリがある```grpc_api/build```、gRPCのインストールフォルダにある```lib```と```lib64```を指定します。
リンクするライブラリにはProtocol Buffersの```user_grpc_proto```と、gRPCのライブラリ群を指定します。
gRPCのライブラリ群はgRPCのサンプルプログラムをコンパイルした時に生成される```link.txt```というファイルから抽出して```libs.mk```として別ファイルにしました。
```libs.mk
LIBDIR += -L../grpc_api/build -L../.local/lib -L../.local/lib64
LIBS += -luser_grpc_proto -labsl_flags_parse -lgrpc++_reflection -lgrpc++ -lprotobuf -lgrpc -lupb_json_lib -lupb_textformat_lib -lupb_message_lib -lupb_base_lib -lupb_mem_lib -lutf8_range_lib -lre2 -lz -lcares -lgpr -labsl_random_distributions -labsl_random_seed_sequences -labsl_random_internal_pool_urbg -labsl_random_internal_randen -labsl_random_internal_randen_hwaes -labsl_random_internal_randen_hwaes_impl -labsl_random_internal_randen_slow -labsl_random_internal_platform -labsl_random_internal_seed_material -labsl_random_seed_gen_exception -lssl -lcrypto -laddress_sorting -ldl -lm -lrt -pthread -labsl_log_internal_check_op -labsl_leak_check -labsl_die_if_null -labsl_log_internal_conditions -labsl_log_internal_message -labsl_log_internal_nullguard -labsl_examine_stack -labsl_log_internal_format -labsl_log_internal_proto -labsl_log_internal_log_sink_set -labsl_log_sink -labsl_log_entry -labsl_log_initialize -labsl_log_globals -labsl_vlog_config_internal -labsl_log_internal_fnmatch -labsl_log_internal_globals -labsl_statusor -labsl_status -labsl_strerror -lutf8_validity -labsl_flags_usage -labsl_flags_usage_internal -labsl_flags_internal -labsl_flags_marshalling -labsl_flags_reflection -labsl_flags_config -labsl_cord -labsl_cordz_info -labsl_cord_internal -labsl_cordz_functions -labsl_cordz_handle -labsl_crc_cord_state -labsl_crc32c -labsl_str_format_internal -labsl_crc_internal -labsl_crc_cpu_detect -labsl_raw_hash_set -labsl_hash -labsl_bad_variant_access -labsl_city -labsl_low_level_hash -labsl_hashtablez_sampler -labsl_exponential_biased -labsl_flags_private_handle_accessor -labsl_flags_commandlineflag -labsl_bad_optional_access -labsl_flags_commandlineflag_internal -labsl_flags_program_name -labsl_synchronization -labsl_graphcycles_internal -labsl_kernel_timeout_internal -labsl_time -labsl_civil_time -labsl_time_zone -labsl_stacktrace -labsl_symbolize -labsl_strings -labsl_strings_internal -labsl_string_view -labsl_int128 -labsl_throw_delegate -labsl_malloc_internal -labsl_debugging_internal -labsl_demangle_internal -labsl_base -lpthread -labsl_raw_logging_internal -labsl_log_severity -labsl_spinlock_wait -lrt  
```
### Makefile
Makefile全体は下記の通りです。
上の```libs.mk```をインクルードしています。
```Makefile
PROGRAM = cpp_server
	
INCLUDES := -I../.local/include -I../grpc_api/build

LIBDIR :=
LIBS :=
-include libs.mk

RM := rm -rf
OBJS := main.o server.o
CC := g++
CFLAGS := -Wall -O2
.SUFFIXES: .cpp .o
.PHONY: all

all: depend $(PROGRAM)
$(PROGRAM): $(OBJS)
	$(CC) -o $(PROGRAM) $^ $(LIBDIR) $(LIBS)

.cpp.o:
	$(CC) $(INCLUDES) $(CFLAGS) -c $<

.PHONY: clean
clean:
	$(RM) $(PROGRAM) $(OBJS) depend.inc

.PHONY: depend
depend: $(OBJS:.o=.cpp)
	-@ $(RM) depend.inc
	-@ for i in $^; do cpp -MM $$i | sed "s/\ [_a-zA-Z0-9][_a-zA-Z0-9]*\.cpp//g" >> depend.inc; done

-include depend.inc
```
# コンパイル
```make```を実行して```cpp_server```が生成されれば成功です。
```
$ make
```

次回はC++のクライアントプログラムを実装します。

# 関連記事

https://qiita.com/att-unno/items/cf9deb4b507cb5b6bd6d?1

https://qiita.com/att-unno/items/81f6760d3f447e7c3741?1

https://qiita.com/att-unno/items/55970c25da1d8be0503f?1

https://qiita.com/att-unno/items/17a239e661ff09badd99?1

https://qiita.com/att-unno/items/551c8554b5b521091611?1