gRPC 和传统 RPC 有啥不一样?一篇讲清楚!

打印 上一主题 下一主题

主题 2089|帖子 2089|积分 6267

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
如今各人做系统开发,都喜欢搞"微服务架构"——简单说就是把一个大系统拆成很多小服务,如许更灵活也更容易扩展。那这些服务之间怎么沟通呢?就得靠一种技术叫 RPC(远程过程调用)。今天我们就来聊聊它的"进化版":gRPC,看看它和传统的 RPC 到底有啥不一样。
一、先搞懂几个概念

什么是 RPC?

可以把它理解成"跨机器调用函数"的方式。就像你在本地调用一个函数一样,但其实它是在另一台服务器上运行的。传统 RPC 有很多种实现,好比 XML-RPC、JSON-RPC、SOAP 等,数据格式多是 XML 或 JSON。
那 gRPC 是啥?

Google 出品的一个更高效的 RPC 框架,基于 HTTP/2 协议,数据格式用的是 Protocol Buffers(简称 Protobuf)。性能好、服从高,还能自动天生代码,听起来就很香对吧?
二、gRPC 和传统 RPC 的几大区别(白话版)

对比点传统 RPCgRPC传输协议通常用 HTTP/1 或 TCPHTTP/2,支持多路复用,速度快数据格式XML/JSON,可读但体积大Protobuf,体积小,剖析快代码天生通常手动写支持自动天生客户端/服务端代码流式处置惩罚一般不支持支持四种调用模式,支持双向流跨语言支持有点费劲官方支持多语言(Go、Python 等)错误处置惩罚用 HTTP 状态码处置惩罚用标准错误码机制,支持详细描述三、举个例子更直观

用传统 JSON-RPC 调接口
  1. {
  2.   "jsonrpc": "2.0",
  3.   "method": "getUserProfile",
  4.   "params": {
  5.     "userId": 123,
  6.     "includeDetails": true
  7.   },
  8.   "id": 1
  9. }
复制代码
人类能看懂,但数据量大,剖析速度也慢。
用 gRPC + Protobuf

首先定义协议:
  1. syntax = "proto3";
  2. service UserService {
  3.   rpc GetUserProfile(UserRequest) returns (UserProfile) {}
  4. }
  5. message UserRequest {
  6.   int32 user_id = 1;
  7.   bool include_details = 2;
  8. }
  9. message UserProfile {
  10.   int32 user_id = 1;
  11.   string username = 2;
  12.   string email = 3;
  13. }
复制代码
然后就可以如许调用:
  1. request = user_pb2.UserRequest(user_id=123, include_details=True)
  2. response = stub.GetUserProfile(request)
  3. print(f"用户名: {response.username}")
复制代码
结构更清晰、体积更小、传输服从更高。
四、请求处置惩罚方式对比

传统RPC的调用方式
  1. # XML-RPC示例
  2. import xmlrpc.client
  3. # 创建客户端
  4. server = xmlrpc.client.ServerProxy("http://localhost:8000")
  5. # 每次调用都会建立新连接
  6. result = server.get_user_info(user_id=123)
  7. print(f"用户信息: {result}")
  8. # 又得重新连接
  9. another_result = server.get_product_details(product_id=456)
复制代码
就像每次打电话都要重新拨号一样,费时间!
gRPC的调用方式
  1. import grpc
  2. import user_service_pb2
  3. import user_service_pb2_grpc
  4. # 创建一个连接通道
  5. with grpc.insecure_channel('localhost:50051') as channel:
  6.     # 创建调用对象
  7.     stub = user_service_pb2_grpc.UserServiceStub(channel)
  8.    
  9.     # 同一个连接可以调用多个方法
  10.     response1 = stub.GetUser(user_service_pb2.GetUserRequest(user_id=123))
  11.     response2 = stub.GetProduct(user_service_pb2.GetProductRequest(product_id=456))
  12.    
  13.     # 还能做流式调用,像看视频一样一点点接收数据
  14.     for product in stub.ListProducts(user_service_pb2.ListProductsRequest(category="手机")):
  15.         print(f"产品: {product.name}, 价格: {product.price}")
复制代码
就像建立一条专线,通话不断,还能边说边听,太方便了!
五、性能差距有多大?

