V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
macscsbf
V2EX  ›  Go 编程语言

标准库中 http.Request 中 WithContext 方法为什么要浅拷贝一次而不是直接赋值?

  •  
  •   macscsbf · 2023-03-06 15:10:30 +08:00 · 1651 次点击
    这是一个创建于 409 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题。

    // WithContext returns a shallow copy of r with its context changed
    // to ctx. The provided ctx must be non-nil.
    //
    // For outgoing client request, the context controls the entire
    // lifetime of a request and its response: obtaining a connection,
    // sending the request, and reading the response headers and body.
    //
    // To create a new request with a context, use NewRequestWithContext.
    // To make a deep copy of a request with a new context, use Request.Clone.
    func (r *Request) WithContext(ctx context.Context) *Request {
    	if ctx == nil {
    		panic("nil context")
    	}
    	r2 := new(Request)
    	*r2 = *r
    	r2.ctx = ctx
    	return r2
    }
    
    9 条回复    2023-03-14 20:55:47 +08:00
    aladdinding
        1
    aladdinding  
       2023-03-06 15:17:24 +08:00
    我也有这个问题诶
    fgwmlhdkkkw
        2
    fgwmlhdkkkw  
       2023-03-06 15:18:31 +08:00
    不改变之前的引用的行为。
    aladdinding
        3
    aladdinding  
       2023-03-06 15:19:15 +08:00
    在 net/http 标准库中,http.Request 中的 WithContext 方法会返回一个新的 http.Request 对象,它会将原始的请求对象复制一份,然后将新的上下文信息赋值给新的请求对象。这样做的原因是为了确保新的请求对象与原始请求对象是完全独立的,以避免在并发处理请求时可能出现的竞争条件。

    具体来说,WithContext 方法会浅拷贝一份原始请求对象的所有字段,包括头部、URL 、请求体等等。然后它会将新的上下文信息赋值给新的请求对象的上下文字段,这个操作是通过直接赋值来完成的。因为上下文信息通常只是一个指针类型的数据结构,因此它的复制是非常快速和低成本的,因此可以直接进行赋值操作。

    通过浅拷贝一份请求对象,可以确保新的请求对象与原始请求对象是独立的。这意味着对新的请求对象的任何修改都不会影响原始请求对象,反之亦然。这在处理并发请求时非常重要,因为多个并发请求可能会同时修改同一个请求对象,这可能会导致不可预料的结果。通过确保每个请求对象都是独立的,可以避免这种情况的发生,从而提高程序的稳定性和可靠性。

    FROM ChatGPT
    Frankcox
        4
    Frankcox  
       2023-03-06 16:31:37 +08:00
    ranleng
        5
    ranleng  
       2023-03-06 17:14:59 +08:00
    > Request is explicitly immutable. That's why we have Request.WithContext.
    lesismal
        6
    lesismal  
       2023-03-06 17:48:18 +08:00   ❤️ 2
    @Frankcox 我来啦,感谢 at !

    当初提这个 pr 是因为我的 nbio 实现的 http 兼容标准库,这涉及到 Request 的 context 字段,每个请求的 context 应该是共用一个全局的,这样当全局的 context 退出(比如进程退出、cancel 时),这个 request 的 handler 中使用这个 context 就可以一块退出(比如使用 Mysql 时传入这个 context )。但这个字段不是导出的,所以没有办法直接设置。

    由于标准库规则严格、不肯开放这个字段导出,至少对于官方而言,他们不考虑 nbio 要解决的海量并发连接数写成数量、内存开销、GC 等问题,所以官方也确实不需要开放导出。

    无奈只能使用魔法了,预先用 WithContext 生成了一个带有公共 context 的 Request ,后面创建新 Request 时 *newReq = *reqWithCtx 整体赋值,就避过没导出不能设置的问题了。不过虽然能用,毕竟 Request 结构体有点大,这么做能用但不划算,凑合用吧,毕竟我也支持官方不随意放权。。。 :joy:
    macscsbf
        7
    macscsbf  
    OP
       2023-03-07 09:09:05 +08:00
    @aladdinding 谢谢
    zizon
        8
    zizon  
       2023-03-14 17:36:45 +08:00
    @lesismal 那 cancel 单个不是会导致影响其他?
    lesismal
        9
    lesismal  
       2023-03-14 20:55:47 +08:00
    @zizon
    单个请求,为什么会想到去 cancel 全局的呢?
    单个请求自己 return 就完事了,除非这是运维发出的停止服务之类的请求。是不是这个理?嘿嘿
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1219 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 23:25 · PVG 07:25 · LAX 16:25 · JFK 19:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.