diff --git a/examples/python/data_transmission/README.cn.md b/examples/python/data_transmission/README.cn.md new file mode 100644 index 00000000000..812a8bbb7d6 --- /dev/null +++ b/examples/python/data_transmission/README.cn.md @@ -0,0 +1,36 @@ +## Data transmission demo for using gRPC in Python + +在Python中使用gRPC时, 进行数据传输的四种方式 [官方指南]() + +- #### 一元模式 + + 在一次调用中, 客户端只能向服务器传输一次请求数据, 服务器也只能返回一次响应 + + `client.py - line:14 - simple_method` + + `server.py - line:17 - SimpleMethod` + +- #### 客户端流模式 + + 在一次调用中, 客户端可以多次向服务器传输数据, 但是服务器只能返回一次响应 + + `clien.py - line:27 - client_streaming_method ` + + `server.py - line:28 - ClientStreamingMethod` + +- #### 服务端流模式 + + 在一次调用中, 客户端只能向服务器传输一次请求数据, 但是服务器可以多次返回响应 + + `clien.py - line:48 - server_streaming_method` + + `server.py - line:41 - ServerStreamingMethod` + +- #### 双向流模式 + + 在一次调用中, 客户端和服务器都可以向对方多次收发数据 + + `client.py - line:63 - bidirectional_streaming_method` + + `server.py - line:59 - BidirectionalStreamingMethod` + diff --git a/examples/python/data_transmission/README.en.md b/examples/python/data_transmission/README.en.md new file mode 100644 index 00000000000..659ee1b93fb --- /dev/null +++ b/examples/python/data_transmission/README.en.md @@ -0,0 +1,37 @@ +## Data transmission demo for using gRPC in Python + +Four ways of data transmission when gRPC is used in Python. [Offical Guide]() + +- #### unary-unary + + In a single call, the client can only send request once, and the server can only respond once. + + `client.py - line:14 - simple_method` + + `server.py - line:17 - SimpleMethod` + +- #### stream-unary + + In a single call, the client can transfer data to the server an arbitrary number of times, but the server can only return a response once. + + `clien.py - line:27 - client_streaming_method` + + `server.py - line:28 - ClientStreamingMethod` + +- #### unary-stream + + In a single call, the client can only transmit data to the server at one time, but the server can return the response many times. + + `clien.py - line:48 - server_streaming_method` + + `server.py - line:41 - ServerStreamingMethod` + +- #### stream-stream + + In a single call, both client and server can send and receive data + to each other multiple times. + + `client.py - line:63 - bidirectional_streaming_method` + + `server.py - line:59 - BidirectionalStreamingMethod` + diff --git a/examples/python/data_transmission/client.py b/examples/python/data_transmission/client.py new file mode 100644 index 00000000000..dee25b3a2ab --- /dev/null +++ b/examples/python/data_transmission/client.py @@ -0,0 +1,114 @@ +# Copyright 2019 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""The example of four ways of data transmission using gRPC in Python.""" + +import time +import grpc + +import demo_pb2_grpc +import demo_pb2 + +SERVER_ADDRESS = "localhost:23333" +CLIENT_ID = 1 + + +# 一元模式(在一次调用中, 客户端只能向服务器传输一次请求数据, 服务器也只能返回一次响应) +# unary-unary(In a single call, the client can only send request once, and the server can +# only respond once.) +def simple_method(stub): + print("--------------Call SimpleMethod Begin--------------") + request = demo_pb2.Request( + client_id=CLIENT_ID, request_data="called by Python client") + response = stub.SimpleMethod(request) + print("resp from server(%d), the message=%s" % (response.server_id, + response.response_data)) + print("--------------Call SimpleMethod Over---------------") + + +# 客户端流模式(在一次调用中, 客户端可以多次向服务器传输数据, 但是服务器只能返回一次响应) +# stream-unary (In a single call, the client can transfer data to the server several times, +# but the server can only return a response once.) +def client_streaming_method(stub): + print("--------------Call ClientStreamingMethod Begin--------------") + + # 创建一个生成器 + # create a generator + def request_messages(): + for i in range(5): + request = demo_pb2.Request( + client_id=CLIENT_ID, + request_data=("called by Python client, message:%d" % i)) + yield request + + response = stub.ClientStreamingMethod(request_messages()) + print("resp from server(%d), the message=%s" % (response.server_id, + response.response_data)) + print("--------------Call ClientStreamingMethod Over---------------") + + +# 服务端流模式(在一次调用中, 客户端只能一次向服务器传输数据, 但是服务器可以多次返回响应) +# unary-stream (In a single call, the client can only transmit data to the server at one time, +# but the server can return the response many times.) +def server_streaming_method(stub): + print("--------------Call ServerStreamingMethod Begin--------------") + request = demo_pb2.Request( + client_id=CLIENT_ID, request_data="called by Python client") + response_iterator = stub.ServerStreamingMethod(request) + for response in response_iterator: + print("recv from server(%d), message=%s" % (response.server_id, + response.response_data)) + + print("--------------Call ServerStreamingMethod Over---------------") + + +# 双向流模式 (在一次调用中, 客户端和服务器都可以向对方多次收发数据) +# stream-stream (In a single call, both client and server can send and receive data +# to each other multiple times.) +def bidirectional_streaming_method(stub): + print( + "--------------Call BidirectionalStreamingMethod Begin---------------") + + # 创建一个生成器 + # create a generator + def request_messages(): + for i in range(5): + request = demo_pb2.Request( + client_id=CLIENT_ID, + request_data=("called by Python client, message: %d" % i)) + yield request + time.sleep(1) + + response_iterator = stub.BidirectionalStreamingMethod(request_messages()) + for response in response_iterator: + print("recv from server(%d), message=%s" % (response.server_id, + response.response_data)) + + print("--------------Call BidirectionalStreamingMethod Over---------------") + + +def main(): + with grpc.insecure_channel(SERVER_ADDRESS) as channel: + stub = demo_pb2_grpc.GRPCDemoStub(channel) + + simple_method(stub) + + client_streaming_method(stub) + + server_streaming_method(stub) + + bidirectional_streaming_method(stub) + + +if __name__ == '__main__': + main() diff --git a/examples/python/data_transmission/demo.proto b/examples/python/data_transmission/demo.proto new file mode 100644 index 00000000000..b2d956c89d5 --- /dev/null +++ b/examples/python/data_transmission/demo.proto @@ -0,0 +1,69 @@ +// Copyright 2019 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// 语法版本声明,必须放在非注释的第一行 +// Syntax version declaration. Must be placed on the first line of non-commentary. + +syntax = "proto3"; +// The document of proto3: https://developers.google.com/protocol-buffers/docs/proto3 + +// 包名定义, Python中使用时可以省略不写 +// Package name definition, which can be omitted in Python. +package demo; + +/* +`message`是用来定义传输的数据的格式的, 等号后面的是字段编号 +消息定义中的每个字段都有唯一的编号 +总体格式类似于Python中定义一个类或者Golang中定义一个结构体 +*/ +/* +`message` is used to define the structure of the data to be transmitted, after the equal sign +is the field number. Each field in the message definition has a unique number. +The overall format is similar to defining a class in Python or a structure in Golang. +*/ +message Request { + int64 client_id = 1; + string request_data = 2; +} + +message Response { + int64 server_id = 1; + string response_data = 2; +} + +// `service` 是用来给gRPC服务定义方法的, 格式固定, 类似于Golang中定义一个接口 +// `service` is used to define methods for gRPC services in a fixed format, similar to defining +//an interface in Golang +service GRPCDemo { + // 一元模式(在一次调用中, 客户端只能向服务器传输一次请求数据, 服务器也只能返回一次响应) + // unary-unary(In a single call, the client can only send request once, and the server can + // only respond once.) + rpc SimpleMethod (Request) returns (Response); + + // 客户端流模式(在一次调用中, 客户端可以多次向服务器传输数据, 但是服务器只能返回一次响应) + // stream-unary (In a single call, the client can transfer data to the server several times, + // but the server can only return a response once.) + rpc ClientStreamingMethod (stream Request) returns (Response); + + // 服务端流模式(在一次调用中, 客户端只能一次向服务器传输数据, 但是服务器可以多次返回响应) + // unary-stream (In a single call, the client can only transmit data to the server at one time, + // but the server can return the response many times.) + rpc ServerStreamingMethod (Request) returns (stream Response); + + // 双向流模式 (在一次调用中, 客户端和服务器都可以向对方多次收发数据) + // stream-stream (In a single call, both client and server can send and receive data + // to each other multiple times.) + rpc BidirectionalStreamingMethod (stream Request) returns (stream Response); +} + diff --git a/examples/python/data_transmission/demo_pb2.py b/examples/python/data_transmission/demo_pb2.py new file mode 100644 index 00000000000..5dfaf837dfd --- /dev/null +++ b/examples/python/data_transmission/demo_pb2.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: demo.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='demo.proto', + package='demo', + syntax='proto3', + serialized_options=None, + serialized_pb=_b('\n\ndemo.proto\x12\x04\x64\x65mo\"2\n\x07Request\x12\x11\n\tclient_id\x18\x01 \x01(\x03\x12\x14\n\x0crequest_data\x18\x02 \x01(\t\"4\n\x08Response\x12\x11\n\tserver_id\x18\x01 \x01(\x03\x12\x15\n\rresponse_data\x18\x02 \x01(\t2\xf0\x01\n\x08GRPCDemo\x12-\n\x0cSimpleMethod\x12\r.demo.Request\x1a\x0e.demo.Response\x12\x38\n\x15\x43lientStreamingMethod\x12\r.demo.Request\x1a\x0e.demo.Response(\x01\x12\x38\n\x15ServerStreamingMethod\x12\r.demo.Request\x1a\x0e.demo.Response0\x01\x12\x41\n\x1c\x42idirectionalStreamingMethod\x12\r.demo.Request\x1a\x0e.demo.Response(\x01\x30\x01\x62\x06proto3') +) + + + + +_REQUEST = _descriptor.Descriptor( + name='Request', + full_name='demo.Request', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='client_id', full_name='demo.Request.client_id', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='request_data', full_name='demo.Request.request_data', index=1, + number=2, 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=20, + serialized_end=70, +) + + +_RESPONSE = _descriptor.Descriptor( + name='Response', + full_name='demo.Response', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='server_id', full_name='demo.Response.server_id', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='response_data', full_name='demo.Response.response_data', index=1, + number=2, 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=72, + serialized_end=124, +) + +DESCRIPTOR.message_types_by_name['Request'] = _REQUEST +DESCRIPTOR.message_types_by_name['Response'] = _RESPONSE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), { + 'DESCRIPTOR' : _REQUEST, + '__module__' : 'demo_pb2' + # @@protoc_insertion_point(class_scope:demo.Request) + }) +_sym_db.RegisterMessage(Request) + +Response = _reflection.GeneratedProtocolMessageType('Response', (_message.Message,), { + 'DESCRIPTOR' : _RESPONSE, + '__module__' : 'demo_pb2' + # @@protoc_insertion_point(class_scope:demo.Response) + }) +_sym_db.RegisterMessage(Response) + + + +_GRPCDEMO = _descriptor.ServiceDescriptor( + name='GRPCDemo', + full_name='demo.GRPCDemo', + file=DESCRIPTOR, + index=0, + serialized_options=None, + serialized_start=127, + serialized_end=367, + methods=[ + _descriptor.MethodDescriptor( + name='SimpleMethod', + full_name='demo.GRPCDemo.SimpleMethod', + index=0, + containing_service=None, + input_type=_REQUEST, + output_type=_RESPONSE, + serialized_options=None, + ), + _descriptor.MethodDescriptor( + name='ClientStreamingMethod', + full_name='demo.GRPCDemo.ClientStreamingMethod', + index=1, + containing_service=None, + input_type=_REQUEST, + output_type=_RESPONSE, + serialized_options=None, + ), + _descriptor.MethodDescriptor( + name='ServerStreamingMethod', + full_name='demo.GRPCDemo.ServerStreamingMethod', + index=2, + containing_service=None, + input_type=_REQUEST, + output_type=_RESPONSE, + serialized_options=None, + ), + _descriptor.MethodDescriptor( + name='BidirectionalStreamingMethod', + full_name='demo.GRPCDemo.BidirectionalStreamingMethod', + index=3, + containing_service=None, + input_type=_REQUEST, + output_type=_RESPONSE, + serialized_options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_GRPCDEMO) + +DESCRIPTOR.services_by_name['GRPCDemo'] = _GRPCDEMO + +# @@protoc_insertion_point(module_scope) diff --git a/examples/python/data_transmission/demo_pb2_grpc.py b/examples/python/data_transmission/demo_pb2_grpc.py new file mode 100644 index 00000000000..b6d6fc72a13 --- /dev/null +++ b/examples/python/data_transmission/demo_pb2_grpc.py @@ -0,0 +1,106 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import demo_pb2 as demo__pb2 + + +class GRPCDemoStub(object): + """service是用来给GRPC服务定义方法的, 格式固定, 类似于Golang中定义一个接口 + `service` is used to define methods for GRPC services in a fixed format, similar to defining an interface in Golang + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.SimpleMethod = channel.unary_unary( + '/demo.GRPCDemo/SimpleMethod', + request_serializer=demo__pb2.Request.SerializeToString, + response_deserializer=demo__pb2.Response.FromString, + ) + self.ClientStreamingMethod = channel.stream_unary( + '/demo.GRPCDemo/ClientStreamingMethod', + request_serializer=demo__pb2.Request.SerializeToString, + response_deserializer=demo__pb2.Response.FromString, + ) + self.ServerStreamingMethod = channel.unary_stream( + '/demo.GRPCDemo/ServerStreamingMethod', + request_serializer=demo__pb2.Request.SerializeToString, + response_deserializer=demo__pb2.Response.FromString, + ) + self.BidirectionalStreamingMethod = channel.stream_stream( + '/demo.GRPCDemo/BidirectionalStreamingMethod', + request_serializer=demo__pb2.Request.SerializeToString, + response_deserializer=demo__pb2.Response.FromString, + ) + + +class GRPCDemoServicer(object): + """service是用来给GRPC服务定义方法的, 格式固定, 类似于Golang中定义一个接口 + `service` is used to define methods for GRPC services in a fixed format, similar to defining an interface in Golang + """ + + def SimpleMethod(self, request, context): + """简单模式 + unary-unary + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ClientStreamingMethod(self, request_iterator, context): + """客户端流模式(在一次调用中, 客户端可以多次向服务器传输数据, 但是服务器只能返回一次响应) + stream-unary (In a single call, the client can transfer data to the server several times, + but the server can only return a response once.) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ServerStreamingMethod(self, request, context): + """服务端流模式(在一次调用中, 客户端只能一次向服务器传输数据, 但是服务器可以多次返回响应) + unary-stream (In a single call, the client can only transmit data to the server at one time, + but the server can return the response many times.) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def BidirectionalStreamingMethod(self, request_iterator, context): + """双向流模式 (在一次调用中, 客户端和服务器都可以向对方多次收发数据) + stream-stream (In a single call, both client and server can send and receive data + to each other multiple times.) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_GRPCDemoServicer_to_server(servicer, server): + rpc_method_handlers = { + 'SimpleMethod': grpc.unary_unary_rpc_method_handler( + servicer.SimpleMethod, + request_deserializer=demo__pb2.Request.FromString, + response_serializer=demo__pb2.Response.SerializeToString, + ), + 'ClientStreamingMethod': grpc.stream_unary_rpc_method_handler( + servicer.ClientStreamingMethod, + request_deserializer=demo__pb2.Request.FromString, + response_serializer=demo__pb2.Response.SerializeToString, + ), + 'ServerStreamingMethod': grpc.unary_stream_rpc_method_handler( + servicer.ServerStreamingMethod, + request_deserializer=demo__pb2.Request.FromString, + response_serializer=demo__pb2.Response.SerializeToString, + ), + 'BidirectionalStreamingMethod': grpc.stream_stream_rpc_method_handler( + servicer.BidirectionalStreamingMethod, + request_deserializer=demo__pb2.Request.FromString, + response_serializer=demo__pb2.Response.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'demo.GRPCDemo', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/examples/python/data_transmission/server.py b/examples/python/data_transmission/server.py new file mode 100644 index 00000000000..f550b9b7bd2 --- /dev/null +++ b/examples/python/data_transmission/server.py @@ -0,0 +1,114 @@ +# Copyright 2019 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""The example of four ways of data transmission using gRPC in Python.""" + +from threading import Thread +from concurrent import futures + +import grpc +import demo_pb2_grpc +import demo_pb2 + +SERVER_ADDRESS = 'localhost:23333' +SERVER_ID = 1 + + +class DemoServer(demo_pb2_grpc.GRPCDemoServicer): + + # 一元模式(在一次调用中, 客户端只能向服务器传输一次请求数据, 服务器也只能返回一次响应) + # unary-unary(In a single call, the client can only send request once, and the server can + # only respond once.) + def SimpleMethod(self, request, context): + print("SimpleMethod called by client(%d) the message: %s" % + (request.client_id, request.request_data)) + response = demo_pb2.Response( + server_id=SERVER_ID, + response_data="Python server SimpleMethod Ok!!!!") + return response + + # 客户端流模式(在一次调用中, 客户端可以多次向服务器传输数据, 但是服务器只能返回一次响应) + # stream-unary (In a single call, the client can transfer data to the server several times, + # but the server can only return a response once.) + def ClientStreamingMethod(self, request_iterator, context): + print("ClientStreamingMethod called by client...") + for request in request_iterator: + print("recv from client(%d), message= %s" % (request.client_id, + request.request_data)) + response = demo_pb2.Response( + server_id=SERVER_ID, + response_data="Python server ClientStreamingMethod ok") + return response + + # 服务端流模式(在一次调用中, 客户端只能一次向服务器传输数据, 但是服务器可以多次返回响应) + # unary-stream (In a single call, the client can only transmit data to the server at one time, + # but the server can return the response many times.) + def ServerStreamingMethod(self, request, context): + print("ServerStreamingMethod called by client(%d), message= %s" % + (request.client_id, request.request_data)) + + # 创建一个生成器 + # create a generator + def response_messages(): + for i in range(5): + response = demo_pb2.Response( + server_id=SERVER_ID, + response_data=("send by Python server, message=%d" % i)) + yield response + + return response_messages() + + # 双向流模式 (在一次调用中, 客户端和服务器都可以向对方多次收发数据) + # stream-stream (In a single call, both client and server can send and receive data + # to each other multiple times.) + def BidirectionalStreamingMethod(self, request_iterator, context): + print("BidirectionalStreamingMethod called by client...") + + # 开启一个子线程去接收数据 + # Open a sub thread to receive data + def parse_request(): + for request in request_iterator: + print("recv from client(%d), message= %s" % + (request.client_id, request.request_data)) + + t = Thread(target=parse_request) + t.start() + + for i in range(5): + yield demo_pb2.Response( + server_id=SERVER_ID, + response_data=("send by Python server, message= %d" % i)) + + t.join() + + +def main(): + server = grpc.server(futures.ThreadPoolExecutor()) + + demo_pb2_grpc.add_GRPCDemoServicer_to_server(DemoServer(), server) + + server.add_insecure_port(SERVER_ADDRESS) + print("------------------start Python GRPC server") + server.start() + server.wait_for_termination() + + # If raise Error: + # AttributeError: '_Server' object has no attribute 'wait_for_termination' + # You can use the following code instead: + # import time + # while 1: + # time.sleep(10) + + +if __name__ == '__main__': + main()