Go高并发微服务分布式实战
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
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" />
我叫 <img src="xxxx" />
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
< <
> >
" "
' '
& &
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/> <
菜单(显示):
用户管理
任务管理
管理员
用户管理
任务管理
普通用户
任务管理
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>© 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高并发微服务分布式实战相关推荐
- 微服务_SpringCloud微服务架构实战:高并发微服务架构设计
高并发微服务架构设计 作为一个 IT 从业人员,我们经常会碰到类似于下面的一些问题: 单个项目巨大而沉重,难以维护. 系统稳定性得不到更有效的保证. 怎样才能持续地提升系统的性能. 怎样才能快速地响应 ...
- 如何使用Spring Cloud实现高并发微服务设计
来自:DBAplus社群 本文根据dbaplus社群第161期线上分享整理而成 讲师介绍 陈韶健 加推科技 技术中心首席架构师 博文视点作者,著有<Spring Cloud与Docker高并发微 ...
- 高级架构师,精通JAVA/高并发/微服务/分布式/中间件
JAVA高级架构师专栏 大纲介绍 Java基础复习 单点系统 Springboot框架应用 页面开发模版引擎 Java核心基础 JVM调优 高并发优化 Spring.Mybatis.Springboo ...
- 基于Springcloud搭建电商平台实现高性能高并发微服务
微服务是什么 对于微服务的定义,可能不同的团队有不同的答案.正所谓一千个读者就有一千个哈姆雷特,但其实大部分人都认为微服务是一种小型的应用程序,并且使用轻量级的设计方法和轻量级的HTTP通信. 它的主 ...
- golang高并发微服务项目——大转盘抽奖系统
源码地址 添加链接描述 项目截图 后台页面: 前台页面: 项目基本架构:
- 微服务 前台调用后台的慢的原因_20年IT农民工分享SpringCloud微服务架构实战文档...
前言 越来越多的企业使用 SpringCloud 实现微服务架构设计.我们可以看到这样一种现象:不管是全新开发,还是系统重构,大家似乎都在争先恐后地使用微服务.对于一个Java开发人员来说,学习微服务 ...
- 微服务Springboot实战大揭秘/高并发/高可用/高负载/互联网技术-任亮-专题视频课程...
微服务Springboot实战大揭秘/高并发/高可用/高负载/互联网技术-320人已学习 课程介绍 Java架构师系列课程是针对有志向架构师发展的广大学员而设置,不管你是工作一到三年, ...
- 阿里云开发者学院电子书《Dubbo分布式服务治理实战》重磅来袭!
简介:Dubbo 是阿里巴巴开源的高性能分布式 RPC 服务治理框架,已经捐献给 Apache 开源组织,最新的版本是 3.0.在阿里巴巴.工行.电信.银联.中国人寿.网易.滴滴.当当等互联网公司中大 ...
- 阿里架构师推荐,微服务分布式构架开发实战PDF,快快收藏吧
什么是微服务架构 微服务是一种软件架构风格,目标是将一个复杂的应用拆分成多个服务模块,每个模块专注单一业务功能对外提供服务,并可以独立编译及部署,同时各模块间互相通信彼此协作,组合为整体对外提供完整服 ...
最新文章
- 贝叶斯定理:AI 不只是个理科生 | 赠书
- StyleCop(C#代码规范分析工具)---2.常用规则介绍(一)
- Error: pgraster_wkb_reader: grayscale band type 10 unsupported
- 12.委托是什么?委托的property声明用什么属性?为什么?
- C++五子棋(三)——判断鼠标有效点击
- java 从未导入_Java 8的10个您从未听说过的功能
- php模拟表单提交登录,PHP模拟表单的post请求实现登录
- 自动化代码部署、代码回滚、命令执行软件之capistrano
- 【云计算】阿里云云计算架构师ACE成长路线v2
- Android Studio报错: Alternatively, to transfer the license agreements from one workstation to another
- echart 全国地图 下钻省、市、区
- ORA-00932: 数据类型不一致: 应为 -, 但却获得 BLOB
- wed基础和http
- Socks代理是什么意思?有什么用?
- 重庆市对口高职计算机类专业vfp,重庆市2015年普通高校对口招收中职毕业生专业技能计算机类技能考试大纲...
- 转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧
- 局部边缘保持滤波(LEP)高动态范围图像HDR压缩 matlab程序(一)
- iOS 图片涂鸦功能
- 【20230103】FROM_UNIXTIME和UNIX_TIMESTAMP函数
- 李阳英语228句口语要素 +校园英语迷你惯用语 +1000句最常用英语口语