Go HTTP 服务器性能问题

2023-12-26

我正在编写一个事件收集器http 服务器,该服务器将承受重负载。因此,在 http 处理程序中,我只是反序列化事件,然后在 goroutine 中的 http 请求-响应周期之外运行实际处理。

由此,我发现如果我以每秒 400 个请求的速度访问服务器,则 99% 的延迟将低于 20 毫秒。但一旦我将请求速率提高到每秒 500 个,延迟就会飙升至超过 800 毫秒。

谁能帮我提供一些关于原因的想法,以便我可以探索更多。

package controller

import (
    "net/http"
    "encoding/json"
    "event-server/service"
    "time"
)

func CollectEvent() http.Handler {
    handleFunc := func(w http.ResponseWriter, r *http.Request) {
        startTime := time.Now()
        stats.Incr("TotalHttpRequests", nil, 1)
        decoder := json.NewDecoder(r.Body)
        var event service.Event
        err := decoder.Decode(&event)
        if err != nil {
            http.Error(w, "Invalid json: " + err.Error(), http.StatusBadRequest)
            return
        }
        go service.Collect(&event)
        w.Write([]byte("Accepted"))
        stats.Timing("HttpResponseDuration", time.Since(startTime), nil, 1)
    }

    return http.HandlerFunc(handleFunc)
}

我以每秒 1000 个请求进行了测试并对其进行了分析。以下是结果。

(pprof) top20
Showing nodes accounting for 3.97s, 90.85% of 4.37s total
Dropped 89 nodes (cum <= 0.02s)
Showing top 20 nodes out of 162
      flat  flat%   sum%        cum   cum%
     0.72s 16.48% 16.48%      0.72s 16.48%  runtime.mach_semaphore_signal
     0.65s 14.87% 31.35%      0.66s 15.10%  syscall.Syscall
     0.54s 12.36% 43.71%      0.54s 12.36%  runtime.usleep
     0.46s 10.53% 54.23%      0.46s 10.53%  runtime.cgocall
     0.34s  7.78% 62.01%      0.34s  7.78%  runtime.mach_semaphore_wait
     0.33s  7.55% 69.57%      0.33s  7.55%  runtime.kevent
     0.30s  6.86% 76.43%      0.30s  6.86%  syscall.RawSyscall
     0.10s  2.29% 78.72%      0.10s  2.29%          runtime.mach_semaphore_timedwait
     0.07s  1.60% 80.32%      1.25s 28.60%  net.dialSingle
     0.06s  1.37% 81.69%      0.11s  2.52%  runtime.notetsleep
     0.06s  1.37% 83.07%      0.06s  1.37%  runtime.scanobject
     0.06s  1.37% 84.44%      0.06s  1.37%  syscall.Syscall6
     0.05s  1.14% 85.58%      0.05s  1.14%  internal/poll.convertErr
     0.05s  1.14% 86.73%      0.05s  1.14%  runtime.memmove
     0.05s  1.14% 87.87%      0.05s  1.14%  runtime.step
     0.04s  0.92% 88.79%      0.09s  2.06%  runtime.mallocgc
     0.03s  0.69% 89.47%      0.58s 13.27%  net.(*netFD).connect
     0.02s  0.46% 89.93%      0.40s  9.15%  net.sysSocket
     0.02s  0.46% 90.39%      0.03s  0.69%  net/http.(*Transport).getIdleConn
     0.02s  0.46% 90.85%      0.13s  2.97%  runtime.gentraceback
(pprof) top --cum
Showing nodes accounting for 70ms, 1.60% of 4370ms total
Dropped 89 nodes (cum <= 21.85ms)
Showing top 10 nodes out of 162
      flat  flat%   sum%        cum   cum%
         0     0%     0%     1320ms 30.21%  net/http.(*Transport).getConn.func4
         0     0%     0%     1310ms 29.98%  net.(*Dialer).Dial
         0     0%     0%     1310ms 29.98%  net.(*Dialer).Dial-fm
         0     0%     0%     1310ms 29.98%  net.(*Dialer).DialContext
         0     0%     0%     1310ms 29.98%  net/http.(*Transport).dial
         0     0%     0%     1310ms 29.98%  net/http.(*Transport).dialConn
         0     0%     0%     1250ms 28.60%  net.dialSerial
      70ms  1.60%  1.60%     1250ms 28.60%  net.dialSingle
         0     0%  1.60%     1170ms 26.77%  net.dialTCP
         0     0%  1.60%     1170ms 26.77%  net.doDialTCP
