Go语言及创建WEB项目(iris)

创建WEB项目(iris)

仓库地址:https://github.com/kataras/iris/

文档:http://www.topgoer.com/Iris/

创建

1
2
3
4
mkdir ZDevOpsGo
cd ZDevOpsGo
go mod init ZDevOpsGo
go get github.com/kataras/iris/v12@master

创建main.go

输入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "github.com/kataras/iris/v12"

func main() {
app := iris.Default()
app.Use(myMiddleware)

app.Handle("GET", "/", func(ctx iris.Context) {
ctx.JSON(iris.Map{"message": "hello world"})
})

// Listens and serves incoming http requests
// on http://localhost:8080.
app.Run(iris.Addr(":8080"))
}

func myMiddleware(ctx iris.Context) {
ctx.Application().Logger().Infof("Runs before %s", ctx.Path())
ctx.Next()
}

运行

1
go run main.go

或者编译后运行exe文件

1
go build

这样就可以访问

http://127.0.0.1:8080/

使用模板

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
package main

import "github.com/kataras/iris/v12"

func main() {
app := iris.New()
// Load all templates from the "./views" folder
// where extension is ".html" and parse them
// using the standard `html/template` package.
app.RegisterView(iris.HTML("./views", ".html"))

// Method: GET
// Resource: http://localhost:8080
app.Get("/", func(ctx iris.Context) {
// Bind: {{.message}} with "Hello world!"
ctx.ViewData("message", "Hello world!")
// Render template file: ./views/index.html
ctx.View("index.html")
})

// Method: GET
// Resource: http://localhost:8080/user/42
// 也可以使用正则表达式
// app.Get("/user/{id:string regexp(^[0-9]+$)}")
app.Get("/user/{id:uint64}", func(ctx iris.Context) {
userID, _ := ctx.Params().GetUint64("id")
ctx.Writef("User ID: %d", userID)
})

// Start the server using a network address.
app.Run(iris.Addr(":8080"))
}

模板文件

views/index.html

1
2
3
4
5
6
7
8
<html>
<head>
<title>Hello Page</title>
</head>
<body>
<h1>{{ .message }}</h1>
</body>
</html>

运行就可以访问

http://localhost:8080

http://localhost:8080/user/42

使用配置文件

项目根目录创建iris.yml

1
2
3
4
FireMethodNotAllowed: true
DisableBodyConsumptionOnUnmarshal: true
TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT
Charset: UTF-8

加载配置文件

1
2
3
// Start the server using a network address.
config := iris.WithConfiguration(iris.YAML("./iris.yml"))
app.Run(iris.Addr(":8080"), config)

项目下的静态文件

假如我们想要处理 http://localhost:8080/static/**/* 的请求都访问项目目录下的 ./static 文件夹,那么按照如下配置就可以了

1
2
3
app := iris.New()
app.HandleDir("/static", "./static")
app.Run(iris.Addr(":8080"))

app.HandleDir("/static", "./static")的第一个参数为请求的URL,第二个参数为本地文件夹

一般前后端不分离我们可以这样配置,让项目可以访问前端的样式、JS、静态页面、图片等等静态的资源。

JSON文件解析

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
package utils

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
)

type Config struct {
Port int32
Project_name string
}

func init() {
fmt.Println("config初始化")
}

func ReadConfig() Config {
ExecPath, _ := os.Getwd()
filepath := path.Join(ExecPath, "config", "config.json")
data, err := ioutil.ReadFile(filepath)
fmt.Println("filepath:", filepath)
v := Config{}
if err != nil {
fmt.Println("读取配置文件失败", err)
return v
}
fmt.Println(string(data))

//读取的数据为json格式,需要进行解码
err = json.Unmarshal(data, &v)
if err != nil {
fmt.Println("解析json失败", err)
} else {
fmt.Println("port:", v.Port)
fmt.Println("Project_name:", v.Project_name)
}
return v
}

对应的JSON文件

config/config.json

1
2
3
4
{
"port":8080,
"project_name":"码客说"
}

注意

JSON库在转换的时候会首字母大写,但是project_name这样的会解析为Project_name, 而不是ProjectName

Marshal():Go数据对象 -> json数据

UnMarshal():Json数据 -> Go数据对象

1
2
3
func Marshal(v interface{}) ([]byte, error)

func Unmarshal(data []byte, v interface{}) error

构建json数据

Marshal()和MarshalIndent()函数可以将数据封装成json数据。

  1. struct、slice、array、map都可以转换成json

  2. struct转换成json的时候,只有字段首字母大写的才会被转换

  3. map转换的时候,key必须为string

  4. 封装的时候,如果是指针,会追踪指针指向的对象进行封装

Marshal函数将会递归遍历整个对象,依次按成员类型对这个对象进行编码,类型转换规则如下:

