分类目录归档:Go语言

golang mssqlserver复制记录总数对比工具

使用mssqlserver复制备份,某天突然发现复制显示一切正常,但是记录数又不一样,明显是停止了,虽然还没有搞懂怎么回事,所以想了这样一个工具来检查复制。程序用go写的,顺便练习一下golang 代码。

package main

import (
	"fmt"
	"log"
	//	"os"
	//	"strings"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mssql"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"github.com/lxn/walk"
	. "github.com/lxn/walk/declarative"
	"github.com/lxn/win"
)

var (
	err         error
	sqliteDB    *gorm.DB
	mssqlDB_src *gorm.DB
	mssqlDB_dsc *gorm.DB
	tvChan      chan TbRow
)

type DsnEnrty struct {
	Id   uint   `gorm:"primary_key"`
	Name string `gorm:"size:64"`
	Src  string `gorm:"size:512"`
	Dest string `gorm:"size:512"`
}
type MyMainWindow struct {
	*walk.MainWindow
	model        *EnvModel
	lb           *walk.ListBox
	te           *walk.TextEdit
	addButton    *walk.Action
	deleteButton *walk.Action
	Name_edit    *walk.LineEdit
	Src_edit     *walk.LineEdit
	Dest_edit    *walk.LineEdit
	Tv           *walk.TableView
	ModelTV      *TvModel
}

type EnvModel struct {
	walk.ListModelBase
	items []DsnEnrty
}

func NewEnvModel() *EnvModel {
	//env := os.Environ()
	//var ListDsn []DsnEnrty
	//ListDsn, err = sqliteDB.Model(&DsnEnrty{}).Find(&ListDsn)

	m := &EnvModel{items: make([]DsnEnrty, 100)}

	sqliteDB.Find(&m.items)
	return m
}

func (m *EnvModel) ItemCount() int {
	return len(m.items)
}

func (m *EnvModel) Value(index int) interface{} {
	return m.items[index].Name
}

type TbRow struct {
	Index   string
	Tname   string
	SrcNum  int
	DestNum int
	Result  string
}

type TvModel struct {
	walk.TableModelBase
	items []TbRow
}

func NewTvModel() *TvModel {
	//m := new(TvModel)
	//m.items = make([]TbRow, 100)
	m := &TvModel{items: []TbRow{}}
	//m.ResetRows()
	return m
}
func (m *TvModel) ResetRows() {
	//m.items = make([]TbRow, 1)
	item := TbRow{}

	item.Tname = "fy"
	item.SrcNum = 100
	item.DestNum = 200
	m.items = append(m.items, item)
	//m.items[0] = item
	m.PublishRowsReset()
}
func (m *TvModel) RowCount() int {
	return len(m.items)
}
func (m *TvModel) Value(row, col int) interface{} {
	item := m.items[row]

	switch col {
	case 0:
		return item.Index
	case 1:
		return item.Tname

	case 2:
		return item.SrcNum

	case 3:
		return item.DestNum

	case 4:
		return item.Result
	}

	panic("unexpected col")
}

func (mw *MyMainWindow) lb_CurrentIndexChanged() {
	idx := mw.lb.CurrentIndex()
	if idx < 0 || idx > len(mw.model.items) {
		return
	}
	dsn := mw.model.items[idx]
	mw.Name_edit.SetText(dsn.Name)
	mw.Src_edit.SetText(dsn.Src)
	mw.Dest_edit.SetText(dsn.Dest)
}

func (mw *MyMainWindow) lb_ItemActivated() {
	value := mw.model.items[mw.lb.CurrentIndex()].Dest

	walk.MsgBox(mw, "Value", value, walk.MsgBoxIconInformation)
}
func (mw *MyMainWindow) tv_showtableview() {
	for {
		select {
		case row := <-tvChan:
			//fmt.Printf("%#v\n", row)
			mw.ModelTV.items = append(mw.ModelTV.items, row)
			mw.ModelTV.PublishRowsReset()
		}
	}
}

type tablelist struct {
	Name string
}

