Go语言中并发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package main

import (
"fmt"
"runtime"
"sync"
)

//goroutine 类似于线程 属于用户态的线程
//goroutine 由go语言运行时(runtime)调度完成 而线程由操作系统调度完成

//需要并发执行任务时 只需将这个任务包装成一个函数 开启goroutine去执行它
//一个goroutine 必定对应一个函数 可以创建多个goroutine去执行相同函数

//操作系统线程 一般有固定的栈内存(通常2MB) 一个goroutine的栈启动时的栈内存(通常2KB) 可以按需增加或缩小

//func TestFn(i int) {
// //函数出错也能释放
// defer wg.Done()
// fmt.Println("Hello World!", i)
// //任务完成后计数器减1
//
//}

//定义全局变量wg 实现goroutine同步
var wg sync.WaitGroup

//GOMAXPROCS 配置多个os线程同时执行
func a() {
defer wg.Done()
for i := 1; i < 10; i++ {
fmt.Println("A:", i)
}
}

func b() {
defer wg.Done()
//for i := 1; i < 10; i++ {
// fmt.Println("B:", i)
//}
i := 0
for {
i ++
}
}

//系统线程与goroutine关系
//一个操作系统线程对应用户态多个goroutine
//go可以同时使用多个操作系统线程
//goroutine和os线程是多对多关系 即m:n

func main() {
//for i := 0; i < 10; i ++ {
// //启动一个任务 计数器加1
// wg.Add(1)
// go TestFn(i)
//}
//wg.Wait()
//fmt.Println("This is MainFunc")
//配置go程序运行时使用多个个逻辑CPU核心 m:n n启动 线程数,通常n设置为cpu逻辑核心
runtime.GOMAXPROCS(1)
wg.Add(5)
go a()
go b()
go b()
go b()
go b()
wg.Wait()
}

Go语言中Channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main

//通道
//通过通道共享内存(先入先出)

//channel 是一种类型 是引用类型 空值nil
//语法
//var 变量 chan 元素类型
//var ch1 chan int
//var ch2 chan bool

//需要通过make初始化
//make(chan 元素类型, [缓冲大小])

//var ch3 = make(chan []int)
//var ch4 = make(chan []string)

//操作
//发送 send 接收 receive 关闭 close
//发送和接收都使用 <-

//对一个关闭的通道发送值会导致panic
//接收 一个关闭的通道 会一直获取值 知道通道为空
//接收 一个关闭且没有值的通道 会得到对应类型的零值
//关闭一个已经关闭的通道会导致panic

//无缓冲的通道 = 阻塞通道 只能在有人接收值的时候才能发送值(必须有接收才能发送) = 同步通道
//有缓冲通道 make(chan int, x) x变量通道能存放元素的数量
//可以通过len 获取通道内元素的数量 cap获取通道的容量

//从通道中循环取值 for range

//单向通道 限制通道只能接收或发送

//chan<- int 只能发送的通道(可以发送不能接收)
func counter(out chan<- int) {
for i := 0; i < 100; i++ {
out <- i
}
close(out)
}

//<- chan int 只能接收的通道(可以接收不能发送)
func squarer(out chan <- int, in <- chan int) {
for i := range in {
out <- i
}
close(out)
}

//func rec(c chan int) {
// ret := <- c
// fmt.Println("接收成功", ret)
//}

func main() {
//定义一个有缓冲的通道
////ch5 := make(chan int, 1)
//ch5 := make(chan int)
//go rec(ch5)
////把10发送到ch5通道中
//ch5 <- 10
//fmt.Println("发送成功")

//从ch5通道接收值赋给变量a
//a := <- ch5
//从ch5通道接收值丢弃
//<- ch5

//关闭通道
//close(ch5)
//fmt.Println(a)

//ch := make(chan int, 5)
//对一个关闭的通道发送值会导致panic
//close(ch)
//ch <- 10
//接收 一个关闭的通道 会一直获取值 知道通道为空
//ch <- 10
//close(ch)
//x := <- ch
//fmt.Println(x)
//接收 一个关闭且没有值的通道 会得到对应类型的零值
//close(ch)
//x := <- ch
//fmt.Println(x)
//关闭一个已经关闭的通道会导致panic
//close(ch)
//close(ch)

//循环取值
//ch1 := make(chan int)
//ch2 := make(chan int)
//
//go func() {
// for i := 0; i < 10; i++ {
// ch1 <- i
// }
// close(ch1)
//}()
//
//go func() {
// for {
// ok判断通道是否关闭 ok为false 说明通道已关闭
// i, ok := <-ch1
// if !ok {
// break
// }
// ch2 <- i * i
// }
// close(ch2)
//}()
//
//for i := range ch2 {
// fmt.Println(i)
//}

////单向通道
//ch1 := make(chan int)
//ch2 := make(chan int)
//
//go counter(ch1)
//go squarer(ch2, ch1)
////循环获取通道中值
//for i := range ch2 {
// fmt.Println(i)
//}
}

Go语言中反射(reflect)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package main

import (
"fmt"
"reflect"
)

