0%

Golang学习(二)

闲扯

2017年的最后一天啦!今天是最后一批90后(1999年12月31日出生)的18岁生日,祝他们生日快乐!明天就是2018年啦,意味着90后已经全部成年,逐步成为社会的中流砥柱啦!
顺便吐槽下,被朋友圈的18岁照片刷屏啦,岁月是把杀猪刀,18岁的少年们都去哪啦!小伙伴们不要只顾着工作,也注意下身体啊,要变强不要变秃啊!不要变成油腻的中年胖子啊!

祝自己在2018年能更加努力,锻炼好身体!还有很多很多事情等我去做!Fight!

言归正传,在Golang学习(一)那篇博客中我们写了一个小工具,当时还没学习到并发,正好元旦假期学习了下并发,来实践改进下我们的工具。


正文

遗留Bug修复

文件复制过程中异常报错,程序没有退出的问题已经修复,创建文件和打开文件时加入了panic

1
srcFile, err := os.Open(src)
2
if err != nil {
3
    fmt.Println(err)
4
    panic("打开文件错误!")
5
}
6
defer srcFile.Close()
7
desFile, err := os.Create(des)
8
if err != nil {
9
    fmt.Println(err)
10
    panic("创建文件错误!")
11
}
12
defer desFile.Close()

使用Golang的gorutine来并发,提高性能

创建一个容量与期望生成文件个数大小相当的布尔型的chan,然后循环执行任务,每次向chan写入一个值,并通过读取chan来阻塞main函数的结束,伪代码如下

1
func main() {
2
    c := make(chan bool, *generateFileNumber)
3
    for count := 0; count < *generateFileNumber; count++ {
4
        dosomething(c)
5
    }
6
    <-c
7
}
8
func dosomething(c) {
9
    do
10
    c <- true
11
}

代码

1
/*
2
 * User: toddlerya
3
 * Date: 2017/12/23
4
 * Update: 2017/12/31
5
 * ds接入模块加压工具
6
 */
7
8
package main
9
10
import (
11
	"flag"
12
	"fmt"
13
	"io"
14
	"math/rand"
15
	"os"
16
	"runtime"
17
	"strconv"
18
	"strings"
19
	"time"
20
)
21
22
func judgeExists(name string) bool {
23
	if _, err := os.Stat(name); err != nil {
24
		if os.IsNotExist(err) {
25
			return false
26
		}
27
	}
28
	return true
29
}
30
31
func copyFile(c chan bool, src, des string) (w int64, err error) {
32
	srcFile, err := os.Open(src)
33
	if err != nil {
34
		fmt.Println(err)
35
		panic("打开文件错误!")
36
	}
37
	defer srcFile.Close()
38
39
	desFile, err := os.Create(des)
40
	if err != nil {
41
		fmt.Println(err)
42
		panic("创建文件错误!")
43
	}
44
	defer desFile.Close()
45
46
	c <- true
47
48
	return io.Copy(desFile, srcFile)
49
}
50
51
func generateRandomNumber(start int, end int) int {
52
	if end < 0 || start < 0 || (end-start) <= 0 {
53
		fmt.Println("[-] 随机数起始值[start]必须大于等于0, 截至值[end]必须大于起始值!")
54
		fmt.Printf("[-] 请检查配置是否正确: start=%v, end=%v\n", start, end)
55
		panic("随机数参数错误")
56
	}
57
	rand.Seed(time.Now().UnixNano())
58
	num := rand.Intn((end - start)) + start
59
	return num
60
}
61
62
func main() {
63
	runtime.GOMAXPROCS(runtime.NumCPU())
64
	srcFilePath := flag.String("s", "/home", "输入原始文件路径")
65
	dstFilePath := flag.String("d", "/tmp", "目标输出路径")
66
	renameFormat := flag.String("f", "abc-{random1}-456_780_{random2}.zip", "参数原始文件替换格式")
67
	random1Start := flag.Int("r1s", 100, "随机参数1的起始值")
68
	random1End := flag.Int("r1e", 999, "随机参数1的截止值")
69
	random2Start := flag.Int("r2s", 100000, "随机参数2的起始值")
70
	random2End := flag.Int("r2e", 999999, "随机参数2的截止值")
71
	generateFileNumber := flag.Int("g", 10000, "需要生成的文件个数")
72
	flag.Parse()
73
	fmt.Printf("[srcFilePath]          :%s\n", *srcFilePath)
74
	fmt.Printf("[dstFilePath]          :%s\n", *dstFilePath)
75
	fmt.Printf("[renameFormat]         :%s\n", *renameFormat)
76
	fmt.Printf("[random1Start]         :%v\n", *random1Start)
77
	fmt.Printf("[random1End]           :%v\n", *random1End)
78
	fmt.Printf("[random2Start]         :%v\n", *random2Start)
79
	fmt.Printf("[random2End]           :%v\n", *random2End)
80
	fmt.Printf("[generateFileNumber]   :%v\n", *generateFileNumber)
81
	fmt.Println("=======================================")
82
	t1 := time.Now()
83
	if judgeExists(*srcFilePath) {
84
		c := make(chan bool, *generateFileNumber)
85
		fmt.Println("[+] 开始随机生成目标数据, 请注意", *dstFilePath, "目录是否有数据生成\t", "计划生成", *generateFileNumber, "个文件Orz")
86
		for count := 0; count < *generateFileNumber; count++ {
87
			random1Val := generateRandomNumber(*random1Start, *random1End)
88
			random2Val := generateRandomNumber(*random2Start, *random2End)
89
			temp := strings.Replace(*renameFormat, "{random1}", strconv.Itoa(random1Val), -1)
90
			newFileName := strings.Replace(temp, "{random2}", strconv.Itoa(random2Val), -1)
91
			dstJoinList := []string{*dstFilePath, newFileName}
92
			newDstFilePath := strings.Join(dstJoinList, "/")
93
			// fmt.Println("[*] 输出随机生成的文件: ", newDstFilePath)
94
			go copyFile(c, *srcFilePath, newDstFilePath)
95
		}
96
		<-c
97
		fmt.Println("[+] 已经完成目标: 共计生成", *generateFileNumber, "个文件")
98
	} else {
99
		fmt.Println("[-] 原始文件不存在, 请检查: ", *srcFilePath)
100
	}
101
	elapsed := time.Since(t1)
102
	fmt.Println("运行耗时: ", elapsed)
103
}

使用效果如下

没有使用gorutine之前

使用gorutine后的并发效果

异常处理

第一次自己写的代码遇到这个文件打开数过多的问题,还有点小激动呢!查看下系统当前的ulimit open files配置值

果断手动改大些(个人笔记本,不会经常进新大量IO操作,就临时改下吧)ulimit -n 65535

修改后的使用效果就是上面的并发测试那个图啦!

后续

其实还有一个使用了sync的版本,但是测试效果和没有使用并发的测试结果一样,甚至还要差一些,还是不懂的太多,留个坑,等搞明白了再来补充。

相关伪代码逻辑如下

1
func main() {
2
    wg := sync.WaitGroup{}
3
	wg.Add(*generateFileNumber)
4
    for count := 0; count < *generateFileNumber; count++ {
5
        dosomething(&wg)
6
    }
7
    wg.Wait()
8
}
9
func dosomething(wg *sync.WaitGroup) {
10
    do
11
    wg.Done()
12
}
13
{% endcodeblock %}

2017年12月31日 于 南京
Email
GitHub