func (mw *MyMainWindow) run_get_sql_tables_info() {
	go mw.get_sql_tables_info()
}
func (mw *MyMainWindow) get_sql_tables_info() {

	idx := mw.lb.CurrentIndex()
	if idx < 0 {
		walk.MsgBox(mw, "出错了", "请选择左边条目再操作", walk.MsgBoxIconError)
		return
	}
	conninfo := mw.model.items[idx]
	mssqlDB_src, err = gorm.Open("mssql", conninfo.Src)
	_, err = get_mssql_version(mssqlDB_src)
	if err != nil {
		walk.MsgBox(mw, "连接源服务器出错", err.Error(), walk.MsgBoxIconError)
		return
	}
	mssqlDB_dsc, err := gorm.Open("mssql", conninfo.Dest)
	_, err = get_mssql_version(mssqlDB_src)
	if err != nil {
		walk.MsgBox(mw, "连接源服务器出错", err.Error(), walk.MsgBoxIconError)
		return
	}
	rows, err := mssqlDB_src.Raw("SELECT Name FROM SysObjects Where XType='U' ORDER BY Name").Rows()
	defer rows.Close()

	//var tmp string
	//var tblist []string
	//var tab_items []TbRow
	for rows.Next() {
		var tmp string
		rows.Scan(&tmp)

		tb := TbRow{}
		tb.Index = conninfo.Name
		tb.Tname = tmp
		mssqlDB_src.Table(tmp).Count(&tb.SrcNum)
		mssqlDB_dsc.Table(tmp).Count(&tb.DestNum)
		//tab_items = append(tab_items, tb)
		if tb.SrcNum == tb.DestNum {
			tb.Result = "-"
		} else {
			tb.Result = "X"
		}
		tvChan <- tb
	}
	defer mssqlDB_src.Close()
	defer mssqlDB_dsc.Close()
}
func get_mssql_version(db *gorm.DB) (versions string, err error) {
	sqlRow := db.Raw("select @@VERSION").Row()

	err = sqlRow.Scan(&versions)
	if err != nil {
		return "", err
	}
	return versions, nil
}

