python对于grpc的简单操作(三)


今天介绍python对于grpc的流式传输,主要有三种,以及视频的流式传输

  1. 客户端流,服务端非流(单流)
  2. 服务端流,客户端非流(单流)
  3. 客户端流,服务端流(双流)

PS:其实无论是单流还是双流,都是在客户端和服务端建立长连接

(一)客户端流,服务端非流(单流)

上proto:

syntax = "proto3";   // 指定protobuf版本

package test;   // 此文件的标识符,不添加也可以,以防止协议消息类型之间的名称冲突

// 定义消息结构类型,字段编号,可不从1开始,但不能重复,request为数据结构名,可自由定义
message request {
	string message = 1;
}


// 定义消息结构类型,数据后面的编号不能重复,response为数据结构名,可自由定义
message response {
	string message = 1;
}

//定义服务,下面定义的这种为最简单的rpc服务,客户端发起请求,服务端返回结果,stream关键字用来定义流式传输
service StreamTest {
	rpc ClientStream (stream request) returns (response) {}
}


client.py

import logging
import time
import grpc
import test_pb2
import test_pb2_grpc


def send_stream():
    message_list = ['1', '2', '3', '4', '5', '6', '7']
    for i in message_list:
        # return #如果想主动断开,可以提前return
        yield test_pb2.request(message=i)
        time.sleep(1)


def run():
    with grpc.insecure_channel('localhost:10086') as channel:
        client = test_pb2_grpc.StreamTestStub(channel)  # 客户端使用Stub类发送请求,参数为频道,为了绑定链接
        response = client.ClientStream(send_stream())  # 需要将上面的send_stream传进来
        print('返回结果:', response.message)


if __name__ == '__main__':
    logging.basicConfig()
    run()

server.py

from concurrent import futures
import logging
import time
import grpc
import test_pb2
import test_pb2_grpc


class StreamTest(test_pb2_grpc.StreamTestServicer):
    def ClientStream(self, request_iterator, context):
        for i in request_iterator:
            # print(dir(request_iterator))
            print(i.message)
            # if i.message == '5':
            #     break
            #     return test_pb2.response(message='提前返回')

        return test_pb2.response(message='ok')

#如果想提前中断传输,客户端可以在接收函数在break或提前return
def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  # 开启多线程
    test_pb2_grpc.add_StreamTestServicer_to_server(StreamTest(), server)  # 注册本地服务
    server.add_insecure_port('[::]:10086')  # 指定端口以及IP
    # server.add_insecure_port('0.0.0.0:10086')# 指定端口以及IP
    server.start()  # 启动服务器 start()是非阻塞的, 将实例化一个新线程来处理请求
    server.wait_for_termination()  # 阻塞调用线程,直到服务器终止


if __name__ == '__main__':
    logging.basicConfig()
    serve()

运行结果
在这里插入图片描述

(二)服务端流,客户端非流(单流)

上proto

syntax = "proto3";   // 指定protobuf版本

package test;   // 此文件的标识符,不添加也可以,以防止协议消息类型之间的名称冲突

// 定义消息结构类型,字段编号,可不从1开始,但不能重复,request为数据结构名,可自由定义
message request {
	string message = 1;
}


// 定义消息结构类型,数据后面的编号不能重复,response为数据结构名,可自由定义
message response {
	string message = 1;
}

//定义服务,下面定义的这种为最简单的rpc服务,客户端发起请求,服务端返回结果,stream关键字用来定义流式传输
service StreamTest {
	rpc ClientStream (stream request) returns (response) {}//客户端发流
	rpc ServerStream (request) returns (stream response) {}//服务端发流
}


client.py

import logging
import time
import grpc
import test_pb2
import test_pb2_grpc


def run():
    with grpc.insecure_channel('localhost:10086') as channel:
        client = test_pb2_grpc.StreamTestStub(channel)  # 客户端使用Stub类发送请求,参数为频道,为了绑定链接
        response = client.ServerStream(test_pb2.request(message='start'))  # 返回结果是一个迭代器
        for i in response:
            print('返回结果:', i.message)
            # if i.message == '5':#客户端主动断开连接可使用braek或return
            #     return