//go语言中变量分为两部分
// 类型信息:预先定义好元信息
// 值信息:程序运行过程中可动态变化

//reflect包
//reflect.TypeOf 获取任意值的类型对象(reflect.Type)

//获取类型信息
func reflectType(x interface{}) {
t := reflect.TypeOf(x)
fmt.Printf("Type: %v, Kind: %v\n", t.Name(), t.Kind())
}

type cat struct {
name string
}

type dog struct {
name string
}

//Kind 获取种类 指底层类型
//go语言反射中 数组、切片、map、指针类型的变量 .Name() 返回是空

//reflect.ValueOf 返回的是reflect.Value,其中包含原始值信息
//reflect.Value 与 原始值 可相互转换

func reflectValue(x interface{}) {
v := reflect.ValueOf(x)
k := v.Kind()
switch k {
case reflect.Int64:
fmt.Printf("Type: Int64, Value: %v\n", int64(v.Int()))
}
}

//Elem() 获取指针对应的值
func reflectSetValue(x interface{}) {
//t := reflect.TypeOf(x)
//fmt.Println(t, t.Name(), t.Kind())
v := reflect.ValueOf(x)
//fmt.Println(v, v.Kind())
v.Elem().SetInt(2)
}

//isNil、isValid

//func (v Value) IsNil() bool 检查v持有值是否为nil v持有类型必须是函数、接口、map、指针、切片之一
//func (v Value) IsValid() bool 检查v是否持有值 如果v是Value的零值返回假 此时v只有调用String、Kind方法
//IsNil常用于判断指针是否为空
//IsValid 常用于判断返回值是否有效

//结构体反射
//t := reflect.TypeOf(b)
//如果t的类型是结构体,还有以下方法
//t.Field(1) StructField 返回索引对应结构体字段的信息
//t.NumField() int 返回结构体字段数量
//t.FieldByName(name string) (StructField, bool) 根据指定字符串返回对应结构体字段的信息
//t.FieldByIndex(index []int) StructField 根据[]int提供的每个结构体字段索引返回对应结构体字段的信息
//t.FieldByNameFunc(match func(string) bool) (StructField, bool)
//t.NumMethod() int 返回方法数量
//t.Method(int) Method 返回第几个方法
//t.MethodByName(string) (Method, bool) 根据指定字符串返回具体方法

// StructField 类型 描述结构体中一个字典信息
//type StructField struct {
// Name string // 字段名
// PkgPath string
// Type Type // 字段类型
// Tag StructTag // 字典标签
// Offset uintptr
// Index []int
// Anonymous bool //是否匿名字段
//}





type student struct {
Name string `json:name`
Score int `json:score`
}

//其他包要能调用,首字母必须大写
func (s student) Study() string {
msg := "学习方法"
return msg
}

func (s student) Sleep() string {
msg := "睡觉方法"
return msg
}

func (s student) TestFn(a string) string {
msg := fmt.Sprintf("%s 测试函数返回值", a)
return msg
}

func main() {
//var a int64 = 200
//reflectType(a)
//var b float32 = 3.1415
//reflectType(b)
//c := dog{name: "wangcai"}
//d := cat{name: "huahua"}
//reflectType(c)
//reflectType(d)
//reflectType([3]int{})
//reflectType([]int{})
//reflectType(map[string]int{})
//reflectType(&a)

//var a int64 = 300
//reflectValue(a)
////将整型20转换为reflect.Value类型
//b := reflect.ValueOf(20)
//fmt.Printf("Type b: %T\n", b)

//a := 100
//reflectSetValue(&a)
//fmt.Println(a)

//var a *int
//println(reflect.ValueOf(a).IsNil())
//println(reflect.ValueOf(a).IsValid())

//b := struct {name string; age int}{name:"Ropon", age: 18}
//t := reflect.TypeOf(b)
//fmt.Println(t.Field(1))
//fmt.Println(t.NumField())
//fmt.Println(t.FieldByName("age2"))

//println(reflect.ValueOf(b).IsValid())

//c := map[string]int{"Ropon": 2}
//println(reflect.ValueOf(c).MapIndex(reflect.ValueOf("Ropon")).IsValid())

stu1 := student{
Name: "Ropon",
Score: 99,
}
//t := reflect.TypeOf(stu1)
//fmt.Println(t.Name(), t.Kind())
//for i := 0;i < t.NumField(); i ++ {
// filed := t.Field(i)
// fmt.Println(filed.Name, filed.Index, filed.Type, filed.Tag.Get("json"))
//}
//scoreField, ok := t.FieldByName("Score")
//if ok {
// fmt.Println(scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json"))
//}

//fmt.Println(t.NumMethod())
v := reflect.ValueOf(stu1)
//for i := 0; i < t.NumMethod(); i++ {
// //fmt.Println(v.Method(i).Type(), t.Method(i).Name)
// //通过反射调用方法传递参数必须是 []reflect.Value类型
// fmt.Println(v.Method(i).Call([]reflect.Value{})[0])
//}
//有参数
res := v.MethodByName("TestFn").Call([]reflect.Value{
reflect.ValueOf("传入变量"),
})
fmt.Println(res[0])
}

