10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Node.js gRPC フレームワーク mali.js の紹介

Last updated at Posted at 2019-04-02

概要

Koa ライクな API を提供する Node.js 製の gRPC フレームワーク、mali.js を試してみる機会があったので、簡単に使い方を紹介します。(公式サイトには Koa ライクとありますが、Express ライクと受け取って良いでしょう)
Node.jsのgrpc実装 grpc パッケージのAPIをラップして簡単にしている印象があるので、手元でgRPCのmockサーバーが必要な際などに役立ちそうだと感じました。

※ 使ってみたという内容記事なので、実装上の tips や実運用でのノウハウなどに関しては触れません。

サーバー実装

gRPC のサーバー/クライアントを実装する際には、まずはじめに .proto でサービス/メッセージタイプの定義を行う必要があります。また定義された.proto ファイルをローディングする方法には以下の二種類に分けられます。

  • Dynamic codegen
    • 実行時に .proto ファイルを解析してコード生成を行う
  • Static codegen
    • 事前に .proto ファイルを解析してコード生成を行う( protoc などのコンパイラを使用する)

また、今回は以下のような .proto 定義を使って紹介を進めます。

syntax = "proto3";
package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc SayHi (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

こちらは、Dynamic codegen を使って mali.js で gRPC サーバーを実装した例です。

import { resolve } from "path";
import Mali from "mali";

const PROTO_PATH = resolve(__dirname, "./protos/helloworld.proto");

function sayHello(ctx: any): void {
  ctx.res = { message: `Hello ${ctx.req.name}` };
}

function sayHi(ctx: any): void {
  ctx.res = { message: `Hi ${ctx.req.name}` };
}

export function main(): void {
  const app = new Mali(PROTO_PATH, "Greeter");
  app.use({ sayHello, sayHi });
  app.start("localhost:50051");
}

main();

こちらは、Static codegen の例です。
まず、前提として以下の手順で、 ./static ディレクトリに service class のコード生成を行っておきます。

yarn add -D grpc-tools grpc_tools_node_protoc_ts

grpc_tools_node_protoc \
--js_out=import_style=commonjs,binary:./static \
--grpc_out=./static \
--plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` \
protos/helloworld.proto

grpc_tools_node_protoc \
--plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
--ts_out=./static \
protos/helloworld.proto

上記の手順で生成された、service 定義を読み込み全体のコードは以下のようになります。

import Mali from "mali";

import * as services from "./static/protos/helloworld_grpc_pb";
import * as messages from "./static/protos/helloworld_pb";

function sayHello(ctx: any): void {
  const reply = new messages.HelloReply();
  reply.setMessage(`Hello ${ctx.req.name}`);
  ctx.res = reply;
}

function sayHi(ctx: any): void {
  const reply = new messages.HelloReply();
  reply.setMessage(`Hi ${ctx.req.name}`);
  ctx.res = reply;
}

export function main(): void {
  const app = new Mali(services, "GreeterService");
  app.use({ sayHello, sayHi });
  app.start("localhost:50051");
}

main();

例えば、Node.js の grpc 標準実装である、 grpc を使って同様のサーバーを実装しようとした場合、最低限のコードは以下のようになります。こちらは、Dynamic codegen の例です。

import { resolve } from "path";
import grpc from "grpc";
import protoLoader from "@grpc/proto-loader";

const PROTO_PATH = resolve(__dirname, "./protos/helloworld.proto");
const packageDefinition = protoLoader.loadSync(PROTO_PATH);
const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;

function sayHello(call: any, callback: any): void {
  callback(null, { message: `Hello ${call.request.name}` });
}

function sayHi(call: any, callback: any): void {
  callback(null, { message: `Hi ${call.request.name}` });
}

function main(): void {
  const server = new grpc.Server();
  server.addService(hello_proto.Greeter.service, {
    sayHello: sayHello,
    sayHi: sayHi
  });
  server.bind("0.0.0.0:50051", grpc.ServerCredentials.createInsecure());
  server.start();
}

main();

mali.js の Dynamic codegen の例と比較すると多少冗長なのが見て取れるかと思います。
mali.js では、Mali のコンストラクタで .proto ファイルのパスを渡すだけで gRPC サーバーの基本的な設定を行うことができます。
また、server に対して service の method を追加する方法も、koa で middleware を登録するように app.use() の API を使って行うのも特徴的です。

参考

10
2
1

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
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?