if __name__ == '__main__':
    logging.basicConfig()
    run()

server.py

from concurrent import futures
import logging
import time
import grpc
import test_pb2
import test_pb2_grpc


class StreamTest(test_pb2_grpc.StreamTestServicer):
    def ClientStream(self, request_iterator, context):
        for i in request_iterator:
            print(i.message)
        return test_pb2.response(message='ok')

    def ServerStream(self, request, context):
        print(request)
        message_list = ['1', '2', '3', '4', '5', '6', '7']
        for i in message_list:
            # if context.is_active():#context.is_active()判断连接状态

            # if i == '5':#服务端主动断开连接可使用braek或return
            #     return

            time.sleep(1)
            print('给客户端发送数据', i)
            yield test_pb2.response(message=i)
        print(context.is_active())


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  # 开启多线程
    test_pb2_grpc.add_StreamTestServicer_to_server(StreamTest(), server)  # 注册本地服务
    server.add_insecure_port('[::]:10086')  # 指定端口以及IP
    # server.add_insecure_port('0.0.0.0:10086')# 指定端口以及IP
    server.start()  # 启动服务器 start()是非阻塞的, 将实例化一个新线程来处理请求
    server.wait_for_termination()  # 阻塞调用线程,直到服务器终止


if __name__ == '__main__':
    logging.basicConfig()
    serve()

运行效果
在这里插入图片描述

(三)客户端流,服务端流(双流)

上proto

syntax = "proto3";   // 指定protobuf版本

package test;   // 此文件的标识符,不添加也可以,以防止协议消息类型之间的名称冲突

// 定义消息结构类型,字段编号,可不从1开始,但不能重复,request为数据结构名,可自由定义
message request {
	string message = 1;
}


// 定义消息结构类型,数据后面的编号不能重复,response为数据结构名,可自由定义
message response {
	string message = 1;
}

//定义服务,下面定义的这种为最简单的rpc服务,客户端发起请求,服务端返回结果,stream关键字用来定义流式传输
service StreamTest {
	rpc ClientStream (stream request) returns (response) {}//客户端发流
	rpc ServerStream (request) returns (stream response) {}//服务端发流
	rpc BothStream (stream request) returns (stream response) {}//双流
}


client.py

import logging
import time
import grpc
import test_pb2
import test_pb2_grpc


def send_stream():
    message_list = ['1', '2', '3', '4', '5', '6', '7']
    for i in message_list:
        time.sleep(1)
        yield test_pb2.request(message=i)


def run():
    with grpc.insecure_channel('localhost:10086') as channel:
        client = test_pb2_grpc.StreamTestStub(channel)  # 客户端使用Stub类发送请求,参数为频道,为了绑定链接
        response = client.BothStream(send_stream())  # 返回结果是一个迭代器
        for i in response:
            print('客户端收到', i.message)


if __name__ == '__main__':
    logging.basicConfig()
    run()

server.py

from concurrent import futures
import logging
import time
import grpc
import test_pb2
import test_pb2_grpc

from threading import Thread


def send(message):
    for i in message:
        yield test_pb2.response(message=i)