Go中解析配置文件demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package main

import (
"fmt"
"io/ioutil"
"reflect"
"strconv"
"strings"
)

// Config 日志文件结构体
type Config struct {
Level string `conf:"level"`
Filepath string `conf:"filepath"`
Filename string `conf:"filename"`
Maxsize int64 `conf:"maxsize"`
}

func parseConf(fileName string, result interface{}) (err error) {
t := reflect.TypeOf(result)
v := reflect.ValueOf(result)
if t.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
err = fmt.Errorf("result必须是一个指针且是结构体指针")
return
}
data, err := ioutil.ReadFile(fileName)
if err != nil {
err = fmt.Errorf("打开配置文件%s失败", fileName)
return
}
lineSlice := strings.Split(string(data), "\r\n")
resMap := map[string]string{}
for index, line := range lineSlice {
//去除收尾空格
line = strings.TrimSpace(line)
//排查空行或注释行
if line == "" || strings.HasPrefix(line, "#") {
continue
}
//排查不含=的行
eqIndex := strings.Index(line, "=")
if eqIndex == -1 {
err = fmt.Errorf("第%d行有语法错误", index+1)
return
}
//排查没有key的行 比如 =dsfafads
//Trim 去除收尾指定字符串
//TrimSpace 去除收尾空格
key := strings.ToLower(strings.TrimSpace(line[:eqIndex]))
//去除首尾双引号"
val := strings.Trim(strings.TrimSpace(line[eqIndex+1:]), "\"")
if key == "" {
err = fmt.Errorf("第%d行有语法错误", index+1)
return
}
resMap[key] = val
}
//结构体反射 reflect.TypeOf(result) 下的方法
//Elem()方法获取指针对应的值
tElem := t.Elem()
vElem := v.Elem()
for i := 0; i < tElem.NumField(); i++ {
// 取到结构体字段信息
field := tElem.Field(i)
// 通过字段名取到tag
tagName := field.Tag.Get("conf")
switch field.Type.Kind() {
case reflect.String:
//再找到值信息设置值
vElem.Field(i).SetString(resMap[tagName])
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
//将任意类型转换为Int64
val64, _ := strconv.ParseInt(resMap[tagName], 10, 64)
vElem.Field(i).SetInt(val64)
}
}
return
}

func main() {
c := &Config{}
err := parseConf("config.conf", c)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(c.Level)
fmt.Println(c.Filename)
fmt.Println(c.Filepath)
fmt.Println(c.Maxsize)
}

Go请求库requests

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
Requests
=======

下载
------------

```
go get -u github.com/ropon/requests/v2
```

使用
-------

```go
//构建一个新对象
req := requests.New()
req.Get("url")

//直接使用默认对象请求
requests.Get("url")

//构建请求头
req.Headers = map[string]string{
"User-Agent": `这里填写自定义UA信息`,
}
req.Header()

//构建Cookie
req.Cookies = map[string]string{
"key": "val",
}
req.Cookie()
```

**GET**:

```go
//无参数Get请求
res, err := req.Get("https://www.ropon.top")
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())

//有参数Get请求
queryData := map[string]interface{}{
"key": "val",
"key2": 123,
"key3": []string{"aa", "aaa"},
"key4": []int{4, 44},
}
res, err := req.Get("https://www.ropon.top", queryData)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())
```

**POST**:

```go
//默认urlencode编码
postData := map[string]interface{}{
"key": "val",
"key2": 123,
"key3": []string{"aa", "aaa"},
"key4": []int{4, 44},
}
res, err := req.Post("https://www.ropon.top", postData)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())

//请求传json
postJsonStr := `{"key": "val"}`
res, err := req.Post("https://www.ropon.top", postJsonStr)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())
```

**PUT**:

```go
//默认urlencode编码
putData := map[string]interface{}{
"key": "val",
"key2": 123,
}
res, err := req.Put("https://www.ropon.top", putData)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())

//请求传json
putJsonStr := `{"key": "val"}`
res, err := req.Put("https://www.ropon.top", putJsonStr)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())
```

**PATCH**:

```go
//默认urlencode编码
patchData := map[string]interface{}{
"key": "val",
"key2": 123,
}
res, err := req.Patch("https://www.ropon.top", patchData)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())

//请求传json
patchJsonStr := `{"key": "val"}`
res, err := req.Patch("https://www.ropon.top", patchJsonStr)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())
```

**DELETE**:

```go
//默认urlencode编码
deleteData := map[string]interface{}{
"key": "val",
"key2": 123,
}
res, err := req.Delete("https://www.ropon.top", deleteData)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())

//请求传json
deleteJsonStr := `{"key": "val"}`
res, err := req.Delete("https://www.ropon.top", deleteJsonStr)
//错误处理
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Text())
```

**Cookies**:

```go
//单独带cookie请求
req.Cookies["key1"] = "val1"
req.Cookie()
res, err := req.Get("https://www.ropon.top")
```

**Headers**:

```go
//单独带header请求
req.Headers["key1"] = "val1"
req.Header()
res, err := req.Get("https://www.ropon.top")
```

