GoLang(cgp)http.Client在Linux守护进程中发出的GET请求挂起。

huangapple go评论119阅读模式
英文:

GoLang(cgp) http.Client get request hangs from Linux daemon process

问题

我已经写了一个简单的GoLang函数,它使用http.Client进行HTTP GET请求并打印响应字符串。然后,我使用以下命令将其导出为.so文件中的C函数。

go build -o libapp.so -buildmode=c-shared libapp.go

然后,使用这个.SO文件和.h文件,从我的C测试程序中调用导出的Go函数。以下是我用于编译和构建C测试程序的命令。

gcc -o goclient goclient.c ./libapp.so

这是GoLang中的CheckConnection函数:

var file io.Writer

//export CheckConnection
func CheckConnection() {

path := "/opt/logs/gologs.txt"
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
    log.Fatal(err)    
}
file = f
log.SetOutput(file)
log.Print("---------Logging started---------")
log.Print("Hello from CheckConnection\r\n")

log.Print("CheckConnection creating http.Client: step1")

var netTransport = &http.Transport{
    Dial: (&net.Dialer{
        Timeout: 10 * time.Second,
    }).Dial,
    TLSHandshakeTimeout: 10 * time.Second,    
}
log.Print("CheckConnection creating http.Client: step2")

var myClient = &http.Client{
    Timeout: time.Second * 10,
    Transport: netTransport,
}
log.Print("CheckConnection creating http.Client: step3")

res,err := myClient.Get("https://www.google.com")
log.Print("CheckConnection creating http.Client: step4")
if err!= nil {
    log.Print("Error ",err)
}
log.Print("CheckConnection creating http.Client: step5")
data,_ := ioutil.ReadAll(res.Body)
log.Print("CheckConnection creating http.Client: step6")
res.Body.Close()
log.Print("CheckConnection creating http.Client: step7")
log.Print("Resp code ",res.StatusCode)
log.Print("Data ",data)

defer log.Print("Returning from CheckConnection");

}

线程在下面的代码行处被阻塞,不会超时或报错:

res,err := myClient.Get("https://www.google.com")

当从守护进程调用CheckConnection()函数时会发生这种情况。如果我从C、C++的main()进程中调用这个函数,它就能正常工作。我在GitHub上报告了同样的问题https://github.com/golang/go/issues/47077。它有一个可以重现该问题的示例代码(Sample.zip)。

请参考GitHub问题https://github.com/golang/go/issues/47077,获取可重现问题的示例代码。

以下是go env的内容:

GO111MODULE=""
GOARCH="386"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="386"
GOHOSTOS="linux"
GOINSECURE="golang.org,googlesource.com"
GOMODCACHE="/home/nilesh/golang/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/nilesh/golang"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_386"
GOVCS=""
GOVERSION="go1.16.4"
GCCGO="gccgo"
GO386="sse2"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/nilesh/SVN/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m32 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build718813889=/tmp/go-build -gno-record-gcc-switches"
英文:

I have written a simple GoLang function which makes HTTP GET request using http.Client and prints the response string. I then exported it as C function in .so file using following command.

go build -o libapp.so -buildmode=c-shared libapp.go

Then using this .SO and .h, called exported Go function from my C test program. Below is the command I used to compile and build my C test program.

gcc -o goclient goclient.c ./libapp.so

Here is CheckConnection function in GoLang

var file io.Writer

//export CheckConnection
func CheckConnection() {

path := "/opt/logs/gologs.txt"
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
	log.Fatal(err)    
}
file = f
log.SetOutput(file)
log.Print("---------Logging started---------")
log.Print("Hello from CheckConnection\r\n")

log.Print("CheckConnection creating http.Client: step1")

var netTransport = &http.Transport{
  Dial: (&net.Dialer{
Timeout: 10 * time.Second,
  }).Dial,
  TLSHandshakeTimeout: 10 * time.Second,    
}
log.Print("CheckConnection creating http.Client: step2")

var myClient = &http.Client{
  Timeout: time.Second * 10,
  Transport: netTransport,
}
log.Print("CheckConnection creating http.Client: step3")

res,err := myClient.Get("https://www.google.com")
log.Print("CheckConnection creating http.Client: step4")
if err!= nil {
log.Print("Error ",err)
}
log.Print("CheckConnection creating http.Client: step5")
data,_ := ioutil.ReadAll(res.Body)
log.Print("CheckConnection creating http.Client: step6")
res.Body.Close()
log.Print("CheckConnection creating http.Client: step7")
log.Print("Resp code ",res.StatusCode)
log.Print("Data ",data)

defer log.Print("Returning from CheckConnection");

}

Thread gets blocked at below line, does not timeout or gives error -

 res,err := myClient.Get("https://www.google.com")

This happens when CheckConnection() function gets called from Daemon process. If I call this function from C,C++ main() process then it works. I have reported same issue on GitHub https://github.com/golang/go/issues/47077. It has sample code to reproduce the issue (Sample.zip).

Please refer to GitHub issue https://github.com/golang/go/issues/47077 for sample reproducible code.

Adding go env below

GO111MODULE=""
GOARCH="386"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="386"
GOHOSTOS="linux"
GOINSECURE="golang.org,googlesource.com"
GOMODCACHE="/home/nilesh/golang/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/nilesh/golang"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_386"
GOVCS=""
GOVERSION="go1.16.4"
GCCGO="gccgo"
GO386="sse2"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/nilesh/SVN/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m32 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build718813889=/tmp/go-build -gno-record-gcc-switches"

答案1

得分: 0

默认情况下,DefaultClient没有超时设置。你可以尝试使用自己的客户端并设置超时时间。

var myClient = &http.Client{
  Timeout: time.Second * 10,
}
英文:

By default there is no timeout for the DefaultClient. Could you try with own client with timeout set up?

var myClient = &http.Client{
  Timeout: time.Second * 10,
}

答案2

得分: 0

在Linux上使用cgp共享库时,如果你从forked进程中调用这些函数,请确保考虑以下几点:

  1. 不要静态链接共享库,而是使用dlopen、dlsym、dlclose动态加载它。

  2. 确保在上述过程中不要在链接时提供共享库名称。

这解决了我的问题,我能够从我的forked进程中进行网络调用。

英文:

When using cgp shared library in your C/C++ program on linux and if you are calling these functions from forked process then make sure you should consider below points.

  1. Do not statically link the shared library, instead load it dynamically using dlopen, dlsym, dlclose.

  2. Make sure while doing above you are not providing shared lib name during linking

This solved my problem and I am able to to make network calls from my forked process.

huangapple
  • 本文由 发表于 2021年5月27日 03:11:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/67711312.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定