class StreamTest(test_pb2_grpc.StreamTestServicer):
    def ClientStream(self, request_iterator, context):
        for i in request_iterator:
            print(i.message)
        return test_pb2.response(message='ok')

    def ServerStream(self, request, context):
        print(request)
        message_list = ['1', '2', '3', '4', '5', '6', '7']
        for i in message_list:
            time.sleep(1)
            print('给客户端发送数据', i)
            yield test_pb2.response(message=i)

    def BothStream(self, request_iterator, context):

        for i in request_iterator:
        	#当然下面的yield也可以放在这,是可以同时接收和发送的
            print('服务端收到',i.message)

        for j in ['a', 'b', 'c', 'd', 'e', 'f', 'g']:
            time.sleep(1)
            yield test_pb2.response(message=j)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  # 开启多线程
    test_pb2_grpc.add_StreamTestServicer_to_server(StreamTest(), server)  # 注册本地服务
    server.add_insecure_port('[::]:10086')  # 指定端口以及IP
    # server.add_insecure_port('0.0.0.0:10086')# 指定端口以及IP
    server.start()  # 启动服务器 start()是非阻塞的, 将实例化一个新线程来处理请求
    server.wait_for_termination()  # 阻塞调用线程,直到服务器终止


if __name__ == '__main__':
    logging.basicConfig()
    serve()

执行效果
在这里插入图片描述

(四)视频的流式传输

proto文件

syntax = "proto3";   // 指定protobuf版本

package test;   // 此文件的标识符,不添加也可以,以防止协议消息类型之间的名称冲突

// 定义消息结构类型,字段编号,可不从1开始,但不能重复,request为数据结构名,可自由定义
message request {
	bytes data = 1;
}


// 定义消息结构类型,数据后面的编号不能重复,response为数据结构名,可自由定义
message response {
	string message = 1;
}

//定义服务,下面定义的这种为最简单的rpc服务,客户端发起请求,服务端返回结果,stream关键字用来定义流式传输
service StreamTest {
	rpc ClientStream (stream request) returns (response) {}
}

client.py

import logging
import os
import grpc
import test_pb2
import test_pb2_grpc



def send_stream():
    with open('input.mp4', 'rb') as f:
        size = os.path.getsize('input.mp4') / 1024
        n = 0
        while True:
            content = f.read(1024)
            if content:
                n = n + 1
                print('传输进度:{}%'.format(round(n / size * 100, 2)))
                yield test_pb2.request(data=content)
            else:
                break


def run():
    with grpc.insecure_channel('localhost:10086', options=[
        ('grpc.max_send_message_length', 256 * 1024 * 1024),  # 修改数据传输的大小限制,因为图片数据可能较大
        ('grpc.max_receive_message_length', 256 * 1024 * 1024),
    ]) as channel:
        client = test_pb2_grpc.StreamTestStub(channel)  # 客户端使用Stub类发送请求,参数为频道,为了绑定链接
        response = client.ClientStream(send_stream())  # 需要将上面的send_stream传进来
        print('返回结果:', response.message)


if __name__ == '__main__':
    logging.basicConfig()
    run()

server.py

from concurrent import futures
import logging
import time

import grpc
import test_pb2
import test_pb2_grpc



class StreamTest(test_pb2_grpc.StreamTestServicer):
    def ClientStream(self, request_iterator, context):
        with open('output.mp4', 'wb') as f:
            for i in request_iterator:
                f.write(i.data)

        print('接收完成')

        return test_pb2.response(message='ok')


# 如果想提前中断传输,客户端可以在接收函数在break或提前return
def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10),options=[
        ('grpc.max_send_message_length', 256 * 1024 * 1024),  # 修改数据传输的大小限制,因为图片数据可能较大
        ('grpc.max_receive_message_length', 256 * 1024 * 1024),
    ])  # 开启多线程
    test_pb2_grpc.add_StreamTestServicer_to_server(StreamTest(), server)  # 注册本地服务
    server.add_insecure_port('[::]:10086')  # 指定端口以及IP
    # server.add_insecure_port('0.0.0.0:10086')# 指定端口以及IP
    server.start()  # 启动服务器 start()是非阻塞的, 将实例化一个新线程来处理请求
    server.wait_for_termination()  # 阻塞调用线程,直到服务器终止


if __name__ == '__main__':
    logging.basicConfig()
    serve()

执行效果
在这里插入图片描述

(五)结语

如果有什么错误的地方,还请大家批评指正。最后,希望小伙伴们都能有所收获。写这些,仅记录自己学习python操作grpc的过程在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值