草庐IT

testing - 如何在 Go 测试中测试泄漏连接?

coder 2023-07-01 原文

在我发现我的程序存在漏洞之后,我就解决了这个问题。但是,现在我正试图找出如何在 Go 测试中“测试 ”泄漏连接?这是我的问题。

我曾尝试更改测试中的请求数量,但没关系。无论我做什么,我测试中的当前 TCP 连接数都保持不变。

func TestLeakingConnections(t *testing.T) {
    getter := myhttp.New()

    s := newServer(ok)
    defer s.Close()

    cur := tcps(t)
    for i := 0; i < 1000; i++ {
        r, _ := getter.GetWithTimeout(s.URL, time.Millisecond*10)
        r.Body.Close()
    }

    for tries := 10; tries >= 0; tries-- {
        growth := tcps(t) - cur
        if growth > 5 {
            t.Error("leaked")
            return
        }
    }
}

// find tcp connections
func tcps(t *testing.T) (conns int) {
    lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
    if err != nil {
        t.Skip("skipping test; error finding or running lsof")
    }

    for _, ls := range strings.Split(string(lsof), "\n") {
        if strings.Contains(ls, "TCP") {
            conns++
        }
    }
    return
}

func newServer(f http.HandlerFunc) *httptest.Server {
    return httptest.NewServer(http.HandlerFunc(f))
}

func ok(w http.ResponseWriter, r *http.Request) {
    w.Header().Add("Content-Type", "application/xml")
    io.WriteString(w, "<xml></xml>")
}

// myhttp package

// ...other code omitted for clarification

func (g *Getter) GetWithTimeout(
    url string,
    timeout time.Duration,
) (
    *http.Response, error,
) {
    // this is the leaking part
    // moving this out of here will stop leaks
    transport := http.Transport{
        DialContext: (&net.Dialer{
            Timeout: timeout,
        }).DialContext,
        TLSHandshakeTimeout:   timeout,
        ResponseHeaderTimeout: timeout,
        ExpectContinueTimeout: timeout,
    }

    client := http.Client{
        Timeout:   timeout,
        Transport: &transport,
    }

    return client.Get(url)
}

// fixture worker package

// some outside code injects getter into fixture_worker like this:
getter := myhttp.New()

// NewWithTimeout creates a new fetcher with timeout threshold
func NewWithTimeout(
    getter myhttp.HTTPGetter,
    fetchURL string,
    timeout time.Duration,
) *Fetcher {
    return &Fetcher{getter, fetchURL, timeout}
}

// Fetch fetches fixture xml
func (f *Fetcher) Fetch() (*parser.FixtureXML, error) {
    res, err := f.getter.GetWithTimeout(f.fetchURL, f.timeout)
    if err != nil {
        if res != nil && res.Body != nil {
            res.Body.Close()
        }
        return nil, errors.Wrap(err, ErrFetch.Error())
    }
    defer res.Body.Close()

    ioutil.ReadAll(res.Body)

    return &parser.FixtureXML{}, nil
}


fixture worker lsof 的输出:https://pastebin.com/fDUkpYsE

测试输出:https://pastebin.com/uGpK0czn

测试永远不会泄漏,而 fixture worker 会泄漏。

Fixture worker 使用与测试相同的代码,使用 myhttp 包请求 http 获取。

最佳答案

如果您希望您的测试代表现实,您需要以与测试之外相同的方式使用它。在这种情况下,您没有阅读任何响应,并且传输恰好在防止丢失连接,因为它会尽快丢弃它们,因为它们无法重复使用。

读取响应将使用连接,并使其进入“泄漏”状态。您还需要在所有情况下正确处理错误,并且始终需要 Close() 响应主体。处理 http 响应并确保其关闭的模式非常简单,并且不一定需要测试(请参阅 What could happen if I don't close response.Body in golang?)

    resp, err := GetWithTimeout(s.URL)
    if err != nil {
        t.Fatal(err)
    }
    ioutil.ReadAll(resp.Body)
    resp.Body.Close()

这可以说是有限的用处,因为最常见的错误将由不正确的错误和响应处理引起,而您没有测试它,因为测试首先需要正确地进行。

这里剩下的问题是您的 GetWithTimeout 方法返回一个错误值一个有效的 http 响应,这与 http 包文档以及大多数用户的期望相矛盾。如果您要插入错误,最好在同一点处理响应以确保正文被关闭和丢弃。

最后,GetWithTimeout 的大部分内容都是多余的,因为不仅每次都创建 Transports 不正确,而且每个请求都创建一个 http.Client 通常是不必要的,因为它们应该被重用并且对同时使用。

关于testing - 如何在 Go 测试中测试泄漏连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45746753/

有关testing - 如何在 Go 测试中测试泄漏连接?的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  5. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  6. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  7. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  8. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  9. ruby - Ruby 的 Hash 在比较键时使用哪种相等性测试? - 2

    我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。

  10. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

随机推荐