func main() {

	sqliteDB, err = gorm.Open("sqlite3", "./sqlserver_sync_checker.db")
	if err != nil {
		log.Fatal("sqlite3 open error:", err)
	}
	if !sqliteDB.HasTable(&DsnEnrty{}) {
		sqliteDB.AutoMigrate(&DsnEnrty{})
	}

	mw := &MyMainWindow{model: NewEnvModel(), ModelTV: NewTvModel()}
	tvChan = make(chan TbRow, 10)
	go mw.tv_showtableview()

	if _, err := (MainWindow{
		AssignTo: &mw.MainWindow,
		Title:    "mssql server 表记录总数对比",
		MinSize:  Size{600, 520},
		Size:     Size{600, 600},
		Layout:   VBox{MarginsZero: true},
		//ToolBar:  mw.newToolBar(),
		Children: []Widget{
			HSplitter{
				Children: []Widget{
					Composite{
						Layout: Grid{Columns: 2},
						Children: []Widget{
							ListBox{
								AssignTo: &mw.lb,
								Model:    mw.model,
								OnCurrentIndexChanged: mw.lb_CurrentIndexChanged,
								OnItemActivated:       mw.lb_ItemActivated,
							},
							Label{
								ColumnSpan: 2,
								Text:       "名称:",
							},
							LineEdit{
								AssignTo:   &mw.Name_edit,
								ColumnSpan: 2,
								Text:       "新名字",
							},
							Label{
								ColumnSpan: 2,
								Text:       "源DSN:",
							},
							LineEdit{
								AssignTo:   &mw.Src_edit,
								ColumnSpan: 2,
								Text:       "sqlserver://username:password@localhost:1433?database=dbname;encrypt=disable",
							},
							Label{
								ColumnSpan: 2,
								Text:       "目标DSN:",
							},
							LineEdit{
								AssignTo:   &mw.Dest_edit,
								ColumnSpan: 2,
								Text:       "sqlserver://username:password@localhost:1433?database=dbname;encrypt=disable",
							},
							PushButton{
								// AssignTo: &acceptPB,
								Text:       "更新",
								ColumnSpan: 2,
								OnClicked: func() {
									idx := mw.lb.CurrentIndex()
									if idx < 0 {
										walk.MsgBox(mw, "出错了", "请选择数据再操作", walk.MsgBoxIconError)
										return
									}

									dsn := mw.model.items[idx]
									dsn.Name = mw.Name_edit.Text()
									dsn.Src = mw.Src_edit.Text()
									dsn.Dest = mw.Dest_edit.Text()
									sqliteDB.Model(&DsnEnrty{}).Where("id=?", dsn.Id).Update(&dsn)
									//mw.lb.SetModel(NewEnvModel())
									//mw.model = NewEnvModel()
									mw.model = NewEnvModel()
									mw.lb.SetModel(mw.model)
									walk.MsgBox(mw, "提示", "保存成功", walk.MsgBoxIconInformation)
								},
							},
							PushButton{
								// AssignTo:  &cancelPB,
								Text:       "新建",
								ColumnSpan: 2,
								OnClicked: func() {
									dsn := DsnEnrty{}
									dsn.Name = mw.Name_edit.Text()
									dsn.Src = mw.Src_edit.Text()
									dsn.Dest = mw.Dest_edit.Text()
									sqliteDB.Model(&DsnEnrty{}).Create(&dsn)
									mw.model = NewEnvModel()
									mw.lb.SetModel(mw.model)
									walk.MsgBox(mw, "提示", "保存成功", walk.MsgBoxIconInformation)
								},
							},
							PushButton{
								// AssignTo:  &cancelPB,
								Text:       "删除",
								ColumnSpan: 2,
								OnClicked: func() {
									idx := mw.lb.CurrentIndex()
									if idx < 0 {
										walk.MsgBox(mw, "出错了", "请选择数据再操作", walk.MsgBoxIconError)
										return
									}
									dsn := mw.model.items[idx]
									message := fmt.Sprintf("确定要删除记录 '%s'?", dsn.Name)
									ret := walk.MsgBox(mw, "删除记录", message, walk.MsgBoxYesNo)
									if ret == win.IDYES {

										sqliteDB.Where("id=?", dsn.Id).Delete(DsnEnrty{})
										mw.model = NewEnvModel()
										mw.lb.SetModel(mw.model)
										walk.MsgBox(mw, "提示", "删除成功", walk.MsgBoxIconInformation)
									}

								},
							},
							PushButton{
								// AssignTo:  &cancelPB,
								Text:       "源-测试连接",
								ColumnSpan: 2,
								OnClicked: func() {
									dsn := mw.Src_edit.Text()
									if len(dsn) == 0 {
										walk.MsgBox(mw, "出错了", "DSN不能为空", walk.MsgBoxIconError)
										return
									}
									db, err := gorm.Open("mssql", dsn)
									defer db.Close()
									sqlver, err := get_mssql_version(db)
									if err != nil {
										walk.MsgBox(mw, "出错了", err.Error(), walk.MsgBoxIconError)
									} else {
										walk.MsgBox(mw, "提示", sqlver, walk.MsgBoxIconInformation)
									}
								},
							},
							PushButton{
								// AssignTo:  &cancelPB,
								Text:       "目标-测试连接",
								ColumnSpan: 2,
								OnClicked: func() {
									dsn := mw.Src_edit.Text()
									if len(dsn) == 0 {
										walk.MsgBox(mw, "出错了", "DSN不能为空", walk.MsgBoxIconError)
										return
									}
									dbdest, err := gorm.Open("mssql", dsn)
									defer dbdest.Close()
									sqlver, err := get_mssql_version(dbdest)
									if err != nil {
										walk.MsgBox(mw, "出错了", err.Error(), walk.MsgBoxIconError)
									} else {
										walk.MsgBox(mw, "提示", sqlver, walk.MsgBoxIconInformation)
									}
								},
							},
						},
					},

					Composite{
						Layout: Grid{Columns: 2},
						Children: []Widget{
							PushButton{
								// AssignTo:  &cancelPB,
								Text:       "获取对比信息",
								ColumnSpan: 1,
								OnClicked:  mw.run_get_sql_tables_info,
							},
							PushButton{
								// AssignTo:  &cancelPB,
								Text:       "清空结果",
								ColumnSpan: 1,
								OnClicked: func() {
									mw.ModelTV.items = []TbRow{}
									mw.ModelTV.PublishRowsReset()
								},
							},

							TableView{
								ColumnSpan:            2,
								AssignTo:              &mw.Tv,
								AlternatingRowBGColor: walk.RGB(239, 239, 239),
								CheckBoxes:            false,
								ColumnsOrderable:      false,
								MultiSelection:        true,
								Columns: []TableViewColumn{
									{Title: "配置", Width: 150},
									{Title: "表", Width: 250},
									{Title: "源记录数", Width: 100},
									{Title: "目标记录数", Width: 100},
									{Title: "结果", Width: 50},
								},
								StyleCell: func(style *walk.CellStyle) {

								},
								Model: mw.ModelTV,
								OnSelectedIndexesChanged: func() {
									//fmt.Printf("SelectedIndexes: %v\n", tv.SelectedIndexes())
								},
							},
						},
					},
				},
			},
		},
	}.Run()); err != nil {
		log.Fatal(err)
	}
}

 

golang并发控制