**Res**:

```go
//获取文本信息
res.Text()
//获取Json,返回requests.Value
res.Json()
res.Json().Get("data", "svc_list").String()
//获取响应头
res.Header()
//获取状态码
res.Status()
//自动格式化为时间
res.Json().Time()
```

ansible之安装及模块一

控制主机安装ansible

1
2
3
mv /etc/yum.repos.d/epel-7.repo{,_bak}
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum install -y ansible

创建秘钥对配置ssh秘钥登录

1
2
3
ssh-keygen #生成秘钥对
ssh-copy-id #复制公钥到远程主机
ssh-copy-id 192.168.8.121 -p 22

ansible命令格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ansible 
-a MODULE_ARGS, --args MODULE_ARGS #模块的参数
-C, --check #检查
-f
--list-hosts #列出主机列表
-m MODULE_NAME #模块名
--syntax-check #语法检查

#查看ansible生成文件
rpm -ql ansible|more
/etc/ansible
/etc/ansible/ansible.cfg #主配置文件
/etc/ansible/hosts #主机配置文件
/etc/ansible/roles

/etc/ansible/hosts #主机配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
# - Comments begin with the '#' character 用#注释
# - Blank lines are ignored 空行可被忽略
# - Groups of hosts are delimited by [header] elements 组用[]命名且在此下面
# - You can enter hostnames or ip addresses
# - A hostname/ip can be a member of multiple groups 一个主机可以在多个组中

192.168.8.32
192.168.8.33
192.168.8.34
#若不是默认root,默认22端口,可通过配置指定
ansible_ssh_user=root ansible_ssh_port=22000

ping模块 不是ICMP协议ping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
ansible 192.168.8.34 -m ping
192.168.8.34 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}

#也可以通过密码登录加-k参数 注意要使用ssh先登录一次

ansible 192.168.8.34 -m ping -k

#检查所有主机
ansible all -m ping

#部分主机
ansible 192.168.8.32,192.168.8.33 -m ping

#分组
[web]
192.168.8.32
192.168.8.33
192.168.8.34
[db]
192.168.8.35
192.168.8.36
[cache]
192.168.8.36

#分组范围
[web]
192.168.8.[32:34]

#查看组中所有主机
ansible web --list-hosts

#并集
ansible web,db -m ping
ansible 'web:db' -m ping

#交集
ansible 'web:&db' -m ping

#差集
ansible 'web:!db' -m ping #web中有db中无

ansible-doc 查看模块帮助信息

1
2
3
-j json #返回帮助信息
-l #列出所有ansible模块
-s #片段式显示帮助信息

命令相关模块 默认

command

1
2
3
4
5
6
7
8
9
10
11
12
ansible web -m command -a 'ls /' #默认是command 
ansible web -a 'ls /' # 可以简写

#参数
chdir #切换目录
ansible web -a 'chdir=/tmp pwd' #编译安装时

creates #如果文件或文件夹不存在就执行 否则跳过
ansible web -a 'creates=/tmp2 pwd'

removes #如果文件或文件夹存在就执行 否则跳过
ansible web -a 'removes=/tmp pwd'

例子

1
2
3
4
5
6
7
#批量创建用户
ansible web -a 'useradd ropon'
#查看是否创建成功
ansible web -a 'id ropon'
#批量设置密码
ansible web -a 'echo "west.cn"|passwd --stdin ropon'
#command模块不支持特殊字符 < > | ; & 所以批量设置密码不成功 需要使用shell模块

shell模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ansible web -m shell -a 'echo "west.cn"|passwd --stdin ropon'

#参数
chdir #切换目录
ansible web -m shell -a 'chdir=/tmp pwd' #编译安装时

creates #如果文件或文件夹不存在就执行 否则跳过
ansible web -m shell -a 'creates=/tmp2 pwd'

removes #如果文件或文件夹存在就执行 否则跳过
ansible web -m shell -a 'removes=/tmp pwd'

#执行远程主机上脚本
cat test.sh
#!/bin/bash
touch ansible_test_x.txt

ansible 192.168.8.32 -m shell -a 'bash /root/test.sh'
ansible 192.168.8.32 -m shell -a '/root/test.sh' #前提此脚本文件有x执行权限

cat python_test.py
#!/bin/python
print "python echo test!"

#同理可执行python脚本
ansible 192.168.8.32 -m shell -a 'python /root/python_test.py'
ansible 192.168.8.32 -m shell -a '/root/python_test.py' #前提此脚本文件有x执行权限

script模块 执行控制主机上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat test.sh
#!/bin/bash
touch /root/control_ansible_test.txt

ansible web -m script -a '/root/test.sh'
#查看是否创建成功
ansible web -a 'ls /root'

#参数
chdir #切换目录
ansible web -m shell -a 'chdir=/tmp pwd' #编译安装时

creates #如果远程主机上此文件或文件夹不存在就执行控制主机上脚本 否则跳过
ansible 192.168.8.32 -m script -a 'creates=/root/root.sh /root/test.sh'

