自分用gRPCメモ

## リポジトリ
https://github.com/k-okina/practice_grpc_example

## メモ
https://www.cnblogs.com/qq952693358/p/6942354.html

## 説明
こんな感じのprotobufを書く

```protobuf:helloworld.proto
syntax = "proto3";

package helloworld;

service World {
  rpc sayHello(RequestType) returns (ResponseType) {};
}

message RequestType {
}

message ResponseType {
  string message = 1;
}
```

```bash
$ protoc -I. --python_out=. --grpc_python_out=./server/python ./proto/helloworld.proto
```

これをpythonをtargetに生成するとprotocすると (protocはprotobuf compileの略っぽい

* helloworld_pb2.py
* helloworld_pb2_grpc.py

の2つのファイルが生成される
この2つのファイルがなんなのかというと、pb2の方がリクエストとレスポンスのクラス定義が入っている。
pb2_grpcの方には、クライアントとサーバーのクラス定義が入っている。

生成される中身はこんな感じ (生成ファイルは.gitignoreするのが吉みたい

```python:helloworld_pb2_grpc.py
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc

from proto import helloworld_pb2 as proto_dot_helloworld__pb2


class WorldStub(object):
  # missing associated documentation comment in .proto file
  pass

  def __init__(self, channel):
    """Constructor.

    Args:
      channel: A grpc.Channel.
    """
    self.sayHello = channel.unary_unary(
        '/helloworld.World/sayHello',
        request_serializer=proto_dot_helloworld__pb2.RequestType.SerializeToString,
        response_deserializer=proto_dot_helloworld__pb2.ResponseType.FromString,
        )


class WorldServicer(object):
  # missing associated documentation comment in .proto file
  pass

  def sayHello(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')


def add_WorldServicer_to_server(servicer, server):
  rpc_method_handlers = {
      'sayHello': grpc.unary_unary_rpc_method_handler(
          servicer.sayHello,
          request_deserializer=proto_dot_helloworld__pb2.RequestType.FromString,
          response_serializer=proto_dot_helloworld__pb2.ResponseType.SerializeToString,
      ),
  }
  generic_handler = grpc.method_handlers_generic_handler(
      'helloworld.World', rpc_method_handlers)
  server.add_generic_rpc_handlers((generic_handler,))
```

```python:helloworld_pb2.py
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: proto/helloworld.proto

import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor.FileDescriptor(
  name='proto/helloworld.proto',
  package='helloworld',
  syntax='proto3',
  serialized_options=None,
  serialized_pb=_b('\n\x16proto/helloworld.proto\x12\nhelloworld\"\r\n\x0bRequestType\"\x1f\n\x0cResponseType\x12\x0f\n\x07message\x18\x01 \x01(\t2H\n\x05World\x12?\n\x08sayHello\x12\x17.helloworld.RequestType\x1a\x18.helloworld.ResponseType\"\x00\x62\x06proto3')
)




_REQUESTTYPE = _descriptor.Descriptor(
  name='RequestType',
  full_name='helloworld.RequestType',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  serialized_options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=38,
  serialized_end=51,
)


_RESPONSETYPE = _descriptor.Descriptor(
  name='ResponseType',
  full_name='helloworld.ResponseType',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='message', full_name='helloworld.ResponseType.message', index=0,
      number=1, type=9, cpp_type=9, label=1,
      has_default_value=False, default_value=_b("").decode('utf-8'),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      serialized_options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  serialized_options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=53,
  serialized_end=84,
)

DESCRIPTOR.message_types_by_name['RequestType'] = _REQUESTTYPE
DESCRIPTOR.message_types_by_name['ResponseType'] = _RESPONSETYPE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)

RequestType = _reflection.GeneratedProtocolMessageType('RequestType', (_message.Message,), dict(
  DESCRIPTOR = _REQUESTTYPE,
  __module__ = 'proto.helloworld_pb2'
  # @@protoc_insertion_point(class_scope:helloworld.RequestType)
  ))
_sym_db.RegisterMessage(RequestType)

ResponseType = _reflection.GeneratedProtocolMessageType('ResponseType', (_message.Message,), dict(
  DESCRIPTOR = _RESPONSETYPE,
  __module__ = 'proto.helloworld_pb2'
  # @@protoc_insertion_point(class_scope:helloworld.ResponseType)
  ))
_sym_db.RegisterMessage(ResponseType)



_WORLD = _descriptor.ServiceDescriptor(
  name='World',
  full_name='helloworld.World',
  file=DESCRIPTOR,
  index=0,
  serialized_options=None,
  serialized_start=86,
  serialized_end=158,
  methods=[
  _descriptor.MethodDescriptor(
    name='sayHello',
    full_name='helloworld.World.sayHello',
    index=0,
    containing_service=None,
    input_type=_REQUESTTYPE,
    output_type=_RESPONSETYPE,
    serialized_options=None,
  ),
])
_sym_db.RegisterServiceDescriptor(_WORLD)

DESCRIPTOR.services_by_name['World'] = _WORLD

# @@protoc_insertion_point(module_scope)
```

ではこの生成された定義ファイルを使ってクライアントとサーバーのコードを書いてみます

## サーバーのコードを書いてみます

```python:server.py
import time
import grpc
from concurrent import futures
from proto import helloworld_pb2
from proto import helloworld_pb2_grpc

SERVER_IP = '127.0.0.1'
SERVER_PORT = '9999'

class Servicer(helloworld_pb2_grpc.WorldServicer):
  def sayHello(self, request, context):
      print('hello from server')
      return helloworld_pb2.ResponseType(message='hello world')


def serve():
    print('Starting server...')
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    helloworld_pb2_grpc.add_WorldServicer_to_server(
        Servicer(), server
    )
    server.add_insecure_port(SERVER_IP + ':' + SERVER_PORT)
    server.start()
    print('Listen :' + SERVER_PORT)
    try:
        while True:
            time.sleep(3600)
    except KeyboardInterrupt:
        print('Stop server')
        server.stop(0)


if __name__ == '__main__':
    serve()
```

Servicerがserviceを定義するもの。 (クラス、メソッドモジュールみたいなもの
で、サービサーをサーバーに追加してます。
プロセスが死なないように無限ループ + スリープをしときます。


## クライアントのコードを書いてみます

```python:client.py
import grpc
from proto import helloworld_pb2
from proto import helloworld_pb2_grpc

SERVER_IP = '127.0.0.1'
SERVER_PORT = '9999'


def run():
  print('start run')
  channel = grpc.insecure_channel(SERVER_IP + ':' + SERVER_PORT)
  stub = helloworld_pb2_grpc.WorldStub(channel)
  stub.sayHello(helloworld_pb2.RequestType())
  print('sayHello')


if __name__ == '__main__':
  run()
```

1. サーバーのチャネルを指定します
2. helloworld_pb2_grpcにあるStubにチャネルを渡してstub instanceを生成します
3. sayHelloを実行します。

## 所管
GRPC良さそう