当前位置: 首页 > news >正文

微服务架构中 gRPC 的应用

一、为什么使用gRPC

1. 性能优势

        传统的前后端交互方式如下:

# server
from flask import Flask, jsonify, requestapp = Flask(__name__)@app.route("/user/<int:user_id>", methods=["GET"])
def get_user(user_id):return jsonify({"id": user_id,"name": f"User {user_id}"})if __name__ == "__main__":app.run(port=5000)-----------------------------------------------------# client
import requestsresp = requests.get("http://localhost:5000/user/1")
print(resp.json())  # 输出: {'id': 1, 'name': 'User 1'}

        但是,在构建大型分布式系统、微服务架构或跨语言通信的系统时,传统的 RESTful 接口和 JSON 传输方式在某些方面逐渐暴露出性能与扩展性的瓶颈。

        比如,一个服务需要每秒处理 1 万个请求,每个请求传输一个结构化的订单对象(比如嵌套字段较多)。那么,就会遇到以下问题:

  • JSON 是文本格式,体积大,解析慢;

  • 每次都需要进行字符串解析和对象映射(反序列化),消耗 CPU;

  • 嵌套结构多时,反序列化容易出错、调试困难。

       而 gRPC 使用 Protobuf,二进制格式,解析速度远快于 JSON,CPU 占用更低。 

        再比如,服务 A 需要同时调用服务 B 和服务 C,然后聚合数据返回前端。那么又会产生如下问题:

  • REST 基于 HTTP/1.1,请求需要串行排队,无法并发复用 TCP;

  • 每个请求都要建立 HTTP 连接(三次握手),慢;

  • 请求头巨大(冗余),比如每次都发送 User-AgentAccept 等;

        gRPC 基于 HTTP/2,支持多路复用(multiplexing),一个连接上同时发送多个请求不阻塞,极大减少延迟。 

 2. 接口协议的统一

        传统项目开发中,多个微服务分别由不同团队维护,使用 REST 风格的 API,有的写 Swagger,有的直接靠 README 文档说明。那么就会有以下几个问题:

  • 没有统一的接口标准;

  • 接口定义冗余、重复、错误;

  • 需要手动维护文档,代码和接口易不一致;

  • 新人不清楚应该怎么调某个服务,需要找人问或抓包。

        gRPC 使用 .proto 统一定义接口 + 数据结构,并自动生成服务端与客户端代码,接口一致性有保障。 

3. 跨语言沟通

         如果服务 A 使用 Java,服务 B 使用 Go,服务 C 使用 Python。它们之间通过 REST 接口通信。以下问题必须解决。

  • 需要各自解析 JSON,接口字段不一致可能导致错误;

  • 字段拼写错误无法被编译器发现;

  • 需要写大量重复的 HTTP 请求封装代码;

  • 零散维护,没有统一规范和 SDK。

        gRPC 支持多语言,使用同一个 .proto 文件就能自动生成多语言 SDK,省时省力。        

        因此,在小项目中,REST 是简单直接的解决方案;但在大型项目中,性能、接口规范、多语言支持和通信模式等方面的挑战,往往让 REST 力不从心。gRPC 就是为这些“高级场景”而生的。 

二、如何使用gRPC

1. 一个简单的例子

1.1 定义 proto 文件(user.proto)
syntax = "proto3";service UserService {rpc GetUser (UserRequest) returns (UserReply);
}message UserRequest {int32 id = 1;
}message UserReply {int32 id = 1;string name = 2;
}

        保存为 user.proto,然后使用 protoc 生成代码: 

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. user.proto

        接下来会生成两个 python 源码文件,分别是:

  • user_pb2.py

    • 包含 Protobuf 消息定义(如 UserRequestUserReply);

    • 提供序列化、反序列化等方法;

    • --python_out=. 参数有关。

  • user_pb2_grpc.py

    • 包含 gRPC 的服务接口定义和 stub 实现

    • 自动生成服务端基类(如 UserServiceServicer)和客户端 stub(如 UserServiceStub);

    • --grpc_python_out=. 参数有关。

 

        比如,user_pb2_grpc.py生成以后可能长这样,

