太nm操蛋了,我tm弄了两小时。
起因
目前我的项目中,当并发量提高的时候会出现UDP的buffer queue full的情况,我怀疑是因为UDP端口释放太慢导致堆积。
于是就打算用golang写一个命令行程序,每个0.5s执行以下cmd语句:
netstat -an | grep "UDP"
过程
我记得golang是有一个exec库的,专门用来跑cmd,但是忘记在哪里看到了,就去网上搜。
首先是网上的教程大多是linux环境下的,所以他们的博客对与windows下的我并不通用。
这一点我试了很久...
One:
使用网上类似这样的代码:
func main() {
c1 := exec.Command("netstat", "-an")
c2 := exec.Command("grep", "UDP")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
err := c1.Run()
if err != nil {
log.Panicln(err.Error())
}
_ = c2.Wait()
}
你就会收获如下错误
就很懵。后来想想是因为grep
是linux下的,之后就找到了windows下的代替品find
Two:
之后代码就变成下面这样:
func main() {
c1 := exec.Command("find", "UDP")
output, err := c1.CombinedOutput()
if err != nil {
fmt.Println(fmt.Sprint(err) + ": " + string(output))
return
}
fmt.Println(string(output))
}
然而你运行之后会得到以下错误:
???这是个什么错误,你倒是告诉我错误是啥啊
后来去stack overflow上找到了方法
简而言之就是,这个err会返回在cmd.Stderr
上面,所以需要打印cmd.Stderr
。或者调用CombinedOutput()
。
Three:
但是你调用之后,会得到以下结果:
???为什么会有乱码
查资料发现是因为cmd的输入输出都是遵循GBK
编码的,但是中文是UTF-8
编码的,所以中文就会显示乱码。
艹cmd是什么落后的东西啊。
之后去网上嫖了一个GBK转UTF-8的:
func ConvertByte2String(byte []byte, charset Charset) string {
var str string
switch charset {
case GB18030:
var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
str = string(decodeBytes)
case UTF8:
fallthrough
default:
str = string(byte)
}
return str
}
最后总算能显示中文了,但是显示的结果如下:
Four:
我看cmd都是这么用的怎么到这就拉垮了?
之后通过打印语句,看了一下以上的命令到底是什么:
猜测可能是因为没有引号导致的,于是就加上引号,代码现在长这样:
func main() {
c1 := exec.Command("netstat", "-an")
c2 := exec.Command("find", "\"UDP\"", "/c")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
err := c1.Run()
if err != nil {
log.Panicln(err.Error())
}
_ = c2.Wait()
}
发现貌似卡住了,根本没输出。
Five:
debug模式进入之后发现是卡在了c2.Wait()这里,这个我也不知道为什么。于是就去网上换了一种方式,来写。
突然看到一篇博客说可以直接通过调用cmd.exe之后设置参数就可以直接一句话完成我最开始想要的cmd语句。
于是现在长这样:
func main() {
c1 := exec.Command("cmd.exe", "netstat -an | find /c \"UDP\"")
output, err := c1.CombinedOutput()
fmt.Println(c1.String())
if err != nil {
fmt.Println(fmt.Sprint(err) + ": " + ConvertByte2String(output, "GB18030"))
return
}
fmt.Println(string(output))
}
结果报了这样的错误:
?我寻思这不就是字符串参数。这个奇奇怪怪的是什么??
之后又尝试了这种写法:
c1 := exec.Command("cmd.exe", "netstat -an | find /c UDP")
结果还是会报错...
有可能是因为cmd的转义字符跟golang的有所不同导致的,于是我采用了raw string的形式
c1 := exec.Command("cmd.exe", `netstat -an | find /c "UDP"`)
结果还是会有这样的错误...
c1 := exec.Command("cmd.exe", `netstat -an | find /c UDP`)
Six:
就在我感到绝望的时候,我看到网上说powershell完美兼容cmd同时还会有更智能地识别,于是我就看怎么用golang调用powershell,发现只需要把之前的cmd改成powershell就可以:
c1 := exec.Command("powershell.exe", `netstat -an | find /c UDP`)
结果还是不对,之后我去网上搜:搜到了结果
https://blog.csdn.net/beebeeyoung/article/details/116677249
c1 := exec.Command("powershell.exe", "netstat -an | find /c `\"UDP`\"")
终于!!!!总算成功!!!!
完结撒花!!!
尾声
最后发现Prometheus地node-exporter里面自带监听端口的metric,所以只需要在grafana里面填上PromQL就可以(
标签:UDP,err,exec,Windows,cmd,Golang,Command,c1 From: https://www.cnblogs.com/Vikyanite/p/16870787.html