2017/08/25

Recent entries from same category

  1. Go 言語プログラミングエッセンスという本を書きました。
  2. errors.Join が入った。
  3. unsafe.StringData、unsafe.String、unsafe.SliceData が入った。
  4. Re: Go言語で画像ファイルか確認してみる
  5. net/url に JoinPath が入った。

gRPC は型の強い RPC を色々な言語を使って実装できる仕組みとライブラリです。

Big Sky :: Protocol Buffers を利用した RPC、gRPC を golang から試してみた。

grpc/grpc · GitHub gRPC - An RPC library and framework https://github.com/grpc/grpc gRPC は Google が開...

https://mattn.kaoriya.net/software/lang/go/20150227144125.htm

とても便利なのですが幾分手数が多いのが難点で、ちょっとしたサービスを gRPC で実装したいと思っていてもそう簡単に作る事が出来ませんでした。

ところが今回ご紹介する lile を使うと、とても簡単に gRPC を使った golang の実装を作れてしまいます。

GitHub - lileio/lile: Easily create gRPC services in Go

readme.md ALPHA: Lile is currently considered "Alpha" in that things may change. Currently I am gath...

https://github.com/lileio/lile

lile は gRPC のスケルトンを生成するコマンドとライブラリセットです。今日はこれを使って簡単に gRPC のサービスを作ってみます。お題は GENE95 辞書 を gRPC 経由で照会するサービスです。

まず lile をインストールするには以下のコマンドを実行します。

$ go get github.com/lileio/lile/...

Windows の人は今 pull-request を作ってるのでそちらを使って下さい。執筆時点でまだマージされてませんが。

lile をインストールしたらまずスケルトンを生成します。

$ lile new gene9go
Creating project in /home/mattn/go/src/github.com/mattn/lile-example/gene9go
Is this OK? [y]es/[n]o
yes
.
├── server
│   ├── server.go
│   └── server_test.go
├── subscribers
│   └── subscribers.go
├── gene9go
│   ├── cmd
│       ├── root.go
│       ├── serve.go
│       ├── subscribe.go
│       └── up.go
│   └── main.go
├── gene9go.proto
├── Makefile
├── Dockerfile
├── .travis.yml
└── .gitignore

git push できるくらいの物が生成されています。次に proto ファイルを編集して独自のインタフェースを作成します。元の proto ファイルは以下の様になっています。

syntax = "proto3";
option go_package = "github.com/mattn/lile-example/gene9go";
package gene9go;

message Request {
  string id = 1;
}

message Response {
  string id = 1;
}

service Gene9go {
  rpc Read (Request) returns (Response) {}
}

これを以下の様に編集しました。

syntax = "proto3";
option go_package = "github.com/mattn/lile-example/gene9go";
package gene9go;

message Request {
  string Word = 1;
}

message Response {
  string Text = 1;
}

service Gene9go {
  rpc Translate (Request) returns (Response) {}
}

辞書引きなので Translate メソッドを追加しています。編集し終えたら Makefile のある場所で make を実行します。するとこの proto ファイルから Translate メソッドのスケルトンが生成されます。

package server

import (
    "errors"

    "github.com/mattn/lile-example/gene9go"
    context "golang.org/x/net/context"
)

func (s Gene9goServer) Translate(ctx context.Context, r *gene9go.Request) (*gene9go.Response, error) {
    return nil, errors.New("not yet implemented")
}
尚、この時点でテストコードのスタブも生成されます。とても便利です。

package server

import (
    "testing"

    "github.com/mattn/lile-example/gene9go"
    "github.com/stretchr/testify/assert"
    context "golang.org/x/net/context"
)

func TestTranslate(t *testing.T) {
    ctx := context.Background()
    req := &gene9go.Request{}

    res, err := cli.Translate(ctx, req)
    assert.Nil(t, err)
    assert.NotNil(t, res)
}

さてでは Translate メソッドの中身を実装します。gene.txt ファイルを読み込んで単語にマッチする次の行を返しているだけです。gene.txt は utf-8 で保存しておいて下さい。

package server

import (
    "bufio"
    "os"
    "strings"

    "github.com/mattn/lile-example/gene9go"
    context "golang.org/x/net/context"
)

func translate(word string) (stringerror) {
    f, err := os.Open("gene.txt")
    if err != nil {
        return "", err
    }
    defer f.Close()

    scanner := bufio.NewScanner(f)
    found := ""
    for scanner.Scan() {
        first := scanner.Text()
        if !scanner.Scan() {
            break
        }

        if strings.ToLower(first) == strings.ToLower(word) {
            found = scanner.Text()
            break
        }
    }
    return found, scanner.Err()
}

func (s Gene9goServer) Translate(ctx context.Context, r *gene9go.Request) (*gene9go.Response, error) {
    text, err := translate(r.GetWord())
    if err != nil {
        return nil, err
    }
    return &gene9go.Response{Text: text}, nil
}

あとはクライアントとサーバを作ります。といっても Register 関数が用意されているのでこちらも簡単。まずはサーバ。

package main

import (
    "log"
    "net"

    "github.com/mattn/lile-example/gene9go"
    "github.com/mattn/lile-example/gene9go/server"
    "google.golang.org/grpc"
)

func main() {
    lis, err := net.Listen("tcp"":11111")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    srv := grpc.NewServer()

    gene9go.RegisterGene9goServer(srv, &server.Gene9goServer{})
    srv.Serve(lis)
}
そしてクライアント。 package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/mattn/lile-example/gene9go"
    "google.golang.org/grpc"
)

func main() {
    conn, err := grpc.Dial("localhost:11111", grpc.WithInsecure())
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    client := gene9go.NewGene9goClient(conn)
    req := &gene9go.Request{
        Word: os.Args[1],
    }
    resp, err := client.Translate(context.Background(), req)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(resp.Text)
}
後から気付いたのですが、gene9go の下にサーバコマンドも生成されてました。いたれりつくせり過ぎる。 A gRPC based service

Usage:
  gene9go [command]

Available Commands:
  help        Help about any command
  serve       Run the RPC server
  subscribe   Subscribe to and process queue messages
  up          up runs both RPC and pubub subscribers

Flags:
  -h, --help   help for gene9go

Use "gene9go [command] --help" for more information about a command.

これだけです。なにこれ超簡単じゃん。サーバを起動した状態で、クライアントに単語を付けて起動します。

$ client Go
1.〜に進行する,行く,をしに行く,動く,過ぎる,至る,及ぶ,2.〜と書いてある

これを実装するのにわずか20分程度しか掛かりませんでした。今まで gRPC を使ったサービスに興味があったけど実装難しいと思っていた方はぜひ lile を使ってみて下さい。あっと言う間に実装できるはずです。

この記事で作ったソースファイルは以下に置いておきます。

GitHub - mattn/lile-example
https://github.com/mattn/lile-example
Posted at by