【发布时间】:2018-08-24 16:27:38
【问题描述】:
我正在编写一个 Go 库来表示各种网络设备,例如交换机、路由器、无线控制器、接入点等,以便自动配置这些设备。到目前为止,我有一个 Device 结构,它有一个公共 Host 字段和各种私有字段,用于处理特定于 SSH 的操作,以及连接到设备、向其发送一组配置命令的方法,以及检索命令的输出。到目前为止,没有一个方法被实现为并发的,主要是因为我不知道哪些方法(如果有的话)会从并发中受益。
作为一个整体,我的问题是通过 SSH 配置设备列表,这似乎是使用并发的一个很好的案例(不要盲目地尝试使用并发来“快速”),因为配置单个设备的过程可能很昂贵,但我不确定在我的应用程序中在哪里实现并发以及如何同步所有内容(线程安全?)。对于互斥锁、等待组、通道和 goroutine,对于像我这样的初学者来说,知道从哪里开始有点令人困惑。我想至少让一个方法同时工作,以便更好地理解 Go 中的(惯用的)并发性。
这是我的Device 结构及其方法。为了清楚说明我想要完成什么以及我对实施细节的想法,它被大量评论。
package device
import (
"golang.org/x/crypto/ssh"
"io"
)
// A Device represents a network device, such as a switch, router, controller, etc.
type Device struct {
Host string // Hostname or IP address
client *ssh.Client // the SSH client connection
session *ssh.Session // the connection to the remote shell
stdin io.WriteCloser // a pipe connected to the remote shell's standard input
stdout io.Reader // a pipe connected to the remote shell's standard output
stderr io.Reader // a pipe connected to the remote shell's standard error
}
// NewDevice constructs a new device with the given hostname or IP address.
func NewDevice(host string) *Device {
return &Device{Host: host}
}
// Connect starts a client connection to the device, starts a remote
// shell, and creates pipes connected to the remote shell's standard input,
// standard output, and standard error.
func (d *Device) Connect(config *ssh.ClientConfig) error {
// TODO: connect to client, start session, setup IO
// Use a goroutine to handle each step? One goroutine for all steps?
return nil
}
// setupIO connects pipes to the remote shell's standard input, output and error.
func (d *Device) setupIO() error {
sshIn, err := d.session.StdinPipe()
if err != nil {
return err
}
d.stdin = sshIn
sshOut, err := d.session.StdoutPipe()
if err != nil {
return err
}
d.stdout = sshOut
sshErr, err := d.session.StderrPipe()
if err != nil {
return err
}
d.stderr = sshErr
return nil
}
// SendConfigSet writes a set of configuration commands to the remote shell's
// standard input then waits for the remote commands to exit.
func (d *Device) SendConfigSet(cmds []string) error {
// TODO: send a set of configuration commands
// Make concurrent? Commands need to be sent in a specific order.
//
// This function will have different setup and cleanup commands
// that will need to be sent depending on a Device's vendor.
// For example, a Cisco device and an HPE device have
// different sets of setup commands needed before sending
// the `cmds` passed to this function, and have different sets of
// cleanup commands that must be sent before exiting.
return nil
}
// sendCmd writes a remote command to the remote shell's standard input
func (d *Device) sendCmd(cmd string) error {
if _, err := d.stdin.Write([]byte(cmd + "\n")); err != nil {
return err
}
return nil
}
// Output reads the remote shell's standard output line by line into a
// slice of strings.
func (d *Device) Output() ([]string, error) {
// TODO: read contents of session standard output
// Concurrently read from stdout and send to channel?
// If so, use a local channel or add an output channel to `Device`?
return nil, nil
}
// Output reads the remote shell's standard error line by line into a
// slice of strings.
func (d *Device) Err() ([]string, error) {
// TODO: read contents of session standard error
// Concurrently read from stderr and send to channel?
// If so, use a local channel or add an error channel to `Device`?
return nil, nil
}
func (d *Device) Close() error {
if err := d.stdin.Close(); err != nil {
return err
}
if err := d.session.Close(); err != nil {
return err
}
if err := d.client.Close(); err != nil {
return err
}
return nil
}
这是我的device 包的示例用法:
package main
import (
"fmt"
"github.com/mwalto7/concurrency/device"
"golang.org/x/crypto/ssh"
"strings"
"time"
)
func main() {
var hosts, cmds []string
config := &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{ssh.Password("password")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: time.Second * 5,
}
outputs := make(chan string)
for _, host := range hosts {
go configure(host, cmds, config, outputs)
}
for i := 0; i < len(hosts); i++ {
res := <-outputs
fmt.Println(res)
}
}
func configure(host string, cmds []string, config *ssh.ClientConfig, outputs <-chan string) {
// omitted error handling for brevity
netDev := device.NewDevice(host)
defer netDev.Close()
netDev.Connect(config)
netDev.SendConfigSet(cmds)
out, _ := netDev.Output()
outputs <- strings.Join(out, "\n")
}
我并不是要求有人为我编写此代码。如果您有代码示例,那很好,但我只是在尝试组织实现并发并了解一般的并发。 p>
【问题讨论】:
-
那么,你的问题是什么?
-
@Peter 我的方法可以使用并发实现吗?如果是这样,我的 cmets 是否在正确的轨道上实现并发?
标签: go concurrency parallel-processing