前回 サーバー起動

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
  service GreetService{
      rpc Greet(GreetRequest) returns (GreetResponse);
  }
  • greet/greetpb/greet.pb.go
  // GreetServiceServer is the server API for GreetService service.
  type GreetServiceServer interface {
  	Greet(context.Context, *GreetRequest) (*GreetResponse, error)
  }
  • greet/greet_server/server.go
  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
  	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
  	s := grpc.NewServer()
  	greetpb.RegisterGreetServiceServer(s, &server{})

4. サーバーを起動

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

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

Serverの全体像

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

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
  message Greeting {
      string first_name = 1;
      string last_name = 2;
  }
  
  message GreetRequest {
      Greeting greeting = 1;
  }
  • greet/greetpb/greet.pb.go
  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
  	req := &greetpb.GreetRequest{
  		Greeting: &greetpb.Greeting{
  			FirstName: "Yusuke",
  			LastName:  "Iwase",
  		},

Clientの全体像

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)
}

前回 サーバー起動