Python项目中Protocol Buffers的应用示例

项目结构

文件介绍

generated
    目录
    用于存储编译后的pb2文件
proto/user.proto
    定义的user协议
client.py
    模拟前后端分离向后端发起请求
server.py
    利用Flask创建简易web应用
    提供获取数据接口

依赖安装

protobuf==4.25.3
flask==3.0.2
flask-cors==4.0.0
(base) PS ......\protobuf-demo> conda activate protobuf-demo
(protobuf-demo) ......\protobuf-demo> pip install -r .\requirements.txt

proto编译

(protobuf-demo) PS D:\workspace\projects-py\protobuf-demo> protoc -I=proto --python_out=generated proto/user.proto

 执行代码后将会在generated目录中生成user.proto对应的pb2文件

文件拆解

proto/user.proto

syntax = "proto3";

package user;

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
  Role role = 4;

  enum Role {
    GUEST = 0;
    USER = 1;
    ADMIN = 2;
  }
}

message UserRequest {
    int32 user_id = 1;
  }

  message UserResponse {
    User user = 1;
  }

  message UserListResponse {
    repeated User users = 1;
  }

user_pb2.py

import requests

from generated import user_pb2



def get_user(user_id, sub_type='json'):
    print(f"Getting user {user_id} in {sub_type} format......")

    response = requests.get(
        url=f"http://localhost:5000/api/users/{user_id}",
        headers={'Accept': f'application/{sub_type}'}
    )
    if response.status_code == 200:
        if sub_type == 'json':
            print(response.json())
        if sub_type == 'protobuf':
            # 反序列化
            user_response = user_pb2.UserResponse()
            user_response.ParseFromString(response.content)
            user = user_response.user
            print(f"ID: {user.id}\n"
                  f"Name: {user.name}\n"
                  f"Email: {user.email}\n"
                  f"Role: {user_pb2.User.Role.Name(user.role)}")
    else:
        print(f"Error: {response.status_code} - {response.text}")


def list_users(sub_type='json'):
    print(f"Listing users in {sub_type} format......")

    response = requests.get(
        url=f"http://localhost:5000/api/users",
        headers={'Accept': f'application/{sub_type}'}
    )

    if response.status_code == 200:
        if sub_type == 'json':
            print(response.json())
        if sub_type == 'protobuf':
            # 反序列化
            user_list_response = user_pb2.UserListResponse()
            user_list_response.ParseFromString(response.content)
            users = user_list_response.users
            for user in users:
                print(f"ID: {user.id}\n"
                      f"Name: {user.name}\n"
                      f"Email: {user.email}\n"
                      f"Role: {user_pb2.User.Role.Name(user.role)}")
    else:
        print(f"Error: {response.status_code} - {response.text}")


if __name__ == '__main__':
    get_user(1, 'json')
    get_user(1, 'protobuf')

    list_users('json')
    list_users('protobuf')

server.py

from flask import Flask, request, Response
from flask_cors import CORS

from generated import user_pb2

app = Flask(__name__)
CORS(app)

# 模拟数据库
users_db = {
    1: user_pb2.User(id=1, name='张三', email='zhangsan@example.com', role=user_pb2.User.Role.ADMIN),
    2: user_pb2.User(id=2, name='李四', email='lisi@example.com', role=user_pb2.User.Role.USER),
    3: user_pb2.User(id=3, name='王五', email='wangwu@example.com', role=user_pb2.User.Role.GUEST),
}


@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    # 检查客户端接受的Content-type
    accept_header = request.headers.get('Accept', '')

    if user_id in users_db:
        user = users_db[user_id]

        # 确认返回数据格式
        if 'application/protobuf' in accept_header:
            # 返回protobuf格式
            response = user_pb2.UserResponse()
            response.user.CopyFrom(user)
            # 序列化返回
            return Response(response.SerializeToString(), content_type='application/protobuf')
        else:
            # 默认返回JSON格式
            return {
                'id': user.id,
                'name': user.name,
                'email': user.email,
                'role': user_pb2.User.Role.Name(user.role),
            }
    else:
        return {'error': 'User not found'}, 404