removes #如果远程主机上此文件或文件夹存在就执行控制主机上脚本 否则跳过
ansible 192.168.8.32 -m script -a 'removes=/root/root.sh /root/test.sh'

文件相关模块

copy模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#参数
backup #创建一个备份文件,以时间戳结尾 backup=yes
dest #目的地址
content
group #修改所属组
mode #修改权限
owner #修改所有者
src #源文件

#复制控制主机本地文件到远程主机
ansible db -m copy -a 'src=/root/test.sh dest=/root/test_copy.sh'

ansible db -m shell -a 'ls -l /root'
192.168.8.35 | CHANGED | rc=0 >>
总用量 20
-rw-r--r--. 1 root root 8956 2月 13 2019 1.sh
-rw-------. 1 root root 1468 2月 13 2019 anaconda-ks.cfg
-rw-r--r-- 1 root root 52 12月 21 15:17 test_copy.sh

ansible db -m copy -a 'src=/root/test.sh dest=/root/test_copy.sh mode=744' #带权限复制

ansible web -m copy -a 'src=/root/test.sh dest=/root/test_copy.sh group=ropon' #带所需组复制
ansible web -m copy -a 'src=/root/test.sh dest=/root/test_copy.sh owner=ropon' #带所有者复制

#复制本地目录到远程主机
ansible web -m copy -a 'src=/etc/init.d dest=/tmp/'
#复制本地目录下所有文件到远程主机
ansible web -m copy -a 'src=/etc/init.d/ dest=/tmp/'

#将内容写入到远程主机
ansible web -m copy -a 'content="测试将内容写入远程主机\n" dest=/tmp/b.txt'

file模块 创建删除文件相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#参数
group #所属组
mode #权限
owner #所有者
path
src #link hard才有效
state
directory
file
touch
link
hard
absent #删除


#创建文件夹
ansible 192.168.8.32 -m file -a 'path=/root/testdir state=directory'

#创建文件
ansible 192.168.8.32 -m file -a 'path=/root/testdir.txt state=touch'

#删除文件或文件夹
ansible 192.168.8.32 -m file -a 'path=/root/testdir.txt state=absent'

#创建软硬连接
ansible 192.168.8.32 -m file -a 'path=/root/testlink src=/etc/fstab state=link'
ansible 192.168.8.32 -m file -a 'path=/root/testlink2 src=/etc/fstab state=hard'

fetch模块 下载文件

1
2
3
4
5
6
#参数
dest #控制主机
src #远程主机

#拷贝远程主机日志信息到控制主机root目录下 给每台主机创建一个文件夹并保留原来目录结构
ansible 192.168.8.32 -m fetch -a 'dest=/root src=/var/log/messages'

ansible之模块二及playbook

yum模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
yum源配置

yum查看安装包组
yum grouplist #查看
yum groupinstall 包名 #安装

#参数
disablerepo #禁用源
enablerepo #启动源
name #包名
state
install 或(present installd latest) #安装
remove 或(absent removed) #卸载

#yum安装nginx
ansible 192.168.8.32 -m yum -a 'name=nginx'

#安装python2 pip
ansible 192.168.8.32 -m yum -a 'name=python2-pip'


#yum安装包组 用@
ansible 192.168.8.32 -m yum -a 'name=@Cinnamon'

pip模块

1
2
3
4
5
6
7
8
pip install 包
pip freeze > ropon.txt #导出当前python环境依赖
pip install -r ropon.txt #安装文件中的包

#参数
requirements
#安装flask
ansible 192.168.8.32 -m pip -a 'name=flask'

service模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#参数
enabled #加入开机自启 no|yes
name #服务名
state
started #启动服务
stopped #停止服务
restarted #重启服务
reloaded #重载服务
enabled #开机自启

#启动服务
ansible 192.168.8.32 -m service -a 'name=nginx state=started'
#查看
ansible 192.168.8.32 -m shell -a 'netstat -tunpl'
#停止服务
ansible 192.168.8.32 -m service -a 'name=nginx state=stopped'
#将nginx服务加入开机自启
ansible 192.168.8.32 -m service -a 'name=nginx enabled=yes'

cron模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#参数
day #天
disabled #禁用
hour #小时
minute #分组
month #月份
job #任务
name #任务名
weekday #周

#创建计划任务
ansible 192.168.8.32 -m cron -a 'minute=49 job="touch /root/cron_test.txt" name=crontest'
#删除计划任务
ansible 192.168.8.32 -m cron -a 'name=crontest state=absent'
#禁用计划任务 # 表示禁用
ansible 192.168.8.32 -m cron -a 'minute=49 job="touch /root/cron_test.txt" name=crontest2 disabled=yes'
#启动禁用任务
ansible 192.168.8.32 -m cron -a 'minute=49 job="touch /root/cron_test.txt" name=crontest2 disabled=no'

user模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
管理员
普通用户
系统用户

#参数
group #组
groups #附加组
home #家目录
name #用户名
password #密码
remove
shell
system
state #状态

#创建用户mysql 指定家目录/root/mysql 指定附加组root 指定uid 指定shell不能登录
ansible db -m user -a 'name=mysql uid=4000 home=/root/mysql groups=root shell=/sbin/nologin'

