Go高并发微服务分布式

1.命令行的用户管理

用户信息存储

=> 内存

=> 结构 [] map

=> 用户 ID name age tel addr

[len] [] map

值类型使用string

用户添加

用户的查询

用户修改

// 请输入需要修改的用户ID:

// users[id] => 在 不在(你输入的用户ID不正确)

// 打印用户信息,提示用户是否确认修改(Y/N)

// Y 提示用户输入修改后内容

// name, age, tel, addr

用户删除

// 请输入需要删除的用户ID:

// users[id] => 在 不在(你输入的用户ID不正确)

// 打印用户信息,提示用户是否确认删除(Y/N)

// Y delete()

// name, age, tel, addr

用户登录:

在程序中定义PASSWORD = "zzzzzzzz"

提示输入密码,如果密码输出3次都失败,提示并退出

如果密码成功,再进行用户管理操作

users.go

package mainimport ("fmt""strconv""strings"
)// 添加用户
func add(pk int, users map[string]map[string]string) {var (// id string = fmt.Sprintf("%d", pk)id   string = strconv.Itoa(pk)name stringage  stringtel  stringaddr string)fmt.Print("请输入姓名:")fmt.Scan(&name)fmt.Print("请输入年龄:")fmt.Scan(&age)fmt.Print("请输入电话:")fmt.Scan(&tel)fmt.Print("请输入地址:")fmt.Scan(&addr)users[id] = map[string]string{"id":   id,"name": name,"tel":  tel,"addr": addr,}fmt.Println(id, name, age, tel, addr)
}// 查询用户
// q == "" 显示全部
// 非空,名称,电话,住址任意一个属性中包含q内容的显示
func query(users map[string]map[string]string) {var q stringfmt.Print("请输入查询信息:")fmt.Scan(&q)title := fmt.Sprintf("%5s|%20s|%5s|%20s|%50s", "ID", "Name", "Age", "Tel", "Addr")fmt.Println(title)fmt.Println(strings.Repeat("-", len(title)))for _, user := range users {if q == "" || strings.Contains(user["name"], q) || strings.Contains(user["age"], q) || strings.Contains(user["tel"], q) || strings.Contains(user["addr"], q) {fmt.Printf("%5s|%20s|%5s|%20s|%50s", user["id"], user["name"], user["age"], user["tel"], user["addr"])fmt.Println()}}
}
func main() {// 存储用户信息users := make(map[string]map[string]string)id := 0fmt.Println("欢迎使用个人用户管理系统")for {var op stringfmt.Print(`1. 新建用户2. 修改用户3. 删除用户4. 查询用户5. 退出请输入指令:`)fmt.Scan(&op)if op == "1" {id++add(id, users)} else if op == "2" {} else if op == "3" {} else if op == "4" {query(users)} else if op == "5" {break} else {fmt.Println("指令错误")}}
}
package mainimport ("fmt""os""strconv""strings"
)const (maxAUTH  = 3password = "******"
)func inputString(prompt string) string {var input stringfmt.Print(prompt)fmt.Scan(&input)return strings.TrimSpace(input)
}// 从命令行输入密码,并进行验证
// 通过返回值告知验证成功还是失败
func auth() bool {var input stringfor i := 0; i < maxAUTH; i++ {fmt.Print("请输入密码:")fmt.Scan(&input)if password == input {return true} else {fmt.Println("密码错误")}}return false
}
func query(users map[int]map[string]string) {// var q string// fmt.Print("请输入查询内容:")// fmt.Scan(&q)q := inputString("请输入查询内容:")fmt.Println("============")for k, v := range users {// id, name, birthday, tel, addr, descif strings.Contains(v["name"], q) || strings.Contains(v["tel"], q) || strings.Contains(v["addr"], q) || strings.Contains(v["desc"], q) {// fmt.Println(k, v)printUser(k, v)fmt.Println("--------------")}}fmt.Println("============")}func printUser(pk int, user map[string]string) {fmt.Println("ID:", pk)fmt.Println("名字:", user["name"])fmt.Println("出生日期:", user["birthday"])fmt.Println("联系方式:", user["tel"])fmt.Println("联系地址:", user["addr"])fmt.Println("备注:", user["desc"])}
func getId(users map[int]map[string]string) int {var id intfor k := range users {if id < k {id = k}}return id + 1
}func inputUser() map[string]string {user := map[string]string{}user["name"] = inputString("请输入名字:")user["birthday"] = inputString("请输入出生日期(2000-01-01):")user["tel"] = inputString("请输入联系方式:")user["addr"] = inputString("请输入联系地址:")user["desc"] = inputString("请输入备注:")return user
}
func add(users map[int]map[string]string) {id := getId(users)// name := inputString("请输入名字:")// birthday := inputString("请输入出生日期(2000-01-01):")// tel := inputString("请输入联系方式:")// addr := inputString("请输入联系地址:")// desc := inputString("请输入备注:")user := inputUser()// users[id] = map[string]string{//   "name":     name,//   "birthday": birthday,//   "tel":      tel,//    "addr":     addr,//   "desc":     desc,// }users[id] = userfmt.Println("[+]添加成功")
}func modify(users map[int]map[string]string) {idString := inputString("请修改用户ID:")if id, err := strconv.Atoi(idString); err == nil {if user, ok := users[id]; ok {fmt.Println("请修改的用户信息:")fmt.Println(user)input := inputString("确定修改(Y/N)?")if input == "y" || input == "Y" {// user := map[string]string{}// user["name"] = inputString("请输入名字:")// user["birthday"] = inputString("请输入出生日期(2000-01-01):")// user["tel"] = inputString("请输入联系方式:")// user["addr"] = inputString("请输入联系地址:")// user["desc"] = inputString("请输入备注:")user := inputUser()users[id] = userfmt.Println("[+]修改成功")}} else {fmt.Println("[-]用户ID不存在")}} else {fmt.Println("[-]输入ID不正确")}
}func del(users map[int]map[string]string) {idString := inputString("请删除的用户ID:")if id, err := strconv.Atoi(idString); err == nil {if user, ok := users[id]; ok {fmt.Println("将删除的用户信息:")fmt.Println(user)input := inputString("确定修改(Y/N)?")if input == "y" || input == "Y" {// user := map[string]string{}// user["name"] = inputString("请输入名字:")// user["birthday"] = inputString("请输入出生日期(2000-01-01):")// user["tel"] = inputString("请输入联系方式:")// user["addr"] = inputString("请输入联系地址:")// user["desc"] = inputString("请输入备注:")delete(users, id)fmt.Println("[+]删除成功")}} else {fmt.Println("[-]用户ID不存在")}} else {fmt.Println("[-]输入ID不正确")}
}
func main() {if !auth() {fmt.Printf("[-]密码%d次错误,程序退出\n", maxAUTH)return}menu := `*********************1. 查询2. 添加3. 修改4. 删除5. 退出*********************`// id, name, birthday, tel, addr, desc// users := map[int][5]string// users := map[int][]string// users := []map[string]string{}// users := [][]string{}// users := [][5]string{}users := map[int]map[string]string{}callbacks := map[string]func(map[int]map[string]string){"1": query,"2": add,"3": modify,"4": del,"5": func (map[int]map[string]string) {os.Exit(1)},}// var id intfmt.Println("欢迎进入个人的用户管理系统")// END:for {fmt.Println(menu)// var op string// fmt.Print("请输入指令")// fmt.Scan(&op)op := inputString("请输入指令:")callbacks, ok := callbacks[op]if ok {callbacks(users)// } else if op == "5" {//   break} else {fmt.Println("指令错误")}// switch op {// case "1"://   query(users)// case "2"://    // id++//     // add(users, id)//     add(users)// case "3"://  modify(users)// case "4"://   del(users)// case "5"://  break END// default://  fmt.Println("指令错误")// }}
}

2.多用户命令行聊天室

go协程、管道、socket

1) net
net 包提供了对 socket 编程的支持, socket 编程分服务端和客户端编程,针对服务端可使用
函数 Listen 创建监听服务,对于客户端可使用函数 Dial 连接服务器
c) 功能
⚫ TCP 服务端开发流程
➢ 创建监听服务
➢ 循环接受客户端连接
➢ 数据处理(向客户端发送数据 / 读客户端发送的数据)
➢ 关闭监听服务
// 监听 0.0.0.0:9999 端口
// 等待客户端连接 //当客户端连接完成后,给客户端发送一个当前时间 //关闭客户端连接

tcp\server.go

服务器处理任意客户端请求 for{}

func main() {addr := "0.0.0.0:9999"listener, err := net.Listen("tcp", addr)if err != nil {fmt.Println(err)os.Exit(-1)}defer listener.Close()fmt.Println("Listen: ", addr)for {client, err := listener.Accept()if err == nil {client.Write([]byte(time.Now().Format("2006-01-02 15:04:05")))client.Close()}}}
package mainimport ("bufio""fmt""net""os""time"
)func main() {addr := ":9999"// 启动监听服务server, err := net.Listen("tcp", addr)if err != nil {fmt.Println(err)os.Exit(-1)}// 延迟关闭服务defer server.Close()fmt.Printf("Listen on: %s\n", server.Addr())for {// 获取客户端连接conn, err := server.Accept()if err == nil {// 使用协程处理与客户端连接go func(conn net.Conn) {defer conn.Close() // 延迟关闭客户端连接fmt.Println("client is connected: %s\n", conn.RemoteAddr())// 读取客户端发送的消息reader := bufio.NewReader(conn)cxt, _, _ := reader.ReadLine()fmt.Println(string(cxt))// 向客户端发送消息fmt.Fprintf(conn, "Time: %s\n", time.Now().Format("2006-01-02 15:04:05"))}(conn)}}
}
服务器可以接收客户端消息,也可以向客户端发送消息
// server.go
package mainimport ("bufio""fmt""net""os""strings"
)func main() {addr := "0.0.0.0:9999"listener, err := net.Listen("tcp", addr)if err != nil {fmt.Println(err)os.Exit(-1)}defer listener.Close()fmt.Println("Listen: ", addr)input := bufio.NewScanner(os.Stdin)for {client, err := listener.Accept()if err == nil {reader := bufio.NewReader(client)writer := bufio.NewWriter(client)fmt.Printf("客户端%s连接成功\n", client.RemoteAddr())for {fmt.Print("请输入(q退出):")input.Scan()text := input.Text()if text == "q" {break}_, err := writer.WriteString(text + "\n")writer.Flush()if err != nil {break}line, err := reader.ReadString('\n')if err != nil {break}fmt.Println("客户端:", strings.TrimSpace(line))}fmt.Printf("客户端%s关闭\n", client.RemoteAddr())client.Close()}}}
// client.go
package mainimport ("bufio""fmt""net""os"
)func main() {addr := "127.0.0.1:9999"conn, err := net.Dial("tcp", addr)if err != nil {fmt.Println(err)os.Exit(-1)}defer conn.Close()reader := bufio.NewReader(conn)writer := bufio.NewWriter(conn)input := bufio.NewScanner(os.Stdin)for {line, err := reader.ReadString('\n')if err != nil {break}fmt.Print("服务器:", line)fmt.Print("请输入(q退出):")input.Scan()if input.Text() == "q" {break}_, err = writer.WriteString(input.Text() + "\n")writer.Flush()if err != nil {break}}}

TCP 客户端开发流程

➢ 连接服务器
➢ 数据处理(向服务端发送数据 / 读服务端发送的数据)
➢ 关闭连接

tcp\client.go

package mainimport ("bufio""fmt""net""os""time"
)func main() {addr := "localhost:9999"// 创建连接conn, err := net.Dial("tcp", addr)if err != nil {fmt.Println(err)os.Exit(-1)}// 延迟关闭连接defer conn.Close()// 向服务器发送消息fmt.Fprintf(conn, "UnixTime: %d\n", time.Now().Unix())// 读取服务端发送的消息reader := bufio.NewReader(conn)cxt, _, _ := reader.ReadLine()fmt.Println(string(cxt))
}

正则

matchstring.go

package mainimport ("fmt""regexp"_ "github.com/go-sql-driver/mysql"
)func main() {// . 任意// \d 数字 \D非数字// \w 数字,大小写英文字母 _// \S 非空白字符 \s非空白字符// \d 数字 0,1,2,3,4,5,6,7,8,9 0|1|2|3|4|5|6|7|8|9 [0123456789] [0-9]// [a-z] /a, -, z/ [a\-z]// [^a-z] 取反// ? 0个或1个// + 至少1个// * 任意多个// {n,m} 字符串数量>=n, <=mvar pattern string = "^132" // 以132开头fmt.Println(regexp.MatchString(pattern, "132xxx"))fmt.Println(regexp.MatchString(pattern, "122xxxx"))// 以132开头,都是数字,长度为11位 [0-9]pattern = "^132\\d{8}$"fmt.Println(regexp.MatchString(pattern, "132xxxx"))fmt.Println(regexp.MatchString(pattern, "132123123"))fmt.Println(regexp.MatchString(pattern, "13212312323"))fmt.Println(regexp.MatchString(pattern, "13212312323x"))// 132 158// 1[35][28] 132 138 152 158// 分组 ()// ^(132)|(158)[0-9]{8}$// 邮箱格式// xxxx@xxx.xx// xxxx(@之前) 数字,大小写英文字母长度1-32个字符// xxx(.之前) 小写英文字母组成 长度1-12字符// xx(.之后) edu// [a-zA-Z0-9]{1,32}@[a-z]{1,12}.edufmt.Println("----email----")//[.]pattern = "[a-zA-Z0-9]{1,32}@[a-z]{1,12}\\.edu"pattern = "^[a-zA-Z0-9]{1,32}@[a-z]{1,12}[.]edu$"fmt.Println(regexp.MatchString(pattern, "a@b.edu"))    // 可以匹配fmt.Println(regexp.MatchString(pattern, "a@1.edu"))    // 不匹配fmt.Println(regexp.MatchString(pattern, "?@b.edu"))    // 不匹配fmt.Println(regexp.MatchString(pattern, "a@bxedu"))    // 不匹配fmt.Println(regexp.MatchString(pattern, "我是a@b.edux")) // 不匹配pattern = regexp.QuoteMeta("^ab")fmt.Println(regexp.MatchString(pattern, "ab"))fmt.Println(pattern)fmt.Println(regexp.MatchString(pattern, "^ab"))
}
PS D:\Workspace\Go\src\projects\todolist> go run matchstring.go
true <nil>
false <nil>
false <nil>
false <nil>
true <nil>
false <nil>
----email----
true <nil>
false <nil>
false <nil>
false <nil>
false <nil>
false <nil>
\^ab
true <nil>

regexp.go

package mainimport ("fmt""regexp"_ "github.com/go-sql-driver/mysql"
)func main() {reg, err := regexp.Compile("^132\\d{8}$")fmt.Println(err, reg)// 匹配 Matchfmt.Println(reg.MatchString("132xxx"))fmt.Println(reg.MatchString("13212312312"))// 替换 Replace 132????????reg, err = regexp.Compile("132\\d{8}")fmt.Println(reg.ReplaceAllString("我的电话是132xxx请记录下", "132????????"))fmt.Println(reg.ReplaceAllString("我的电话是13212312312请记录下", "132????????"))// 查找 Findfmt.Println(reg.FindAllString("我的电话是13212312312,13212312313,15812312313", -1))// 分割reg, err = regexp.Compile("[:;\\t,]")fmt.Println(reg.Split("13212312312,13212312313,15812312313:xxx;zzzz\tyyyyy", -1))
}
PS D:\Workspace\Go\src\projects\todolist> go run regexp.go
<nil> ^132\d{8}$
false
true
我的电话是132xxx请记录下
我的电话是132????????请记录下
[13212312312 13212312313]
[13212312312 13212312313 15812312313 xxx zzzz yyyyy]

longest.go

package mainimport ("fmt""regexp"_ "github.com/go-sql-driver/mysql"
)func main() {reg, _ := regexp.Compile("[ab0-9]+")fmt.Println(reg.FindAllString("0-a23-b3456", -1))// 定义非贪婪模式reg, _ = regexp.Compile("(?U)[ab0-9]+")fmt.Println(reg.FindAllString("0-a23-b3456", -1))// 将非贪婪模式转换为贪婪模式reg.Longest()fmt.Println(reg.FindAllString("0-a23-b3456", -1))
}
PS D:\Workspace\Go\src\projects\todolist> go run longest.go
[0 a23 b3456]
[0 a 2 3 b 3 4 5 6]
[0 a23 b3456]

Web爬虫

Goquery

godocscaper.go

package mainimport ("fmt""log""github.com/PuerkitoBio/goquery"
)func main() {q := "goquery"url := "https://godoc.org/?q=" + q// 发起http请求获取响应并创建Document结构体指针document, err := goquery.NewDocument(url)if err != nil {log.Fatal(err)}// 在docuemnt中通过选择器去查找元素// <tagname>// 标签选择器// 获取所有的a标签selection := document.Find("a")selection.Each(func(index int, tag *goquery.Selection) {href, exists := tag.Attr("href")// tag.Html()fmt.Println(tag.Text(), href, exists)})fmt.Println("==============class==============")// class选择器// .className// table// 在table下获取所有的超链接document.Find(".table-condensed").Find("a").Each(func(index int, tag *goquery.Selection) {href, exists := tag.Attr("href")// tag.Html()fmt.Println(tag.Text(), href, exists)})// id选择器// #idfmt.Println(document.Find("#x-search").Attr("class"))fmt.Println(document.Find("#x-search").Html())fmt.Println(document.Find("#x-search").Text())// 符合选择器// tag + class// <div></div><div class="nav"></div><span class="nav"></span>// tag.class// 子孙选择器// selector1 selector2 selector3 ...fmt.Println("=========子孙选择器============")document.Find(".table-condensed a").Each(func(index int, tag *goquery.Selection) {href, exists := tag.Attr("href")// tag.Html()fmt.Println(tag.Text(), href, exists)})// 子选择器// selector1 > selector2// document.Find(selector1).ChildrenFiltered(selector2)
}

RPC

rpcserver

rpcclient

HTML结构

模板技术

package mainimport ("fmt"htmlTemplate "html/template""os""text/template"
)func main() {// 显示数据tplText := "我叫 {{ . }}"tpl, err := template.New("tpl").Parse(tplText)fmt.Println(err)tpl.Execute(os.Stdout, `<img src="xxxx" />`)fmt.Println()htmlTpl, err := htmlTemplate.New("tpl").Parse(tplText)htmlTpl.Execute(os.Stdout, `<img src="xxxx" />`)
}
PS D:\Workspace\Go\src\projects\template> go run main.go
<nil>
我叫 <img src="xxxx" />
我叫 &lt;img src="xxxx" /&gt;

Go语言操作Mysql数据库

Mysql的基本概念和操作方法

数据库基础

库、表、SQL

Mysql操作

1. 安装2. 创建新用户create user golang identified by "1234";grant all privileges on database.table to name@addr identified by password;addr %database.table *.* database.*grant all privileges on *.* to golang @'%' identified by "1234";flush privileges;
root/1234 禁止远程访问root用户3. 库database创建:create database name default charset utf8mb4;查询:show databases;查看:show create database name;删除:drop database name;4. 表use database;创建create table name (colname coltype 修饰,index indexname (colname, colname1, colname2)) engine=innodb default charset=utf8mb4;colname: 列名 小写英文字母,数字, _组成coltype: 数值类型intbigintfloatdoubledecimal(m, n)字符串类型varchar(n)char(n)时间类型datadatetimetime文本类型text 64Kmediumtext 16Mlongtext 4G二进制类型bloblongblobjson/array...修饰:主键: primary key唯一: unique自动增长: auto_increment默认值: default 0, default ''是否允许为null, 不允许为NULL, NOT NULL注释: COMMENT ''索引:indexcreate table task(id bigint primary key auto_increment,name varchar(64) not null default '' comment '任务名称',status int not null default 0 comment '状态, 0: 新建,1: 正在执行,2: 停止, 3: 完成',start_time datetime comment '开始时间',complete_time datetime comment '完成时间',deadline_time datetime not null comment '截至时间',index idx_name (name),index idx_deadline_time(deadline_time)) engine=innodb default charset utf8mb4 auto_increment 1000;查看desc name;show create table name;查询show tables;删除表:drop table name;修改表alter table name 动作;只允许添加列:alter table name add column colname coltype 修饰;删除列:alter table name drop column colname;修改列:alter table name modify column colname coltype 修饰;5. 索引create index name on table (column, column2, ...);drop index name on table;create unique index name on table (column, column2, ...);6. 转义 ``
7. 表数据操作增insert into `table`(`c1`, `c2`, `c3`, `c4`) values(v1, v2, v3, v4);insert into `table`(`c1`, `c2`, `c3`, `c4`) values(v11, v12, v13, v14),(v21, v22, v23, v24),(v41, v42, v43, v44);查select * from table;select c1, c2 from table;select * from table where colname condition value;condition:=, !=, >, <, >=, <=like 模糊匹配开头: like value%结尾: like %value包含: like %value%% 任务多个__ 一个like '%/%%' escape '/'in (v1, v2) colname = v1 或者 colname = v2colname between v1 and v2 => colname >= v1 and colname <= v2逻辑关系与and或orcolname = v1 or colname = v2非notc1 = v1 and (c2 = v2 or c3 = v3)删delete from table;delete from table where 条件;truncate table name;改update tableset colname=v1, col2=v2, col3=v3;update tableset colname=v1, col2=v2, col3=v3where 条件;sql:用户,权限,运维 操作:库,表 操作: truncate table name; 重建表数据 操作:8: 数量count(*)count(id)9. as 别名10. 查询排序order by colname [asc|desc], col2 [asc|desc]分页展示多少条数据 每页的数据量展示第几页 页面limit 限制查询数量offset 设置偏移每页展示5条 limit 5第一页 offset 0第二页 offset 5 (pageNum - 1) * pageSize分组IP time url status_codeip出现的次数status_code出现的次数url, status_code出现的次数ip url status_code出现的次数group byselect [] from table group by colname,colname2 [having 过滤条件;]select 元素必须是 指定分组的列名或者聚合类结果count(*)求和最小值最大值平均值insert into task(name, content, deadline_time) values('1', '1', '2020-06-06'),('2', '1', '2020-06-05'),('3', '1', '2020-06-04'),('4', '1', '2020-06-02'),('5', '1', '2020-06-02'),('6', '1', '2020-06-02'),('7', '1', '2020-06-03');create table accesslog(id bigint primary key auto_increment,logtime datetime not null comment '访问时间',ip varchar(128) not null default '' comment '访问来源',url varchar(4096) not null default '' comment '访问地址',status int not null default 0 comment '状态码') engine=innodb default charset utf8mb4;insert into accesslog(logtime, ip, url, status) values('2020-06-06 01:01:01', '1.1.1.1', '/index', 200),('2020-06-05 01:01:02', '1.1.1.1', '/home', 302),('2020-06-07 01:01:02', '1.1.1.1', '/back.zip', 404),('2020-06-06 01:01:04', '1.1.1.1', '/ip.exe', 404),('2020-06-05 01:01:04', '1.1.1.2', '/ip.exe', 404),('2020-06-06 01:01:04', '1.1.1.2', '/ip.exe', 404),('2020-06-05 01:01:04', '1.1.1.2', '/ip.exe', 404),('2020-06-08 01:01:04', '1.1.1.3', '/home', 200);每天的访问数量datetime => date '年-月-日'select date_format(logtime, '%Y-%m-%d') as log_day, count(*) as cnt from accessloggroup by log_day;ip出现的次数select ip, count(*) from accesslog group by ip;status_code出现的次数select status, count(*) from accesslog group by status;url, status_code出现的次数select url, status, count(*) from accesslog group by url, statusip url status_code出现的次数select ip, url, status, count(*) from accesslog group by ip, url, status;create table score(id bigint primary key auto_increment,day date not null comment '日期',name varchar(32) not null default '' comment '姓名',score float not null default 0 comment '分数')engine=innodb default charset utf8mb4;insert into score(day, name, score) values('2020-06-01', '烽火', 8),('2020-06-01', '魏超', 6),('2020-06-01', '阿宁', 7),('2020-06-02', '烽火', 5),('2020-06-02', '魏超', 5),('2020-06-02', '阿宁', 7),('2020-06-03', '烽火', 7),('2020-06-03', '魏超', 10),('2020-06-03', '阿宁', 9);统计每个人的总分, 最高分, 最低分,平均分求和 sum最小值 min最大值 max平均值 avgselect name, sum(score),max(score),min(score), avg(score) from score group by name;联查多张表进行查询数据joinleft join oninner join onright join oncreate table user(id bigint primary key auto_increment,name varchar(32) not null default '',status int not null default 0 comment '0:在职, 1:离职') engine=innodb default charset utf8mb4;alter table task add column user bigint;insert into user(name, status) values('詹林', 0),('阿宁', 0),('cc', 1);insert into task(name, content, deadline_time, user) values('完成web todolist', '', now(), 1),('跑10圈', '', now(), 1),('洗衣服', '', now(), 1),('完成web todolist', '', now(), 2);每个人(名字)未完成的任务select user.name, task.name from task left join user on task.user=user.id where task.status!=3;select user.name, task.name from task right join user on task.user=user.id where task.status!=3;insert into task(name, content, deadline_time, user) values('不存在用户的任务', '', now(), 5);11. 内置函数(调用)now()md5()date_format(time, layout)12. 扩展pg => 表继承taskdatetask_2020_05task_2020_06insert int taskselect * from task过程13.用户,权限,运维 操作: 执行库,表 操作: 执行, 读表数据 操作: 增,删,改 => 执行查 => 读执行读编辑1. 点击编辑按钮 id => 内容显示2. 内容修改 id => 提交按钮 => 数据验证 => 更新数据input type="hidden" value="{{ task.ID }}"

Go语言的常用包和使用方式

testdb\main.go

package mainimport ("database/sql""fmt"
)// 导入beego包func main() {// user:password@tcp(host:port)/database?charset=utf8mb4&loc=PRC&parseTime=truedsn := "root:1234@tcp(192.168.204.130:3306)/todolist?charset=utf8mb4&loc=PRC&parseTime=true" // 字符串的格式由对应的驱动进行定义db, err := sql.Open("mysql", dsn)fmt.Println(db, err)fmt.Println(db.Ping())// 执行// sql => go 字符串fmt.Println(db.Exec(`create table if not exists testwu(id bigint primary key auto_increment,name varchar(32) not null default '' comment 'testwu名字') engine=innodb default charset utf8mb4;`))sql := `update task set status = ?`result, err := db.Exec(sql, 3)fmt.Println(sql, err)fmt.Println(result.RowsAffected())tid := "2 or 1=1"result, _ = db.Exec(`update task set status = 2 where id=?`, tid)fmt.Println(result.RowsAffected())result, _ = db.Exec(`delete from task where id=?`, 16)fmt.Println(result.RowsAffected())tname := "买个电视x"content := ""deadline := 2022 - 10 - 10result, err = db.Exec(`insert into task(name, content, deadline_time) value(?, ?, ?)`, tname, content, deadline)fmt.Println(err)fmt.Println(result.LastInsertId())fmt.Println(result.RowsAffected())// 读rows, err := db.Query("select id, name from task where id > ? limit 1", 18)var (id   intname string)if rows.Next() {rows.Scan(&id, &name)fmt.Println(id, name)}// sql语句不能占位 展示变量row := db.QueryRow("select id, name from task where id>? order by id desc", 10)err = row.Scan(&id, &name)fmt.Println(err, id, name)}

tx\main.go

package mainimport ("database/sql""fmt"_ "github.com/go-sql-driver/mysql"
)// id 增加 money
func changeMoney(tx *sql.Tx, id int, money float64) error {if money < 0 {// 检查var accountMoney float64err := tx.QueryRow("select money from account where id=?", id).Scan(&accountMoney)if err != nil {return err}if accountMoney < -money {return fmt.Errorf("没有足够的金额")}}_, err := tx.Exec("update account set money=money+? where id=?", money, id)return err
}func main() {// user:password@tcp(host:port)/database?charset=utf8mb4&loc=PRC&parseTime=truedsn := "root:1234@tcp(192.168.204.130:3306)/todolist?charset=utf8mb4&loc=PRC&parseTime=true" // 字符串的格式由对应的驱动进行定义/*create table account(id bigint primary key auto_increment,name varchar(32) not null default '',money decimal(10, 5) not null default 0) engine=innodb default charset utf8mb4;insert into account(name, money) values("wu", 1000);insert into account(name, money) values("烽火", 1000);*/db, _ := sql.Open("mysql", dsn)// 转账var a, b = 1, 2// a => b money// a - money// b + moneyvar money float64 = 100.0// 同时成功同时失败// 事务tx, _ := db.Begin()err1 := changeMoney(tx, a, -money)err2 := changeMoney(tx, b, money)fmt.Println(err1, err2)if err1 == nil && err2 == nil {// 提交事务tx.Commit()} else {// 回滚tx.Rollback()}}

stmt\main.go

package mainimport ("database/sql""fmt""time"_ "github.com/go-sql-driver/mysql"
)// id 增加 money
func changeMoney(tx *sql.Tx, id int, money float64) error {if money < 0 {// 检查var accountMoney float64err := tx.QueryRow("select money from account where id=?", id).Scan(&accountMoney)if err != nil {return err}if accountMoney < -money {return fmt.Errorf("没有足够的金额")}}_, err := tx.Exec("update account set money=money+? where id=?", money, id)return err
}func main() {// user:password@tcp(host:port)/database?charset=utf8mb4&loc=PRC&parseTime=truedsn := "root:1234@tcp(192.168.204.130:3306)/todolist?charset=utf8mb4&loc=PRC&parseTime=true" // 字符串的格式由对应的驱动进行定义db, _ := sql.Open("mysql", dsn)start := time.Now()stmt, _ := db.Prepare(`insert into account(name, money) values(?, ?)`)for i := 0; i < 10000; i++ {stmt.Exec(fmt.Sprintf("a_", i), 1000)}fmt.Println(time.Now().Sub(start))
}

go-demo/todolist at main · yunixiangfeng/go-demo · GitHub

todolist\main.go

package mainimport ("database/sql""fmt""log""net/http""strings""text/template""time""unicode/utf8"_ "github.com/go-sql-driver/mysql"
)const (dbDriver   = "mysql"dbUser     = "root"dbPassword = "1234"dbName     = "todolist"dbHost     = "192.168.204.130"dbPort     = 3306
)const (listenAdd = ":8888"
)const (sqlTasks      = "select task.id, task.name, task.status, task.start_time, task.complete_time, task.deadline_time, user.name as user from task left join user on task.user=user.id"sqlCreateTask = "insert into task(name, content, deadline_time) values(?, ?, ?)"sqlDeleteTask = "delete from task where id = ?"
)const (dateTimeLayout = "2006-01-02 15:04:05"
)var (statusMap = map[int]string{0: "新建",1: "正在进行",2: "暂停",3: "完成",}
)type Task struct {ID           intName         stringStatus       intStartTime    *time.TimeCompleteTime *time.TimeDeadlineTime *time.TimeUser         *stringContent      string
}func (task *Task) StatusText() string {return statusMap[task.Status]
}type TaskForm struct {ID           intName         stringStatus       intDeadlineTime stringContent      stringUser         int
}func main() {dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true&loc=PRC", dbUser, dbPassword, dbHost, dbPort, dbName)// 打开数据库连接池db, err := sql.Open(dbDriver, dsn)if err != nil {log.Fatal(err)}// 测试数据库连接if err := db.Ping(); err != nil {log.Fatal(err)}// 显示任务列表http.HandleFunc("/", func(response http.ResponseWriter, request *http.Request) {tasks := make([]Task, 0, 20)rows, err := db.Query(sqlTasks)if err == nil {for rows.Next() {var task Taskerr := rows.Scan(&task.ID, &task.Name, &task.Status, &task.StartTime, &task.CompleteTime, &task.DeadlineTime, &task.User)if err == nil {tasks = append(tasks, task)} else {fmt.Println(err)}}}funcs := template.FuncMap{"datetime": func(t *time.Time) string {if t == nil {return "--"}return t.Format(dateTimeLayout)},"status": func(status int) string {//status int => string//if//switchreturn statusMap[status]},}// 模板函数必须在解析模板之前进行设置tmpl := template.Must(template.New("tpl").Funcs(funcs).ParseFiles("views/tasks.html"))tmpl.ExecuteTemplate(response, "tasks.html", struct {Tasks []Task}{tasks})})http.HandleFunc("/add/", func(response http.ResponseWriter, request *http.Request) {var (task   TaskFormerrors = make(map[string]string))if request.Method == http.MethodGet {// 加载模板} else if request.Method == http.MethodPost {name := strings.TrimSpace(request.PostFormValue("name"))content := strings.TrimSpace(request.PostFormValue("content"))deadlineTime := strings.TrimSpace(request.PostFormValue("deadline_time"))task = TaskForm{Name:         name,Content:      content,DeadlineTime: deadlineTime,}nameLength := utf8.RuneCountInString(task.Name)if nameLength == 0 {errors["name"] = "任务名不能空"} else if nameLength > 32 {errors["name"] = "任务名不能超过32个字符"}contentLength := utf8.RuneCountInString(task.Content)if contentLength > 512 {errors["content"] = "任务描述不能超过512个字符"}if _, err := time.Parse("2006-01-02", deadlineTime); err != nil {errors["deadline_time"] = "任务期限不能为空"}// 验证完成,无错误if len(errors) == 0 {db.Exec(sqlCreateTask, task.Name, task.Content, task.DeadlineTime)http.Redirect(response, request, "/", http.StatusFound)}}tmpl := template.Must(template.ParseFiles("views/add_task.html"))tmpl.ExecuteTemplate(response, "add_task.html", struct {Task   TaskFormErrors map[string]string}{task, errors})})http.HandleFunc("/delete/", func(response http.ResponseWriter, request *http.Request) {id := request.FormValue("id")db.Exec(sqlDeleteTask, id)http.Redirect(response, request, "/", http.StatusFound)})http.ListenAndServe(listenAdd, nil)
}

todolist\views\tasks.html

<!DOCTYPE html>
<html><head><meta charset="utf-8"/><title>任务列表</title></head><body><a href="/add/">新建</a><table><thead><tr><th>名称</th><th>负责人</th><th>状态</th><th>开始时间</th><th>完成时间</th><th>限期</th><th>操作</th></tr></thead><tbody>{{ range .Tasks }}<tr><td>{{ .Name }}</td><td>{{ .User }}</td><td>{{ .StatusText }}</td><td>{{ datetime .StartTime }}</td><td>{{ datetime .CompleteTime }}</td><td>{{ datetime .DeadlineTime }}</td><td><a href="/delete/?id={{ .ID }}">删除</a></td></tr>{{ end }}</tbody></table></body>
</html>

todolist\views\add_task.html

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>添加任务</title></head><body><form action="/add/" method="POST"><label>名称: </label> <input type="text" name="name" value="{{ .Task.Name }}" />{{ .Errors.name }}<br /><label>描述: </label> <textarea name="content">{{ .Task.Content }}{{ .Errors.content }}</textarea><br /><label>期限: </label> <input type="date" name="deadline_time" value="{{ .Task.DeadlineTime }}"/>{{ .Errors.deadline_time }} <br/><input type="submit" value="提交" /></form></body>
</html>

0817 day10

1. 注册过程
    a. 打开注册页面 / GET
    b. 填写信息 点击注册按钮 提交数据 /register/ POST

持久化使用文件

2. Todolist
    a. 显示任务列表 => /

1. 洗衣服            x
        2. 看电视            x
        3. 看数             x

b. 添加流程
        i. 添加超链接 => 添加页面
        ii. 填写数据 点击添加按钮 添加数据到持久化
        iii. 重定向到任务列表页面
    c. 修改流程
        i. 点击修改按钮 => 修改数据标识(ID)提交到服务器端 => 修改页面(原数据需要填充在表单中)
        ii. 修改数据 点击提交按钮  修改数据到持久化
        iii. 重定向到任务列表页面
    4. 删除流程
        i. 点击删除按钮 => 删除数据标识(ID)提交到服务器端
        ii. 删除数据,修改数据到持久化
        iii. 重定向到任务列表

id
name
user
progress
status

1. 模板 => 加载
2. 模板 + 数据 => html
    如何显示数据
    如何遍历数据
    如何用条件

0824 day11

1. 用户列表显示
    前 -> 后
    / => controller => model => view

后->前
    model->controller->view->handle

a. /users/ => Action => HandleFunc
    b. model => 文件中加载 => 返回
    c. views => users.html

2. 用户列表查询
    用户输入数据(input q) form
    form action=? method=?
    /users/ get q=xxx
    FormValue("q")
    users = GetUsers(q)

/users/query post

PostFormValue("q")

GetUsers()
    for users
    users contains q

3. 用户登陆
    a. 打开登陆页面
        Get /user/login => Action => Execute(user/login.html)

b. 登陆流程
        POSt /user/login

方法一: 获取用户名/密码 => 验证 找输入用户名/密码都相等的用户
        [用]方法二: 通过用户名去查找用户 => 没找到 User
                    找到 => 判断密码是否正确(通过User方法来验证)

认证成功
            => 跳到到任务列表
        认证失败
            => 返回到输入信息页面,并提示错误,及用户原输入信息

发现用户没有登陆时跳转到登陆页面让进行登陆
    机制:跟踪用户状态
    session + cookie

你银行办业务

你(浏览器)                 银行(服务器)
    1. 第一次去银行              开户                     0
                                给你银行卡
    2. 第二次去银行带上卡           存1w                  1w
    3. 第3次去银行带上卡            取1k                  9k
    4. 没带银行卡                想要取钱(银行拒绝)

cookie的存储: 在浏览器
    cookie的信息: 卡号

浏览器                                  服务器
    1. 第一次请求                               开辟一定存储空间(编号=>session ID, 存储空间=>session)
                                               将session ID返回给客户端

response header
                                                    Set-Cookie: session=xxxxx
        浏览器接收到请求存储cookie信息

2. 第二次请求   浏览器会读取cookie信息(session ID)并通过http请求提交给服务器      (登陆 成功 在存储空间User)
                                            获取Session ID 查找对应存储空间中的数据
    3. 以后请求中都会带cookie信息                   从存储空间中尝试获取User,如果没有获取到, 未登陆
                                                                         如果获取到,已登录

技术:
        怎么生成Session ID => go.uuid

存储内存/文件/数据库/redis

怎么设置, 怎么取修改response header set-cookie
        获取cookie request

4. 退出登陆
    session销毁
    cookie销毁

5. 添加用户
     a. 打开添加页面
        Get /users/create/
     b. 提交数据
        Post /users/create/
        r.PostFormValue
        验证
            用户名长度 4-12
                不允许重复
            密码长度 6-30
            出生日期 1960 - 2019
        验证成功 添加用户并持久化
        验证失败,返回添加页面,并回显错误和输入信息

day12

mvc

day13

表 => struct
列 => struct 属性
行数据 => struct 对象

数据操作 => struct 对象方法的调用/函数的调用

day17

beego.Get("/", function(c *context.Context) {

})

/users/delete/?id=1
/users/delete/1/

:name

/users/delete/:id:int/

type UserController struct {
    beego.Controller
}

beego.Router("/user/", &UserController{})

PUT /user/
GET /user/

Login
Error

Get /user/ => Login
Post /user/ => Login
其他的 /user/ => Error
PUT /user/ => Create
DELETE /user/ = > Delete

beego.Router("/user/", &UserController{}, "get,post:Login;put:Create;delete:Delete;*:Error")

beego.AutoRouter(&UserController{})

Login
Error
Create
Delete

/user/login => Login
/user/error => Error

login:
body: name, password

delete:/delete/?id=1

type LoginForm struct {
    Name string `form:"name"`
    Password string `form:"password"`
}

1. layouts 公共js/css
2. LayoutContent
    每个页面都自己的js/css
    LayoutSections

LayoutStyles
    LayoutScripts

3. Database
    a. table => 页面 => DataTable生成分页页面数据(前端查询 js)
    b. ajax => 请求数据 => DataTable根据ajax返回数据生成分页页面数据(前端查询 js)
    c. 全后端 ajax

jQuery.get
    jQuery.post
    url, {}, function(response) {}, "json"

"code" : 200/400/403/500
    "text": "",
    "result": nil/[]/{}

创建
    dialog => 内容(index.html) 不发送请求
编辑
    a. dialog => 表单内容(index.html)
                数据 => 发送请求获取的 ajax => 填充到表单中
                对应关系的问题
    dialog => ajax 获取表单内容+数据 html => 放在dialog中
        jQuery(selector).load(url) ajax

day18

AKDxc4

< &lt;
> &gt;
" &quot;
' '
& &amp;

outercondition = innercondition and

(name like '%xxx%' or desc like '%xxx%') and create_user = 1

total, totalFilter

管理员
    total  select count(*) from user

totalFilter select count(*) from user where q=xxxx

普通用户
     total  select count(*) from user where create_user = xxxx

totalFilter select count(*) from user where q=xxxx and create_user  = xxx

先设置用户条件 求count total
    再设置查询条件 求count totalFilter

第二页 10 10

1. 排序,后端需要维护0 => name关系
    => 能不能前端 直接告诉后端用哪个列进行排序(列名)

2. 页面上有不能排序的列
    => datatable能不能针对某一列指定进行排序

3. key => search[value] order[0][dir]/order[0][column]
    自定定义
4. 参数传递了一堆 columns[7]

=> 能不能自定义提交发起ajax的参数

1. 修改datatable的布局 dom: lftip 定义一个放置button按钮div
    l: 显示分页数据
    f: 搜索
    t: 表格内容
    i: 搜索数量
    p: 翻页

< f>
    <"className" >
    <"#id">

<"row" <"col-2" l><"col-2" i><"col-8"p>>

<div class="row">
        <div class="col-2"> l</div>
        <div class="col-2"> i</div>
        <div class="col-2"> p</div>
    </div>
2. datatable绘制完成后 使用jquery再button div中插入咱们的按钮
    div html方法

<"row" <"col-5" l><"col-6" f><"#buttons.col-1">>tip

<div class="row">
        <div class="col-5"> l</div>
        <div class="col-6"> f</div>
        <div class="col-1" id="buttons"></div>
    </div>

用户管理平台cmdb

第一版 go1.13.15+k8s1.18.8+beego v1.12.2+mysql+prometheus

用户管理模块 新建、查询、修改、删除用户功能,用户密码修改功能

用户管理

登陆验证

管理页加载

用户数据加载

增/删/改/锁定/解锁

云主机管理

主机资源监控

prometheus节点、任务、目标、告警功能,alartmanager告警通知,短信tencent_sms,邮件smtp

redis保存session

kubernetes管理deployment、service、ingress创建,查询,修改,删除功能。

go-demo/cmdb at main · yunixiangfeng/go-demo · GitHub

day12

自述

MVC:

M=>Model: 模型
V=>View: 视图
C=>Controller: 控制器

分层原则
客户端请求: http协议 => url, method, params

服务器处理客户端过程: url => handler => params => db => template => html
a. 定义处理器
    params
b. 处理器调用数据库对数据进行处理(增/删/改/查)
c. 处理器调用模板基础去渲染页面
d. 定义url处理器管理

客户端渲染: http渲染页面

day13

controller逻辑图

自述

登录成功后显示用户列表
url->用户列表页面展示
Controller => Model(获取用户数据) => View => Router

用户认证
记录用户状态? 记录在哪里?
HTTP无状态? 下一次请求

cookie session机制
状态记录 => session
    在什么时间记录(代码什么位置)?
    登录成功 记录状态 (session) sessionid
    setcookie sessionid
状态的跟踪 => (sessionid) => cookie

登录状态
    无sessionid
    有sessionid sessionid无对应session信息
    有sessionid sessionid无session登录状态

未登录(无session登录标识)
        跳转到登录页面
    已登录 => 正常逻辑

用户鉴权

beego
    开启: 配置 SessionOn=true/false
    存储位置: 内存,文件,数据库
            SessionProvider: file/mysql/redis
    存储的地址
            SessionProviderConfig
    cookie中存储sessionid的名字
            SessionName
    失效时间
        SessionGCMaxLifetime = 3600 s

操作
        存储session
            controller: SetSession key value 可以是任意类型的
                        持久化的编码方式 gob 注册
        获取session
            controller: GetSession key => value interface{}
                        断言
        销毁session
            key1
            key2

controller: DelSession(key)
            DestorySession()

1. session(登录检查)
    在任何需要登录以后才能访问的action执行之前都需要进行检查
2. 如果访问登录页面
    检查session已存在(用户已登录,就不在打开登录页面,直接跳转到首页)

1. 公共地方检查
    beego的执行过程

数据操作
存储: Table
数据 增/删/改/查

数据定义 Table => 列,类型 => 数据 => 增,删,改,查
面向对象 类 => 属性(属性名, 类型) => 实例 => 方法调用
ORM

day14

自述

静态资源下载在本地

static

/static/ => static
/static/a.js => static/a.js
/static/a/b.js => static/a/b.js

用户管理

// 任务管理

资产管理
机房管理
工单
告警管理
统计图标

1. 编辑
    a. 打开编辑
        GET id => 查找 => 渲染
    b. 提交
        POST id/xxx => 更新数据 => (成功)跳转到列表页面
                                  (失败)

day15

代码结构

部署

自述

[x]1. 用户管理 => 修改自己密码

a => 打开用户修改密码页面

/*

controller 参数 uid => 验证用户uid当前用户 => 是 => 获取用户信息显示 (用户名) => 加载视图

不是 =>

session => uid => loginuser

*/

controller => 加载视图

views/tpl

rounter(url=>controller)

b. => 提交数据

旧密码 => 新密码(确认)

Form

parseForm

检查旧密码

新密码 和 确认密码是不是一致

新旧不能一样

密码 必须包含数字,大小写英文,特殊字符(.@!$#) 至少3中, 6

update user set password="xxx" where id=uid

[x]2. CSRF

网络攻击

扩展请求伪造

a. 配置

开启CSRF防护

配置Token => key

过期时间

b. 打开页面生成TOken

从Controller生成 传递到页面

c. 提交数据提交Token

beego自动验证(POST,DELETE,PUT)

csrf_token =>

[x]3. Flash

处理成功后将消息存储 cookie

想要显示时从存储中获取消息并显示 从cookie中读取/删掉

页面使用后端模板

从当前Controller某个URL跳转到另外一个URL

存储

flash := beego.NewFlash

flash.Set(key, value)

flash.Store(&c.Controller)

// 获取

flash := beego.ReadFromRequest(&c.Controller)

// readFromRequest     c.Data[key] = value

flash.Data

[x]4. 错误处理

[x]5. 验证

[x]6. 日志

[x]7. Layout&LayoutSection

[x]13. Cache

缓存

8. 用户权限

jd

万志强 => user

kk => user

管理侧 => 添加商品

维护价格

用户管理

角色 不同角色 不同功能

相同角色 不同数据

水平权限: 相同角色, 数据不共享, 数据的属性

垂直权限: 不同角色 A => 1, 2, 3 A-> 4(权限错误,提权)

B => 2, 3, 4

Todolist

user 角色 role 1 => 管理员 2 =>普通用户

修改密码

Task管理

用户管理

管理员: 操作用户管理, 所有用户的Task管理

UserController

Prepare => 判断用户时非管理员 => 跳转到无权限页面

是管理员 => 继续访问

TaskController

普通用户: Task管理,只能对自己创建的任务进行管理

Task => User属性

TaskController

Query => user == current.ID

Add => current.ID => db

Detail => id, user => data

modify => id, id =data => data.user == current.ID

delete => id => data, data.user == current.ID

如何限制普通用户操作UserController

发起请求-》请求操作成功

如果限制未登录用户操作UserController

session => User 有 放行

无 跳转到登录页面

role {

1 : [controller1.Action1, controller2.Action2],

2 : [controller1.Action1, controller1.Action2]

}

Authorization => session => user => role => actions

GetControllerAndAction

在 actions => 有权限

不在 => 无权限

9. 过滤器

[x]10. 部署

[x]11. HTTPS

[x]12. cobra

beego orm --db --force --verbose

main web

db

init

name {

attr: value;

}

html->head

<style type="text/css">

</style>

<link href="css path" rel="stylesheet" type="text/css" />

User

type userService struct {

}

Add

Find/Query

Detail

Delete

UserService = new(userService)

A UserService.Add

B UserServiceInstance

day16

csrf

自述

c.Data["token_input"] = template.HTML(`<input type="text" value="xxx" name="xxxx" />`)

{{ token_input }} // <input xxxx/> []

beego 防止xss攻击 <input/> &lt;

菜单(显示):
    用户管理
    任务管理

管理员
    用户管理
    任务管理

普通用户
    任务管理

user => role
role => template => if => func

hasPerssion(.user, "key")

menus := []Module{
    Moudule("user", "action", "用户管理", [contaoller.action]),
    Moudule("task", "action", "任务管理")
},

垂直权限 : rbac
水平权限 : 基于属性的

role =>
    管理员 => user, task
    普通用户 => task

Prepare()
    loginUser.Role => Modules => [Controller.action]

c.GetControllerAndAction() in [Controller.Action]

else
        无权限
        c.Redirect()
        c.Abort("notpermission")

day19

1. Alartmanager告警通知
    a. 短信 腾讯
    b. 邮件 smtp

告警通知以分组为单位
    告警处理以告警为单位

2. Redis
    a. redis基本操作
    b. redigo使用
    c. beego session redis
    d. beego cache redis
3. syncd 代码部署
    a. 介绍&功能演示
    b. gin介绍
    c. 代码走读
        认证&授权 jwt
        编译过程
        部署过程

目的:
        a. 学习别人的开发思路
        b. beego => gin
        c. 学习看开源代码

同步方式
    需要等待执行完成后返回
异步方式
    不需要等待执行结束直接返回

队列
        db
        rabbitmq/kafka
        redis
        file

redis:
    key value 基于内存的存储

1. 缓存
    2. 分布式功能, 持久化 rdp, aof 存储
    3. 分布式锁, 原子操作 setnx
        zookeeper/etcd
    4. 消息队列, aof
        rabbitmq/kafka/activmq

key value => map key value=>type

type:
        key:
            组成 英文字母(大小写), 数字, _
            开头 英文字母
            功能层级名称
                cmdb:xxx:

cmdb:test:
        常用的数据类型
        key
            判断key是否存储
                keys *
                exstis key
            判断key类型
                type key
            设置过期时间/ 查看过期时间
                ttl/expire
            删除key
                del
        string
            设置/更新:
                set key value
            获取
                get key
            加1(n)/减1(n)
                incr/decr
                incrby/decrby
            追加
                append

缓存:
                1. 先从缓存中获取
                    获取到返回
                2. 查询/计算
                    放入缓存 设置失效时间
                    返回
        list []interface{}
            左/右

放入元素PUSH
                 LPUSH/RPUSH

拿出元素POP
                LPOP/RPOP
                阻塞
            获取元素数量
                llen

查看所有元素
                0 -1
                lrange key start end
            2 1

队列
                先入先出
                    LPUSH RPOP
                    RPUSH LPOP

阻塞
                    RPOP => 无元素返回nil

for {
                value := rpop => value
                if value == nil {
                    time.Sleep(1s)
                    continue
                }
                op(value)
            }

for {
                value := brpop => key value
                if value == nil {
                    continue
                }
                op(value)
            }
        hash map
            key hash
                k => v

set 集合 元素不重复
            sadd
            srem
            sinmember
            smembers

sinter
            sdiff
            sunion
        zset 有序集合
            zadd

优先级任务
            权重 任务ID

zrange key 0 0
            zrevrange key 0 0

zrem member

task:id hash

地理位置
        bitmap
        ...

默认无认证
        require

go 操作

开发流程

创建项目结构

修改配置文件

编写启动文件

登陆

用户/Token模型定义

登陆页面加载

提交用户名/密码登陆验证

验证结果处理

用户管理

登陆验证

管理页加载

用户数据加载

增/删/改/锁定/解锁

Token查看/生成

gocmdb

第二版

go install github.com/GoAdminGroup/adm@latestmkdir gocmdb/server && gocmdb/serveradm init -l cngo mod init github.com/yunixiangfeng/gocmdb/servergo mod tidy

云主机管理平台

© All Rights Reserved. GoAdmin

    <div class="row" style="padding-top: 60px; clear: both;"><div class="col-md-12 text-center"><p><small>&copy; All Rights Reserved. GoAdmin</small></p></div></div>

滑动验证码_PC

<script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
<script>let captcha = new TencentCaptcha("2078016841", function (res) {console.log(res);if (res.ret === 0) {$.ajax({dataType: 'json',type: 'POST',url: '\/admin/signin',async: 'true',data: {'username': $("#username").val(),'password': $("#password").val(),'token': res.ticket},success: function (data) {location.href = data.data.url},error: function (data) {alert('登录失败');}});} else {alert("验证失败")}}, {});function submitData() {captcha.show()}
</script>

主机资源监控与实战

syncd 代码部署

a. 介绍&功能演示
    b. gin介绍
    c. 代码走读
        认证&授权 jwt
        编译过程
        部署过程

容器云k8s与二次开发

day20-0829

docker基本使用

k8s基本概念和环境搭建

k8s基本操作

k8s dashboard

k8s client创建资源

k8s client获取资源

k8s client修改资源和删除资源

k8s web管理

k8s ingress

1. k8s搭建

kubeadm

kubeadm init \
    --apiserver-advertise-address 10.0.0.10 \
    --image-repository registry.aliyuncs.com/google_containers \
    --kubernetes-version v1.18.6 \
    --service-cidr 10.1.0.0/16 \
    --pod-network-cidr 10.244.0.0/16

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

discovery-token-ca-cert-hash: https://kubernetes.io/zh/docs/reference/setup-tools/kubeadm/kubeadm-join/
    /api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login
2. k8s client介绍
3. k8s管理
    deployment
    service
    ingress
4. docker

API与SDK的介绍与使用

配置/证书下载

- https://pan.baidu.com/s/1vfcvOR8g4Y4Hf4v34M2OeA&shfl=sharepset

kubeadm部署

- 准备环境

安装docker

k8s

kubeadm安装

dashboard安装

二进制部署

kubectl命令

资源配置文件YAML

docker

Docker是一个用于开发,交付和运行应用程序的开放平台

安装

架构

技术

镜像

容器

容器时一个镜像的容器实例

挂载

网络

dockerfile

docker-compose

自建私有镜像仓库

使用Go语言对k8s资源对象操作与控制

k8s client获取资源

D:\codes\k8sclient\etc\kube.conf

apiVersion: v1
clusters:
- cluster:certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01EZ3lPVEEwTURBeE9Wb1hEVE13TURneU56QTBNREF4T1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUNQCjdQdmdidFp5b3FDRVh1dVMrZFZTNThaUlZXTVN4VkRmRkFXR1F2cEVLb2tuNGZRMWNUVmcvaGt1L0xwTE9NeEsKWll2TWRETFRnVWFjZGlJOTlFM216eWNKZ2pKbVN0VWJMZWlqbEFaNDZzYVEvZHJlaUxIYSt0R2Q3bVpCcmZXMgoxWG5jSUYyMm5Oa0pMaS9JNloyalZqYkMvaytwS2FoSDhPeU9HU2kwdWVUZ2lhWjRMeElwT1k5U2dONmQvREdrCkVWSmRXa1hkRTRZV1B4MHpQcXFIUW9XN1VVdFlzcnBTUXR0NTE2bkNEdTlacmwvc2tlb2dRbDdOTWVUSkR3RWEKZTI5NVFsY1g5RlVBTHh2eVBsbmpLWjRTK3lueVdXR1VmTTBzOGJ3bWIycWdTV2hqSHdRalY2ZVJiODdyYVdpVQpOZW91dG9najJzeEFxL3FUdEtrQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFEdHROK1hydUZwbkFNdmxZRStFb3BVL0J0SUMKM0VSMFZacG5idk9yNm16ZDM4UHNFS24zWGlQcGFKeUpMRENUeHcyT3c5RXRIRHV0QlFlQStHNkhqRlJBUEZSZApSZC9hdXkvUkwyK3YycU1XNEt0YUxvWDdXaXhiQ1JBNEs2dk83UVhGeCtab1RETDR0K3VjL2ZlanQ4dkdyc1V2CjYzaXB1MHp5U3NWZGIwbmJlRlpTa3VGOHM2eUZPVlIxdlhxa0FjVGQrek9lcUs0MkVzRlRBNVFaZ080Nm5UclAKcHdiK1lZVnlodTZsWUc4c2tDZ0NjSWdnMW41bC9FNXpiQUo1YzVwdjN3S200dGZEVVJON0xmbVVPa01iUlAwQQprakcwOHNUOGNmOEJEdTNiZWN1b3FQNW9ZVTRVcUFGZUFETEhlSFFNdklpRXVQdlBDbU5WNzZiVFRiMD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=server: https://192.168.204.130:6443name: kubernetes
contexts:
- context:cluster: kubernetesuser: kubernetes-adminname: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-adminuser:client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJWkRtRHo3ZVkzWHN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TURBNE1qa3dOREF3TVRsYUZ3MHlNVEE0TWprd05EQXdNakZhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXpwUmdKMnZUdnVIeFVNTDMKbENBSVMxSzB5eitaLzU3SW1uZnFkY3NtU2RPK2t0UjJRQlRrUzJSNTNlWGp1a1U1NTNhUDl5M3BWWHE4bk9oSwpOYU9yMnVLWCt6MXVraTRWaHdnRGdHT0ZnYjl5VVMzSnhmc3V4V2NtY2NSWGtHK3pCVmJHTjRSUTdiTk5LdE5iCjN2VSszNHV3UEozQWJ3Z3JZQ2poUDFWcDBkZWF2VFlBMThMUHJnYVFkUG1IclE0WEtwdFJWWld6cEdmK2RKR0IKZklBLzVSR2VBMmRBK1R0SkVDSUhXakhDdDBISkhxK3h3YzhRN09YUmduaFRUTXF4dHNCcC9hUjd6VnRzYUliTQpaR1RhbzlQYVk3UWp1WnRGU0lpZFhHaTZjeVdkSUxCVWpMemdTY2Z2RTJ0QnR3d1UvVTJYSk9wWmhldklWMVAyCng2V1U5UUlEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLVVA5RFZXZ2oxZ3hmSEV5S0ZoMlZkc1B1MjI3NlFoRW80RQpvTTBOSngzQnZ2d1N0M3pTMXhyVFJqOGsraG8zS1dJSEVXWTRjK1M1N25lNU1Gd3BaSTZQT2xqM2Rla0FkOXZhCnIwS1plSnZGRTN4WCt0YzhhR1ZEZ3NLbkozWDhBQW4wUXhubFpjUDhHUjZieC9uaGpaeDNMWUNZVjFGTytyOXMKTkp3UjR6MDhiUUpFQ0x3RSswT2R6eUdYb1c1SXRKalRSMGE2Zm5ReGVSeUJtMm11ZHdBeXdDVW1lYm1EcVNCQgpOejZ4M2FVcXJyRDZ3cGZVa25acGZkT04rRVR5WjVTOXhSQzl0RE4vOVhMQ2dHSFRaNkQyZkZBM3NoOCtRU0ZlCnZobkhaVFpZK2d2eGRLUXFBT2U5NlllajNkZTlJK0ZXbFpBTFNRVnJ4dGpUYklIeG12ND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBenBSZ0oydlR2dUh4VU1MM2xDQUlTMUsweXorWi81N0ltbmZxZGNzbVNkTytrdFIyClFCVGtTMlI1M2VYanVrVTU1M2FQOXkzcFZYcThuT2hLTmFPcjJ1S1grejF1a2k0Vmh3Z0RnR09GZ2I5eVVTM0oKeGZzdXhXY21jY1JYa0crekJWYkdONFJRN2JOTkt0TmIzdlUrMzR1d1BKM0Fid2dyWUNqaFAxVnAwZGVhdlRZQQoxOExQcmdhUWRQbUhyUTRYS3B0UlZaV3pwR2YrZEpHQmZJQS81UkdlQTJkQStUdEpFQ0lIV2pIQ3QwSEpIcSt4CndjOFE3T1hSZ25oVFRNcXh0c0JwL2FSN3pWdHNhSWJNWkdUYW85UGFZN1FqdVp0RlNJaWRYR2k2Y3lXZElMQlUKakx6Z1NjZnZFMnRCdHd3VS9VMlhKT3BaaGV2SVYxUDJ4NldVOVFJREFRQUJBb0lCQUV1Z0ZmTllqaFA3TXhTVgp5M3oyblJLMkhHbXJ4dnpGYkRyZ1czenorZmhkQkE5TXFGMmRTRll4V2t2WnRSeWo2eWJKU0xyOG04Y25QNVZSCmxKaythZE9mMEhPeGNhRWlMYzlaSjY4QXdBZFh4c25oTVZUQk44WWNsUDVoR28xTjF3UEZXSnRLWFRZbnhjQS8KMEFvM1RlVVlobFFxakNBWnBZZDJiNzkzelYxOEgzdGpyUkZpRFlJUGx0eGp0UXhDaDJhM1BVOEJFbWxQU2RFKwpPdHJvOEhuMlFWdG9VaFJ1c0lIQTVNMldPK3oyWnJ5SlNYNjZWRmx2OE40OHRNMG1DNmZCN1dYc2dHTDYvSms4CkxqbXd5VUJNenlJTXVXdHNpMlc3cUdSTURIblVJRnZVdGRiSEVkL3FFRXNnU2M1WTZaRlFveExFdnBuMlZlYWEKai9PdUV3RUNnWUVBL2cvNXJLNHBic0tsd0pwMmw0Q3VBMGEzbXJJNDhiQVVwc1JzOXRaVmtXQTFuVm41SGtoagpLaElhMDB2N1cyRm9JUjV1SHFLMjNzZ3pjdHVEd2dkMWRBVnZKRUxFRk50ak9BYXBFOXJyblc2MWk0bHFXZ1NVCnp2aEtmYXdXQXA4Z2YzbGFjYmpJeTQ0QWgrVGlXbjkweVVPSkxTRXRDazZwTW9aZklBNTlBTmtDZ1lFQTBDZXkKTldEV2JKSHpNcE1mSW9tQ2NINlVYcEJDTWV4MFFrR3ZJQnI4ak5zQXdoTHJZMWdsSm1uKzhFdGlieWVQTG1vYQp3WTdncllsSzR2dDlTTjAvSDZzeWZMUmVhZ1lVTlREY01LbnI3eGNCb2V3QW1JbURNRFVGdURzUzhjKysxdmw4CkhEMDh6V0hEL0lKd2IrVmJ6Szc4OWxBajVsSHMrQVBUdTlwaW8zMENnWUIyQXlHc3JuR2NlMW5XNzJqcTB1RUIKc0pXVWkvaWJlM2o4UmYxL1l1djRUVUphUnZMS3VFRW54NlVpUlFjSzJXSXZFQjJDcVg1Y1dZNWNhYzc0RDlMbApBNmt1cEx6RUcyd3BHQjd0bENFaHpjMFNkZEFxNURuak1iNFlSaGtyT3BNejQwQzUxbVdlOStVVE9xUlIrU1pjClhyeVhjL09oK0F2cjVqTEZoelZWY1FLQmdIcW5PV29ja3B6TTcybllxUnIzdmdXOWdIMnNNV1VyZUdIbVJHUDkKb3R6NDJ4eUFlM1ZCWmpxWmNLQjFPeDVXU0JkSWJGV3JkQmF0ZEpRRkxwQzExZEU2Vm5pRzY2ODd2OEtMOU9Nego3Uk1vRWswd1BEV2xxY2pKSllLbVJJWjZMSENOOTZUSUxNQzBvQUIxZC8xblA4MS9PdzJFc1hLd3lacG0zdWV0ClNqd2RBb0dCQUlBdmtKSFMwbElNdlExNWJzQWhWbkpLc295RTZ2Vm8yVmtMNS9VdUlrYzdtMGVDKzZ1WWNEMzEKWnl6ZUZzcEZFbG1MbERiZXFueTdHMEY0d09PTEhoZFk2SUxmdkRpakhIU1VxbFZiWGp2cExieWRoVzFSNlhXRApHQStZU3c0akEwY1lEZzFsaU1zSzlqd2plVmJVNWxTRzZzMHN0V2ZkK2xVVXVSOThrNFJVCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

D:\codes\k8sclient\list.go

package mainimport ("context""fmt""log"metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd"
)func list() {configPath := "etc/kube.conf"config, err := clientcmd.BuildConfigFromFlags("", configPath)if err != nil {log.Fatal(err)}clientset, err := kubernetes.NewForConfig(config)if err != nil {log.Fatal(err)}nodeList, _ := clientset.CoreV1().Nodes().List(context.TODO(), metaV1.ListOptions{})fmt.Println("node:")for _, node := range nodeList.Items {fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n%s\n",node.Name,node.Status.Phase,node.Status.Addresses,node.Status.NodeInfo.OSImage,node.Status.NodeInfo.KubeletVersion,node.Status.NodeInfo.OperatingSystem,node.Status.NodeInfo.Architecture,node.CreationTimestamp,)}namespaceList, _ := clientset.CoreV1().Namespaces().List(context.TODO(), metaV1.ListOptions{})fmt.Println("namespace:")for _, namespace := range namespaceList.Items {fmt.Println(namespace.Name, namespace.CreationTimestamp, namespace.Status.Phase)}servicesList, _ := clientset.CoreV1().Services("").List(context.TODO(), metaV1.ListOptions{})fmt.Println("service:")for _, service := range servicesList.Items {fmt.Println(service.Namespace, service.Name, service.Spec.Type, service.CreationTimestamp, service.Spec.ClusterIP, service.Spec.Ports)}deploymentList, _ := clientset.AppsV1().Deployments("").List(context.TODO(), metaV1.ListOptions{})fmt.Println("deployment:")for _, deployment := range deploymentList.Items {fmt.Println(deployment.Namespace, deployment.Name, deployment.Labels, deployment.CreationTimestamp, deployment.Spec.Selector.MatchLabels, deployment.Status.Replicas, deployment.Status.AvailableReplicas)}
}

k8s client创建资源

D:\codes\k8sclient\createDeploy.go

package mainimport ("context""fmt"appsV1 "k8s.io/api/apps/v1"coreV1 "k8s.io/api/core/v1"metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd"
)func createDeploy() {configPath := "etc/kube.conf"config, _ := clientcmd.BuildConfigFromFlags("", configPath)clientset, _ := kubernetes.NewForConfig(config)namespace := "default"var replicas int32 = 3deployment := &appsV1.Deployment{ObjectMeta: metaV1.ObjectMeta{Name: "nginx",Labels: map[string]string{"env": "dev",},},Spec: appsV1.DeploymentSpec{Replicas: &replicas,Selector: &metaV1.LabelSelector{MatchLabels: map[string]string{"app": "nginx","env": "dev",},},Template: coreV1.PodTemplateSpec{ObjectMeta: metaV1.ObjectMeta{Name: "nginx",Labels: map[string]string{"app": "nginx","env": "dev",},},Spec: coreV1.PodSpec{Containers: []coreV1.Container{{Name:  "nginx",Image: "nginx:1.16.1",Ports: []coreV1.ContainerPort{{Name:          "http",Protocol:      coreV1.ProtocolTCP,ContainerPort: 80,},},},},},},},}deployment, err := clientset.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metaV1.CreateOptions{})fmt.Println(err, deployment)
}

D:\codes\k8sclient\createService.go

package mainimport ("context""fmt"coreV1 "k8s.io/api/core/v1"metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd"
)func createService() {configPath := "etc/kube.conf"config, _ := clientcmd.BuildConfigFromFlags("", configPath)clientset, _ := kubernetes.NewForConfig(config)namespace := "default"service := &coreV1.Service{ObjectMeta: metaV1.ObjectMeta{Name: "nginx-service",Labels: map[string]string{"env": "dev",},},Spec: coreV1.ServiceSpec{Type: coreV1.ServiceTypeNodePort,Selector: map[string]string{"env": "dev","app": "nginx",},Ports: []coreV1.ServicePort{{Name:     "http",Port:     80,Protocol: coreV1.ProtocolTCP,},},},}service, err := clientset.CoreV1().Services(namespace).Create(context.TODO(), service, metaV1.CreateOptions{})fmt.Println(err, service)
}

D:\codes\k8sclient\deleteResource.go

package mainimport ("context"metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd"
)func main() {configPath := "etc/kube.conf"config, _ := clientcmd.BuildConfigFromFlags("", configPath)clientset, _ := kubernetes.NewForConfig(config)namespace := "default"name, serviceName := "nginx", "nginx-service"clientset.AppsV1().Deployments(namespace).Delete(context.TODO(), name, metaV1.DeleteOptions{})clientset.CoreV1().Services(namespace).Delete(context.TODO(), serviceName, metaV1.DeleteOptions{})// clientset.ExtensionsV1beta1().Ingresses()
}

D:\codes\k8sclient\editDeploy.go

package mainimport ("context""fmt"metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd"
)func editDeploy() {configPath := "etc/kube.conf"config, _ := clientcmd.BuildConfigFromFlags("", configPath)clientset, _ := kubernetes.NewForConfig(config)namespace := "default"var replicas int32 = 1name := "nginx"deployment, err := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), name, metaV1.GetOptions{})deployment.Spec.Replicas = &replicasdeployment.Spec.Template.Spec.Containers[0].Image = "nginx:1.14"deployment, err = clientset.AppsV1().Deployments(namespace).Update(context.TODO(), deployment, metaV1.UpdateOptions{})fmt.Println(err, deployment)
}

D:\codes\k8sclient\editReplicas.go

package mainimport ("context""fmt"metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd"
)func editReplicas() {configPath := "etc/kube.conf"config, _ := clientcmd.BuildConfigFromFlags("", configPath)clientset, _ := kubernetes.NewForConfig(config)namespace := "default"name := "nginx"scale, err := clientset.AppsV1().Deployments(namespace).GetScale(context.TODO(), name, metaV1.GetOptions{})scale.Spec.Replicas = 2scale, err = clientset.AppsV1().Deployments(namespace).UpdateScale(context.TODO(), name, scale, metaV1.UpdateOptions{})fmt.Println(err, scale)
}

https://github.com/yunixiangfeng/go-demo/tree/main/k8sclient

go-demo/cmdb at main · yunixiangfeng/go-demo · GitHub

ELK日志系统

搜索引擎基础

Elasticsearch基础

Logstash基础

kibana基础

Go语言并发编程

go并发模型

并发合并文件

channel

并发安全性

多路复用

HTTP标准库解读

实现自己的router

http协议

go http编程

router

验证器和中间件

GIN

Socket编程

网络通信原理

聊天室的实现

tcp编程

tcp面向字节流和udp理论

TLS和websocket理论

websocket编程方式

Go语言操作数据库

mysql基础理论

mysql最佳实践

go语言操作mysql

第三方sql-builder

自己实现sql

gorm

go语言操作mongo

文件中转站

工具开发

以系统方式开发

项目工程

Demo后端开发

DemoIoc后端开发

RPC

RPC入门

Protobuf编解码

proto3语法

GRPC

grpc入门

CMDB API

框架支持GRPC

CMDB

云资源Provider

云凭证管理

云资源同步API

用户中心

用户中心

审计中心

消息队列技术与Kafka

审计中心

Web 入门

JavaScript基础

Web入门基础-HTML

Web入门基础-CSS

Web入门基础-浏览器

一、 API Server开发

API Server Pipeline开发

二、 Pipeline调度器开发

Informer和服务注册

三、订阅SCM事件

调度器Controller开发

Step调度器开发与Node开发

API Server 订阅SCM事件

Go高并发微服务分布式实战相关推荐

  1. 微服务_SpringCloud微服务架构实战:高并发微服务架构设计

    高并发微服务架构设计 作为一个 IT 从业人员,我们经常会碰到类似于下面的一些问题: 单个项目巨大而沉重,难以维护. 系统稳定性得不到更有效的保证. 怎样才能持续地提升系统的性能. 怎样才能快速地响应 ...

  2. 如何使用Spring Cloud实现高并发微服务设计

    来自:DBAplus社群 本文根据dbaplus社群第161期线上分享整理而成 讲师介绍 陈韶健 加推科技 技术中心首席架构师 博文视点作者,著有<Spring Cloud与Docker高并发微 ...

  3. 高级架构师,精通JAVA/高并发/微服务/分布式/中间件

    JAVA高级架构师专栏 大纲介绍 Java基础复习 单点系统 Springboot框架应用 页面开发模版引擎 Java核心基础 JVM调优 高并发优化 Spring.Mybatis.Springboo ...

  4. 基于Springcloud搭建电商平台实现高性能高并发微服务

    微服务是什么 对于微服务的定义,可能不同的团队有不同的答案.正所谓一千个读者就有一千个哈姆雷特,但其实大部分人都认为微服务是一种小型的应用程序,并且使用轻量级的设计方法和轻量级的HTTP通信. 它的主 ...

  5. golang高并发微服务项目——大转盘抽奖系统

    源码地址 添加链接描述 项目截图 后台页面: 前台页面: 项目基本架构:

  6. 微服务 前台调用后台的慢的原因_20年IT农民工分享SpringCloud微服务架构实战文档...

    前言 越来越多的企业使用 SpringCloud 实现微服务架构设计.我们可以看到这样一种现象:不管是全新开发,还是系统重构,大家似乎都在争先恐后地使用微服务.对于一个Java开发人员来说,学习微服务 ...

  7. 微服务Springboot实战大揭秘/高并发/高可用/高负载/互联网技术-任亮-专题视频课程...

    微服务Springboot实战大揭秘/高并发/高可用/高负载/互联网技术-320人已学习 课程介绍         Java架构师系列课程是针对有志向架构师发展的广大学员而设置,不管你是工作一到三年, ...

  8. 阿里云开发者学院电子书《Dubbo分布式服务治理实战》重磅来袭!

    简介:Dubbo 是阿里巴巴开源的高性能分布式 RPC 服务治理框架,已经捐献给 Apache 开源组织,最新的版本是 3.0.在阿里巴巴.工行.电信.银联.中国人寿.网易.滴滴.当当等互联网公司中大 ...

  9. 阿里架构师推荐,微服务分布式构架开发实战PDF,快快收藏吧

    什么是微服务架构 微服务是一种软件架构风格,目标是将一个复杂的应用拆分成多个服务模块,每个模块专注单一业务功能对外提供服务,并可以独立编译及部署,同时各模块间互相通信彼此协作,组合为整体对外提供完整服 ...

最新文章

  1. 贝叶斯定理:AI 不只是个理科生 | 赠书
  2. StyleCop(C#代码规范分析工具)---2.常用规则介绍(一)
  3. Error: pgraster_wkb_reader: grayscale band type 10 unsupported
  4. 12.委托是什么?委托的property声明用什么属性?为什么?
  5. C++五子棋(三)——判断鼠标有效点击
  6. java 从未导入_Java 8的10个您从未听说过的功能
  7. php模拟表单提交登录,PHP模拟表单的post请求实现登录
  8. 自动化代码部署、代码回滚、命令执行软件之capistrano
  9. 【云计算】阿里云云计算架构师ACE成长路线v2
  10. Android Studio报错: Alternatively, to transfer the license agreements from one workstation to another
  11. echart 全国地图 下钻省、市、区
  12. ORA-00932: 数据类型不一致: 应为 -, 但却获得 BLOB
  13. wed基础和http
  14. Socks代理是什么意思?有什么用?
  15. 重庆市对口高职计算机类专业vfp,重庆市2015年普通高校对口招收中职毕业生专业技能计算机类技能考试大纲...
  16. 转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧
  17. 局部边缘保持滤波(LEP)高动态范围图像HDR压缩 matlab程序(一)
  18. iOS 图片涂鸦功能
  19. 【20230103】FROM_UNIXTIME和UNIX_TIMESTAMP函数
  20. 李阳英语228句口语要素 +校园英语迷你惯用语 +1000句最常用英语口语

热门文章

  1. IMU-CAM联合标定测试(只依靠数据集)
  2. 解读元宇宙定义,探寻元宇宙入口,了解元宇宙产业布局
  3. 中邮速递IPO初审 引爆电商物流大战
  4. <慢慢变富> 松浦弥太郎,读后感
  5. 前端-js基础-循环
  6. 广州美食--特色小吃桂林米粉
  7. 关于sql数据库附加不上的问题
  8. 白皮书下载|新一代金融全栈信创云解决方案及成功案例
  9. windows驱动开发7:应用程序和驱动程序的通信
  10. 阿里巴巴矢量图标的使用