将值接收器用于包含 http.Client 的结构上的方法会影响连接池吗?

Will using value receiver for a method on a struct that contains a http.Client affect connection pooling?

我对指针接收器和值接收器的使用理解比较薄弱。这是我无法在两者之间做出决定的场景:

我最近学会了重新使用已创建的 http.Client 个对象,而不是每次都创建一个新的 http.Client,以便从连接池中受益。所以我做了这样的事情:

type MailClient struct {
    HTTPClient *http.Client

    // ... bunch of other stuff
}

func newMailClient( // ... arguments for initializing stuff ) *MailClient {
    return &MailClient{
        HTTPClient: &http.Client{},

        // ... init other stuff
    }
}

func (c *MailClient) SendMail( // ... arguments that form a email request ) {
    // ... prepare the email request

    httpResp, err := c.HTTPClient.Do( // ... args for sending )
    if err != nil {
        // ... handle error
    }
    defer httpResp.Body.Close()

    // ...
}

这样,只要在同一个 MailClient 上调用 SendMail(),我希望连接池能够启动(我理解 [=17 的默认 MaxIdleConnsPerHost =]默认为2,无需自定义)。

但是注意到我定义 SendMail() 来使用指针接收器了吗?是的,我真的不知道我为什么这么做。我只是希望通过使用指针接收器,每次调用该方法时,它都是 MailClient 的同一个实例,而不是它的副本。我还认为值接收者阻止修改接收者的事实是因为它是一个副本。

出于同样的原因,我也谨慎地将 MailClient 结构中的 HTTPClient 字段定义为 http.Client 的指针 - 我不知道值指针是如何工作的。

所以这里总结一下我的问题:

  1. 值接收者是否会导致方法中使用接收者的副本?
  2. 将接收器类型更改为 MailClient 会影响连接池行为吗?
  3. HTTPClient 字段更改为 http.Client 会影响连接池行为吗?

要记住的最重要的部分是方法调用转换为与接收器的函数调用。适用于函数调用的所有规则都适用于具有相应接收者类型(值或引用)的方法调用。查看此内容的一个好方法是使用以下语法

type XType struct {
    A int
    B *int
}

func (x XType) Mutate() {
    x.A = 1
    *x.B = 2
}

func (x *XType) MutateRef() {
    x.A = 2
    *x.B = 3
}

func main() {
    i := 5
    x1 := XType{
        A: 9999,
        B: &i,
    }
    //calling Mutate as a function passing the receiver by value
    XType.Mutate(x1)
    log.Printf("%#v", x1)
    log.Printf("%#v", *x1.B)
    //calling MutateRef as a function passing the receiver by reference
    (*XType).MutateRef(&x1)
    log.Printf("%#v", x1)
    log.Printf("%#v", *x1.B)
}

另一件需要注意的事情是,当按值传递结构时,所有字段都会被复制,将指针想象成一个 int,因此副本仍将保存相同的地址值。

现在,您可以轻松找到问题的答案:

  1. 值接收者是否会导致方法中使用接收者的副本?

    是的,因为调用函数传递值是一回事,适用相同的规则

  2. 将接收方类型更改为 MailClient 是否会影响连接池行为

    不是真的,因为客户端是一个指针。即使您使用值接收器调用该方法,您仍将使用副本中的值指向的客户端

  3. 将 HTTPClient 字段更改为 http.Client 会影响连接池行为吗?

    我不这么认为,因为 http.Transport 负责连接池,它是参考 http.Client 设置的。不过,最好的办法是使用指向客户端的指针

连接池在 http.Transport. Your application uses the default transport because the application does not set the client transport field 中实现。

无论应用程序使用指针与值接收器做什么,都不会影响连接池,因为应用程序使用 default transport.