异步grpc客户端

背景

  • 大部分情况可能是同步服务端+异步客户端同步服务端+同步客户端异步服务端+异步客户端
  • 针对io密集型服务,异步+异步种方式效率最高,
  • 但是还有一种情况是,服务端是cpu密集型,客户端是io密集型,那么服务端如果采用异步,效率还不如同步高,客户端由于是io型,因此,我们可以选择同步服务端+异步客户端

安装环境

python39 -m pip install grpcio==1.39.0 grpcio-tools==1.39.0 -i https://pypi.douban.com/simple

定义接口文件

helloworld.proto

syntax = "proto3";

package helloworld;


// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

服务端

服务端采用线程池,server.py

"""
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. pb/helloworld.proto
"""
from concurrent import futures
import time
import grpc
import helloworld_pb2 as helloworld_pb2
import helloworld_pb2_grpc as helloworld_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24


class Greeter(helloworld_pb2_grpc.GreeterServicer):

    def SayHello(self, request, context):
        import time
        time.sleep(1)  # 休眠1秒钟
        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port('[::]:50051')
    print("GreeterServicer start at port 50051...")
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)


if __name__ == '__main__':
    serve()

客户端

客户端使用异步客户端,异步调用20次,
client.py代码如下:

import asyncio
import time
import grpc
import helloworld_pb2 as pb_dot_helloworld__pb2
import helloworld_pb2_grpc as pb_dot_helloworld_pb2__grpc


async def hello(name):
    async with grpc.aio.insecure_channel('localhost:50051') as channel:
        stub = pb_dot_helloworld_pb2__grpc.GreeterStub(channel)
        response = await stub.SayHello(pb_dot_helloworld__pb2.HelloRequest(name=name))
        print("GreeterService client received: " + response.message)


async def main():
    await asyncio.gather(*[hello(f'zs{i}') for i in range(20)])


if __name__ == '__main__':
    t = time.time()
    asyncio.run(main())
    print(time.time() - t)

运行

  • 生成grpc代码
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. helloworld.proto
  • 运行服务端
python server.py

  • 运行客户端
pyhton client.py
  • 客户端打印输出
    在这里插入图片描述

代码分析

  • 服务端SayHello()接口每次调用休眠1秒钟,10个线程提供服务,那么服务端每秒钟最多可以处理10个请求
  • 客户端未开启多线程多进程,一共异步发出20个请求,客户端打印输出顺序不一致,并且打印结果显示2秒钟处理完毕,说明异步生效

总结

当我们碰到io密集型场景中又有cpu密集型任务的时候,我们可以采用此种同步服务端+异步客户端的方案。
例如,当我们使用fastapi提供对外服务时,不仅有普通的对数据库增删查改等io操作,还有大量的本地数据计算等cpu型任务,那么我们可以把cpu型任务使用gprc封装成服务端,同时提供异步grpc客户端给fastapi调用,这样一来,虽然cpu计算还是那么慢,但是fastapi本身由于不被cpu任务拖累,却可以接受更多的io操作。
那么新的问题又来了,这种异步方式和使用消息中间件的异步方式,哪一种更好呢?欢迎讨论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值