import grpc
import user_pb2 as user__pb2class UserServiceStub(object):def __init__(self, channel):self.GetUser = channel.unary_unary('/UserService/GetUser',request_serializer=user__pb2.UserRequest.SerializeToString,response_deserializer=user__pb2.UserReply.FromString,)class UserServiceServicer(object):def GetUser(self, request, context):# 用户应实现此方法raise NotImplementedError('Method not implemented!')def add_UserServiceServicer_to_server(servicer, server):rpc_method_handlers = {'GetUser': grpc.unary_unary_rpc_method_handler(servicer.GetUser,request_deserializer=user__pb2.UserRequest.FromString,response_serializer=user__pb2.UserReply.SerializeToString,),}generic_handler = grpc.method_handlers_generic_handler('UserService', rpc_method_handlers)server.add_generic_rpc_handlers((generic_handler,))
1.2 定义gRPC 服务端
import grpc
from concurrent import futures
import user_pb2
import user_pb2_grpcclass UserService(user_pb2_grpc.UserServiceServicer):def GetUser(self, request, context):return user_pb2.UserReply(id=request.id, name=f"User {request.id}")def serve():server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))user_pb2_grpc.add_UserServiceServicer_to_server(UserService(), server)server.add_insecure_port('[::]:50051')server.start()print("gRPC server started on port 50051")server.wait_for_termination()if __name__ == "__main__":serve()
1.3 定义gRPC 客户端
import grpc
import user_pb2
import user_pb2_grpcdef run():channel = grpc.insecure_channel('localhost:50051')stub = user_pb2_grpc.UserServiceStub(channel)response = stub.GetUser(user_pb2.UserRequest(id=1))print(response)  # 输出: id: 1 name: "User 1"if __name__ == "__main__":run()

 2. 请求流程

        首选,浏览器发起请求,后端框架会把这个请求路由到 handler。然后在处理业务逻辑时,会调用 gRPC 客户端(gRPC连接池)。然后,gRPC客户端发起调用:

response = stub.GetUser(UserRequest(id=1))

        然后,gRPC框架内部(由proto生成的user_pb2_grpc.py)执行如下逻辑,此时,会进行这样的转化:UserRequest(id=1) 对象 → .SerializeToString() → 二进制数据。最后,数据通过 HTTP/2 发送给 gRPC 服务端。

# user_pb2_grpc.py 中 Stub 定义了:
self.GetUser = channel.unary_unary('/UserService/GetUser',  # 服务路径request_serializer=UserRequest.SerializeToString,  # 序列化函数response_deserializer=UserReply.FromString,  # 反序列化函数
)

        然后,gRPC 服务端接收到二进制数据后。使用 UserRequest.FromString 自动反序列化 →  UserRequest(id=1) 对象。然后调用gRPC服务端所写的代码:

class MyUserService(UserServiceServicer):def GetUser(self, request, context):# request 是反序列化后的 UserRequest 实例return UserReply(id=request.id, name=f"User {request.id}")

         最后,使用 UserReply.SerializeToString → 序列化成二进制,发回客户端。客户端接收响应后,使用 UserReply.FromString → 得到 UserReply 对象,作为函数返回值返回给调用者。

rpc_method_handlers = {'GetUser': grpc.unary_unary_rpc_method_handler(servicer.GetUser,request_deserializer=user__pb2.UserRequest.FromString,response_serializer=user__pb2.UserReply.SerializeToString,),
}

3. 总结

        gRPC 客户端请求时会将 Python 中的请求对象通过 request_serializer(如 .SerializeToString())进行序列化,发送给服务端,服务端接收后反序列化调用你实现的 UserServiceServicer.GetUser() 方法并返回响应对象,再通过 response_serializer 序列化后传回客户端,客户端用 response_deserializer(如 .FromString())反序列化恢复为 Python 对象。

http://www.lryc.cn/news/602876.html

相关文章:

  • Rust 最短路径、Tide、Partial、Yew、Leptos、数独实践案例
  • Hugging Face-环境配置
  • 洛谷 P10448 组合型枚举-普及-
  • HTML响应式SEO公司网站源码
  • 归雁思维:解锁自然规律与人类智慧的桥梁
  • 疯狂星期四文案网第22天运营日记
  • CFIHL: 水培生菜的多种叶绿素 a 荧光瞬态图像数据集
  • 递归算法的一些具体应用
  • TDSQL 技术详解
  • go‑cdc‑chunkers:用 CDC 实现智能分块 强力去重
  • Apache Ignite 的 JDBC Client Driver(JDBC 客户端驱动)
  • 利用frp实现内网穿透功能(服务器)Linux、(内网)Windows
  • OpenGL进阶系列22 - OpenGL SuperBible - bumpmapping 例子学习
  • 短剧系统开发上线全流程攻略:从架构设计到性能优化
  • 页面性能优化
  • Go性能优化深度指南:从原理到实战
  • C++-关于协程的一些思考
  • Linux 远程连接与文件传输:从基础到高级配置
  • 多系统集成前端困境:老旧工控设备与新型Web应用的兼容性突围方案
  • Docker笔记(基本命令、挂载本地gpu、Dockerfile文件配置、数据挂载、docker换源)
  • 3Dmax模型位置归零
  • [机缘参悟-237]:AI人工神经网络与人类的神经网络工作原理的相似性
  • Java项目:基于SSM框架实现的进销存管理系统【ssm+B/S架构+源码+数据库+毕业论文+远程部署】
  • Java Collections工具类
  • Mac查看本机ip地址
  • 【密码学】3. 流密码
  • 互信息:理论框架、跨学科应用与前沿进展
  • 【实时Linux实战系列】实时运动分析系统的构建
  • 表征学习:机器认知世界的核心能力与前沿突破
  • 组件化(一):重新思考“组件”:状态、视图和逻辑的“最佳”分离实践