写此小程序的原因是,研究小米的open-falcon的时候用他们的插件管理感觉不适合我们公司,就自动动手写了个基于服务端的版本控制的,命令下载时基于http的所以也比较方便.
代码写的比较紧急有点乱,先这样吧,回头用起来再调优一下.
先发下执行的结果:
开始解析cfg.json配置文件.解析配置文件成功: {gyc D:/code/20151117/src/ssh true 27.0.0.1:2789 http://127.0.0.1:1789/pkg/ :1789 true 60} 开始初始化本地命令... 初始化本地命令完成... [{client.go D:\code\20151117\src\ssh\client.go 256034f7168a3937b124ad89227f0ea9 {sshd D:\code\20151117\src\ssh\sshd af7ec72582c33ecd97dc5abf0e747e92}] D:/code/20151117/src/ssh 2015-11-18 17:46:29获取服务端版本! {fmt.exe fe117de8dbf1a460e66d2a799bde17cf 30 } 2015-11-18 17:46:29开始验证本地! {Test01.exe e6a54c6f478d93828cb635e97d710ba6 30 } 2015-11-18 17:46:29开始验证本地! 开始下载:http://127.0.0.1:1789/pkg/fmt.exe 开始下载:http://127.0.0.1:1789/pkg/Test01.exe tmp/Test01.exe D:/code/20151117/src/ssh/Test01.exe tmp/fmt.exe D:/code/20151117/src/ssh/fmt.exe 2015-11-18 17:46:29 开始执行Test01.exe 2015-11-18 17:46:29 开始执行fmt.exe 命令Test01.exe结果:This is Test01 命令fmt.exe结果:2015-11-18 17:46:29.3219182 +0800 CST 命令Test01.exe结果:This is Test01 命令fmt.exe结果:2015-11-18 17:46:59.3536359 +0800 CST 2015-11-18 17:47:29获取服务端版本! 清除过期的命令:Test01.exe. {fmt.exe fe117de8dbf1a460e66d2a799bde17cf 30 } 2015-11-18 17:47:29 退出执行Test01.exe 命令fmt.exe结果:2015-11-18 17:47:29.3933541 +0800 CST
废话不多说直接上源码:
服务端简单的代码小例子:
server.go
package main import ( "encoding/json" "fmt" "net" ) type versionInfo struct { Name string `json:name` Md5 string `json:md5` Interval int `json:interval` Args string `json:args` } //测试代码.服务端可以自己按需求定义. func main() { var list []versionInfo list = []versionInfo{{"fmt.exe","fe117de8dbf1a460e66d2a799bde17cf",30,""},{"Test01.exe","e6a54c6f478d93828cb635e97d710ba6",""}} //这些内容命令版本多的时候可以用数据库来控制,不多就随便自己搞搞吧,b,err := json.Marshal(list) if err != nil { fmt.Printf("初始话数据失败:%s\n",err) return } lis,err := net.Listen("tcp",":2789") if err != nil { fmt.Printf("初始化服务端失败:%s\n",err) return } defer lis.Close() for { con,err := lis.Accept() if err != nil { fmt.Println(err) continue } go func(con net.Conn) { defer con.Close() buf := make([]byte,512) n,err := con.Read(buf) if err != nil { fmt.Println(err) return } if string(buf[:n]) != "gyc" { //这里的值需要根据自己来控制,比如每次收到的请求到数据库查询来取版本返回. con.Write([]byte("No this group!")) return } con.Write(b) }(con) } }
下面是每个实例的代码.
我得用目录是:new
getServerVersion.go
package new import ( "encoding/json" "fmt" "io/IoUtil" "net" "os" "time" ) func GetVersion(group string) []RemoteVersionInfo { var list []RemoteVersionInfo This: con,err := net.Dial("tcp",Getconfig().GetVersionServer) if err != nil { fmt.Printf("%s 从%s获取命令版本信息错误: %s\n",GetNowTime(),Getconfig().GetVersionServer,err) time.Sleep(60e9) goto This } defer con.Close() _,err = con.Write([]byte(group)) if err != nil { fmt.Printf("发送%s的version请求失败:%s\n",group,err) time.Sleep(60e9) goto This } buf,err := IoUtil.ReadAll(con) if err != nil { fmt.Printf("%s 获取命令版本信息错误: %s\n",err) time.Sleep(60e9) goto This } if string(buf) == "No this group!" { fmt.Printf("版本库找不到此分组:%s\n",group) os.Exit(1) } err = json.Unmarshal(buf,&list) if err != nil { fmt.Printf("%s 解析获取的版本消息错误: %s\n",err) time.Sleep(60e9) goto This } return list }init_var.go
package new import ( "encoding/json" "fmt" "io/IoUtil" "os" "path/filepath" "strings" "sync" ) var CmdPath string var config ConfigJson var Lock *sync.RWMutex = new(sync.RWMutex) var CmdFuncMap *FuncMap func Getconfig() *ConfigJson { Lock.RLock() defer Lock.RUnlock() return &config } func init() { err := parseConfig("cfg.json") if err != nil { os.Exit(1) } info,err := os.Lstat(Getconfig().CmdDirPath) CmdPath = Getconfig().CmdDirPath if err != nil || !info.IsDir() { fmt.Printf("校验目录失败: %s \n",err) err := initDir() if err != nil { os.Exit(1) } fmt.Printf("使用默认配置做根目录: %s\n",CmdPath) } initBaseCmdInfo(CmdPath) CmdFuncMap = &FuncMap{make(map[string]*ExecCmdInfo),new(sync.RWMutex)} } func parseConfig(configPath string) error { fmt.Printf("开始解析%s配置文件.",configPath) b,err := IoUtil.ReadFile(configPath) if err != nil { fmt.Printf("读取配置文件出错: %s\n",err) return err } err = json.Unmarshal(b,&config) if err != nil { fmt.Printf("解析配置出错: %s\n",err) return err } fmt.Println("解析配置文件成功: ",config) return nil } func initDir() error { fmt.Println("初始化默认路径.") p,err := os.Getwd() if err != nil { fmt.Printf("获取当前目录错误: %s\n",err) return err } info,err := os.Lstat("cmd") if err == nil && info.IsDir() { CmdPath = strings.Replace(p,`\`,`/`,20) + `/cmd` return nil } err = os.Mkdir("cmd",0644) if err != nil { fmt.Printf("初始化文件夹失败: %s\n",err) return err } CmdPath = strings.Replace(p,20) + `/cmd` return nil } func initBaseCmdInfo(path string) { fmt.Println("开始初始化本地命令...") err := filepath.Walk(path,run) if err != nil { fmt.Printf("%s 初始化命令列表出错: %s",err) os.Exit(1) } fmt.Println("初始化本地命令完成...") }run.go
package new import ( "fmt" "os/exec" "strings" "time" ) func R() { if Getconfig().Debug { fmt.Printf("%s获取服务端版本!\n",GetNowTime()) } list := GetVersion(Getconfig().Group) CmdFuncMap.Lock.Lock() for k,m := range CmdFuncMap.Map { if !Contain(k,list) { fmt.Printf("清除过期的命令:%s.\n",k) m.Exit <- true delete(CmdFuncMap.Map,k) } } CmdFuncMap.Lock.Unlock() for _,cmdVersion := range list { fmt.Println(cmdVersion) CmdFuncMap.Lock.RLock() k,ok := CmdFuncMap.Map[cmdVersion.Name] CmdFuncMap.Lock.RUnlock() if ok { if k.M5 == cmdVersion.Md5 { continue } loadcmdbaseinfo := LoadCmdBaseInfo{k.Name,k.Path,k.M5} go Update(loadcmdbaseinfo,cmdVersion) continue } cmd := index(cmdVersion) if cmd != nil { CmdFuncMap.Lock.Lock() CmdFuncMap.Map[cmd.Name] = cmd go cmd.Run() CmdFuncMap.Lock.Unlock() } } } func index(r RemoteVersionInfo) *ExecCmdInfo { if Getconfig().Debug { fmt.Printf("%s开始验证本地!\n",GetNowTime()) } var g chan bool = make(chan bool,1) for _,v := range LocalCmdList { if v.Name == r.Name && v.Md5 == r.Md5 { return &ExecCmdInfo{r.Name,v.Path,r.Md5,r.Interval,split(r.Args),g} } } v := LoadCmdBaseInfo{r.Name,fmt.Sprintf("%s/%s",CmdPath,r.Name),r.Md5} go Update(v,r) return nil } func Update(v LoadCmdBaseInfo,r RemoteVersionInfo) { var url string = Getconfig().GetCmdAddr if !strings.HasSuffix(url,"/") { url = url + "/" } url = url + r.Name str := Download(r.Name,url) if str != strings.ToLower(r.Md5) { return } CmdFuncMap.Lock.Lock() defer CmdFuncMap.Lock.Unlock() k,ok := CmdFuncMap.Map[r.Name] if ok { k.Exit <- true time.Sleep(1e9) } if !MoveFile(fmt.Sprintf("%s/%s","tmp",v.Path) { return } var g chan bool = make(chan bool,1) E := &ExecCmdInfo{r.Name,g} CmdFuncMap.Map[r.Name] = E go E.Run() } func (this *ExecCmdInfo) Run() { fmt.Printf("%s 开始执行%s\n",this.Name) var exit bool = false go func() { b := <-this.Exit if b { exit = b } }() for { if exit { break } cmd := exec.Command(this.Path,this.Args...) //err := cmd.Run() b,err := cmd.Output() if err != nil { fmt.Printf("执行%s出错:%s\n",this.Name,err) } if Getconfig().Debug { fmt.Printf("命令%s结果:%s",string(b)) } time.Sleep(time.Second * time.Duration(this.Interval)) } fmt.Printf("%s 退出执行%s\n",this.Name) }
type.go
package new import "sync" type ConfigJson struct { Group string `json:group` CmdDirPath string `json:cmddirpath` AutoUpdate bool `json:autoupdate` GetVersionServer string `json:vetversionserver` GetCmdAddr string `json:getcmdaddr` Listen string `json:listen` Debug bool `json:debug` Update int `json:update` } type RemoteVersionInfo struct { Name string `json:name` Md5 string `json:md5` Interval int `json:interval` Args string `json:args` } type ExecCmdInfo struct { Name string Path string M5 string Interval int Args []string Exit chan bool } type LoadCmdBaseInfo struct { Name string Path string Md5 string } type FuncMap struct { Map map[string]*ExecCmdInfo Lock *sync.RWMutex }
usefunc.go
package new import ( "crypto/md5" "fmt" "io" "net/http" "os" "strings" "time" ) var LocalCmdList []LoadCmdBaseInfo func run(path string,info os.FileInfo,err error) error { if err != nil { return err } if info.IsDir() { return nil } m5 := Md5(path) if m5 != "" { LocalCmdList = append(LocalCmdList,LoadCmdBaseInfo{info.Name(),path,m5}) } return nil } func Md5(path string) string { File,err := os.Open(path) if err != nil { fmt.Printf("%s 校验%s的md5出错:%s\n",err) return "" } M := md5.New() io.Copy(M,File) b := M.Sum([]byte{}) return fmt.Sprintf("%x",b) } func GetNowTime() string { return time.Now().Format("2006-01-02 15:04:05") } func Download(name,url string) string { resp,err := http.Get(url) fmt.Printf("开始下载:%s\n",url) if err != nil || resp.StatusCode != 200 { fmt.Printf("%s 下载%s出错: %s\n",url,err) return "" } os.Mkdir("tmp",0644) defer resp.Body.Close() name = "tmp/" + name File,err := os.Create(name) if err != nil { fmt.Printf("%s 创建文件%s错误: %s\n",name,err) return "" } io.Copy(File,resp.Body) File.Close() return Md5(name) } func split(str string) []string { var l []string list := strings.Split(str," ") for _,v := range list { if len(v) == 0 { continue } if strings.Contains(v," ") { list := strings.Split(v," ") for _,v := range list { if len(v) == 0 { continue } l = append(l,v) } continue } l = append(l,v) } return l } func MoveFile(s,d string) bool { fmt.Println(s,d) sFile,err := os.Open(s) if err != nil { fmt.Printf("移动文件%s出错:%s\n",s,err) return false } defer sFile.Close() dFile,err := os.Create(d) if err != nil { fmt.Printf(" 新建文件%s出错:%s\n",err) return false } defer dFile.Close() io.Copy(dFile,sFile) return true } func Contain(k string,list []RemoteVersionInfo) bool { for _,v := range list { if k == v.Name { return true } } return false }
main.go
package main import ( "fmt" "new" "time" ) func main() { j := new.Getconfig() go func() { for { new.R() time.Sleep(time.Second * time.Duration(j.Update)) } }() select {} }下面是:实例的配置文件:cfg.json
{"Group":"gyc",
"CmdDirPath":"D:/code/201508221",
"AutoUpdate":true,
"GetVersionServer":"127.0.0.1:2789",
"GetCmdAddr":"http://127.0.0.1:1789/pkg/",
"Listen":":1789",
"Debug":true,
"Update":60}原文链接:https://www.f2er.com/go/190088.html