Merge pull request #19938 from kerbalwzy/master

Add a quick-start example for basic usages in Python
pull/20256/head
Lidi Zheng 6 years ago committed by GitHub
commit 5931fde9c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      examples/python/data_transmission/README.cn.md
  2. 37
      examples/python/data_transmission/README.en.md
  3. 114
      examples/python/data_transmission/client.py
  4. 69
      examples/python/data_transmission/demo.proto
  5. 174
      examples/python/data_transmission/demo_pb2.py
  6. 106
      examples/python/data_transmission/demo_pb2_grpc.py
  7. 114
      examples/python/data_transmission/server.py

@ -0,0 +1,36 @@
## Data transmission demo for using gRPC in Python
在Python中使用gRPC时, 进行数据传输的四种方式 [官方指南](<https://grpc.io/docs/guides/concepts/#unary-rpc>)
- #### 一元模式
在一次调用中, 客户端只能向服务器传输一次请求数据, 服务器也只能返回一次响应
`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`

@ -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](<https://grpc.io/docs/guides/concepts/#unary-rpc>)
- #### 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`

@ -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()

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

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

@ -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,))

@ -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()
Loading…
Cancel
Save