Go 强制关闭SSH客户端会话
我已经编写了一个SSH客户端来连接网络设备,并且在运行命令超过25秒时通过“选择”设置超时。我注意到一些设备有另一个IOS,一旦触发超时,它就不能通过Close()方法删除SSH会话,从而导致GoRouting泄漏。我需要保持客户端并断开会话,以便为下一个命令做好准备。看起来goroutine在那个时候不会永远终止!你们知道吗Go 强制关闭SSH客户端会话,go,ssh,Go,Ssh,我已经编写了一个SSH客户端来连接网络设备,并且在运行命令超过25秒时通过“选择”设置超时。我注意到一些设备有另一个IOS,一旦触发超时,它就不能通过Close()方法删除SSH会话,从而导致GoRouting泄漏。我需要保持客户端并断开会话,以便为下一个命令做好准备。看起来goroutine在那个时候不会永远终止!你们知道吗 go func() { r <- s.Run(cmd) }() select { case err := &l
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)
}
例程===================================runtime.goexit in/usr/local/go/src/runtime/asm_amd64.s
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
通道
r
阻止Go例程返回,因为它没有被清空。我已经编写了您的代码的改编版本,并插入了一个等待组来演示问题:
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")
}
这是因为我们的代码在wg.Wait()
行上处于死锁状态。这表明Go例程中的wg.Done()
从未到达
正如评论所指出的,缓冲通道可以在这里提供帮助。但只有在调用s.Close()
运行正常,但错误已丢失:
$ go run buffered.go
Client has been opened
Timed out, closing
Main finished successfully
另一种选择是将通道完全排空1次:
select {
case err := <-r:
fmt.Println(err)
case <-time.After(1 * time.Second):
s.Close()
fmt.Println("Timed out, closing")
fmt.Println(<-r)
}
当我们运行时,我们看到错误也被打印出来:
$ go run incorrect.go
Client has been opened
Timed out, closing
Run() closed
Main finished successfully
在现实世界中,一个将运行多个围棋例程。因此,您需要在
循环中使用一些计数器,或进一步利用等待组功能。是否缓冲r
通道?当您超时时,没有接收器接受来自s.Run的结果。您可能仍然需要实际终止远程进程;您是否尝试过在超时时使用s.Signal
将其杀死?同样,是r
缓冲了吗?它需要缓冲,否则goroutine无法返回。不要关闭会话以中止,杀死进程并等待它(Run正在调用Wait you)我相信由s触发的进程。Run没有正确停止,因此,它无法取消分配资源。您是否尝试将sigterm/sig kill发送到该进程?使用Start和wait代替Run,就像
$ go run buffered.go
Client has been opened
Timed out, closing
Main finished successfully
select {
case err := <-r:
fmt.Println(err)
case <-time.After(1 * time.Second):
s.Close()
fmt.Println("Timed out, closing")
fmt.Println(<-r)
}
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")
}
}
$ go run incorrect.go
Client has been opened
Timed out, closing
Run() closed
Main finished successfully