我正在进行第一次生产web服务,所以我对语言和一些概念/模式都很陌生。
我的问题与处理程序有关,本质上是如何在不降低性能的情况下提取重复的代码。
我已经越过这个模式来包装http.Handle
或http.HandlerFunc
来清理代码。例如,此博客文章使用适配器模式https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81#.hvsc236iv
最终可能会出现类似这样的内容(从blob帖子中复制):
http.Handle("/", Adapt(indexHandler, AddHeader("Server", "Mine"),
CheckAuth(providers),
CopyMgoSession(db),
Notify(logger),
)
基本上是一个深度嵌套的函数调用。
我遇到的问题是堆栈中发生了什么以及服务的性能如何?使用此模式,每个用户请求将向堆栈添加至少5个堆栈帧。这是可以接受的还是会在流量很高时对性能产生负面影响?
答案 0 :(得分:5)
链接中间件基本上只是让链条的处理程序调用下一个,通常基于条件是否一切顺利。或者在另一种方法中,一些外部机制可以逐个调用处理程序。
但是,所有事情都归结为处理程序将被调用。 Handler.ServeHTTP()
方法如下所示:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
一个带有2个参数且没有返回值的简单方法。参数的类型为http.ResponseWriter
(接口类型)和*http.Request
(指针类型)。
因此,对处理程序ServeHTTP()
的调用涉及两件事:制作其参数的副本 - 这很快,因为它们很小,并实际进行调用(处理堆栈更新,如创建一个新的堆栈帧,记录返回地址,保存已使用的寄存器,并执行被调用的函数) - 这也非常快(参见答案末尾的引用)。
那你应该担心调用函数吗?不会。与包含所有内容的处理程序相比,这是否会降低性能?是。差异显着吗?否。提供HTTP请求可能需要数百毫秒(包括网络延迟)。在你的处理程序中调用10个函数不会明显变慢。
如果您担心因函数调用而导致性能下降,那么您的应用程序将包含一个main()
函数。显然没有人想要那样。您可以创建函数来将最初的大问题分解为较小的问题(递归直到它为#34;足够小并且#34;独立),您可以监督和重用< / em>和 test 独立于其他人,你汇总你的大问题。它不是性能问题,而是可维护性和可重用性。您是否真的想要复制那些检查用户身份的100行代码给所有10个不同的处理程序?
最后一件事。你应该关注&#34;消费&#34;堆栈(导致堆栈溢出错误)?答案是不。 goroutine以一个小的4096字节堆栈开始,根据需要增长和缩小,而不会有用完的风险。请在Why is a Goroutine’s stack infinite?了解详情,详情请见FAQ: Why goroutines instead of threads?
为了使堆栈变小,Go的运行时使用可调整大小的有限堆栈。一个新的goroutine给了几千字节,这几乎总是足够的。当它不是时,运行时会增加(并缩小)用于自动存储堆栈的内存,允许许多goroutine存在于适量的内存中。 CPU开销平均每个函数调用大约三个廉价指令。