GoによるgRPCのテスト Unnary実装

   · ☕ 3 min read

前回 サーバー起動

Unaryについて

こちらを参照。

First let’s look at the simplest type of RPC, where the client sends a single request and gets back a single response.

Unaryは、クライアントが単一のリクエストを送信し、それに対し単一のレスポンスが返ってくる、最もシンプルなRPCです。

Serverの実装

  1. Serverの実装をする <=(1)
  2. Listenerを作る
  3. server構造体を用意して(1)を割り当てる
  4. サーバーを起動

1. Serverを実装する

.protoで記入したrpcを満たすように、server.goを実装する。

 今回、greet.protoでは service GreetServiceとしてrpcを設定した。この.protoをprotobufコマンドでgenerateすると、GreetServiceServerインターフェースが生成される。このインターフェースを満たすようにサーバーの実装を行う必要がある。

  • greet/greetpb/greet.proto

    1
    2
    3
    
    service GreetService{
        rpc Greet(GreetRequest) returns (GreetResponse);
    }
    
  • greet/greetpb/greet.pb.go

    1
    2
    3
    4
    
    // GreetServiceServer is the server API for GreetService service.
    type GreetServiceServer interface {
      Greet(context.Context, *GreetRequest) (*GreetResponse, error)
    }
    
  • greet/greet_server/server.go

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    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
    }
    

2. Listenerを作る

引き続きsever.goに追記していく。

  • greet/greet_server/server.go

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

3. server構造体を用意して(1)を割り当てる

引き続きsever.goに追記していく。

  • greet/greet_server/server.go

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

4. サーバーを起動

引き続きsever.goに追記していく。

  • greet/greet_server/server.go

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

Serverの全体像

以上で完成です。上記のようなserver.goが出来上がります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
type server struct{}

// 1.
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
}
// /1.

func main() {
	fmt.Println("Hello world!")
  // 2.
	lis, err := net.Listen("tcp", "0.0.0.0:50051")
	if err != nil {
		log.Fatalf("Failed to listen: %v", err)
	}
  // /2.
  // 3.
	s := grpc.NewServer()
	greetpb.RegisterGreetServiceServer(s, &server{})
  // /3.
  // 4.
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
  // /4.
}

Clientの実装

  1. ClisentConnectionを作成する
  2. client構造体を用意して Connection を割り当てる
  3. Requestを用意する
  4. clientを使用してRequestを投げる

3. Requestを用意する

 リクエストは.protoに記述した構造体の構造を守るように実装する。
今回はmessage GreetRequestに対してmessage Greetingを含む形で.protoを作成した。この.protoから生成されるgreet.pb.goへは、この入れ子構造を保った構造体が作られる。要点としては、.protoにてリクエストの構造体のスキーマを決定する必要がある、ということになるかと思う。

  • greet/greetpb/greet.proto

    1
    2
    3
    4
    5
    6
    7
    8
    
    message Greeting {
        string first_name = 1;
        string last_name = 2;
    }
      
    message GreetRequest {
        Greeting greeting = 1;
    }
    
  • greet/greetpb/greet.pb.go

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    type Greeting struct {
      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"`
      XXX_NoUnkeyedLiteral struct{} `json:"-"`
      XXX_unrecognized     []byte   `json:"-"`
      XXX_sizecache        int32    `json:"-"`
    }
      
    type GreetRequest struct {
      Greeting             *Greeting `protobuf:"bytes,1,opt,name=greeting,proto3" json:"greeting,omitempty"`
      XXX_NoUnkeyedLiteral struct{}  `json:"-"`
      XXX_unrecognized     []byte    `json:"-"`
      XXX_sizecache        int32     `json:"-"`
    }
    
  • greet/greet_client/client.go

    1
    2
    3
    4
    5
    
      req := &greetpb.GreetRequest{
          Greeting: &greetpb.Greeting{
              FirstName: "Yusuke",
              LastName:  "Iwase",
          },
    

Clientの全体像

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func main() {
	fmt.Println("Hello I'm a client")
  // 1.
	cc, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("could not connect: %v", err)
	}
	// /1.
	defer cc.Close()
	// 2.
	c := greetpb.NewGreetServiceClient(cc)
	// /2.
	doUnary(c)
}

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

前回 サーバー起動

Share on

whasse
WRITTEN BY
whasse
Web Developer