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 服务器性能问题 的相关文章

  • 预填充 UICollectionView 单元重用队列

    问题 我有一个应用程序 只有一个UICollectionView我第一次滚动它时很卡顿 我已将来源范围缩小到正在创建新单元格 2 的事实 使用initWithFrame 因为周围没有可以重复使用的细胞 初始滚动后 重用队列不为空 单元格可以
  • mclapply 用户时间大于已用时间

    我正在尝试使用mclapply的功能parallel封装在R 该函数通过计算对数似然距离将值分配给序列矩阵 这是一个 CPU 密集型操作 所结果的system time价值观令人困惑 gt system time mclapply work
  • 对于较大的请求正文,Nginx proxy_cache_key $request_body 将被忽略

    我使用 nginx 作为反向代理 我希望它能够缓存 POST 请求 我的后端已正确配置为为 POST 请求返回适当的缓存控制标头 在 nginx 中我配置了 proxy cache methods POST proxy cache key
  • Angular2:动态同步http请求

    Goal 发出一系列同步 http 请求并能够将它们作为一个可观察流进行订阅 示例 不工作 let query arr test1 test2 test3 function make request query arr if query a
  • 如何加快 Java VM (JVM) 的启动时间?

    我正在运行启动多个 JVM 进程的测试 与 JVM 内运行的实际测试时间相比 JVM 的总结启动时间非常重要 我怎样才能加快速度 我已经使用了 client 选项 这确实有帮助 但没有我想要的那么多 还有其他方法吗 比如预加载一堆 JVM
  • 如何让 HttpClient 返回状态码和响应正文?

    我试图让 Apache HttpClient 触发 HTTP 请求 然后显示 HTTP 响应代码 200 404 500 等 以及 HTTP 响应正文 文本字符串 重要的是要注意我正在使用v4 2 2因为大多数 HttpClient 示例都
  • Node.js 上的 Connect 出现“无法 GET /”

    我正在尝试使用以下方式开始提供一些静态网页connect像这样 var connect require connect var nowjs require now var io require socket io var app conne
  • 每个存储桶的最大沙发底座视图数

    假设存储桶中有大量数据 gt 100GB gt 100M 文档 gt 12 种文档类型 并且假设每个视图仅适用于一种文档类型 那么每个存储桶有多少视图就太多了 或者以另一种方式问 在什么时候应该将某些文档类型拆分到单独的存储桶中 以节省处理
  • 在 HTTP PATCH 请求中包含数据的正确方法

    当我组合 HTTP PATCH 请求时 可以选择哪些选项来包含 URL 参数之外的数据 以下任何一项都有效吗 最常见的选择是什么 多部分 表单数据 应用程序 x www form urlencoded Raw JSON 还有其他的吗 HTT
  • 当语料库有100亿个独特的DNA序列时,如何使用BK树实现快速模糊搜索引擎?

    我正在尝试使用BK tree https news ycombinator com item id 14022424python 中的数据结构 用于存储约 100 亿个条目的语料库 1e10 以实现快速模糊搜索引擎 一旦我添加超过 1000
  • CoreAnimation 性能分析 - CAReplicatorLayer 与 CAShapeLayer

    我正在制作一个依赖 CoreAnimation 的应用程序 它有一个 CAReplicatorLayer 和一个 CAShapeLayer 作为子层 当进行 12 次复制 然后对路径进行动画处理 在 touchMoved 上更改它 时 一旦
  • 用 OpenCL C 编写快速线性系统求解器

    我正在编写一个 OpenCL 内核 它将涉及求解线性系统 目前我的内核太慢了 提高线性系统部分的性能似乎是一个不错的起点 我还应该注意 我并没有尝试使我的线性求解器并行 我正在研究的问题在宏观层面上已经是令人尴尬的并行 以下是我编写的 C
  • 在防火墙后面使用 GitHub,无需 SSH 访问

    我真的很想使用 GitHub 但我的公司一切都被锁定了 现在 我只能通过HTTP协议使用Tortoise SVN 我可以以同样的方式使用 GitHub 吗 如果是这样 怎么办 我认为你一直能够克隆github https github co
  • PHP 错误请求中 HTTP 请求失败

    我可以直接从浏览器请求 URL Web 服务 WS 但是当我在代码中使用 file get contents 或 fopen 方法时 我收到一条错误消息 有人有不使用curl的解决方案吗 public function sendHttpRe
  • Java中精确的时间测量

    Java 提供了两种获取当前时间的方法 System nanoTime and System currentTimeMillis 第一个给出的结果以纳秒为单位 但实际精度比这要差得多 许多微秒 JVM 是否已经为每台特定机器提供了最佳的价值
  • 为什么我的代码在编译用于分析 (-pg) 时在多线程下运行比在单线程下运行慢?

    我正在写一个光线追踪器 最近 我在程序中添加了线程 以利用 i5 四核上的附加内核 奇怪的是 应用程序的调试版本现在运行速度变慢 但优化后的构建运行速度比添加线程之前更快 我将 g pg 标志传递给 gcc 以进行调试构建 并将 O3 标志
  • 使用 HTTP-Basic 身份验证发出 HTTP GET 请求

    我需要为我正在开发的 Flash Player 项目构建一个代理 我只需要使用 HTTP Basic 身份验证向另一个 URL 发出 HTTP GET 请求 并提供来自 PHP 的响应 就好像 PHP 文件是原始源一样 我怎样才能做到这一点
  • 与简单的文件请求相比,您预计 Web 服务请求的响应时间开销是多少?

    我正在开发一个 asp net Web 服务应用程序 以向使用 jQuery ajax 发出请求的小部件提供 json 格式的数据 我一直在使用 FireBug Net 视图来检查数据请求需要多长时间 在我最初的原型中 我只是请求静态 js
  • 如何在 PHP 数组中的另一个已知(通过键或指针)元素之后有效地插入元素?

    给定一个数组 a array abc 123 k1 gt v1 k2 gt v2 78 tt k3 gt v3 当其内部指针指向其元素之一时 如何在当前元素之后插入元素 如何在键已知元素 例如 k1 之后插入元素 表现护理 您可以通过使用拆
  • 为什么 Firefox 会忽略缓存标头并在刷新时重新验证?

    我有一些不可变的图像资源 可以永久缓存 Chrome 似乎尊重我的响应标头 并且不会重新验证资源 以下是 Chrome 中其中一项资源的示例 正如你所看到的 我包括cache control public max age expires e

随机推荐

  • 无法使用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 个请求的速度访