0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NestJSとC++間でgRPC(③ C++でgRPCサーバを作成)

Last updated at Posted at 2024-07-03

はじめに

最終目標

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のインストールフォルダにあるliblib64を指定します。
リンクするライブラリには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をインクルードしています。

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++のクライアントプログラムを実装します。

関連記事

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?