Golang 数据类型 JSON 数据类型
bool Boolean
int float Number
string String
struct JSON Object 再根据成员递归打包
数组或切片 JSON Array
[]byte base64编码后的JSON String
map JSON Object, key必须是string
interface{} 按照内部实际进行转换
nil null
channel, func UnsupportedTypeError

注意

JSON转换不支持list

Sqlite

安装GCC环境

Sourceforge官网,下载MinGW-W64 GCC-8.1

下载x86_64-win32-sjlj

解压后把类似于D:\Tools\mingw64\bin添加到系统环境变量Path

添加后重启开发软件,后输入

1
go get github.com/mattn/go-sqlite3

示例

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
package main

import (
"database/sql"
"fmt"

_ "github.com/mattn/go-sqlite3"
)

func main() {
db, err := sql.Open("sqlite3", "./mydb.db")
checkErr(err)

//创建表
sql_table := `
CREATE TABLE IF NOT EXISTS userinfo(
uid INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(64) NULL,
departname VARCHAR(64) NULL,
created DATE NULL
);
`
db.Exec(sql_table)

//插入数据
stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)")
checkErr(err)

res, err := stmt.Exec("小红", "研发部门", "2012-12-09")
checkErr(err)

id, err := res.LastInsertId()
checkErr(err)

fmt.Println("插入用户ID:", id)
//更新数据
stmt, err = db.Prepare("update userinfo set username=? where uid=?")
checkErr(err)

res, err = stmt.Exec("小明", id)
checkErr(err)

affect, err := res.RowsAffected()
checkErr(err)

fmt.Println("更新用户条数:", affect)

//查询数据
rows, err := db.Query("SELECT * FROM userinfo")
checkErr(err)

for rows.Next() {
var uid int
var username string
var department string
var created string
err = rows.Scan(&uid, &username, &department, &created)
checkErr(err)
fmt.Println("uid:", uid)
fmt.Println("username:", username)
fmt.Println("department:", department)
fmt.Println("created:", created)
}

//删除数据
stmt, err = db.Prepare("delete from userinfo where uid=?")
checkErr(err)

res, err = stmt.Exec(id)
checkErr(err)

affect, err = res.RowsAffected()
checkErr(err)

fmt.Println("删除条数:", affect)

db.Close()

}

func checkErr(err error) {
if err != nil {
panic(err)
}
}

注意

panic 会触发程序崩溃,正式上线不要用

Mysql

下载依赖

1
go get github.com/go-sql-driver/mysql

示例

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
package main

