公司开发的播放软件在某些店铺使用时出现播放卡顿的现象,店员使用360网络自检工具测试,结果显示正常,播放QQ音乐或者网易音乐也是正常的,领导层认为是开发团队的问题,于是和客户一对一沟通提供技术支持,最终确认客户网络使用自建DNS服务器,店铺DNS主服务器在广州,主服务器设置的DNS配置访问我们使用的CDN会出现延迟高的情况。
为了测试其他几百家店铺是否有相同的问题,用Go语言开发了网络自检程序,因为Go语言可以直接生成二进制可执行文件,对各个类型的操作系统都适用,店铺拿到程序双击运行即可,免去了装虚拟机和运行时环境的繁琐。
程序分几个步骤检测网络环境:
- 下载我公司提供的CDN加速后文件,下载后比对是否完整,计算下载时间
- 下载竞品的CDN加速后文件,计算下载时间
- 本地ip地址
- 公网ip地址
- traceroute到我公司的文件服务器地址,记录整个路由链路
- 将信息通过邮件方式上报到开发人员的邮箱
代码如下:
package main
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"github.com/go-basic/ipv4"
"github.com/go-mail/mail"
"github.com/go-ping/ping"
"golang.org/x/text/encoding/simplifiedchinese"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"os/signal"
"runtime"
"strconv"
"strings"
"time"
)
var content string
var urls = [...]string{"www.lavaradio.com", "api-safe-pro.lavaradio.com", "static01.lavaradio.com", "img01.lavaradio.com", "audio01.dmhmusic.com", "audio02.dmhmusic.com", "audio03.dmhmusic.com", "audio04.dmhmusic.com"}
var mp3_urls = [...]string{"https://ws.stream.qqmusic.qq.com/C400003OSSk60F2Vvx.m4a?guid=7533888128&vkey=1C75FDFE9076A5C120FD01D9393BCAA7420E418C5D43A8CD60691D73A72E217E613BAA8C88EE2316F1260CB420DED3C7642AC175265CCF9C&uin=0&fromtag=66",
"http://audio04.dmhmusic.com/73_16_T10054723832_128_4_1_0_sdk-cpm/cn/0103/M00/F7/F4/ChR45V7CdUWAf2I1ACPH89lgI14122.mp3?xcode=dfbdb4a731997a1b7841b75aeadf4f7a4c1c446"}
var ping_time int = 10
var ping_timeout time.Duration = time.Second * 10
var sysType string = runtime.GOOS
func main() {
ip := ipv4.LocalIP()
content = "======= Detection of network state begin =======" + "\n"
content += "Local ip address: " + ip
content += "\n"
getExternalIp()
defer sendBack()
defer download()
defer getTracert()
defer goPing()
defer getDns()
}
func goPing() {
content += "\n"
content += "=== Ping test begin ==="
for i, v := range urls {
pinger, err := ping.NewPinger(v)
if sysType == "windows" {
pinger.SetPrivileged(true)
}
pinger.Count = ping_time
pinger.Timeout = ping_timeout
if err != nil {
content += "Error: " + err.Error() + "\n"
fmt.Printf(err.Error())
}
// listen for ctrl-C signal
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for _ = range c {
pinger.Stop()
}
}()
pinger.OnRecv = func(pkt *ping.Packet) {
fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v\n",
pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
}
pinger.OnFinish = func(stats *ping.Statistics) {
info_base := fmt.Sprintf("\n--- %s ping statistics ---\n", stats.Addr)
info_count := fmt.Sprintf("%d packets transmitted, %d packets received, %v%% packet loss\n",
stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
info_time := fmt.Sprintf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n",
stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
content += info_base
content += info_count
content += info_time
if i == len(urls)-1 {
//fmt.Print(content)
}
content += "\n"
//fmt.Print(info_count)
//fmt.Print(info_time)
}
fmt.Printf("PING %s (%s):\n", pinger.Addr(), pinger.IPAddr())
pinger.Run()
}
}
func download() {
content += "=== Download testing begin ===" + "\n"
for i, u := range mp3_urls {
var fsize int64
client := new(http.Client)
client.Timeout = time.Second * 10 //设置超时时间
start_time := time.Now()
resp, err := client.Get(u)
//get方法获取资源
//resp, err := client.Get(url)
content += "Current downloading: " + u + "\n"
content += "Server returns status: " + resp.Status + "\n"
content += "Server type:" + resp.Header.Get("Server") + "\n"
content += "Connection type: " + resp.Header.Get("Connection") + "\n"
content += "Access-Control :" + resp.Header.Get("Access-Control-Allow-Origin") + "\n"
if err != nil {
println(err)
content += "Error: " + err.Error()
}
fsize, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 32)
if err != nil {
println(err)
content += "Error: " + err.Error()
}
println("Remote size: ", fsize)
content += "Remote size: " + strconv.FormatInt(fsize, 10) + "\n"
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
println(err.Error())
content += "Error: " + err.Error()
}
content += "Local size :" + strconv.Itoa(len(data)) + "\n"
println("Local size :", len(data))
content += "Used time: " + (time.Now().Sub(start_time).String())
if i == len(mp3_urls)-1 {
println(content)
}
content += "\n"
}
content += "======= Detection of network state finish ======="
fmt.Println("End!!!!" + "\n" + content)
fmt.Println("Test successful! Thank you for your cooperation! ")
time.Sleep(time.Duration(2) * time.Second)
}
func getDns() {
content += "=== Get DNS information begin ===" + "\n"
command := "cat /etc/resolv.conf"
par := ""
if sysType == "windows" {
command = "ipconfig"
par = "/all"
}
str1, err := RunCommandWithErr(command, par)
if err != nil {
fmt.Println(err.Error())
} else {
content += str1 + "\n"
}
}
func getTracert() {
content += "=== Tracert begin ===" + "\n"
command := "traceroute audio04.dmhmusic.com"
par := ""
if sysType == "windows" {
command = "tracert"
par = "audio04.dmhmusic.com"
}
str1, err := RunCommandWithErr(command, par)
if err != nil {
fmt.Println(err.Error())
} else {
content += str1 + "\n"
}
}
func getExternalIp() string {
resp, err := http.Get("http://members.3322.org/dyndns/getip")
if err != nil {
return ""
}
defer resp.Body.Close()
ip, _ := ioutil.ReadAll(resp.Body)
content += "Internet IP address: " + string(ip)
content += "\n"
fmt.Print(content)
return string(ip)
}
func sendBack() {
mailTo := []string{
"dev@foxmail.com",
}
//邮件主题为"Hello"
subject := "Testing information."
// 邮件正文
//body := "Hello,by gomail sent"
err := SendMail(mailTo, subject, content)
if err != nil {
log.Println(err)
fmt.Println("send fail")
return
}
//fmt.Println("send successfully")
}
type IPInfo struct {
Code int `json:"code"`
Data IP `json:"data`
}
type IP struct {
Country string `json:"country"`
CountryId string `json:"country_id"`
Area string `json:"area"`
AreaId string `json:"area_id"`
Region string `json:"region"`
RegionId string `json:"region_id"`
City string `json:"city"`
CityId string `json:"city_id"`
Isp string `json:"isp"`
}
func TaobaoAPI(ip string) *IPInfo {
url := "http://ip.taobao.com/service/getIpInfo.php?ip="
url += ip
resp, err := http.Get(url)
if err != nil {
return nil
}
defer resp.Body.Close()
out, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil
}
var result IPInfo
if err := json.Unmarshal(out, &result); err != nil {
return nil
}
return &result
}
func runInLinux(cmd string) string {
fmt.Println("Running Linux cmd:", cmd)
result, err := exec.Command("/bin/sh", "-c", cmd).Output()
if err != nil {
fmt.Println(err.Error())
}
return strings.TrimSpace(string(result))
}
func runInWindows(cmd string) string {
fmt.Println("Running Win cmd:", cmd)
result, err := exec.Command("cmd", "/c", cmd).Output()
if err != nil {
fmt.Println(err.Error())
}
return strings.TrimSpace(string(result))
}
func RunCommand(cmd string) string {
if runtime.GOOS == "windows" {
return runInWindows(cmd)
} else {
return runInLinux(cmd)
}
}
func RunLinuxCommand(cmd string) string {
if runtime.GOOS == "windows" {
return ""
} else {
return runInLinux(cmd)
}
}
func runInLinuxWithErr(cmd string) (string, error) {
fmt.Println("Running Linux cmd:" + cmd)
result, err := exec.Command("/bin/sh", "-c", cmd).Output()
if err != nil {
fmt.Println(err.Error())
}
return strings.TrimSpace(string(result)), err
}
func runInWindowsWithErr(cmd string) (string, error) {
fmt.Println("Running Windows cmd:" + cmd)
result, err := exec.Command("cmd", "/c", cmd).Output()
if err != nil {
fmt.Println(err.Error())
}
return strings.TrimSpace(string(result)), err
}
func runInWindowsWithErrEncode(cmdStr string, par string) (string, error) {
command := cmdStr
params := []string{par}
cmd := exec.Command(command, params...)
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
}
cmd.Start()
in := bufio.NewScanner(stdout)
var cmdContent string
for in.Scan() {
cmdRe := ConvertByte2String(in.Bytes(), "GB18030")
fmt.Println(cmdRe)
cmdContent += cmdRe + "\n"
}
//cmd.Wait()
return cmdContent, err
}
func RunCommandWithErr(cmd string, par string) (string, error) {
if runtime.GOOS == "windows" {
//return runInWindowsWithErr(cmd)
return runInWindowsWithErrEncode(cmd, par)
} else {
return runInLinuxWithErr(cmd)
}
}
func RunLinuxCommandWithErr(cmd string) (string, error) {
if runtime.GOOS == "windows" {
return "", errors.New("could not run in Windows Os")
} else {
return runInLinuxWithErr(cmd)
}
}
type Charset string
const (
UTF8 = Charset("UTF-8")
GB18030 = Charset("GB18030")
)
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
}
func SendMail(mailTo []string, subject string, body string) error {
m := mail.NewMessage()
m.SetHeader("From", "sender@lovinc.com")
m.SetHeader("To", "receive@lovinc.com")
m.SetHeader("Subject", subject)
m.SetBody("text/html", strings.ReplaceAll(content, "\n", "</br>"))
//m.Attach("/home/Alex/lolcat.jpg")
d := mail.NewDialer("smtp.qiye.163.com", 465, "sender@lovinc.com", "senderPassword")
d.StartTLSPolicy = mail.MandatoryStartTLS
if err := d.DialAndSend(m); err != nil {
panic(err)
}
return nil
}