文档章节

我第1个可用的golang小程序

老匡
 老匡
发布于 2014/04/24 13:28
字数 1432
阅读 408
收藏 0
  1. 为什么要写这个程序

  2. 寻找时间同步Api

  3. 编写同步程序

  4. 封装成windows service

 

  1. 为什么要写这个程序

            一直在关注golang, 从2011年4月配置环境写了个hello world!(http://weibo.com/1409046677/wr4n3vdYFV), 后来没再动手写golang代码了, 只是关注golang方面的信息/资讯。去年到今年稍认真的看了一些golang的基础语法,然后前几天家里的台式电脑又开机用了一下,可能是bios电池没电的原因吧, 总是开机后需要重新设置时间。于是就想不如用golang写个windows服务自动更新系统时间,就这样才有了今天这个golang小程序。

            另外可能有人会说使用windows时间同步功能就行了,没必要麻烦。这里我的主要目的是突然兴趣所使想用golang练手,其次是windows自带的时间同步功能可能由于墙的原因不好使,反正我手动同步都没有成功过,如图:

  2. 寻找时间同步Api

            搜索资料时了解到Network Time Protocol(NTP)(http://baike.baidu.com/view/60648.htm)能很精准的同步时间,我不要那么高要求,只是获取最新时间,更新时间就行,所以得在网上找个可用的能获取最新时间API,开始担心墙,想找国内的,百度有一个http://open.baidu.com/special/time/,但是个页面,不是我想要的,最后确定使用国外的http://www.earthtools.org/timezone-1.1/35.86166/104.195397

  3. 编写同步程序

            挺简单的一个小程序,思路就是获取API数据,取到时间,然后设置到系统时间,由于还不熟悉golang的api,不知道怎样使用golang来更新系统时间,后来就使用golang调用dos命令来更新。

            在编写代码时,明明知道在golang中,大小写分别表示public和private,但在定义Timezone结构体,里面的变量使用小写,导致总是取不到正确的数据,折腾了不少时间。另外为了ISO-8859-1编码花了点时间找资料。

  4. 封装成windows service

            关于编写windows service,开始搜索到资料http://sanatgersappa.blogspot.com/2013/07/windows-service-with-go-easy-way.html (需要翻墙),感觉NSSM(http://nssm.cc/)挺方便的,考虑使用NSSM程序,使用NSSM把exe程序注册成windows服务,命令:

            nssm install MyService d:\MyService.exe
            (where d:\MyService.exe is the full path to the exe file).

    我当时就写了2个批处理程序CreateService.bat:
    nssm install SyncSystemTimeService %CD%\SyncSystemTim.exe

    DeleteService.bat:
    sc delete SyncSystemTimeService

            windows服务注册成功了,启动SyncSystemTimeService服务后,我的程序好像并没有生效,我怀疑要么必须使用完整的路径(不能用%CD%变量), 要么可能使用%~dp0而不是%CD%(参考资料:http://stackoverflow.com/questions/16255184/how-do-i-find-the-current-directory-of-a-batch-file-and-then-use-it-for-the-pat),后来我没有继续查原因,在网上找到了使用golang写的一个服务程序:http://bitbucket.org/kardianos/service, 把代码拉下来,调整一下就达到我想要的要求。

    附上完整代码:

    config.txt:

     10s



    SyncSystemTime.go

    package main
    
    import (
      "bitbucket.org/kardianos/service"
      "bitbucket.org/kardianos/osext"
      "encoding/xml"
      "errors"
      "fmt"
      "io"
      "io/ioutil"
      "net/http"
      "os"
      "os/exec"
      "time"
    )
    
    var log service.Logger
    
    func main() {
      var name = "SyncSystemTimeService"
      var displayName = "Sync System Time Service"
      var desc = "同步更新window时间服务(通过网络获取最新时间)"
    
      var s, err = service.NewService(name, displayName, desc)
      log = s
    
      if err != nil {
        fmt.Printf("%s unable to start: %s", displayName, err)
        return
      }
      if len(os.Args) > 1 {
        var err error
        verb := os.Args[1]
    
        switch verb {
          case "install":
            err = s.Install()
            if err != nil {
              fmt.Printf("Failed to install: %s\n", err)
              return
            }
            fmt.Printf("Service \"%s\" installed.\n", displayName)
          case "remove":
            err = s.Remove()
            if err != nil {
              fmt.Printf("Failed to remove: %s\n", err)
              return
            }
            fmt.Printf("Service \"%s\" removed.\n", displayName)
          case "run":
            DoWork()
          case "start":
            err = s.Start()
            if err != nil {
              fmt.Printf("Failed to start: %s\n", err)
              return
            }
            fmt.Printf("Service \"%s\" started.\n", displayName)
          case "stop":
            err = s.Stop()
            if err != nil {
              fmt.Printf("Failed to stop: %s\n", err)
              return
            }
            fmt.Printf("Service \"%s\" stopped.\n", displayName)
        }
        return
      }
    
      err = s.Run(func() error {
        go DoWork()
        return nil
      }, func() error {
        StopWork()
        return nil
      })
    
      if err != nil {
        s.Error(err.Error())
      }
    }
    
    func DoWork() {
      log.Info("I'm Running!")
    
      const defaultSchedulingTime = "30m" // minutes
      schedulingTime := defaultSchedulingTime
      configFile := "config.txt"
      configPath, err := osext.ExecutableFolder()
      if err != nil {
        log.Warning(err.Error())
      } else {
        configFile = configPath + configFile
      }
      log.Info(configFile)
    
      timeConfig, err := ioutil.ReadFile(configFile)
      if err == nil {
        schedulingTime = string(timeConfig)
      } else {
        log.Warning(err.Error())
      }
      timeDuration, err := time.ParseDuration(schedulingTime)
      if err != nil {
        log.Warning(err.Error())
        timeDuration, err = time.ParseDuration(defaultSchedulingTime)
      }
    
      Go()
      timer := time.NewTicker(timeDuration)
      for {
        select {
          case <-timer.C:
          Go()
        }
      }
      select {}
    }
    
    func StopWork() {
      log.Info("I'm Stopping!")
    }
    
    func Go() {
      networkTime := GetNetworkTime()
      SetSystemTime(networkTime)
    }
    
    /**1.035.86166104.1953978H21 Apr 2014 23:08:092014-04-21 23:08:09 +08002014-04-21 15:08:09Unknown*/
    type Timezone struct {
      LocalTime string `xml:"localtime"`
      IsoTime string `xml:"isotime"`
      UtcTime string `xml:"utctime"`
    }
    
    func charsetReader(charset string, r io.Reader) (io.Reader, error) {
      if charset == "ISO-8859-1" || charset == "iso-8859-1" {
        return r, nil
      }
      return nil, errors.New("Unsupported character set encoding: " + charset)
    }
    
    func GetNetworkTime() string {
      const web_service_url = "http://www.earthtools.org/timezone-1.1/35.86166/104.195397"
      result, err1 := http.Get(web_service_url)
      if err1 != nil {
        log.Warning(err1.Error())
        return ""
      }
      defer result.Body.Close()
    
      var timezone Timezone
      data := xml.NewDecoder(result.Body)
      data.CharsetReader = charsetReader
      if err := data.Decode(&timezone); err != nil {
        return ""
      }
    
      // fmt.Println(timezone.UtcTime)
      return timezone.UtcTime
    }
    
    func SetSystemTime(dateTime string) {
      if dateTime == "" {
        return
      }
    
      currentTime := time.Now().Format("2006-01-02 15:04:05")
      logContent := currentTime
      convertTime, err1 := time.Parse("2006-01-02 15:04:05", dateTime)
      if err1 != nil {
        log.Warning(err1.Error())
        return
      }
    
      convertTime = convertTime.Add(8 * time.Hour)
      logContent = logContent + " --> " + convertTime.Format("2006-01-02 15:04:05")
      compareValue := convertTime.Format("2006-01-02 15:04:05")
      // 如果时间(年月日时分)相同,则不更新
      if currentTime[0:len(currentTime)-3] == compareValue[0:len(compareValue)-3] {
        log.Info("same time, not to update: " + currentTime + " | " + compareValue)
        return
      }
    
      _, err2 := exec.Command("CMD", "/C", "DATE", convertTime.Format("2006-01-02")).Output()
      if err2 != nil {
        log.Error(err2.Error())
      }
      _, err2 = exec.Command("CMD", "/C", "TIME", convertTime.Format("15:04:05")).Output()
      if err2 != nil {
        log.Error(err2.Error())
      }
      currentTime = time.Now().Format("2006-01-02 15:04:05")
      logContent = logContent + " --> " + currentTime
      log.Info(logContent)
      // WriteLogFile(logContent)
    }
    
    /*
    func WriteLogFile(logContent string) error {
      fileName := "log.txt"
      file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
      if err != nil {
        log.Fatal("error opening file: %v", err)
        return err
      }
      defer file.Close()
    
      log.SetOutput(file)
      log.Println(logContent)
      return nil
    }
    */
  5.  

    BTW: 由于log打印的日志可以在windows的事件查看器中看到,所以我把WriteLogFile函数注释掉了。

    小程序,小功能,请轻拍,欢迎批评意见。

© 著作权归作者所有

老匡

老匡

粉丝 1
博文 1
码字总数 1432
作品 0
广州
私信 提问
《Python入门》学习笔记之Golang

Golang爱好者的《Python入门》学习笔记。 网址:http://www.imooc.com/learn/177 老师:廖雪峰 第1章 课程介绍 1-1 课程介绍 Golang的特点:优雅、明确、简单。 Golang适合的领域:Web网站和...

学习吧
2015/10/17
48
0
基于Docker的Golang开发

版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/50651258 基于Docker的Golang开发 作者:chszs,未经博主允许不得转载。经许可...

chszs
2016/02/11
0
0
『Go 语言学习专栏』-- 第五期

大家好,我叫谢伟,是一名程序员。 我正在着手扎实学习 golang , 这个专栏是我的输出整理文章。 希望能带有心人一起学习 golang , 力求从入门到中级程序员水平。 我们已经研究了: Golang 环...

谢小路
2018/05/11
0
0
Go笔记 2.1 第1个程序 helloworld

我正在学习酷酷的 Golang,可点此查看帖子Golang学习笔记汇总。 1 程序介绍 在 C:Gotest 目录下,有一堆官方提供的示例。找到大部分教程都推荐的第1个程序,helloworld.go。 发现没有,每个语...

iotisan
2018/03/23
0
0
Golang学习之GOROOT、PATH、GOPATH及go get

GOROOT GOPATH 及 PATH 设置 a.添加系统变量GOROOT:安装完Go第一件事就是设置GOROOT。例如我的Go安装在C:Go目录,则要设置 GOROOT = C:Go b.修改环境变量PATH:将%GOROOT%bin加到环境变量P...

yearnfar
2013/12/24
25K
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二)

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二) List 类型的操作 1、 向列表左侧添加数据 Long leftPush = redisTemplate.opsForList().leftPush("name", name); 2、 向列表右......

TcWong
今天
7
0
排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
昨天
4
0
mysql概览

学习知识,首先要有一个总体的认识。以下为mysql概览 1-架构图 2-Detail csdn |简书 | 头条 | SegmentFault 思否 | 掘金 | 开源中国 |

程序员深夜写bug
昨天
10
0
golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web

micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go-micro环境, golang微服务框架...

非正式解决方案
昨天
9
0
前端——使用base64编码在页面嵌入图片

因为页面中插入一个图片都要写明图片的路径——相对路径或者绝对路径。而除了具体的网站图片的图片地址,如果是在自己电脑文件夹里的图片,当我们的HTML文件在别人电脑上打开的时候图片则由于...

被毒打的程序猿
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部