(pprof) 

问题

我正在使用另一个 goroutine,因为我不希望处理发生在 http 请求-响应周期中。

这是一个常见的谬误(因此也是陷阱)。推理思路似乎是合理的:您正在尝试“在其他地方”处理请求,以尝试 尽快处理入口 HTTP 请求。

问题是“其他地方”仍然是一些代码同时运行以及其余的请求处理流程。 因此,如果该代码运行slower比入口请求的速率, 你的处理 goroutine 会堆积起来,基本上会耗尽一个或 更多资源。具体取决于实际处理: 如果它受 CPU 限制,则会造成 CPU 的自然争用 在所有这些之间GOMAXPROCS执行的硬件线程; 如果它绑定到网络 I/O,它将在 Go 运行时调度程序上创建负载,该调度程序必须划分其可用的执行量子 在所有那些想要被执行的 goroutine 之间; 如果它绑定到磁盘 I/O 或其他系统调用,您将拥有 创建的操作系统线程激增,等等……

本质上,you are queueing工作单位转换自 入口 HTTP 请求,但是队列不能解决过载问题。它们可能被用来吸收过载的短尖峰, 但这仅在此类尖峰被周期“包围”时才有效 负载至少略低于您提供的最大容量 系统。 您排队的事实在您的案例中并没有直接看到,但它是 在那里,它通过迫使你的系统超越其自然状态而表现出来 容量——你的“队列”开始无限增长。

请阅读这篇经典文章 https://ferd.ca/queues-don-t-fix-overload.html仔细了解为什么你的方法行不通 在现实的生产环境中工作。 密切注意厨房水槽的那些照片。

该怎么办?

不幸的是,几乎不可能给出简单的解决方案 因为我们不会在您的设置中使用您的代码来处理您的工作负载。 尽管如此,这里还是有几个值得探索的方向。

在最广泛的范围内,试着看看你是否有一些容易 您当前无法看到的系统中明显的瓶颈。 例如,如果所有这些并发工作协程最终 与 RDBM 实例对话,其磁盘 I/O 可能很容易连载所有那些只会等待轮到他们的 goroutine 他们的数据被接受。 瓶颈可能更简单——比如,在每个工作协程中 您在持有锁时不小心执行了一些长时间运行的操作 被所有这​​些 goroutine 竞争; 这显然将它们全部序列化。

下一步将是实际measure(我的意思是,通过编写基准) 单个工人需要多少时间才能完成其工作单元。 然后你需要测量当增加时这个数字如何变化 并发因素。 收集这些数据后,您将能够做到 有根据的预测实际的评价您的系统 能够处理请求。

下一步是仔细考虑制作系统的策略 满足这些计算出的期望。通常这意味着限制速率 的入口请求。有不同的方法可以实现这一目标。 看着golang.org/x/time/rate https://godoc.org/golang.org/x/time/rate基于时间的速率限制器,但可以从技术含量较低的开始 诸如使用缓冲通道作为计数信号量等方法。 超出您能力范围的请求可能会被拒绝 (通常带有 HTTP 状态代码 429,请参阅this https://www.rfc-editor.org/rfc/rfc6585#section-4)。 你也可以考虑短暂排队,但我只会尝试这个 充当馅饼上的樱桃——也就是说,当你拥有其余的时候 完全整理出来了。

如何处理被拒绝的请求取决于您的情况 环境。通常,您会尝试通过部署更多内容来“水平扩展” 不仅仅是一项服务来处理您的请求并指导您的客户 切换可用的服务。 (我要强调的是,这意味着几个独立的服务——如果它们都共享一些收集数据的目标接收器 他们的数据,他们可能受到该接收器的最终容量的限制, 添加更多系统不会给你带来任何好处。)

