网易首页 > 网易号 > 正文 申请入驻

【性能】性能比较:REST vs gRPC vs 异步通信

0
分享至

  微服务之间的通信方式对微服务架构内的各种软件质量因素有重大影响(有关微服务网络内通信的关键作用的更多信息)。沟通方式会影响软件的性能和效率等功能性需求,以及可变性、可扩展性和可维护性等非功能性需求。因此,有必要考虑不同方法的所有优缺点,以便在具体用例中合理选择正确的沟通方式。
本文比较了以下样式:REST、gRPC 和使用消息代理 (RabbitMQ) 的异步通信,在微服务网络中了解它们对软件的性能影响。沟通方式的一些最重要的属性(反过来会影响整体表现)是:

  数据传输格式

  连接处理

  消息序列化

  缓存

  负载均衡

  数据传输格式

  
虽然使用 AMQP 协议(高级消息队列协议)的异步通信和 gRPC 通信使用二进制协议进行数据传输,但 REST-API 通常以文本格式传输数据。与基于文本的协议相比,二进制协议的效率要高得多 [1,2]。因此,使用 gRPC 和 AMQP 进行通信会导致较低的网络负载,而使用 REST API 时可以预期更高的网络负载。

  连接处理

  
REST-API 通常建立在 HTTP/1.1 协议之上,而 gRPC 依赖于 HTTP/2 协议的使用。HTTP/1.1、HTTP/2 以及 AMQP 都在传输层使用 TCP 来确保稳定的连接。要建立这样的连接,需要在客户端和服务器之间进行详细的通信。这些性能影响同样适用于所有沟通方式。但是,对于 AMQP 或 HTTP/2 连接,通信连接的初始建立只需要执行一次,因为这两种协议的请求都可以多路复用。这意味着可以将现有连接重用于使用异步或 gRPC 通信的后续请求。另一方面,使用 HTTP/1.1 的 REST-API 为与远程服务器的每个请求建立新连接。

  Necessary communication to establish a TCP-Connection

  消息序列化

  
通常,在通过网络传输消息之前,使用 JSON 执行 REST 和异步通信以进行消息序列化。另一方面,gRPC 默认以协议缓冲区格式传输数据。协议缓冲区通过允许使用更高级的序列化和反序列化方法来编码和使用消息内容 [1] 来提高通信速度。然而,选择正确的消息序列化格式取决于工程师。关于性能,protocol buffers 有很多优势,但是当必须调试微服务之间的通信时,依赖人类可读的 JSON 格式可能是更好的选择。

  
缓存

  
有效的缓存策略可以显着减少服务器的负载和必要的计算资源。由于其架构,REST-API 是唯一允许有效缓存的通信方式。REST-API 响应可以被其他服务器和缓存代理(如 Varnish)缓存和复制。这减少了 REST 服务的负载并允许处理大量的 HTTP 流量 [1]。但是,这只有在基础架构上部署更多服务(缓存代理)或使用第三方集成后才有可能。gRPC 官方文档和 RabbitMQ 文档都没有介绍任何形式的缓存。

  
负载均衡

  
除了临时存储响应之外,还有其他技术可以提高服务速度。负载均衡器(例如 mod_proxy)可以高效透明的方式在服务之间分配 HTTP 流量 [1]。这可以实现使用 REST API 的服务的水平扩展。Kubernetes 作为容器编排解决方案,无需任何调整即可对 HTTP/1.1 流量进行负载均衡。另一方面,对于 gRPC,需要在网络上提供另一个服务(linkerd)[3]。异步通信无需进一步的帮助即可支持负载平衡。消息代理本身扮演负载均衡器的角色,因为它能够将请求分发到同一服务的多个实例。消息代理为此目的进行了优化,并且它们的设计已经考虑到它们必须具有特别可扩展性的事实[1]。

  
实验

  
为了能够评估各个通信方法对软件质量特性的影响,开发了四个微服务来模拟电子商务平台的订单场景。

  微服务部署在由三个不同服务器组成的自托管 Kubernetes 集群上。服务器通过千兆 (1000 Mbit/s) 网络连接,位于同一数据中心,服务器之间的平均延迟为 0.15 毫秒。每次实验运行时,各个服务都部署在相同的服务器上。这种行为是通过 pod 亲和性来实现的。
所有微服务都是用 GO 编程语言实现的。个别服务的实际业务逻辑,例如与数据库的通信,为了不被选择的通信方法之外的其他影响,故意不实现。因此,收集的结果不能代表这种类型的微服务架构,但可以使实验中的通信方法具有可比性。相反,业务逻辑的实现是通过将程序流程延迟 100 毫秒来模拟的。因此,在通信中,总延迟为 400 毫秒。
开源软件k6用于实现负载测试。

  
实现

  
Golang 标准库中包含的 net/http 模块用于提供 REST 接口。使用标准库中也包含的 encoding/json 模块对请求进行序列化和反序列化。所有请求都使用 HTTP POST 方法。
“谈话很便宜。给我看看密码。”

  package main

  import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"

  "github.com/google/uuid"