在多线程编程中,我经常会用到2个方面的线程控制,一是启停线程,二是控制线程数量,在golang中,启动的是协程,一个比线程更好的东西。为了实现这两个功能,在github  fork  grtm 并进行了功能增强

更多例子请看 github.com/fy138/grtm

一、启停线程

package main

import (
	"fmt"
	//	"runtime"
	"time"

	"github.com/fy138/grtm"
)

func myfunc(me interface{}) {
	fmt.Println("hello+" + me.(string))
	time.Sleep(time.Second * 2)
}
func main() {
	gm := grtm.NewGrManager()
	//在创建gm后新建一个进程接收出错信息
	go func(gm *grtm.GrManager) {
		for {
			select {
			case err := <-gm.ErrChan:
				fmt.Println("Received error:", err.Error())
			case notify := <-gm.NotiChan:
				fmt.Println("Received Notify:", notify)
			}
		}
	}(gm)

	gm.NewLoopGoroutine("myfunc", myfunc, "1")
	gm.NewLoopGoroutine("myfunc2", myfunc, "2")
	time.Sleep(time.Second * 3)

	gm.StopLoopGoroutine("aaaaaa")
	time.Sleep(time.Second * 3)

	gm.StopLoopGoroutine("myfunc2")
	time.Sleep(time.Second * 3)

	gm.NewLoopGoroutine("myfunc", myfunc, "1")
	time.Sleep(time.Second * 3)

	for {
		for k, v := range gm.GetAllTask() {
			fmt.Printf("task name:%s,task id:%d,task name2:%s\n", k, v.Gid, v.Name)
		}
		fmt.Printf("NumTask:%d\n", gm.GetTaskTotal())
		time.Sleep(time.Second * 1)
	}
}

输出是这样的

hello+1
hello+2
hello+1
hello+2
Received error: not found goroutine name :aaaaaa
hello+1
hello+2
hello+1
Received Notify: gid[1597969999]quit
hello+1
Received error: goroutine channel already defined: "myfunc"
hello+1
hello+1
task name:myfunc,task id:5577006791947779410,task name2:myfunc
NumTask:1
task name:myfunc,task id:5577006791947779410,task name2:myfunc
NumTask:1
hello+1
task name:myfunc,task id:5577006791947779410,task name2:myfunc
NumTask:1

二、限制线程数量

package main

import (
	"fmt"
	"runtime"
	"time"

	"github.com/fy138/grtm"
)

func main() {
	go func() {
		for {
			//get  goroutine total
			fmt.Println("go goroutines:", runtime.NumGoroutine())
			time.Sleep(time.Second * 1)
		}

	}()
	//建立线程池
	pool_1 := grtm.NewPool(3)
	pool_2 := grtm.NewPool(2)
	for i := 100; i >= 1; i-- {
		fmt.Println("I=", i)
		//通过通道来限制goroutine 数量
		/* 下面是第一种调用方法 */
		pool_1.LimitChan <- true //importan
		pool_1.AddTask(Download, i, "Download_1")

		/* 下面是第二种调用方法 */
		pool_2.LimitChan <- true //importan
		go func(i int, str string) {
			Download2(i, str)
			//函数执行完释放通道
			defer func() {
				<-pool_2.LimitChan
			}()
		}(i, "Download_2")

	}
	time.Sleep(time.Second * 20) //防止主线程提前退出
}

func Download(args ...interface{}) {
	time.Sleep(2 * time.Second)
	fmt.Printf("%s => %d \n", args[0].([]interface{})[1].(string), args[0].([]interface{})[0].(int))
}
func Download2(i int, str string) {
	time.Sleep(2 * time.Second)
	fmt.Printf("%s => %d \n", str, i)
}

输出结果

I= 100
go goroutines: 4
I= 99
I= 98
go goroutines: 9
Download_2 => 100
I= 97
Download_1 => 100
go goroutines: 9
Download_2 => 99
I= 96
Download_1 => 99
Download_1 => 98
go goroutines: 8
Download_2 => 98
I= 95
Download_1 => 97
go goroutines: 8
Download_2 => 97
I= 94
Download_1 => 96
go goroutines: 8
Download_2 => 96
I= 93
Download_1 => 95
go goroutines: 8
Download_2 => 95
I= 92
Download_1 => 94
go goroutines: 8
Download_2 => 94
I= 91

 

golang 小文件写入测试程序

最近总是怀疑磁盘有问题,所以写了一个小程序,测试磁盘写入程序

package main

import (
	"fmt"
	"io/ioutil"
	//	"log"
	"os"
	"runtime"
	"strconv"
	"time"

	"github.com/lxn/walk"
	. "github.com/lxn/walk/declarative"
)