让我再说一遍,一般问题没有神奇的解决方案: 如果你的完整系统(使用你正在编写的这个 HTTPservice) 只是它的前端、网关、部分)只能处理N负载的RPS, 没有任何散射go processRequest()将会成功 更快地处理请求。 Go 提供的简单并发并不是 A银子弹 https://en.wikipedia.org/wiki/No_Silver_Bullet, 这是一把机枪。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go HTTP 服务器性能问题 的相关文章

  • 如何提高 Field.set 的性能(也许使用 MethodHandles)?

    我正在编写一些调用的代码Field set https docs oracle com en java javase 11 docs api java base java lang reflect Field html set java l
  • Chrome 开发者工具中“网络”选项卡中的“连接”是什么意思?为什么它仅在某些网站上显示?

    我一直试图在网上寻找解释 但似乎找不到 如果您在 Chrome 上访问像 youtube com 这样的网站 并将鼠标悬停在与文件名 http www youtube com 相对应的蓝色条上 您会看到四种不同的内容 阻塞 Sending
  • 投射回更专业的界面

    我正在用 Go 编写一个游戏 在 C 中 我将所有实体类存储在 BaseEntity 类的数组中 如果一个实体需要在世界中移动 那么它将是一个从 BaseEntity 派生的 PhysEntity 但添加了方法 我尝试模仿这是 go pac
  • nsq 无法通过连接到 nsqlookupd 来消费消息

    我尝试使用 docker compose 来运行 nsq docker compose yml如下 version 3 services nsqlookupd image nsqio nsq command nsqlookupd ports
  • 样式组件如何影响性能?

    使用样式组件是否比样式表更会降低 Web 应用程序的速度 如果我关心性能并且没有任何依赖于 props 的样式 我是否应该放弃样式组件并使用样式表 当您有很多小组件时 同时使用样式化组件渲染 性能开销可能会很有意义 绝对值得测试以删除小元素
  • 在 IntelliJ IDEA 中运行。多个文件和错误未定义:数据

    我想使用 IntelliJ IDE 社区版编写代码GO Go语言 我安装了正确的插件 并安装了构建应用程序所需的所有工具 我的应用程序包含以下两个文件 每个都在目录中 事件服务器 Main go Data go 如果我想使用 Run Ctl
  • HTTP部分上传、断点续传的标准方法

    我正在开发 http 客户端 服务器框架 并寻找处理部分上传的正确方法 与使用带有 Range 标头的 GET 方法进行下载相同 但是 HTTP PUT 并不打算恢复 据我所知 PATCH 方法不接受 Range 标头 有没有办法通过 HT
  • STL 容器速度与数组

    我刚刚开始从事一个科学项目 其中速度非常重要 HPC 我目前正在设计数据结构 该项目的核心是双值 3D 网格 以求解偏微分方程 由于这里的速度可能比代码的简单性更重要 我想知道 STL 与通常的 C 风格数组相比如何执行 就我而言 因为它是
  • 空 while 循环有什么影响?

    我知道这可能是一个有点 愚蠢 的问题 但有时 我只想循环直到条件为假 但我不喜欢让循环保持为空 所以代替 Visible true while IsRunning Visible false 我通常prefer while IsRunnin
  • 在处理程序之后访问 HTTP 请求上下文

    在我的日志记录中间件 链中的第一个 中 我需要访问一些在链下游的某些身份验证中间件中编写的上下文 并且仅在处理程序本身执行之后 旁注 需要首先调用日志记录中间件 因为我需要记录请求的持续时间 包括在中间件中花费的时间 此外 当权限不足时 身
  • 在golang中获取TTFB(第一个字节的时间)值

    我正在尝试获取 TTFB 值和 Connect 值 c exec Command curl w Connect time connect TTFB time starttransfer Total time time total o dev
  • 如何在 Golang 中将 []byte XML 转换为 JSON 输出

    有没有办法在 Golang 中将 XML byte 转换为 JSON 输出 我有以下功能body is byte但我想在一些操作之后将此 XML 响应转换为 JSON 我试过了Unmarshal in xml打包没有成功 POST func
  • 是否可以修改 $_SESSION 变量?

    恶意用户是否可以将 SESSION 在 php 中 变量设置为他想要的任何值 很大程度上取决于您的代码 有一点非常明显 SESSION username REQUEST username
  • Golang中如何删除字符串的最后一个字符?

    我想删除字符串的最后一个字符 但在此之前我想检查最后一个字符是否是 如何才能做到这一点 以下是删除尾随加号的几种方法 package main import fmt strings func TrimSuffix s suffix stri
  • 为什么 Android Eclipse 不断刷新外部文件夹并花费很长时间?

    我只有一部新的 Android 手机 我一直在修补一些基本的应用程序 每当我保存任何内容时 Eclipse 的 Android 插件就会刷新外部文件夹 这让我抓狂 通常我不会介意 但当需要 10 秒才能刷新时 我开始注意到 我已经搜索过 其
  • 错误“binary.Write:无效类型”是什么意思?

    下面显示的代码 我创建了一个结构类型并希望将其编码为二进制 但它显示binary Write invalid type main Stu错误 我读过一些类似的代码 但我找不到为什么我的代码不起作用 type Stu struct Name
  • 如何有效地从 DB2 表中删除所有行

    我有一个大约有 50 万行的表 我想删除所有行 如果我做简单的delete from tbl 事务日志已满 我不关心这种情况下的事务 无论如何我都不想回滚 我可以删除许多事务中的行 但是有更好的方法吗 如何有效地从 DB2 中的表中删除所有
  • 磁盘寻道时间测量方法

    我编写了一个脚本来测量 HDD 上的寻道时间 并且其完成方式的微小变化会导致显着不同的时间 第一个周期在磁盘开头的区域内进行跳转 第二个周期选择磁盘上执行查找的随机区域 相同大小 这种方法显然不同 但我不明白为什么它会改变结果 请注意 对于
  • Go 的范围不能超过 (类型接口 {})

    我正处于尝试将我的注意力集中在 Go 上的婴儿阶段 目前 我正在模拟一个 API 请求 该请求返回包含对象数组的 JSON 格式的字符串 我试图找出迭代每个记录并访问每个字段的最合适的方法 最终 每个字段都将写入 Excel 电子表格 但现
  • 从 HTTP 登录到 HTTPS

    我的网站默认使用 HTTP 我确实有一个启用 HTTPS 的证书 但只有其上的某些区域强制建立安全连接 登录是通过 Ajax 处理的 我想开始使用 SSL 即使请求来自 HTTP 我尝试强制请求的地址具有 HTTPS 并且它完美地回复 然而

