18
11

More than 5 years have passed since last update.

RPCフレームワークTwirpを試してみる

Posted at

先日TwitchからリリースされたRPCフレームワークTwirpを試してみました。

Twirpとは

TwirpはgRPCと同じようにProtocol Buffersの.protoファイルからGoのコードを生成しますが以下のような違いがあります。

  • HTTP/1.1を使用
    • gRPCはHTTP/2を使用
  • JSONクライアントサポート
    • 他の言語でクライアントを書くのが比較的簡単
    • cURL等を使ってコマンドラインからリクエストを送信しデバッグすることができる

動作に必要なツールをインストール

protocなどのツールをインストールします。

Install and Update Twirp · twitchtv/twirp Wiki

Go(1.7+)とProtocol Bufferをインストール。(Goは環境変数の設定が必要かもしれません。)

brew install go
brew install protobuf

retoolをインストール。
go gettwirpをインストールすることもできますがretoolを使うとプロジェクトごとにインストールできます。

go get github.com/twitchtv/retool

protoファイルの作成とコード生成

twirpのWikiにディレクトリ構成等のベストプラクティスがまとめてあったので、これを参考に進めてみます。

Best Practices · twitchtv/twirp Wiki

今回はhelloworldというサービスを作ることにします。

ディレクトリを作成。

mkdir -p cmd/server
mkdir -p cmd/client
mkdir -p rpc/helloworld

protoファイルを作成

rpc/helloworld/service.proto
syntax = "proto3";
package twirp.example.helloworld;
option go_package = "helloworld";

service HelloWorld {
  rpc Hello(HelloReq) returns (HelloResp);
}

message HelloReq {
  string subject = 1;
}

message HelloResp {
  string text = 1;
}

retoolprotoc-gen-goprotoc-gen-twirpをインストール。

retool add github.com/golang/protobuf/protoc-gen-go master
retool add github.com/twitchtv/twirp/protoc-gen-twirp master

コード生成コマンド実行用のMakefileを作成します。retoolでインストールしたツールを使う場合はコマンドの頭にretool doをつけます。

gen:
    retool do protoc --proto_path=. --twirp_out=. --go_out=. rpc/helloworld/service.proto

make genコマンドを実行するとrpc/helloworldservice.pg.goservice.twirp.goが生成されます。

サーバーとクライアントを実装

生成されたコードを使ってサーバーとクライアントを実装します。

cmd/server/main.go
package main

import (
    "context"
    "net/http"

    pb "github.com/atsaki/twirp-example/rpc/helloworld"
)

type HelloWorldServer struct{}

func (s *HelloWorldServer) Hello(ctx context.Context, req *pb.HelloReq) (resp *pb.HelloResp, err error) {
    return &pb.HelloResp{Text: "Hello " + req.GetSubject()}, nil
}

func main() {
    twirpHandler := pb.NewHelloWorldServer(&HelloWorldServer{}, nil)
    mux := http.NewServeMux()
    mux.Handle(pb.HelloWorldPathPrefix, twirpHandler)
    http.ListenAndServe(":8080", mux)
}
cmd/client/main.go
package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"

    pb "github.com/atsaki/twirp-example/rpc/helloworld"
)

func main() {
    client := pb.NewHelloWorldProtobufClient("http://localhost:8080", &http.Client{})

    subject := ""
    if len(os.Args) > 1 {
        subject = os.Args[1]
    }
    resp, err := client.Hello(context.Background(), &pb.HelloReq{Subject: subject})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(resp.GetText())
}

サーバーを実行した状態で、

go run cmd/server/main.go

クライアントを実行すると、

go run cmd/client/main.go Twirp

以下のような文字列が出力されます。

Hello Twirp

cURLでリクエストを送信する

上の例ではProtocol Buffersのクライアントを作成しサーバーにリクエストを送信しましたが、cURLなどでリクエストを送信し結果をJSONで取得することもできます。

リクエストは以下のURIに送信します。
<package>.protoファイルでpackageに指定した値になります。

/twirp/<package>.<service>/<method>

HTTP Routing and Serialization · twitchtv/twirp Wiki

今回の例では次のようになります。

curl -X POST \
     -H "Content-Type:application/json" \
     -d '{"subject": "Twirp"}' \
     http://localhost:8080/twirp/twirp.example.helloworld.HelloWorld/Hello

サーバーが起動している状態で実行すると結果がJSONで返ってきます。

{"text":"Hello Twirp"}
18
11
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
18
11