var (
	limitChan chan bool
	t         = time.Tick(time.Second)
	org_path  string
)

type MyMainWindow struct {
	*walk.MainWindow
	edit           *walk.TextEdit
	bnt_selectFile *walk.PushButton
	bnt_start      *walk.PushButton
	path           string
	sbi_gonum      *walk.StatusBarItem
	sbi_status     *walk.StatusBarItem
	file_number    *walk.LineEdit
	go_number      *walk.LineEdit
}

func (mw *MyMainWindow) bnt_selectFile_action() {

	dlg := new(walk.FileDialog)
	dlg.FilePath = mw.path
	dlg.Title = "请选择配置目录..."
	//dlg.Filter = "配置文件 (*.ini)|*.ini|所有文件 (*.*)|*.*"

	if ok, err := dlg.ShowBrowseFolder(mw); err != nil {
		//mw.edit.AppendText("Error : File Open\r\n")
		walk.MsgBox(mw, "出错了", "请选择配置目录", walk.MsgBoxIconError)
		return
	} else if !ok {
		//mw.edit.AppendText("Cancel\r\n")
		return
	}
	//mw.edit.SetText("")
	mw.path = dlg.FilePath
	org_path = mw.path //防止目录不断重复加深
	mw.sbi_status.SetText("写入目录:" + mw.path)
}
func (mw *MyMainWindow) bnt_start_action() {

	if len(mw.path) == 0 {
		//walk.MsgBox(mw, "出错了", "请选择目录", walk.MsgBoxIconError)
		mw.path = "./testdir"
		//return
	} else {
		mw.path = mw.path + "/testdir"
	}
	mw.sbi_status.SetText("写入目录:" + mw.path)
	go dotest(mw)

}

func write_file(path string, data []byte) {
	err := ioutil.WriteFile(path, data, os.ModeAppend)
	if err != nil {
		//log.Print("write file error:", err.Error())
	}
	defer func() {
		<-limitChan //释放队列
	}()
}
func show_goroutine(mw *MyMainWindow) {
	go func() {
		for {
			select {
			case <-t:
				//每秒打印一次goroutine数量
				mw.sbi_gonum.SetText(fmt.Sprintf("NumGoroutine: %d", runtime.NumGoroutine()))
			}
		}
	}()
}
func dotest(mw *MyMainWindow) {

	path := mw.path
	number, _ := strconv.Atoi(mw.file_number.Text())
	ChanNum, _ := strconv.Atoi(mw.go_number.Text())

	limitChan = make(chan bool, ChanNum)
	filecount := []byte(mw.edit.Text())
	if len(filecount) == 0 {
		walk.MsgBox(mw, "出错了", "内容不能为空", walk.MsgBoxIconError)
		return
	}

	mw.bnt_selectFile.SetEnabled(false)
	mw.bnt_start.SetEnabled(false)

	file_len := float32(len(filecount))
	util_mb := float32(1024 * 1024)
	//log.Printf("template lengh:%f mb", file_len/util_mb)

	_, err := os.Stat(path)
	if err != nil {
		os.MkdirAll(path, os.ModePerm)
	}

	t1 := time.Now().UnixNano()

	var size float32 = 0.00
	for i := 1; i < number; i++ {
		size += file_len
		//ioutil.WriteFile(, filecount, os.ModeAppend)
		limitChan <- true
		go write_file(fmt.Sprintf("%s/%d.txt", path, i), filecount)
	}
	t2 := time.Now().UnixNano()
	t_size := size / util_mb
	t_ms := (t2 - t1) / 1e6
	t_s := (t2 - t1) / 1e9
	if t_s == 0 {
		t_s = 1
	}

	mw.sbi_status.SetText(fmt.Sprintf("内容:%f kb,共写入:%f mb,花费:%d ms,每秒写入%d个文件", file_len/1024, t_size, t_ms, int64(number)/t_s))
	mw.bnt_selectFile.SetEnabled(true)
	mw.bnt_start.SetEnabled(true)
	mw.path = org_path
}
func main() {
	mw := &MyMainWindow{}

	go show_goroutine(mw)

	MW := MainWindow{
		AssignTo: &mw.MainWindow,
		Title:    "文件写入速度测试 v1.0",
		MinSize:  Size{500, 600},
		Size:     Size{500, 600},

		Layout: VBox{},
		Children: []Widget{
			Composite{
				Layout: Grid{Columns: 2},
				Children: []Widget{
					Label{
						Text: "写入文件数量:",
					},
					LineEdit{
						AssignTo: &mw.file_number,
						Text:     "1000",
					},
					Label{
						Text: "线程数量:",
					},
					LineEdit{
						AssignTo: &mw.go_number,
						Text:     "10",
					},
				},
			},
			Composite{
				Layout: Grid{Columns: 1},
				Children: []Widget{
					PushButton{
						AssignTo:  &mw.bnt_selectFile,
						Text:      "选择测试目录...",
						OnClicked: mw.bnt_selectFile_action,
					},
					PushButton{
						AssignTo:  &mw.bnt_start,
						Text:      "开始",
						OnClicked: mw.bnt_start_action,
					},

					TextEdit{
						AssignTo: &mw.edit,
						ReadOnly: false,
						VScroll:  true,
					},
				},
			},
		},
		StatusBarItems: []StatusBarItem{
			StatusBarItem{
				AssignTo: &mw.sbi_gonum,
				//Text:     "准备中...",
				Width: 120,
			},
			StatusBarItem{
				AssignTo: &mw.sbi_status,
				Text:     "准备中...",
				Width:    600,
			},
		},
	}

	if _, err := MW.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

}