#删除mysql用户但不删除此用户家目录
ansible db -m user -a 'name=mysql state=absent'

#删除mysql用户并删除此用户家目录
ansible db -m user -a 'name=mysql state=absent remove=yes'

group模块

1
2
3
4
5
6
7
8
9
10
11
#参数
gid #组id
name #组名
system #系统组
state

#创建mysql系统组
ansible db -m group -a 'name=mysql system=yes'

#删除mysql系统组
ansible db -m group -a 'name=mysql state=absent'

ansible脚本

1
2
3
4
5
6
#yaml
#格式 严格缩进 严格对齐
#字典 key:value
#列表 -
#后缀名
#yaml、yml

ansible-playbook

1
2
3
4
5
6
7
8
#执行顺序 从上往下
#特性 不管执行多少次结果都一样

#参数
-C #检查 会执行一次脚本但不生效
-f #并发
--list-hosts #列出主机列表
--syntax-check #语法检查

简单例子

1
2
3
4
5
6
7
8
9
10
#创建用户例子
- hosts: 192.168.8.32
tasks:
- name: createuser
user: name=ropon01

#执行脚本
ansible-playbook p1.yml
#检查是否创建成功
ansible 192.168.8.32 -m shell -a 'id ropon01'

传参 动态执行脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
- hosts: 192.168.8.32
tasks:
- name: create{{ user }}
user: name={{ user }}

#方式一 -e
ansible-playbook -e 'user=ropon02' p2.yml
#检查是否创建成功
ansible 192.168.8.32 -m shell -a 'id ropon02'

#方式二 主机后面 指定参数 192.168.8.32 user=ropon03
ansible-playbook p2.yml
#检查是否创建成功
ansible 192.168.8.32 -m shell -a 'id ropon03'

#方式三 主机组[:vars] 组后面指定参数(新起一行写与之前的主机名)
[web:vars]
user=ropon04

cat p3.pml
- hosts: web
tasks:
- name: create{{ user }}
user: name={{ user }}

ansible-playbook p3.yml
#检查是否创建成功
ansible web -m shell -a 'id ropon04'

#方式四 yml文件vars指定参数
cat p3.pml
- hosts: web
vars:
- user: ropon05
tasks:
- name: create{{ user }}
user: name={{ user }}

ansible-playbook p4.yml
#检查是否创建成功
ansible web -m shell -a 'id ropon05'

#方式五 register
cat p5.yml
- hosts: web
tasks:
- name: sum
shell: echo 3+3|bc
register: user
- name: createropon06
user: name=ropon0{{user.stdout}}

ansible-playbook p5.yml

#参数优先级
-e > playbook vars > 主机hosts文件

ansible之setup及roles

setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#查看所有参数
ansible web -m setup

#搜索
ansible web -m setup -a 'filter=*ip*'

#常用参数
ansible_all_ipv4_addresses #所有ipv4地址
ansible_default_ipv4 #默认ipv4地址
ansible_all_ipv6_addresses #所有ipv6地址
ansible_date_time #远程主机时间
ansible_distribution #系统版本
ansible_distribution_major_version #系统版本
ansible_env #系统环境变量
ansible_hostname #系统主机名
ansible_fqdn #系统全名
ansible_machine #系统架构
ansible_memory_mb #系统的内存信息
ansible_os_family #系统家族 Redhat
ansible_pkg_mgr #系统包管理工具 yum
ansible_processor_cores #系统每颗cpu的核数
ansible_processor_count #系统cpu的颗数

条件判断 when

1
2
3
4
5
6
7
8
9
10
11
12
13
#web分组下
#默认ipv4是192.168.7.222 在root目录下创建testipfile文件
#主机名是ebs-13 在root目录下创建test3dir文件夹
cat p1.yml
- hosts: web
remote_user: root
tasks:
- name: touch2file
file: path=/root/testipfile state=touch
when: ansible_default_ipv4.address == "192.168.7.222"
- name: touch3dir
file: path=/root/test3dir state=directory
when: ansible_hostname == "ebs-13"

tags 执行时通过-t 指定

1
2
3
4
5
6
7
8
9
10
11
12
13
#remote_user: root 指定运行用户,默认是root
cat p2.yml
- hosts: web
remote_user: root
tasks:
- name: touch2file
file: path=/root/testip2file state=touch
- name: touch3dir
file: path=/root/test3dir state=directory
tags: touchdir

#执行
ansible-playbook -t touchdir p2.yml

循环 with_items

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#批量创建用户
cat p3.yml
- hosts: web
tasks:
- name: createuser
user: name={{ item }}
with_items:
- ropon11
- ropon12
- ropon13
- ropon14

#批量删除用户
cat p4.yml
- hosts: web
tasks:
- name: createuser
user: name={{ item }} state=absent
with_items:
- ropon11
- ropon12
- ropon13
- ropon14

