Golang中每个HTTP请求都会创建新的处理器对象副本吗?
Golang中每个HTTP请求都会创建新的处理器对象副本吗? 你好,
我想知道,在给定的设置中(不使用指针接收器),每个HTTP请求是否会在内存中创建 User、Service 和 Storage 对象的新副本,还是它们只创建一次并用于后续的HTTP请求?
谢谢
package main
import (
"database/sql"
"net/http"
)
func main() {
storage := Storage{
Database: nil, // 实际数据库连接在此处
}
service := Service{
Storage: storage,
}
user := User{
Service: service,
}
mux := http.DefaultServeMux
mux.HandleFunc("/create", user.Post)
// ...
}
type User struct {
Service Service
}
func (u User) Post(w http.ResponseWriter, r *http.Request) {
// ...
u.Service.create( /**/ )
// ...
}
type Service struct {
Storage Storage
}
func (s Service) create( /**/ ) {
// ...
s.Storage.insert( /**/ )
// ...
}
type Storage struct {
Database *sql.DB
}
func (s Storage) insert( /**/ ) {
// ...
}
更多关于Golang中每个HTTP请求都会创建新的处理器对象副本吗?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
假设你是因为担心副本问题才这么问的
是的,这正是我所考虑的。感谢你的解释。非常感谢。
更多关于Golang中每个HTTP请求都会创建新的处理器对象副本吗?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
为了传递给每个使用值接收器(而非指针接收器)调用的函数,会创建副本,因此以下每次调用都会生成副本:
func (u User) Post(w http.ResponseWriter, r *http.Request)
func (s Service) create( /**/ )
func (s Storage) insert( /**/ )
(*http.ServeMux).HandleFunc 的 handler 参数类型为 func(http.ResponseWriter, *httpRequest),因此 user.Post 实际上被“包装”进了一个闭包中。这样,每次 ServeMux 调用 handler 时,User 都会被复制到 Post 的栈帧中以便执行。接着,当 Post 调用 Service.create 时,它同样会将其内嵌的 Service 复制到 create 的栈帧中,依此类推……
话虽如此,在你的示例中,sizeof(User{}) == sizeof(Service{}) == sizeof(Storage{}) == sizeof(*sql.DB),所以你实际上只是在传递一个指针,这效率极高,因此你无需担心(假设你提问是因为担心副本问题)。
在Go的HTTP处理中,每个请求不会创建新的User、Service和Storage对象副本。这些对象在程序启动时只创建一次,然后被所有HTTP请求共享使用。
具体分析如下:
- 对象初始化:在
main()函数中,Storage、Service和User对象各创建一次 - 方法绑定:
mux.HandleFunc("/create", user.Post)只是将user.Post方法注册为处理器,不会复制user对象 - 请求处理:当HTTP请求到达时,Go会调用已注册的处理器函数,但不会复制接收器对象
验证示例:
package main
import (
"fmt"
"net/http"
"sync/atomic"
)
type Counter struct {
count int64
}
func (c Counter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
current := atomic.AddInt64(&c.count, 1)
fmt.Fprintf(w, "Request count: %d\n", current)
fmt.Printf("Memory address: %p\n", &c)
}
func main() {
counter := Counter{}
fmt.Printf("Initial address: %p\n", &counter)
http.Handle("/", counter)
http.ListenAndServe(":8080", nil)
}
运行这个程序并发送多个请求,你会发现:
counter对象的内存地址始终相同- 所有请求共享同一个计数器实例
重要注意事项:
- 由于使用值接收器,在方法调用时会发生接收器的值复制
- 但基础对象(
User、Service、Storage)在内存中只有一份 - 如果需要在方法中修改对象状态,应该使用指针接收器
示例展示值复制的影响:
func (u User) Post(w http.ResponseWriter, r *http.Request) {
// 这里的u是原始user对象的副本
// 对u.Service的修改不会影响原始user对象
fmt.Printf("Post method u address: %p\n", &u)
}
在你的代码中,虽然方法调用时会发生值复制,但底层的数据结构(如sql.DB连接)是通过指针共享的,所以数据库连接不会被复制。