编译方法请查看
https://github.com/lxn/walk

golang多线程控制,小文件写入测试

最近遇到需要测试共享盘小文件写入的问题,所以自己写了一个程序,测试小文件写入速度,源码如下

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"time"
)

var (
	limitChan   chan bool 
	t         = time.Tick(time.Second)
)

func watching() {
	fmt.Printf("NumGoroutine: %d\n", runtime.NumGoroutine())
}
func write_file(path string, data []byte) {
	err := ioutil.WriteFile(path, data, os.ModeAppend)
	if err != nil {
		log.Print("write file error:", err.Error())
	}
	defer func() {
		<-limitChan //释放队列
	}()
}
func init() {
	go func() {
		for {
			select {
			case <-t:
				watching() //每秒打印一次goroutine数量
			}
		}
	}()
}
func main() {

	path := flag.String("path", "./testdir", "test path")
	template := flag.String("template", "template.txt", "write file template")
	number := flag.Int("num", 1000, "number of files")
	ChanNum := flag.Int("gonum", 10, "number of goroutine")
	flag.Parse()

	limitChan = make(chan bool, *ChanNum) //重新定义

	filecount, err := ioutil.ReadFile(*template)
	if err != nil {
		log.Fatal("read template error:", err)
	}
	file_len := float32(len(filecount))
	util_mb := float32(1024 * 1024)
	log.Printf("template lengh:%f mb", file_len/util_mb)

	_, err = os.Stat(*path)
	if err != nil {
		os.MkdirAll(*path, os.ModePerm)
	}

	t1 := time.Now().UnixNano()
	i := 1
	var size float32 = 0.00
	for {
		size += file_len
		//ioutil.WriteFile(, filecount, os.ModeAppend)
		limitChan <- true
		go write_file(fmt.Sprintf("%s/%d.txt", *path, i), filecount)
		i++
		if i == *number {
			break
		}
	}
	t2 := time.Now().UnixNano()
	t_size := size / util_mb
	t_ms := (t2 - t1) / 1e6
	t_s := (t2 - t1) / 1e9
	if t_s == 0 {
		t_s = 1
	}
	log.Printf("write lengh:%f mb", t_size)
	log.Printf("this time is:%d ms", t_ms)
	log.Printf("write file speed:%d files of 1 second", int64(*number)/t_s)
}

本程序加入了队列控制goroutine的数量,由于对goroutine数量控制不是很好,之前写了一个小程序进行验证

package main

import (
	"fmt"
	"time"
)

var (
	limt chan bool
	t    = time.Tick(time.Second)
)

func main() {
	go func() {
		for {
			select {
			case <-t:

				for i := 1; i <= 5; i++ {
					<-limt
				}
			}
		}
	}()
	limt = make(chan bool, 10)
	i := 1
	for {
		fmt.Println(i)
		limt <- true
		i++
	}
}

参考goroutine控制
http://blog.csdn.net/yxw2014/article/details/20957429
http://blog.csdn.net/miao0916/article/details/55045894

Golang控制goroutine的启动与关闭

在用walk写一个窗口程序,当需要执行其它任务的时候,你要用一个新的线程去做,所以想到任务的停止和启动。在go中,很容易可以做到这些,因为go是通过管道通迅的。

package main

import (
	"fmt"
	"time"
)