import (
"database/sql"
"fmt"

_ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func initDB() {
//"用户名:密码@[连接方式](主机名:端口号)/数据库名"
db, _ = sql.Open("mysql", "root:123456@(127.0.0.1:3306)/zdb") // 设置连接数据库的参数
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(5)
}

func main() {
initDB()
defer db.Close() //关闭数据库
err := db.Ping() //连接数据库
if err != nil {
fmt.Println("数据库连接失败")
return
}

//操作一:执行数据操作语句
sql := "insert into t_user (name) values ('berry')"
result, _ := db.Exec(sql) //执行SQL语句
n, _ := result.RowsAffected() //获取受影响的记录数
fmt.Println("受影响的记录数是", n)

//操作二:执行预处理

users := [2][]string{{"ketty"}, {"rose"}}
stmt, _ := db.Prepare("insert into t_user (name) values (?)") //获取预处理语句对象
for _, s := range users {
stmt.Exec(s[0]) //调用预处理语句
}

//操作三:单行查询
// var id, name string
// rows := db.QueryRow("select * from t_user where id=4") //获取一行数据
// rows.Scan(&id, &name)
// fmt.Println(id, "--", name)

//操作四:多行查询
rows, _ := db.Query("select * from t_user") //获取所有数据
var id, name string
for rows.Next() { //循环显示所有的数据
rows.Scan(&id, &name)
fmt.Println(id, "--", name)
}
rows.Close()
}

注意

defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。

连接池的实现关键在于SetMaxOpenConns和SetMaxIdleConns,其中:

SetMaxOpenConns用于设置最大打开的连接数,默认值为0表示不限制。
SetMaxIdleConns用于设置闲置的连接数。

设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。

解析Nginx配置

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
196
197
198
199
200
201
package utils

import (
"bufio"
"encoding/json"
"fmt"
"io"
"os"
"path"
"strings"
)

func init() {
fmt.Println("nginx_config初始化")
}

// 定义存储的结构体
type NginxConfig struct {
Name string
Value string
Comment []string
Children *[]NginxConfig
}

// 读取Nginx配置
func ReadNginxConfig(filepath string) []NginxConfig {
f, err := os.Open(filepath)
if err != nil {
fmt.Println(err.Error())
}
config_arr := []string{}
//建立缓冲区,把文件内容放到缓冲区中
buf := bufio.NewReader(f)
for {
//遇到\n结束读取
b, errR := buf.ReadBytes('\n')
if errR != nil {
if errR == io.EOF {
break
}
fmt.Println(errR.Error())
}
config_arr = append(config_arr, strings.TrimSpace(string(b)))

}
configlist := []NginxConfig{}
level := 0
lastchar := ' '
levelConf := []NginxConfig{}
var conf *NginxConfig
lastComment := []string{}
for line_num, v := range config_arr {

if strings.HasPrefix(v, "#") {
lastComment = append(lastComment, strings.Replace(v, "#", "", 1))
} else if strings.Contains(v, "{") {
if lastchar == '{' {
level += 1
}
if level < len(levelConf) {
conf = &levelConf[level]
} else {
conf = new(NginxConfig)
conf.Children = new([]NginxConfig)
levelConf = append(levelConf, *conf)
}
if len(lastComment) > 0 {
if conf != nil {
conf.Comment = lastComment
lastComment = []string{}
}
}
tempstr := strings.Split(v, "{")[0]
tempstr = strings.TrimSpace(tempstr)
tem_arr := strings.Split(tempstr, " ")

// fmt.Println("tem_arr:", tem_arr)
if len(tem_arr) > 0 {
conf.Name = tem_arr[0]
if len(tem_arr) > 1 {
conf.Value = tem_arr[1]
}
}

if level == 0 {
configlist = append(configlist, *conf)
} else {
lastLevelConfig := &levelConf[level-1]
*lastLevelConfig.Children = append(*lastLevelConfig.Children, *conf)
}

fmt.Printf("%d %s level %d\n", line_num, v, level)
lastchar = '{'
} else if strings.Contains(v, "}") {
if lastchar == '}' {
level -= 1
}

levelConf = levelConf[:len(levelConf)-1]
if level < len(levelConf) {
conf = &levelConf[level]
} else {
conf = nil
}

fmt.Printf("%d %s level %d\n", line_num, v, level)
lastchar = '}'
} else {
if conf != nil {
tempstr := strings.TrimSpace(v)
if len(tempstr) > 0 {
var confitem *NginxConfig = new(NginxConfig)
tem_arr := strings.Split(tempstr, " ")
if len(tem_arr) >= 2 {
confitem.Name = tem_arr[0]
rightstr := strings.Join(tem_arr[1:], " ")
if strings.Contains(rightstr, ";") {
confitem.Value = strings.Split(rightstr, ";")[0]
if len(lastComment) > 0 {
confitem.Comment = lastComment
lastComment = []string{}
}
*conf.Children = append(*conf.Children, *confitem)
}
}
}
}
}
}

return configlist
}

// 写入Nginx配置
func WriteConfig(filepath string, NginxConfig []NginxConfig) {
strArr := new([]string)
for _, value := range NginxConfig {
writeConfigStr(value, strArr, 0)
}

// fmt.Println("*strArr", *strArr)
file, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println("文件打开失败", err)
}
//及时关闭file句柄
defer file.Close()
//写入文件时,使用带缓存的 *Writer
write := bufio.NewWriter(file)
for i := 0; i < len(*strArr); i++ {
write.WriteString((*strArr)[i] + "\n")
}
//Flush将缓存的文件真正写入到文件中
write.Flush()
}

func spaceByLevel(level int) string {
str := ""
for i := 0; i < level; i++ {
str += " "
}
return str
}

func writeConfigStr(NginxConfig NginxConfig, strArr *[]string, level int) {
spacestr := spaceByLevel(level)
if len(NginxConfig.Comment) != 0 {
for i := 0; i < len(NginxConfig.Comment); i++ {
*strArr = append(*strArr, spacestr+"#"+NginxConfig.Comment[i])
}
}
if NginxConfig.Children != nil {
*strArr = append(*strArr, spacestr+NginxConfig.Name+" "+NginxConfig.Value+" {")
for _, value := range *NginxConfig.Children {
writeConfigStr(value, strArr, level+1)
}
*strArr = append(*strArr, spacestr+"}")
} else {
*strArr = append(*strArr, spacestr+NginxConfig.Name+" "+NginxConfig.Value+";")
}

}

func main() {
ExecPath, _ := os.Getwd()
filepath := path.Join(ExecPath, "nginx", "test.psvmc.cn.conf")
configlist := ReadNginxConfig(filepath)
showLog := false
if showLog {
jsondata, err := json.Marshal(configlist)
if err != nil {
fmt.Println(err)
}

fmt.Println("configlist:", len(configlist))
fmt.Println("configlist:", string(jsondata))
}

filepath2 := path.Join(ExecPath, "nginx", "test.psvmc.cn2.conf")
WriteConfig(filepath2, configlist)
}