#嵌套循环 先创建用户组 再创建用户 通过字典
cat p5.yml
- hosts: web
tasks:
- name: creategroup
group: name={{ item }}
with_items:
- ropon11
- ropon12
- ropon13
- ropon14
- name: createuser
user: name={{ item.name }} group={{item.group}}
with_items:
- {'name':ropon11,'group':ropon11}
- {'name':ropon12,'group':ropon12}
- {'name':ropon13,'group':ropon13}
- {'name':ropon14,'group':ropon14}

#批量删除用户组和用户
cat p6.yml
- hosts: web
tasks:
- name: creategroup
group: name={{ item }} state=absent
with_items:
- ropon11
- ropon12
- ropon13
- ropon14
- name: createuser
user: name={{ item.name }} group={{item.group}} state=absent
tags: createuser
with_items:
- {'name':ropon11,'group':ropon11}
- {'name':ropon12,'group':ropon12}
- {'name':ropon13,'group':ropon13}
- {'name':ropon14,'group':ropon14}

template 语法jinja2 会替换其中变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#安装redis 修改配置文件 启动服务
- hosts: db
remote_user: root
tasks:
- name: install_redis
yum: name=redis
tags: install
- name: copyconf
template: dest=/etc/redis.conf src=redis.conf.j2
tags: copy
- name: start_redis
service: name=redis
tags: start

handlers 定义默认不会执行 通过notify 触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- hosts: db
remote_user: root
tasks:
- name: install_redis
yum: name=redis
tags: install
- name: copyconf
template: dest=/etc/redis.conf src=redis.conf.j2
tags: copy
notify: restart
- name: start_redis
service: name=redis
tags: start
handlers:
- name: restart
service: name=redis state=restarted

roles

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#比如安装httpd服务
mkdir -p httpd/{handlers,tasks,templates,vars}
#handlers
#main.yml
- name: Reload Httpd
service: name=httpd state=reloaded
#tasks
#main.yml include 具体yml文件
- include: group.yml
- include: user.yml
- include: install.yml
- include: config.yml
- include: start.yml
#group.yml
- name: Create startup group
group: name={{ GROUPNAME }} system=yes
#user.yml
- name: Create starup user
user: name={{ USERNAME }} system=yes shell=/sbin/nologin
#install.yml
- name: Install Httpd
yum: name=httpd
#config.yml
- name: Copy conf
template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
tags: copyconf
notify: Reload Httpd
#start.yml
- name: Start Httpd
service: name=httpd enabled=yes
#templates
#httpd.conf.j2
Listen {{ ansible_default_ipv4.address }}:{{ PORT }}
...
User {{ USERNAME }}
Group {{ GROUPNAME }}
...
#vars
#main.yml
PORT: 80
USERNAME: www
GROUPNAME: www
#与httpd目录同级新建httpd_roles.yml文件
- hosts: web
remote_user: root
roles:
- role: httpd

ansible-playbook http_roles.yml
#更新配置文件触发重载httpd服务
ansible-playbook -t copyconf http_roles.yml

git常用命令

#配置用户信息
git config –global user.email “ropon@west.cn
git config –global user.name “Ropon”
#初始化
cd /home/work
git init
#查看状态
git status
#新建忽略文件或文件件配置文件
touch .gitignore
cat .gitignore
.idea
*.exe
*.sql
#添加到缓存区
git add .
#提交到版本库
git commit -m “备注信息”
#查看提交记录
git log
#查看所有提交记录
git reflog
#从缓存区将文件拉到工作区
git reset HEAD main.go
#撤销之前修改回到上次提交时状态
git checkout – main.go
#回滚到某个版本
git reset –hard 9c3c9f8 或 4a04c2b3661b70478af9032dd7e72455e73d86fb
#对比工作区与缓存区的差异
git diff
#对比版本库与缓存区的差异
git diff –cached

#将当前工作区文件暂存到某个地方
git stash
#查看stash列表
git stash list
#将暂存恢复到当前工作区并删除此暂存
git stash pop
#将暂存恢复到当前工作区但不删除此暂存
git stash apply
#删除stash
git stash drop

#新建分支
git branch 分支名
#查看分支
git branch
#切换到某个分支
git checkout 分支名
#删除分支
git branch -d 分支名
#创建分支并切换到该分支
git checkout -b 分支名
基于某个tag创建新分支
git branch 分支名 tab名称
git checkout 分支名

#打标签
git tag -a v1.0 -m “备注信息”
#查看标签
git tag

1
2
3
4
5
6
7
8
9

git远程管理

