gpt4 book ai didi

go - 强制关闭 SSH 客户端 session

转载 作者:行者123 更新时间:2023-12-01 19:11:12 28 4
gpt4 key购买 nike

我编写了一个 SSH 客户端来连接到网络设备,一旦运行命令超过 25 秒,我就通过“选择”设置超时。我注意到一些设备有另一个 IOS,一旦超时被触发并导致 goroutinge 泄漏,它就无法通过 Close() 方法删除与它们的 SSH session 。我需要跟上客户端并断开 session 以准备好下一个命令。看起来 goroutine 在那个时候不会永远终止!你们有什么想法吗?

    go func() {
r <- s.Run(cmd)
}()

select {
case err := <-r:
return err
case <-time.After(time.Duration(timeout) * time.Second):
s.Close()
return fmt.Errorf("timeout after %d seconds", timeout)
}

通过堆分析,我看到了以下内容:
2.77GB 99.44% 99.44% 2.77GB 99.44% bytes.makeSlice
     0     0% 99.44%     2.77GB 99.44%  bytes.(*Buffer).ReadFrom

0 0% 99.44% 2.77GB 99.44% golang.org/x/crypto/ssh.(*Session).start.func1

0 0% 99.44% 2.77GB 99.44% golang.org/x/crypto/ssh.(*Session).stdout.func1

0 0% 99.44% 2.77GB 99.44% io.Copy

0 0% 99.44% 2.77GB 99.44% io.copyBuffer

0 0% 99.44% 2.78GB 99.93% runtime.goexit

例程 ========================/usr/local/go/src/runtime/asm_amd64.s 中的 runtime.goexit
     0     2.78GB (flat, cum) 99.93% of Total

. . 1993: RET

. . 1994:

. . 1995:// The top-most function running on a goroutine

. . 1996:// returns to goexit+PCQuantum.

. . 1997:TEXT runtime·goexit(SB),NOSPLIT,$0-0

. 2.78GB 1998: BYTE $0x90 // NOP

. . 1999: CALL runtime·goexit1(SB) // does not return

. . 2000: // traceback from goexit1 must hit code range of goexit

. . 2001: BYTE $0x90 // NOP

. . 2002:


. . 2003:TEXT runtime·prefetcht0(SB),NOSPLIT,$0-8

最佳答案

channel r阻止 Go 例程返回,因为它没有被清空。我已经编写了您代码的改编版本并插入了一个 WaitGroup 来演示问题:

func main() {
var wg sync.WaitGroup // This is only added for demonstration purposes
s := new(clientSession)

r := make(chan error)

go func(s *clientSession) {
wg.Add(1)
r <- s.Run()
wg.Done() // Will only be called after s.Run() is able to return
}(s)

fmt.Println("Client has been opened")

select {
case err := <-r:
fmt.Println(err)
case <-time.After(1 * time.Second):
s.Close()
fmt.Println("Timed out, closing")
}

wg.Wait() // Waits until wg.Done() is called.

fmt.Println("Main finished successfully")
}

Go Playground 似乎终止了程序,所以我创建了一个 gist带有完整的可运行代码。当我们运行 incorrect.go :
$ go run incorrect.go
Client has been opened
Timed out, closing
fatal error: all goroutines are asleep - deadlock!
....

那是因为我们的代码在 wg.Wait() 上死锁了线。这表明 wg.Done()在 Go 例程中从未达到过。

正如评论所指出的,缓冲 channel 可以在这里提供帮助。但前提是你不再关心错误,在调用 s.Close() 之后
r := make(chan error, 1)

buffered.go 运行正常,但错误丢失:
$ go run buffered.go
Client has been opened
Timed out, closing
Main finished successfully

另一种选择是精确地排空 channel 1 次:
select {
case err := <-r:
fmt.Println(err)
case <-time.After(1 * time.Second):
s.Close()
fmt.Println("Timed out, closing")
fmt.Println(<-r)
}

或者通过包装 selectfor循环(无缓冲 channel ):
X:
for {
select {
case err := <-r:
fmt.Println(err)
break X // because we are in main(). Normally `return err`
case <-time.After(1 * time.Second):
s.Close()
fmt.Println("Timed out, closing")
}
}

当我们运行 drain.go 我们看到错误也打印出来:
$ go run incorrect.go
Client has been opened
Timed out, closing
Run() closed
Main finished successfully

在现实世界中,一个人会运行多个 Go 程序。所以你会想在 for 上使用一些计数器。循环或进一步利用 WaitGroup 功能。

关于go - 强制关闭 SSH 客户端 session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37040099/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com