LoginSignup
0
0

More than 1 year has passed since last update.

【gRPC】GoでUnary APIを実装する

Last updated at Posted at 2022-01-29

はじめに

前の記事で紹介したgRPCのAPIタイプの内、1リクエストに対して1レスポンスを返すUnaryを実装してみました。

実装

細かい手順に分けて実装していきます。

準備

GoでgRPCやProtocol Bufferを実装するにあたり、以下のライブラリを導入します。

$ go get -u google.golang.org/grpc
$ go get -u github.com/golang/protobuf/protoc-gen-go

また、シェルにパスを通します(今回は.bash_profile)。

export GO_PATH=~/go
export PATH=$PATH:/$GO_PATH/bin

スキーマ作成(.protoファイルの作成)

greet.protoというファイルを作成し、スキーマを定義します。
送信するメッセージの型をGreetingで定義し、Unaryによるリクエストとレスポンスの型をGreetRequestGreetResponseで定義します。
また、APIのタイプ(どのようなリクエストとレスポンスの形式か)をサービスGreetServiceで定義します。

syntax = "proto3";

package greet;
option go_package="./greet/greetpb";

message Greeting {
    string first_name = 1; // タグ番号
    string last_name = 2;
}

message GreetRequest {
    Greeting greeting = 1;
}

message GreetResponse {
    string result = 1;
}

service GreetService{
    // Unary
    rpc Greet(GreetRequest) returns (GreetResponse) {};
}

コード生成(.pb.goファイルの作成)

以下のコマンドにより、作成したスキーマからコード(Protocol Buffer)を生成します。

protoc greet/greetpb/greet.proto --go_out=plugins=grpc:.

するとスキーマで定義したGreetingの構造体の型やそのレシーバ(メソッド)などがGoのコードとして生成されます(以下が生成コードの一部)。
これらのメソッドをクライアントとサーバで使用することで、gRPCによる通信を実現することができます。

type Greeting struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    FirstName string `protobuf:"bytes,1,opt,name=first_name,json=firstName,proto3" json:"first_name,omitempty"`
    LastName  string `protobuf:"bytes,2,opt,name=last_name,json=lastName,proto3" json:"last_name,omitempty"`
}

func (x *Greeting) GetFirstName() string {
    if x != nil {
        return x.FirstName
    }
    return ""
}

func (x *Greeting) GetLastName() string {
    if x != nil {
        return x.LastName
    }
    return ""
}

サーバ実装

生成したAPIをサーバに実装(フック)します。
server構造体を用意して、Protocol Buffer(greet.pb.go)で定義されたGreetServiceServerと同じ型でメソッド(レシーバ)を埋め込みます。

レシーバGreet()では、リクエストで取得したfirstNameHello [firstName]というレスポンスで返すような処理を行います。
Greet()内のGetGreeting()などもProtocol Bufferで定義されたものを用います。

server.go
package main

import (
    "context"
    "fmt"
    "log"
    "net"

    "github.com/suzuki0430/grpc-project/greet/greetpb"

    "google.golang.org/grpc"
)

type server struct{}

func (*server) Greet(ctx context.Context, req *greetpb.GreetRequest) (*greetpb.GreetResponse, error) {
    fmt.Printf("Greet function was invoked with %v\n", req)
    firstName := req.GetGreeting().GetFirstName()
    result := "Hello " + firstName
    res := &greetpb.GreetResponse{
        Result: result,
    }
    return res, nil
}

func main() {
    fmt.Println("Hello world")

    lis, err := net.Listen("tcp", "0.0.0.0:50051")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }

    s := grpc.NewServer()
    greetpb.RegisterGreetServiceServer(s, &server{})

    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

サーバをlistenする処理はmain()内に記述します。
サーバの立ち上げには以下のコマンドを利用します。

go run greet/greet_server/server.go

クライアント実装

Protocol Buffer(greet.pb.go)に定義されたメソッドを用いてリクエストの作成・送信とレスポンスの取得を行う処理をdoUnary()にまとめます。
server.goに記述したメソッドGreet()をクライアント側で呼び出しているように、この部分でRPC(クライアントでサーバのコードを実行する)を行っています。

client.go
package main

import (
    "context"
    "fmt"
    "io"
    "log"
    "time"

    "google.golang.org/grpc/codes"

    "github.com/suzuki0430/grpc-project/greet/greetpb"

    "google.golang.org/grpc"
    "google.golang.org/grpc/status"
)

func main() {
    fmt.Println("Hello I'm a client")
    cc, err := grpc.Dial("localhost:50051", grpc.WithInsecure)
    if err != nil {
        log.Fatalf("could not connect: %v", err)
    }
    defer cc.Close()

    c := greetpb.NewGreetServiceClient(cc)

    doUnary(c)
}

func doUnary(c greetpb.GreetServiceClient) {
    fmt.Println("Starting to do a Unary RPC...")
    req := &greetpb.GreetRequest{
        Greeting: &greetpb.Greeting{
            FirstName: "Katsumi",
            LastName:  "Yamada",
        },
    }
    res, err := c.Greet(context.Background(), req)
    if err != nil {
        log.Fatalf("error while calling Greet RPC: %v", err)
    }
    log.Printf("Response from Greet: %v", res.Result)
}

参考資料

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