```shell
git remote add origin ...
git push origin dev
git fetch origin dev
git merge origin/dev
git pull
1
2
3
4
5
6
7
8
9
10
#撤销commit
#HEAD^ 上一版本
--soft #不删除工作空间改动代码 撤销commit 不撤销git add .
--hard #删除工作空间改代码 撤销commit 撤销git add . 回到上次commit的状态
git reset --soft HEAD^
git reset --soft HEAD~1
#如果进行2次commit 想撤回
git reset --soft HEAD~2
#如果备注写错
git commit --amend 进入vim编辑器修改
#git dev 分支合并到master并提交到远程仓库
#查看当前分支
git branch
#切换到master分支
git checkout master
#拉取最新代码
git pull
#将dev分支上的代码合并到master
git merge dev
#推送到远程master仓库
git push origin master

Python3操作excel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/1/17 19:55
# @Author : Ropon
# @File : excel_test.py

# 读excel
# pip3 install xlrd
# import xlrd

# excelName = r"./excel/1-12月.xls"
# book = xlrd.open_workbook(excelName)
# 获取所有sheet页的名字
# sheetNames = book.sheet_names()
# print(sheetNames)
# # 根据sheet索引获取表格sheet
# sheet = book.sheet_by_index(8)
# # 根据sheet名字获取表格sheet
# sheet2 = book.sheet_by_name("9月")
# 获取当前sheet的名称
# print(sheet.name)

# 获取指定行和列的数据 cell(行索引, 列索引)
# r1 = sheet.cell(0, 0).value
# r2 = sheet.cell(1, 0).value
# r3 = sheet.cell(1, 1).value
# r4 = sheet.cell(1, 2).value
# print(r1, r2, r3, r4)

# 获取单元格数据的类型
# ctype_text = {
# XL_CELL_EMPTY: 'empty', 0
# XL_CELL_TEXT: 'text', 1
# XL_CELL_NUMBER: 'number', 2
# XL_CELL_DATE: 'xldate', 3
# XL_CELL_BOOLEAN: 'bool', 4
# XL_CELL_ERROR: 'error', 5
# XL_CELL_BLANK: 'blank', 6
# }
# cType = sheet.cell(100, 10).ctype
# print(cType)

# 获取excel有多少行和列
# rows = sheet.nrows
# cols = sheet.ncols
# print(rows, cols)

# 根据索引获取某一行所有数据
# rowData = sheet.row_values(0)
# print(rowData)

# 根据索引获取某一列所有数据
# colData = sheet.col_values(2)
# print(colData)

# 写excel
# pip3 install xlwt
import xlwt
# 只能写不能读

# 创建样式字体红色并加粗
style = "font:colour_index red, bold on;"
styleBoldRed = xlwt.easyxf(style)

# 新建一个excel
book = xlwt.Workbook()
# 添加sheet页
sheet = book.add_sheet("技术部")
# 将数据写入指定行和列
sheet.write(0, 0, "one", styleBoldRed)
sheet.write(0, 1, "two")
sheet.write(1, 1, "three")
sheet.write(1, 2, "four")
# 保存
book.save("writeTest.xls")

# 修改excel
# pip3 install xlutils3
# import xlrd
# from xlutils3.copy import copy
#
# excelName = "./excel/1-12月.xls"
# book = xlrd.open_workbook(excelName)
# writeBook = copy(book)
#
# sheet = book.sheet_by_name("9月")
# r1 = sheet.cell(2, 2).value
# print(r1)
# data = r1 + "测试写入"
# # 查看类的成员
# # print(dir(writeBook))
#
# # 根据sheet索引获取表格sheet
# writeSheet = writeBook.get_sheet(8)
# writeSheet.write(2, 16, data)
# writeBook.save("1-12月_ok.xls")

# openpyxl 读excel 不支持xls
# import openpyxl

# excelName = r"./excel/1-12月1.xlsx"
# book = openpyxl.load_workbook(excelName)
# 获取所有sheet页的名字
# sheetNames = book.get_sheet_names()
# print(sheetNames)
# 根据sheet索引获取表格sheet
# sheet = book.worksheets[8]
# 根据sheet名字获取表格sheet
# sheet2 = book.get_sheet_by_name("9月")
# 获取当前sheet的名称
# print(sheet.title)

# 获取excel有多少行和列
# rows = sheet.max_row
# cols = sheet.max_column
# print(rows, cols)


# 获取每一行的内容,这是一个生成器,有每一行的数据 每一行数据有一个元素类型包裹
# sheet.rows

# 获取每一列的内容,这是一个生成器,有每一列的数据 每一列数据有一个元素类型包裹
# sheet.columns

# 根据索引获取第一行所有数据
# for cell in list(sheet.rows)[0]:
# print(cell.value)

# 根据索引获取某一列所有数据
# for cell in list(sheet.columns)[1]:
# print(cell.value)

# 获取指定行和列的数据 cell(行索引, 列索引) 索引从1开始
# r1 = sheet.cell(1, 1).value
# r2 = sheet.cell(2, 1).value
# r3 = sheet.cell(2, 2).value
# r4 = sheet.cell(2, 3).value
# print(r1, r2, r3, r4)

# 获取第一行第一列(A)单元格数据
# print(sheet["A1"].value)

# openpyxl 写excel 不支持xls
# import openpyxl
#
# # 新建一个excel
# book = openpyxl.Workbook()
# # 添加sheet页
# sheet = book.create_sheet("技术部")
# # 获取当前活跃页 默认第一工作页
# # sheet = book.active
#
# # 将数据写入指定行和列 索引从1开始
# sheet.cell(1, 1, "one")
# sheet.cell(1, 2, "two")
# sheet.cell(2, 2, "three")
# sheet.cell(2, 3, "four")
# # 保存
# book.save("writeTest2.xlsx")