1. 直接上代码
func (s *ProjectPipeLineService) ApplyAppDoSSHCmd(host, cmd string) (string, error) {
user := "root"
password := "12345678"
port := "22"
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{ssh.Password(password)},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
results := make(chan string, 1) // 字符串通道
timeout := time.After(10 * time.Second)
// 执行命令的协程
go func(host string, port string) {
results <- sshUtils.ExecuteCmd(cmd, host, port, config) // 把执行结果通过通道发送给result
}(host, port)
select {
case res := <-results: // 从通道接收结果,保存在res
if res != ""{
return res, nil
}
case <-timeout:
errMsg := fmt.Sprintf("Timed out as ssh host %s and exec cmd %s! \n", host, cmd)
glog.Error(errMsg)
return "", errors.New(errMsg)
}
return "", errors.New(fmt.Sprintf("host %s, cmd %s fail", host, cmd))
}
上面的测试代码,开启协程来执行shell命令,协层间通过channel进行消息通信
2. string类型的channel
results := make(chan string, 1) // 这个1,说明这个channel可容纳1个string类型的对象
此处,容易引起歧义,为啥是make(chan string, 1),而不是 make(chan string, 1024), 字符串长度,难道不该足够容器结果字串?
咱们来看看string类型的定义,在go的源码中src/runtime/string.go
,string的定义如下:
type stringStruct struct {
str unsafe.Pointer
len int
}
可以看出,在给string类型的对象赋值时,是通过str这个指针指向某个存储字符串的内存地址。
因此,string类型的channel,它发送/接收的是 stringStruct这个结构体类型的变量本身,而不是它指向的字符串。
3. string类型的channel,发送/接收都会有一次string对象赋值时的深拷贝,不适合
因此,咱们用string指针类型的channel
results := make(chan *string, 1) // string指针类型的channel
4. 是否有种方法避免频繁的分配/回收字符串S呢?
string指针类型,还存在一个问题就是,每次发送/接收消息,不同的消息实体,string指向的这块只读区域,会频繁的分配和释放,时间久了,会积累很多内存碎片,下面的程序提供了一种方法。
import (
"fmt"
"bytes"
)
type Buffer struct {
buf []byte
off int
lastRead readOp
}
var addr [512]byte
func send(c chan bytes.Buffer, n string, v int) {
sbuf := bytes.NewBuffer(addr[0:0]) // 通过切片关联,而其底层数组是固定的
_, err := sbuf.WriteString(fmt.Sprintf("%s,%d", n, v))
if err == nil {
c <- *buf
}
}
定义一足够容纳长度的底层数组,通过切片操作,构建一个bytes.Buffer对象,后续对这个Buffer进行读写操作就可以了。
1)底层数组足够容纳信息,addr[0:0]切片操作,切片首索引就是底层数组的首索引,通过切片的读写,也就是对底层数组的读写
2)发送/接收bytes.Buffer对象,避免了发送/接收整个消息,也避免了频繁分配/回收整个消息体字符串