"gitlab.com/timbastin/bachelorarbeit/common"
"gitlab.com/timbastin/bachelorarbeit/config"
)

  type restServer struct {
httpClient http.Client
}

  func (server *restServer) handler(res http.ResponseWriter, req *http.Request) {
// only allow post request.
if req.Method != http.MethodPost {
bytes, _ := json.Marshal(map[string]string{
"error": "invalid request method",
})
http.Error(res, string(bytes), http.StatusBadRequest)
return
}

  reqId := uuid.NewString()

  // STEP 1 / 4
log.Println("(REST) received new order", reqId)

  var submitOrderDTO common.SubmitOrderRequestDTO

  b, _ := ioutil.ReadAll(req.Body)

  err := json.Unmarshal(b, &submitOrderDTO)
if err != nil {
log.Fatalf(err.Error())
}

  checkIfInStock(1)

  invoiceRequest, _ := http.NewRequest(http.MethodPost,
fmt.Sprintf("%s/invoices", config.MustGet("customerservice.rest.address").
(string)), bytes.NewReader(b))
// STEP 2
r, err := server.httpClient.Do(invoiceRequest)
// just close the response body
r.Body.Close()
if err != nil {
panic(err)
}

  shippingRequest, _ := http.NewRequest(http.MethodPost,
fmt.Sprintf("%s/shipping-jobs", config.MustGet("shippingservice.rest.address").
(string)), bytes.NewReader(b))

  // STEP 3
r, err = server.httpClient.Do(shippingRequest)
// just close the response body
r.Body.Close()
if err != nil {
panic(err)
}

  handleProductDecrement(1)
// STEP 5
res.WriteHeader(201)
res.Write(common.NewJsonResponse(map[string]string{
"state": "success",
}))
}

  func startRestServer() {
server := restServer{
httpClient: http.Client{},
}
http.HandleFunc("/orders", server.handler)
done := make(chan int)
go http.ListenAndServe(config.MustGet("orderservice.rest.port").(string), nil)
log.Println("started rest server")
<-done
}

  RabbitMQ 消息代理用于异步通信,部署在同一个 Kubernetes 集群上。消息代理和各个微服务之间的通信使用 github.com/spreadway/amqp 库进行。该库是 GO 编程语言官方文档推荐的。

  