func p() {
	fmt.Println("test")
	time.Sleep(time.Second * 3)

}
func worker(ch chan int) {
	for {
		select {
		case <-ch:
			return //收到信号就退出线程
		default:
			p()
		}
	}
}
func main() {
	ch := make(chan int)

	go worker(ch)

	time.Sleep(time.Second * 10)
	ch <- 1  //发送退出线程的命令
	fmt.Println("finish.")
	for {
	}

}

 

golang int转[]byte

今天遇到int 转[]byte的问题,在百度搜了很多结果,发现很复杂,突然想到string转[]byte非常方便,那么int转string也很方便,下面是获取程序pid并写入pid文件的代码,非常容易,os.Getpid()反回int类型,strconv.Itoa 将int转string,
[]byte将string转byte

err = ioutil.WriteFile(PidFile, []byte(strconv.Itoa(os.Getpid())), 0644)

 

golang模拟点击浏览器

发现一个很好玩的浏览器模拟操作包,下面演示一下,打开网站,点击某个链接

package main

import (
	"fmt"
	"time"

	"github.com/go-vgo/robotgo"
	"sourcegraph.com/sourcegraph/go-selenium"
)

func main() {
	var webDriver selenium.WebDriver
	var err error
	caps := selenium.Capabilities(map[string]interface{}{"browserName": "firefox"})
	if webDriver, err = selenium.NewRemote(caps, "http://localhost:9515"); err != nil {
		fmt.Printf("Failed to open session: %s\n", err)
		return
	}
	defer webDriver.Quit()

	err = webDriver.Get("http://www.yiyou.org")
	if err != nil {
		fmt.Printf("Failed to load page: %s\n", err)
		return
	}

	robotgo.MoveMouseSmooth(323, 186) //移动鼠标
	robotgo.MouseClick("left", true) //单击
	time.Sleep(time.Second * 5)
	robotgo.MoveMouseSmooth(422, 412)
	robotgo.MouseClick("left", true)
	time.Sleep(time.Second * 5)

	cookies, err := webDriver.GetCookies() //获取cookie
	if err != nil {
		fmt.Println(err)
	}
	time.Sleep(time.Second * 5)
	fmt.Print("Cookie:", cookies)
	fmt.Print(robotgo.GetMousePos())
	for {
	}
}

需要先运行chromedriver,下载地址:
https://chromedriver.storage.proxy.ustclug.org/index.html

使用golang监控目录文件变化

想写个程序,监控目录和文件变化,原先目录非常大,所以感觉要用goroutine对每个目录派生一个goroutine进程,但程序在运行的时候发现,打开的目录非常多,以致系统出错,我们先来看看这个失败的程序,目录小是没有问题的。

// main.go
package main

import (
	//	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	//	"regexp"
	"runtime"
	"strings"

	"github.com/fsnotify/fsnotify"
)

type Fm struct {
	Basedir string
	Watcher *fsnotify.Watcher
	Wdone   chan bool
	Dirchan chan string
}

func (fm *Fm) Init(basedir string) {
	fm.Basedir = filepath.FromSlash(basedir)
	var err error
	fm.Watcher, err = fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("create watcher error:", err)
	}

	fm.Wdone = make(chan bool)

	go func() {
		for {
			select {
			case event := <-fm.Watcher.Events:
				//log.Println("event:", event)
				fm.process_event(event)

			case err := <-fm.Watcher.Errors:
				log.Println("watcher error:", err)
			}
		}
	}()

	fm.Dirchan = make(chan string, 1000)
}

func (fm *Fm) walkdir(path string) {
	//log.Println("basedir:", path)
	fm.Dirchan <- path
	dir, err := ioutil.ReadDir(path)
	if err != nil {
		log.Fatal("opendir:", err)
	}
	for _, fi := range dir {

		fpath := filepath.FromSlash(path + "/" + fi.Name())
		if fi.IsDir() {

			if strings.HasPrefix(fi.Name(), ".") {
				continue
			}
			if strings.HasPrefix(fi.Name(), "..") {
				continue
			}
			if strings.Contains(fi.Name(), "lost+found") {
				continue
			}
			go fm.walkdir(fpath)
		} else {
			fm.Dirchan <- fpath
		}
		//log.Println("path:", fpath)
		//ch <- fpath
	}
	//
}
func (fm *Fm) lister() {
	var path string
	for {
		select {
		case path = <-fm.Dirchan:
			err := fm.Watcher.Add(path)
			if err != nil {
				log.Fatal("add watcher error:", err)
			}
		}
	}
}
func (fm *Fm) Start() {
	go fm.walkdir(fm.Basedir)
	go fm.lister()
	defer fm.Watcher.Close()
	<-fm.Wdone
}
func (fm *Fm) process_event(event fsnotify.Event) {
	switch event.Op {
	case fsnotify.Create:
		fm.Watcher.Add(event.Name)
		log.Println("create:", event.Name)
	case fsnotify.Rename, fsnotify.Remove:
		log.Println("remove:", event.Name)
		fm.Watcher.Remove(event.Name)
	case fsnotify.Write:
		log.Println("write:", event.Name)
	}
}
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU() / 2)
	/*
	  echo 50000000 > /proc/sys/fs/inotify/max_user_watches
	  echo 327679 > /proc/sys/fs/inotify/max_queued_events
	*/
	filem := new(Fm)
	filem.Init(os.Args[1])
	filem.Start()
}

