python操作grpc复杂示例 grpc go python_客户端

gRPC因其传输速度快,很适合业务量大、高并发的网络通信场景,线程池的实现方式性能受限,而AsyncIO异步方式是1个高性能的处理并发请求的框架,gRPC 应用了 python AsyncIO模块技术,编写并提供了一套异步API接口集— gRPC AsyncIO API,其性能稳定,非常适合于高并发、大流量的网络通信场景。

下面以实例来说明如何实现异步 gRPC的过程。
本文实例已在 windows10, ubuntu上运行测试通过。

1、准备 probobuf 接口文件

按下面内容新建 demo.proto 文件

syntax = "proto3";

package demo;

service RouteGuide {
  rpc GetFeature(Point) returns (Feature) {}
}

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

message Feature {
  string name = 1;
  Point location = 2;
}

编译proto 文件

python -m grpc_tools.protoc --proto_path=. --python_out=. --grpc_python_out=. demo.proto

生成如下两个文件:
demo_pb2.py
demo_pb2_grpc.py

2. 异步Server端实现

gRPC Server构造器

由于异步方式是通过协程来执行任务,实际上是单线程方式,构造器比较简单
grpc.aio.server(migration_thread_pool=None, handlers=None, interceptors=None, options=None, maximum_concurrent_rpcs=None, compression=None)
通常只需要设置 maximum_concurrent_rpcs 参数即可,即同时允许rpc并发调用数量,默认无限制

服务端的主要方法

add_insecure_port(address) 绑定网络地址

下面3个方法是异步方式实现:
async start() 启动服务
async stop(grace) 停止服务
async wait_for_termination(timeout=None) 中止event loop循环

服务器代码
接口消息类中,接口函数要用异步方式来执行。
注意:不要在异步接口函数中使用阻塞进程的语句。

from concurrent import futures
from datetime import datetime

import grpc
import demo_pb2
import demo_pb2_grpc
import asyncio

class RouteGuideServicer(demo_pb2_grpc.RouteGuideServicer):
    """Provides methods that implement functionality of route guide server."""
    async def GetFeature(self, request, context):
        print(datetime.now(),'\n',request)
        return demo_pb2.Feature(name="abc", location=request)

async def server():
    server = grpc.aio.server(maximum_concurrent_rpcs=10)
    demo_pb2_grpc.add_RouteGuideServicer_to_server(        
        RouteGuideServicer(), server)

    server.add_insecure_port('[::]:50051')
    await server.start()
    await server.wait_for_termination()


if __name__ == '__main__':
    asyncio.run(server())

3. 异步客户端实现

当服务器采用异步方式时,gRPC的客户端采用普通方式,异步方式都可以。 本节介绍异步客户端的实现

异步客户端构建器

channel 类是客户端类,非加密客户端的构建方法:
grpc.aio.insecure_channel(target, options=None, compression=None, interceptors=None

客户端代码
客户端stub 层的接口调用函数要有异步方式执行。

from __future__ import print_function

import grpc
import demo_pb2
import demo_pb2_grpc
import asyncio

CHANNEL_OPTIONS = [('grpc.lb_policy_name', 'pick_first'),
                   ('grpc.enable_retries', 0),
                   ('grpc.keepalive_timeout_ms', 10000)]

async def guide_get_feature(stub):
    point = demo_pb2.Point(latitude=409146138, longitude=-746188906)
    feature = await stub.GetFeature(point)
    if not feature.location:
        print("Server returned incomplete feature")
        return
    if feature.name:
        print("Feature called %s at %s" % (feature.name, feature.location))
    else:
        print("Found no feature at %s" % feature.location)
    

async def main():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    
    # with grpc.aio.insecure_channel('localhost:50051') as channel:
    async with grpc.aio.insecure_channel(target='localhost:50051',
                                         options=CHANNEL_OPTIONS) as channel:
        stub = demo_pb2_grpc.RouteGuideStub(channel)
        print("-------------- GetFeature --------------")
        await guide_get_feature(stub)


if __name__ == '__main__':
    asyncio.run(main())

4. 测试代码

打开两个终端窗口,分别运行server.py, client.py
server.py 窗口

(enva) D:\workplace\python\Simple_project\grpc\gRPC_demo>py server_a.py
2023-02-05 23:57:34.983625
 latitude: 409146138
longitude: -746188906

2023-02-05 23:57:36.518631
 latitude: 409146138
longitude: -746188906

client.py 窗口

(enva) D:\workplace\python\Simple_project\grpc\gRPC_demo>py client_a.py
-------------- GetFeature --------------
Feature called abc at latitude: 409146138
longitude: -746188906


(enva) D:\workplace\python\Simple_project\grpc\gRPC_demo>py client_a.py
-------------- GetFeature --------------
Feature called abc at latitude: 409146138
longitude: -746188906


(enva) D:\workplace\python\Simple_project\grpc\gRPC_demo>

总结

异步gRPC与之前普通方式gRPC实际过程基本一致,主要的区别如下:

  • 异步server构造器不需要线程池参数,异步协程是在同1个线程中执行
  • 异步gRPC要求接口函数及调用都使用 async – await 来修饰
  • 执行时使用异步 event loop,即通过ayscio.run( )来运行。

python操作grpc复杂示例 grpc go python_python_02