nginx配置

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
# 这是upstream
upstream test_psvmc_cn {
server 110.110.110.110:8080;
server 120.120.120.120:8080;
}
server {
# 这是http
listen 80;
# 这是https
listen 443;
server_name test.psvmc.cn;
client_max_body_size 200m;
ssl on;
ssl_certificate /etc/nginx/cert/xhkjedu.pem;
ssl_certificate_key /etc/nginx/cert/xhkjedu.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
# 路径映射
location / {
proxy_pass http://test_psvmc_cn/;
proxy_cookie_path / /;
proxy_redirect / /;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 200m;
client_body_buffer_size 128k;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_buffer_size 64k;
proxy_buffers 8 64k;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 128k;
send_timeout 60;
}
}

解析后的数据

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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
[
{
"Name": "upstream",
"Value": "test_psvmc_cn",
"Comment": [
" 这是upstream"
],
"Children": [
{
"Name": "server",
"Value": "110.110.110.110:8080",
"Comment": null,
"Children": null
},
{
"Name": "server",
"Value": "120.120.120.120:8080",
"Comment": null,
"Children": null
}
]
},
{
"Name": "server",
"Value": "",
"Comment": null,
"Children": [
{
"Name": "listen",
"Value": "80",
"Comment": [
" 这是http"
],
"Children": null
},
{
"Name": "listen",
"Value": "443",
"Comment": [
" 这是https"
],
"Children": null
},
{
"Name": "server_name",
"Value": "test.psvmc.cn",
"Comment": null,
"Children": null
},
{
"Name": "client_max_body_size",
"Value": "200m",
"Comment": null,
"Children": null
},
{
"Name": "ssl",
"Value": "on",
"Comment": null,
"Children": null
},
{
"Name": "ssl_certificate",
"Value": "/etc/nginx/cert/xhkjedu.pem",
"Comment": null,
"Children": null
},
{
"Name": "ssl_certificate_key",
"Value": "/etc/nginx/cert/xhkjedu.key",
"Comment": null,
"Children": null
},
{
"Name": "ssl_session_timeout",
"Value": "5m",
"Comment": null,
"Children": null
},
{
"Name": "ssl_ciphers",
"Value": "ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4",
"Comment": null,
"Children": null
},
{
"Name": "ssl_protocols",
"Value": "TLSv1 TLSv1.1 TLSv1.2",
"Comment": null,
"Children": null
},
{
"Name": "ssl_prefer_server_ciphers",
"Value": "on",
"Comment": null,
"Children": null
},
{
"Name": "location",
"Value": "/",
"Comment": [
" 路径映射"
],
"Children": [
{
"Name": "proxy_pass",
"Value": "http://test_psvmc_cn/",
"Comment": null,
"Children": null
},
{
"Name": "proxy_cookie_path",
"Value": "/ /",
"Comment": null,
"Children": null
},
{
"Name": "proxy_redirect",
"Value": "/ /",
"Comment": null,
"Children": null
},
{
"Name": "proxy_set_header",
"Value": "Host $host",
"Comment": null,
"Children": null
},
{
"Name": "proxy_set_header",
"Value": "X-Real-IP $remote_addr",
"Comment": null,
"Children": null
},
{
"Name": "proxy_set_header",
"Value": "X-Forwarded-For $proxy_add_x_forwarded_for",
"Comment": null,
"Children": null
},
{
"Name": "client_max_body_size",
"Value": "200m",
"Comment": null,
"Children": null
},
{
"Name": "client_body_buffer_size",
"Value": "128k",
"Comment": null,
"Children": null
},
{
"Name": "proxy_connect_timeout",
"Value": "300s",
"Comment": null,
"Children": null
},
{
"Name": "proxy_send_timeout",
"Value": "300s",
"Comment": null,
"Children": null
},
{
"Name": "proxy_read_timeout",
"Value": "300s",
"Comment": null,
"Children": null
},
{
"Name": "proxy_busy_buffers_size",
"Value": "64k",
"Comment": null,
"Children": null
},
{
"Name": "proxy_temp_file_write_size",
"Value": "64k",
"Comment": null,
"Children": null
},
{
"Name": "proxy_buffer_size",
"Value": "64k",
"Comment": null,
"Children": null
},
{
"Name": "proxy_buffers",
"Value": "8 64k",
"Comment": null,
"Children": null
},
{
"Name": "fastcgi_buffer_size",
"Value": "128k",
"Comment": null,
"Children": null
},
{
"Name": "fastcgi_buffers",
"Value": "4 128k",
"Comment": null,
"Children": null
},
{
"Name": "send_timeout",
"Value": "60",
"Comment": null,
"Children": null
}
]
}
]
}
]

这样Nginx配置文件就能完全解析了