リポジトリ
メモ
説明
こんな感じのprotobufを書く
helloworld.proto
syntax = "proto3";
package helloworld;
service World {
rpc sayHello(RequestType) returns (ResponseType) {};
}
message RequestType {
}
message ResponseType {
string message = 1;
}
$ 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するのが吉みたい
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,))
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)
ではこの生成された定義ファイルを使ってクライアントとサーバーのコードを書いてみます
サーバーのコードを書いてみます
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を定義するもの。 (クラス、メソッドモジュールみたいなもの
で、サービサーをサーバーに追加してます。
プロセスが死なないように無限ループ + スリープをしときます。
クライアントのコードを書いてみます
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()
- サーバーのチャネルを指定します
- helloworld_pb2_grpcにあるStubにチャネルを渡してstub instanceを生成します
- sayHelloを実行します。
所管
GRPC良さそう