@app.route('/api/users', methods=['GET'])
def list_users():
    # 检查客户端接受的Content-type
    accept_header = request.headers.get('Accept', '')

    if 'application/protobuf' in accept_header:
        # 返回protobuf格式
        response = user_pb2.UserListResponse()
        for user in users_db.values():
            response.users.append(user)
        # 序列化返回
        return Response(response.SerializeToString(), content_type='application/protobuf')
    else:
        # 默认返回JSON格式
        return [
            {
                'id': user.id,
                'name': user.name,
                'email': user.email,
                'role': user_pb2.User.Role.Name(user.role),
            }
            for user in users_db.values()
        ]

if __name__ == '__main__':
    app.run(debug=True, port=5000)

client.py

import requests

from generated import user_pb2



def get_user(user_id, sub_type='json'):
    print(f"Getting user {user_id} in {sub_type} format......")

    response = requests.get(
        url=f"http://localhost:5000/api/users/{user_id}",
        headers={'Accept': f'application/{sub_type}'}
    )
    if response.status_code == 200:
        if sub_type == 'json':
            print(response.json())
        if sub_type == 'protobuf':
            # 反序列化
            user_response = user_pb2.UserResponse()
            user_response.ParseFromString(response.content)
            user = user_response.user
            print(f"ID: {user.id}\n"
                  f"Name: {user.name}\n"
                  f"Email: {user.email}\n"
                  f"Role: {user_pb2.User.Role.Name(user.role)}")
    else:
        print(f"Error: {response.status_code} - {response.text}")


def list_users(sub_type='json'):
    print(f"Listing users in {sub_type} format......")

    response = requests.get(
        url=f"http://localhost:5000/api/users",
        headers={'Accept': f'application/{sub_type}'}
    )

    if response.status_code == 200:
        if sub_type == 'json':
            print(response.json())
        if sub_type == 'protobuf':
            # 反序列化
            user_list_response = user_pb2.UserListResponse()
            user_list_response.ParseFromString(response.content)
            users = user_list_response.users
            for user in users:
                print(f"ID: {user.id}\n"
                      f"Name: {user.name}\n"
                      f"Email: {user.email}\n"
                      f"Role: {user_pb2.User.Role.Name(user.role)}")
    else:
        print(f"Error: {response.status_code} - {response.text}")


if __name__ == '__main__':
    get_user(1, 'json')
    get_user(1, 'protobuf')

    list_users('json')
    list_users('protobuf')

环境启动 

启动服务端

(protobuf-demo) PS D:\workspace\projects-py\protobuf-demo> python .\server.py

启动客户端 

(protobuf-demo) PS D:\workspace\projects-py\protobuf-demo> python .\client.py

结果输出如下

Getting user 1 in json format......
{'email': 'zhangsan@example.com', 'id': 1, 'name': '张三', 'role': 'ADMIN'}
Getting user 1 in protobuf format......
ID: 1
Name: 张三
Email: zhangsan@example.com
Role: ADMIN
Listing users in json format......
[{'email': 'zhangsan@example.com', 'id': 1, 'name': '张三', 'role': 'ADMIN'}, {'email': 'lisi@example.com', 'id': 2, 'name': '李四', 'role': 'USER'}, {'email': 'wangwu@example.com', 'id': 3, 'name': '王五', 'role': 'GUEST'}]
Listing users in protobuf format......
ID: 1
Name: 张三
Email: zhangsan@example.com
Role: ADMIN
ID: 2
Name: 李四
Email: lisi@example.com
Role: USER
ID: 3
Name: 王五
Email: wangwu@example.com
Role: GUEST

总结

  1. protobuf 定义.proto 文件定义了数据结构,这是跨语言通信的基础

  2. 代码生成protoc 工具根据 .proto 文件生成各语言的代码

  3. 序列化/反序列化:使用 SerializeToString() 和 ParseFromString() 方法

  4. 内容协商:通过 HTTP 的 Accept 头决定返回 JSON 还是 protobuf 格式

  5. 类型安全:protobuf 提供了强类型检查,减少了运行时错误

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值