package main

  import (
"encoding/json"
"log"

  "github.com/streadway/amqp"
"gitlab.com/timbastin/bachelorarbeit/common"
"gitlab.com/timbastin/bachelorarbeit/config"
"gitlab.com/timbastin/bachelorarbeit/utils"
)

  func handleMsg(message amqp.Delivery, ch *amqp.Channel) {
log.Println("(AMQP) received new order")
var submitOrderRequest common.SubmitOrderRequestDTO
err := json.Unmarshal(message.Body, &submitOrderRequest)
utils.FailOnError(err, "could not unmarshal message")

  checkIfInStock(1)

  handleProductDecrement(1)
ch.Publish(config.MustGet("amqp.billingRequestExchangeName").(string), "",
false, false, amqp.Publishing{
ContentType: "application/json",
Body: message.Body,
})

  func getNewOrderChannel(conn *amqp.Connection) (*amqp.Channel, string) {
ch, err := conn.Channel()
utils.FailOnError(err, "could not create channel")

  ch.ExchangeDeclare(config.MustGet("amqp.newOrderExchangeName").
(string), "fanout", false, false, false, false, nil)

  queue, err := ch.QueueDeclare(config.MustGet("orderservice.amqp.consumerName").
(string), false, false, false, false, nil)

  utils.FailOnError(err, "could not create queue")

  ch.QueueBind(queue.Name, "", config.MustGet("amqp.newOrderExchangeName").
(string), false, nil)
return ch, queue.Name
}

  func startAmqpServer() {
conn := common.NewAmqpConnection(config.MustGet("amqp.host").(string))
defer conn.Close()

  orderChannel, queueName := getNewOrderChannel(conn)

  msgs, err := orderChannel.Consume(
queueName,
config.MustGet("orderservice.amqp.consumerName").(string),
true,
false,
false,
false,
nil,
)

  utils.FailOnError(err, "could not consume")

  forever := make(chan bool)
log.Println("started amqp server:", queueName)
go func() {
for d := range msgs {
go handleMsg(d, orderChannel)
}
}()
<-forever
}

  gRPC 客户端和服务器使用 gRPC 文档推荐的 google.golang.org/grpc 库。数据的序列化是使用协议缓冲区完成的。

  package main

  import (
"log"
"net"

  "context"

  "gitlab.com/timbastin/bachelorarbeit/common"
"gitlab.com/timbastin/bachelorarbeit/config"
"gitlab.com/timbastin/bachelorarbeit/pb"
"gitlab.com/timbastin/bachelorarbeit/utils"
"google.golang.org/grpc"
)

  type OrderServiceServer struct {
CustomerService pb.CustomerServiceClient
ShippingService pb.ShippingServiceClient
pb.UnimplementedOrderServiceServer
}

  func (s *OrderServiceServer) SubmitOrder(ctx context.Context,
request *pb.SubmitOrderRequest) (*pb.SuccessReply, error) {
log.Println("(GRPC) received new order")
if s.CustomerService == nil {
s.CustomerService, _ = common.NewCustomerServiceClient()
}
if s.ShippingService == nil {
s.ShippingService, _ = common.NewShippingServiceClient()
}

  checkIfInStock(1)

  // call the product service on each iteration to decrement the product.
_, err := s.CustomerService.CreateAndProcessBilling(ctx, &pb.BillingRequest{
BillingInformation: request.BillingInformation,
Products: request.Products,
})

  utils.FailOnError(err, "could not process billing")

  // trigger the shipping job.
_, err = s.ShippingService.CreateShippingJob(ctx, &pb.ShippingJob{
BillingInformation: request.BillingInformation,
Products: request.Products,
})

  utils.FailOnError(err, "could not create shipping job")

  handleProductDecrement(1)

  return &pb.SuccessReply{Success: true}, nil
}

  func startGrpcServer() {
listen, err := net.Listen("tcp", config.MustGet("orderservice.grpc.port").(string))
if err != nil {
log.Fatalf("could not listen: %v", err)
}

  grpcServer := grpc.NewServer()

  orderService := OrderServiceServer{}
// inject the clients into the server
pb.RegisterOrderServiceServer(grpcServer, &orderService)

  // start the server
log.Println("started grpc server")
if err := grpcServer.Serve(listen); err != nil {
log.Fatalf("could not start grpc server: %v", err)
}
}

  收集数据

  
检查成功和失败的订单处理的数量,以确认它们所经过的时间。如果直到确认的持续时间超过 900 毫秒,则订单流程被解释为失败。选择此持续时间是因为在实验中可能会出现无限长的等待时间,尤其是在使用异步通信时。每次试验都会报告失败和成功订单的数量。
每种架构总共进行了 12 次不同的测量,每种情况下同时请求的数量不同,传输的数据量也不同。首先,在低负载下测试每种通信方式,然后在中等负载下,最后在高负载下测试。低负载模拟 10 个,中等负载模拟 100 个,高负载模拟 300 个同时向系统发出的请求。在这六次测试运行之后,要传输的数据量会增加,以了解各个接口的序列化方法的效率。数据量的增加是通过订购多个产品来实现的。

  
结果

  
gRPC API 架构是实验中研究的性能最佳的通信方法。在低负载下,它可以接受的订单数量是使用 REST 接口的系统的 3.41 倍。此外,平均响应时间比 REST-API 低 9.71 毫秒,比 AMQP-API 低 9.37 毫秒。

  本文 https://jiagoushi.pro/performance-comparison-rest-vs-grpc-vs-asynchronous-communication 讨论:知识星球【首席架构师圈】或者加微信小号【cea_csa_cto】或者加QQ群【792862318】 公众号

  【jiagoushipro】
【超级架构师】
精彩图文详解架构方法论,架构实践,技术原理,技术趋势。
我们在等你,赶快扫描关注吧。

  【cea_csa_cto】
50000人社区,讨论:企业架构,云计算,大数据,数据科学,物联网,人工智能,安全,全栈开发,DevOps,数字化.

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
小编挂了多年没人要房子,昨天高价卖掉了!!

小编挂了多年没人要房子,昨天高价卖掉了!!

新浪财经
2026-04-22 09:28:54
白鹿这套“白里透黑”,谁看了不心痒?

白鹿这套“白里透黑”,谁看了不心痒?

飛娱日记
2026-04-22 08:06:06
中超7轮下来!两支不败球队 铜梁龙要感谢前申花阿马杜

中超7轮下来!两支不败球队 铜梁龙要感谢前申花阿马杜

80后体育大蜀黍
2026-04-22 10:24:35
越南一把手很焦虑:和中国一对比,才知道原来我们落后了这么多

越南一把手很焦虑:和中国一对比,才知道原来我们落后了这么多

芳芳历史烩
2026-04-21 00:55:07
台军在2026年4月20日,拍摄到过台湾海峡的解放军海军辽宁号航母

台军在2026年4月20日,拍摄到过台湾海峡的解放军海军辽宁号航母

安安说
2026-04-21 10:54:32
惊天揭秘!中南医院女医护与权色交易的背后真相!

惊天揭秘!中南医院女医护与权色交易的背后真相!

人生录
2026-04-22 11:49:14
美媒:伊朗最高领袖穆杰塔巴已批准同美方进行谈判,白宫20日一整天都在等德黑兰,如出现进展迹象,特朗普也可能同意延长停火期限

美媒:伊朗最高领袖穆杰塔巴已批准同美方进行谈判,白宫20日一整天都在等德黑兰,如出现进展迹象,特朗普也可能同意延长停火期限

极目新闻
2026-04-21 09:44:58
女大学生校园内被一群狗围堵撕咬 全身被咬20多处伤口 学校回应:已组织保安进行处理

女大学生校园内被一群狗围堵撕咬 全身被咬20多处伤口 学校回应:已组织保安进行处理

闪电新闻
2026-04-21 22:10:35
商家发视频吐槽:景区人满为患,却无人消费,评论区全是人间清醒

商家发视频吐槽:景区人满为患,却无人消费,评论区全是人间清醒

谭谈社会
2026-04-21 10:12:09
爆冷1-1!布朗空砍36+7,塔图姆准三双,探花秀爆发,绿军被高估

爆冷1-1!布朗空砍36+7,塔图姆准三双,探花秀爆发,绿军被高估

篮球扫地僧
2026-04-22 10:03:17
兰姐终于破防!不聊汪宝儿真相扎心,外人听了都心寒!

兰姐终于破防!不聊汪宝儿真相扎心,外人听了都心寒!

乐悠悠娱乐
2026-04-22 11:12:44
不到24小时,日本面临双重麻烦,高市陷绝境:连个能帮忙的都没有

不到24小时,日本面临双重麻烦,高市陷绝境:连个能帮忙的都没有

椰青美食分享
2026-04-21 17:45:03
小伙80万元开面馆十几天倒闭,两年后40万开包子店,日营业额过万正筹备第二家门店

小伙80万元开面馆十几天倒闭,两年后40万开包子店,日营业额过万正筹备第二家门店

极目新闻
2026-04-21 13:34:19
中方禁令为何等100天才爆发?日本媒体瞒不住了,真相太现实

中方禁令为何等100天才爆发?日本媒体瞒不住了,真相太现实

国际阿尝
2026-04-22 10:45:27
人有没有钱,一看便知:没钱的子女,大多有3大特质、3大穷习惯

人有没有钱,一看便知:没钱的子女,大多有3大特质、3大穷习惯

第一桶金学派
2025-06-30 10:18:46
融入城市,麦克托米奈在自家花园里放了一尊马拉多纳的雕像

融入城市,麦克托米奈在自家花园里放了一尊马拉多纳的雕像

懂球帝
2026-04-22 10:01:02
若没有此人,中国历史或将改写!晚年离休后享受什么待遇?

若没有此人,中国历史或将改写!晚年离休后享受什么待遇?

史之铭
2026-04-19 15:49:20
“被忽视”的豪华SUV,从21.98万一口气降到12万多,可惜无人识货

“被忽视”的豪华SUV,从21.98万一口气降到12万多,可惜无人识货

隔壁说车老王
2026-04-22 08:14:09
刚刚,直线爆拉!688048,20cm涨停

刚刚,直线爆拉!688048,20cm涨停

中国基金报
2026-04-22 10:46:16
38.6亿落槌:王健林欠的钱,已经让永辉快撑不住了

38.6亿落槌:王健林欠的钱,已经让永辉快撑不住了

BT财经
2026-04-22 07:00:03
2026-04-22 13:15:00
架构师研究会
架构师研究会
全网【架构师研究会】大数据,云,物联网,智能计算
1240文章数 4333关注度
往期回顾 全部

科技要闻

凌晨突发!ChatGPT Images 2.0发布

头条要闻

KTV服务员被指强奸14岁女生 官方通报

头条要闻

KTV服务员被指强奸14岁女生 官方通报

体育要闻

一到NBA季后赛,四届DPOY就成了主角

娱乐要闻

复婚无望!baby黄晓明陪小海绵零交流

财经要闻

伊朗拒绝出席 特朗普宣布延长停火期限

汽车要闻

四款全球首秀+AI落地 大众汽车集团在华转型全面提速

态度原创

亲子
教育
房产
手机
公开课

亲子要闻

管不住,一身的反骨

教育要闻

2026 成都新初高中大爆发!七中 、 树德、 西川、北二外领衔,9 月集体亮相!

房产要闻

狂抢284轮!中海海口再拿重磅宅地!

手机要闻

小米卢伟冰:REDMI K90 Max官方300元的内存补贴会尽量延续

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版