上面程序有意思的地方是,递归目录,用多线程进行通讯,但是目录和文件很多的时候,产生的线程也非常多。

下面来个简单的例子,这个例子可以正常工作,速还不错,140G的小文件目录,大约需要1.7G虚拟内存,实占大约500m左右,由于和sersync性能相差太远,所以暂时放弃了这个监控使用。

// main.go
package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/fsnotify/fsnotify"
)

//type MyWatcher *fsnotify.Watcher
func doev(watcher *fsnotify.Watcher, event fsnotify.Event) {
	switch event.Op {
	case fsnotify.Create:
		watcher.Add(event.Name)
		log.Println("create:", event.Name)
	case fsnotify.Rename, fsnotify.Remove:
		log.Println("remove:", event.Name)
		watcher.Remove(event.Name)
	case fsnotify.Write:
		log.Println("write:", event.Name)
	}
}
func main() {
	watchdir := os.Args[1]
	var err error
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				//log.Println("event:", event)
				doev(watcher, event)

			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()
	err = watcher.Add(watchdir)
	if err != nil {
		log.Fatal(err)
	}
	err = filepath.Walk(watchdir, func(path string, info os.FileInfo, err error) error {
		err = watcher.Add(path)
		if err != nil {
			log.Fatal(err)
		}
		return nil
	})
	if err != nil {
		fmt.Printf("walk error [%v]\n", err)
	}
	<-done
}

 

用go写windows系统服务

用go+nssm非常容易实现windows系统服务,先看下面的程序

package main

func main() {
 //Call this function where the action happpens
 doStuff()
}

func doStuff() {
 for {
  //the actual stuff happens here.
 }
}

下载nssm复制到c:\windows目录,执行

nssm install MyService d:\MyService.exe

nssm下载地址:http://nssm.cc/description

参考http://sanatgersappa.blogspot.co.id/2013/07/windows-service-with-go-easy-way.html

使用go连接mssql

使用go连接mssql还是比较方便,注意连接字符串要加上

encrypt=disable
package main

import _ "github.com/denisenkom/go-mssqldb"
import "database/sql"
import "log"
import "fmt"
import "flag"

var debug = flag.Bool("debug", false, "enable debugging")
var password = flag.String("password", "", "the database password")
var port *int = flag.Int("port", 1433, "the database port")
var server = flag.String("server", "", "the database server")
var user = flag.String("user", "", "the database user")

func main() {
	flag.Parse() // parse the command line args

	if *debug {
		fmt.Printf(" password:%s\n", *password)
		fmt.Printf(" port:%d\n", *port)
		fmt.Printf(" server:%s\n", *server)
		fmt.Printf(" user:%s\n", *user)
	}

	connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;encrypt=disable", *server, *user, *password, *port)
	if *debug {
		fmt.Printf(" connString:%s\n", connString)
	}
	conn, err := sql.Open("mssql", connString)
	if err != nil {
		log.Fatal("Open connection failed:", err.Error())
	}
	defer conn.Close()

	stmt, err := conn.Prepare("select 1, 'abc'")
	if err != nil {
		log.Fatal("Prepare failed:", err.Error())
	}
	defer stmt.Close()

	row := stmt.QueryRow()
	var somenumber int64
	var somechars string
	err = row.Scan(&somenumber, &somechars)
	if err != nil {
		log.Fatal("Scan failed:", err.Error())
	}
	fmt.Printf("somenumber:%d\n", somenumber)
	fmt.Printf("somechars:%s\n", somechars)

	fmt.Printf("bye\n")

}

 

使用方法:
E:\Go_Project\mssql_t1>mssql_t1.exe –debug=true –password=123456–server=127.0.0.1 –user=sa

参考

http://studygolang.com/resources/4094

http://www.cnblogs.com/songxingzhu/p/5849148.html