场景:获取 1000 个用户信息
传统 REST(HTTP/1 + JSON)版本:
  1. import requests
  2. import time
  3. start_time = time.time()
  4. users = []
  5. # 发送1000个独立的HTTP请求,每次都要建连接
  6. for i in range(1000):
  7.     response = requests.get(f"http://api.example.com/users/{i}")
  8.     users.append(response.json())
  9. duration = time.time() - start_time
  10. print(f"REST API: 获取了{len(users)}个用户,耗时{duration:.2f}秒")
  11. # 输出: REST API: 获取了1000个用户,耗时10.45秒
复制代码
gRPC 版本:
  1. import grpc
  2. import user_pb2
  3. import user_pb2_grpc
  4. import time
  5. start_time = time.time()
  6. with grpc.insecure_channel('api.example.com:50051') as channel:
  7.     stub = user_pb2_grpc.UserServiceStub(channel)
  8.    
  9.     # 一次请求获取所有用户,批量处理
  10.     users = list(stub.GetUsers(user_pb2.GetUsersRequest(limit=1000)))
  11. duration = time.time() - start_time
  12. print(f"gRPC: 获取了{len(users)}个用户,耗时{duration:.2f}秒")
  13. # 输出: gRPC: 获取了1000个用户,耗时1.23秒
复制代码
总结:gRPC 更快,由于它:

  • 支持毗连复用(不消每次都重新连)
  • 使用 Protobuf,数据更轻更快
  • 流式处置惩罚,批量服从高
六、错误处置惩罚方式对比

REST 错误处置惩罚:

服务端返回的错误:
  1. {
  2.   "error": {
  3.     "code": 404,
  4.     "message": "User not found",
  5.     "details": "The user with ID 12345 does not exist"
  6.   }
  7. }
复制代码
客户端处置惩罚:
  1. fetch('/api/users/12345')
  2.   .then(response => {
  3.     if (!response.ok) {
  4.       return response.json().then(err => {
  5.         throw new Error(`${err.error.message}: ${err.error.details}`);
  6.       });
  7.     }
  8.     return response.json();
  9.   })
  10.   .catch(error => console.error('错误:', error));
复制代码
靠 HTTP 状态码,但格式不统一,必要手动剖析。
gRPC 错误处置惩罚:

服务端定义错误:
  1. def GetUser(self, request, context):
  2.     user = database.find_user(request.user_id)
  3.     if not user:
  4.         context.set_code(grpc.StatusCode.NOT_FOUND)
  5.         context.set_details(f"找不到用户 {request.user_id}")
  6.         return user_pb2.UserProfile()  # 返回空对象
  7.     return user
复制代码
客户端处置惩罚错误:
  1. try:
  2.     response = stub.GetUser(request)
  3.     print(f"用户信息: {response}")
  4. except grpc.RpcError as e:
  5.     if e.code() == grpc.StatusCode.NOT_FOUND:
  6.         print(f"错误: 用户不存在 - {e.details()}")
  7.     else:
  8.         print(f"RPC错误: {e.code()} - {e.details()}")
复制代码
标准的错误码 + 描述,客户端可以直接 catch。像处置惩罚本地异常一样方便!
七、实际应用场景选择

什么时候用传统REST API?


  • 前端直接调API
    1. // 浏览器调REST API就很方便
    2. fetch('/api/products')
    3.   .then(res => res.json())
    4.   .then(products => console.log(products));
    复制代码
  • 接第三方平台 好比接微信支付、支付宝API,人家都是REST的,你也得跟着来
  • 简单系统 小项目不寻求性能,REST开发速度快
什么时候用gRPC?


  • 微服务内部通信 服务多了,内部调用频繁,用gRPC又快又稳
  • 实时数据应用
    1. // 股票价格实时推送
    2. func (s *StockServer) PriceStream(request *pb.StockRequest, stream pb.StockService_PriceStreamServer) error {
    3.   for {
    4.     price := getLatestPrice(request.Symbol)
    5.     stream.Send(&pb.StockPrice{
    6.       Symbol: request.Symbol,
    7.       Price: price,
    8.       Timestamp: time.Now().Unix(),
    9.     })
    10.     time.Sleep(1 * time.Second)
    11.   }
    12. }
    复制代码
  • 移动端应用 手机流量金贵,gRPC数据小,省流量
  • 多语言系统 Python服务调Go服务,Java服务调C#服务,都不是问题
八、总结一句话

REST API就像平凡话,各人都听得懂;gRPC像高速公路,固然有门槛,但一旦上了路就飞快!
如果你在做面向平凡用户的接口,或者简单系统,REST API足够了。
但如果你在构建微服务、必要高性能、多语言、流式处置惩罚能力,那就果断上gRPC!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

科技颠覆者

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表