分享一个小东西,自动到指定的地方下载动态版本的命令,然后根据配置循环执行调用.

原创 2015年11月18日 17:37:32

写此小程序的原因是,研究小米的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", 30, ""}}
	//这些内容命令版本多的时候可以用数据库来控制,不多就随便自己搞搞吧,
	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", GetNowTime(), 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", GetNowTime(), 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", GetNowTime(), 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", r.Name), v.Path) {
		return
	}
	var g chan bool = make(chan bool, 1)
	E := &ExecCmdInfo{r.Name, v.Path, r.Md5, r.Interval, split(r.Args), g}
	CmdFuncMap.Map[r.Name] = E
	go E.Run()
}

func (this *ExecCmdInfo) Run() {
	fmt.Printf("%s 开始执行%s\n", GetNowTime(), 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", this.Name, string(b))
		}
		time.Sleep(time.Second * time.Duration(this.Interval))
	}
	fmt.Printf("%s 退出执行%s\n", GetNowTime(), 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", GetNowTime(), path, 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", GetNowTime(), 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", GetNowTime(), 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", s, 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}




版权声明:本文为博主原创文章,交流邮箱czxichen@163.com,交流群:259693140(此群不允许闲聊)

循环执行存储过程

有这样一个需求,一存储过程有一个datetime类型的输入参数,现要将2010年7月的每一天作为输入,执行存储过程。实现脚本如下: DECLARE @RC intDECLARE @order_date...
  • karen_wang
  • karen_wang
  • 2011年03月28日 18:27
  • 2310

php循环建立新的文件根据文件名移动文件到指定文件夹修改文件名称

写一个php文件放到你想要批量处理的文件目录下,运行文件即可完成 注意:确认你的文件名是不是时间戳,还有文件里的php文件,要加上判断!不要误删了! set_time_limit(0); //...
  • hj960511
  • hj960511
  • 2016年07月01日 14:41
  • 734

输入参数能动态调决定调用哪个实现类 spring的一个特性

今天做公司的以前项目的时候发现项目中有个特别好的东西,记录下来,分享一下 发现spring有个这样的功能,我也不知道这个是东西应该怎么称呼,就是通过输入参数,动态决定调用接口的实现类。简单理解就是在...
  • dyllove98
  • dyllove98
  • 2013年06月28日 23:22
  • 5125

利用Handler循环调用自身,实现在线程中无限循环的功能

如果某个功能需要实现无限循环,肯定是不能在UI线程中执行的,xiangd
  • aishang5wpj
  • aishang5wpj
  • 2014年06月24日 10:00
  • 2545

C#动态选择调用某个指定内部函数

C#动态选择调用某个指定内部函数
  • python
  • python
  • 2016年03月09日 16:01
  • 831

自我执行的匿名函数是如何工作的

在理解自我执行函数之前我们先理解一下函数声明函数,函数表达式,匿名函数他们之间的区别 运用function  fname(){.......}    //使用function关键字,然后指定...
  • MacGuffinBox
  • MacGuffinBox
  • 2016年12月22日 08:49
  • 424

linux 开机自动执行脚本或者一些指定的程序

环境:xp电脑主机+vm虚拟机+fedora9 先说点实在的东西,在虚拟机上要上电启动后自动挂载自己在xp上的共享文件夹,那么你可以这样: vim /etc/rc.local 把你要...
  • zhaoyue007101
  • zhaoyue007101
  • 2013年03月05日 15:42
  • 5139

python利用字典保存配置实现动态调用模块类方法

python利用字典保存配置实现动态调用模块类方法
  • 5iasp
  • 5iasp
  • 2014年04月09日 10:41
  • 3829

struts.xml及动态方法调用

struts.xml详解 1. 用于配置struts常量的        name:struts提供固定常量名称。此名称从 default.properties文件获得        value:常...
  • CSDN_GIA
  • CSDN_GIA
  • 2017年01月14日 13:42
  • 539

循环调用存储过程代码

发现SQL存储过程中中批量循环 修改数据表中的字段内容时把历史的字段数值插入到另外的数据表中操作比在ASP中要简单多,速度快多了.实际业务应用范围可以是.从公司历史定单数据表中查询某段时间内的一批用户...
  • xqf222
  • xqf222
  • 2007年11月15日 22:42
  • 4233
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:分享一个小东西,自动到指定的地方下载动态版本的命令,然后根据配置循环执行调用.
举报原因:
原因补充:

(最多只允许输入30个字)