随机推荐

  • 无法使用react js将文件上传到djangorest框架

    我正在使用 React Js 将图像上传到 django Restframework 在这里我使用 fetch API 发送 post 请求 Eapp jsx import React Component from react class
  • 过滤至少有两个模式匹配的地方

    我的 data table 中有很多文本数据 我有几个我感兴趣的文本模式 我想对表格进行子集化 以便它显示匹配的文本至少两个的模式 由于某些模式已经是非此即彼的事实 这使得情况变得更加复杂 例如 paul john 我想我要么想要一个表示在
  • 如何将当前行的值除以下一行的值?

    在 Spark Sql 1 6 版本中 使用DataFrames 有没有一种方法可以计算特定列的每一行当前行与下一行相除的分数 例如 如果我有一个只有一列的表 如下所示 Age 100 50 20 4 我想要以下输出 Franction 2
  • 如何显示下一张/上一张卡片的RecyclerView的一部分

    实现此功能的最佳策略是什么 我有一个带有卡片的水平 RecyclerView 每张卡片都会填满整个屏幕 但我希望它显示下一张卡片和上一张卡片 如果它有多个项目 的一部分 我知道我可以通过设置我的卡来实现这一点android layout w
  • 从java中的String中删除除少数特定标签之外的Html标签

    我的输入是纯文本字符串 要求是删除除少数特定标签之外的所有 html 标签 例如 p li u u li li 如果这些特定标签具有类似属性class or id 我想删除这些属性 几个例子 a href Link a gt Link li
  • Hadoop 2.2.0 与 Mahout 0.8 兼容吗?

    我的 hadoop 集群版本 2 2 0 与 mahout 0 8 一起运行 它兼容吗 因为每当我运行这个命令时 bin mahout recommenditembased input mydata dat usersFile user d
  • Google 云端硬盘帮助需要访问自己的云端硬盘帐户

    我想在网页上访问我自己的谷歌驱动器 但允许任何人上传文件并限制下载访问或仅向用户显示有限的文件以供下载 Drive API 假设我将访问其他用户的凭据 但我想要的恰恰相反 任何人都可以查看我的文件 限制查看内容 但可以自由上传 我googl
  • 为什么不能修改 Mongoose 查询返回的数据(例如:findById)

    当我尝试更改 Mongoose 查询返回的数据的任何部分时 它没有任何效果 昨天我花了大约两个小时试图解决这个问题 有各种各样的问题 clone s 使用临时存储变量等等 最后 就在我以为自己要疯了的时候 我找到了解决办法 所以我想将来有人
  • GWT:如何从 RootPanel 获取对按钮的引用?

    我正在使用 GWT 2 4 在我的 onModuleLoad 方法中 给定一个字符串 id 如何从 RootPanel 对象获取对页面上现有按钮的引用 我正在尝试这个 public void onModuleLoad final Butto
  • 存储一个正整数需要多少位?

    存储一个正整数 例如数十亿 需要多少位 您是否必须使用 log2 N 才能找到答案 由于我多次看到错误报告的答案 我想我会发布正确的答案 表示正整数 n 所需的位数为 bits floor log2 n 1 其中 log2 表示以 2 为底
  • Fabric JS:鼠标按下时复制/粘贴对象

    我正在尝试创建一个块游戏 您可以从菜单中选择形状并将它们放置在画布上 有一个形状菜单 您可以将形状拖到画布上 我希望它在将克隆拖到画布上时将主要形状保留在菜单中 这可能吗 我创建了一个 jsfiddle 来提供帮助 JSFIDDLE htt
  • gfortran 不适用于 Mac OS X 10.9

    我将 Mac 更新到 OS X 10 9 GM 然后发现 gfortran 不起作用 构建任何程序时 它都会显示 ld library not found for lcrt1 10 5 o collect2 ld return 1 有谁知道
  • ReactJS - 即使从选择下拉列表中选择相同的选项也会触发事件

    当从 ReactJS 的下拉列表中选择一个选项时 如何触发事件 目前我正在使用onChange但即使再次选择相同的选项 我也需要触发一个事件 当前代码
  • java.net.ConnectException

    我看过一些关于这个主题的帖子 但我仍然不知道出了什么问题 以下是代码 import java sql public class SQL public static void main String args Connection conn
  • 从IP地址获取位置[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话
  • AttributeError:“float”对象没有属性“split”

    我正在调用这条线 lang modifiers keyw strip for keyw in row language modifiers split if not isinstance row language modifiers flo
  • android.view.Surface - OutOfResourcesException

    我有这个奇怪的错误 但没有找到任何可能的解决方案 在使用我的应用程序后 问题总是随机出现 该应用程序几乎可以在所有设备上完美运行 仍然存在此问题的设备之一正在运行 CM 7 1 0 我知道很多 CM7 用户都在抱怨类似的问题 不幸的是 我有
  • 如何有条件地在 JSP 页面中显示一个 div 的内容而不是另一个 div 的内容?

    我对JSP开发很陌生 我有以下疑问 如果进入 JSP 页面我有 2div像这样 div p SUCCESS p div div p FAILURE p div 我必须根据 a 的值仅显示这些 div 之一status变量放入Http会话只能
  • 比较文件内字母顺序的最佳方法?

    我有一个文件 其中有很多字母序列 其中一些序列可能是相同的 所以我想对它们进行全部比较 我正在做这样的事情 但这并不完全是我想要的 for line in fl line line split for elem in line if gt
  • Go HTTP 服务器性能问题

    我正在编写一个事件收集器http 服务器 该服务器将承受重负载 因此 在 http 处理程序中 我只是反序列化事件 然后在 goroutine 中的 http 请求 响应周期之外运行实际处理 由此 我发现如果我以每秒 400 个请求的速度访