Go高并发微服务分布式实战
/ users[id] => 在 不在(你输入的用户ID不正确)// users[id] => 在 不在(你输入的用户ID不正确)在程序中定义PASSWORD = "!值类型使用string。=> 用户 ID name age tel addr。// 打印用户信息,提示用户是否确认修改(Y/N)// 打印用户信息,提示用户是否确认删除(Y/N)提示输入密码,如果密码输出3次都失败,提示并退出。//
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 main
import (
"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 string
age string
tel string
addr 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 string
fmt.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 := 0
fmt.Println("欢迎使用个人用户管理系统")
for {
var op string
fmt.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 main
import (
"fmt"
"os"
"strconv"
"strings"
)
const (
maxAUTH = 3
password = "******"
)
func inputString(prompt string) string {
var input string
fmt.Print(prompt)
fmt.Scan(&input)
return strings.TrimSpace(input)
}
// 从命令行输入密码,并进行验证
// 通过返回值告知验证成功还是失败
func auth() bool {
var input string
for 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, desc
if 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 int
for 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] = user
fmt.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] = user
fmt.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 int
fmt.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 main
import (
"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 main
import (
"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 main
import (
"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 main
import (
"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 main
import (
"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, <=m
var 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}.edu
fmt.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 main
import (
"fmt"
"regexp"
_ "github.com/go-sql-driver/mysql"
)
func main() {
reg, err := regexp.Compile("^132\\d{8}$")
fmt.Println(err, reg)
// 匹配 Match
fmt.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????????"))
// 查找 Find
fmt.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 main
import (
"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 main
import (
"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选择器
// #id
fmt.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 main
import (
"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: 数值类型
int
bigint
float
double
decimal(m, n)
字符串类型
varchar(n)
char(n)
时间类型
data
datetime
time
文本类型
text 64K
mediumtext 16M
longtext 4G
二进制类型
blob
longblob
json/array
...
修饰:
主键: primary key
唯一: unique
自动增长: auto_increment
默认值: default 0, default ''
是否允许为null, 不允许为NULL, NOT NULL
注释: COMMENT ''
索引:
index
create 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 = v2
colname between v1 and v2 => colname >= v1 and colname <= v2
逻辑关系
与
and
或
or
colname = v1 or colname = v2
非
not
c1 = v1 and (c2 = v2 or c3 = v3)
删
delete from table;
delete from table where 条件;
truncate table name;
改
update table
set colname=v1, col2=v2, col3=v3;
update table
set colname=v1, col2=v2, col3=v3
where 条件;
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_code
ip出现的次数
status_code出现的次数
url, status_code出现的次数
ip url status_code出现的次数
group by
select [] 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 accesslog
group 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, status
ip 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
平均值 avg
select name, sum(score),max(score),min(score), avg(score) from score group by name;
联查
多张表进行查询数据
join
left join on
inner join on
right join on
create 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 => 表继承
task
date
task_2020_05
task_2020_06
insert int task
select * from task
过程
13.
用户,权限,运维 操作: 执行
库,表 操作: 执行, 读
表数据 操作: 增,删,改 => 执行
查 => 读
执行
读
编辑
1. 点击编辑按钮 id => 内容显示
2. 内容修改 id => 提交按钮 => 数据验证 => 更新数据
input type="hidden" value="{{ task.ID }}"
Go语言的常用包和使用方式
testdb\main.go
package main
import (
"database/sql"
"fmt"
)
// 导入beego包
func main() {
// user:password@tcp(host:port)/database?charset=utf8mb4&loc=PRC&parseTime=true
dsn := "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 - 10
result, 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 int
name 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 main
import (
"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 float64
err := 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=true
dsn := "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 + money
var 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 main
import (
"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 float64
err := 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=true
dsn := "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 main
import (
"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 int
Name string
Status int
StartTime *time.Time
CompleteTime *time.Time
DeadlineTime *time.Time
User *string
Content string
}
func (task *Task) StatusText() string {
return statusMap[task.Status]
}
type TaskForm struct {
ID int
Name string
Status int
DeadlineTime string
Content string
User 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 Task
err := 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
//switch
return 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 TaskForm
errors = 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 TaskForm
Errors 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@latest
mkdir gocmdb/server && gocmdb/server
adm init -l cn
go mod init github.com/yunixiangfeng/gocmdb/server
go 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>
<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:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJWkRtRHo3ZVkzWHN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TURBNE1qa3dOREF3TVRsYUZ3MHlNVEE0TWprd05EQXdNakZhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXpwUmdKMnZUdnVIeFVNTDMKbENBSVMxSzB5eitaLzU3SW1uZnFkY3NtU2RPK2t0UjJRQlRrUzJSNTNlWGp1a1U1NTNhUDl5M3BWWHE4bk9oSwpOYU9yMnVLWCt6MXVraTRWaHdnRGdHT0ZnYjl5VVMzSnhmc3V4V2NtY2NSWGtHK3pCVmJHTjRSUTdiTk5LdE5iCjN2VSszNHV3UEozQWJ3Z3JZQ2poUDFWcDBkZWF2VFlBMThMUHJnYVFkUG1IclE0WEtwdFJWWld6cEdmK2RKR0IKZklBLzVSR2VBMmRBK1R0SkVDSUhXakhDdDBISkhxK3h3YzhRN09YUmduaFRUTXF4dHNCcC9hUjd6VnRzYUliTQpaR1RhbzlQYVk3UWp1WnRGU0lpZFhHaTZjeVdkSUxCVWpMemdTY2Z2RTJ0QnR3d1UvVTJYSk9wWmhldklWMVAyCng2V1U5UUlEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLVVA5RFZXZ2oxZ3hmSEV5S0ZoMlZkc1B1MjI3NlFoRW80RQpvTTBOSngzQnZ2d1N0M3pTMXhyVFJqOGsraG8zS1dJSEVXWTRjK1M1N25lNU1Gd3BaSTZQT2xqM2Rla0FkOXZhCnIwS1plSnZGRTN4WCt0YzhhR1ZEZ3NLbkozWDhBQW4wUXhubFpjUDhHUjZieC9uaGpaeDNMWUNZVjFGTytyOXMKTkp3UjR6MDhiUUpFQ0x3RSswT2R6eUdYb1c1SXRKalRSMGE2Zm5ReGVSeUJtMm11ZHdBeXdDVW1lYm1EcVNCQgpOejZ4M2FVcXJyRDZ3cGZVa25acGZkT04rRVR5WjVTOXhSQzl0RE4vOVhMQ2dHSFRaNkQyZkZBM3NoOCtRU0ZlCnZobkhaVFpZK2d2eGRLUXFBT2U5NlllajNkZTlJK0ZXbFpBTFNRVnJ4dGpUYklIeG12ND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBenBSZ0oydlR2dUh4VU1MM2xDQUlTMUsweXorWi81N0ltbmZxZGNzbVNkTytrdFIyClFCVGtTMlI1M2VYanVrVTU1M2FQOXkzcFZYcThuT2hLTmFPcjJ1S1grejF1a2k0Vmh3Z0RnR09GZ2I5eVVTM0oKeGZzdXhXY21jY1JYa0crekJWYkdONFJRN2JOTkt0TmIzdlUrMzR1d1BKM0Fid2dyWUNqaFAxVnAwZGVhdlRZQQoxOExQcmdhUWRQbUhyUTRYS3B0UlZaV3pwR2YrZEpHQmZJQS81UkdlQTJkQStUdEpFQ0lIV2pIQ3QwSEpIcSt4CndjOFE3T1hSZ25oVFRNcXh0c0JwL2FSN3pWdHNhSWJNWkdUYW85UGFZN1FqdVp0RlNJaWRYR2k2Y3lXZElMQlUKakx6Z1NjZnZFMnRCdHd3VS9VMlhKT3BaaGV2SVYxUDJ4NldVOVFJREFRQUJBb0lCQUV1Z0ZmTllqaFA3TXhTVgp5M3oyblJLMkhHbXJ4dnpGYkRyZ1czenorZmhkQkE5TXFGMmRTRll4V2t2WnRSeWo2eWJKU0xyOG04Y25QNVZSCmxKaythZE9mMEhPeGNhRWlMYzlaSjY4QXdBZFh4c25oTVZUQk44WWNsUDVoR28xTjF3UEZXSnRLWFRZbnhjQS8KMEFvM1RlVVlobFFxakNBWnBZZDJiNzkzelYxOEgzdGpyUkZpRFlJUGx0eGp0UXhDaDJhM1BVOEJFbWxQU2RFKwpPdHJvOEhuMlFWdG9VaFJ1c0lIQTVNMldPK3oyWnJ5SlNYNjZWRmx2OE40OHRNMG1DNmZCN1dYc2dHTDYvSms4CkxqbXd5VUJNenlJTXVXdHNpMlc3cUdSTURIblVJRnZVdGRiSEVkL3FFRXNnU2M1WTZaRlFveExFdnBuMlZlYWEKai9PdUV3RUNnWUVBL2cvNXJLNHBic0tsd0pwMmw0Q3VBMGEzbXJJNDhiQVVwc1JzOXRaVmtXQTFuVm41SGtoagpLaElhMDB2N1cyRm9JUjV1SHFLMjNzZ3pjdHVEd2dkMWRBVnZKRUxFRk50ak9BYXBFOXJyblc2MWk0bHFXZ1NVCnp2aEtmYXdXQXA4Z2YzbGFjYmpJeTQ0QWgrVGlXbjkweVVPSkxTRXRDazZwTW9aZklBNTlBTmtDZ1lFQTBDZXkKTldEV2JKSHpNcE1mSW9tQ2NINlVYcEJDTWV4MFFrR3ZJQnI4ak5zQXdoTHJZMWdsSm1uKzhFdGlieWVQTG1vYQp3WTdncllsSzR2dDlTTjAvSDZzeWZMUmVhZ1lVTlREY01LbnI3eGNCb2V3QW1JbURNRFVGdURzUzhjKysxdmw4CkhEMDh6V0hEL0lKd2IrVmJ6Szc4OWxBajVsSHMrQVBUdTlwaW8zMENnWUIyQXlHc3JuR2NlMW5XNzJqcTB1RUIKc0pXVWkvaWJlM2o4UmYxL1l1djRUVUphUnZMS3VFRW54NlVpUlFjSzJXSXZFQjJDcVg1Y1dZNWNhYzc0RDlMbApBNmt1cEx6RUcyd3BHQjd0bENFaHpjMFNkZEFxNURuak1iNFlSaGtyT3BNejQwQzUxbVdlOStVVE9xUlIrU1pjClhyeVhjL09oK0F2cjVqTEZoelZWY1FLQmdIcW5PV29ja3B6TTcybllxUnIzdmdXOWdIMnNNV1VyZUdIbVJHUDkKb3R6NDJ4eUFlM1ZCWmpxWmNLQjFPeDVXU0JkSWJGV3JkQmF0ZEpRRkxwQzExZEU2Vm5pRzY2ODd2OEtMOU9Nego3Uk1vRWswd1BEV2xxY2pKSllLbVJJWjZMSENOOTZUSUxNQzBvQUIxZC8xblA4MS9PdzJFc1hLd3lacG0zdWV0ClNqd2RBb0dCQUlBdmtKSFMwbElNdlExNWJzQWhWbkpLc295RTZ2Vm8yVmtMNS9VdUlrYzdtMGVDKzZ1WWNEMzEKWnl6ZUZzcEZFbG1MbERiZXFueTdHMEY0d09PTEhoZFk2SUxmdkRpakhIU1VxbFZiWGp2cExieWRoVzFSNlhXRApHQStZU3c0akEwY1lEZzFsaU1zSzlqd2plVmJVNWxTRzZzMHN0V2ZkK2xVVXVSOThrNFJVCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
D:\codes\k8sclient\list.go
package main
import (
"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 main
import (
"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 = 3
deployment := &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 main
import (
"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 main
import (
"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 main
import (
"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 = 1
name := "nginx"
deployment, err := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), name, metaV1.GetOptions{})
deployment.Spec.Replicas = &replicas
deployment.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 main
import (
"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 = 2
scale, 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.对称加密
DES数组分级
DES子密钥生成
DES加密过程
S盒替换
分组模式
AES
2.非对称加密
RSA
对称加密和非对称加密结合使用
RSA加密过程
椭圆曲线加密
3.哈希算法
哈希函数的基本特征
SHA1
MD5
哈希函数的应用
数字签名
密码学api
数据结构与算法
1.链表
单向链表
package main
import "fmt"
// 链表中的一个元素
type Node struct {
Info int
Next *Node
}
// 链表
type List struct {
Head *Node
Len int
}
func (list *List) Add(ele int) {
node := &Node{Info: ele, Next: nil}
if list.Len == 0 {
list.Head = node
} else {
head := list.Head
for i := 1; i < list.Len; i++ {
head = head.Next
}
head.Next = node
}
list.Len += 1
}
func (list *List) Travers() {
if list.Len == 0 {
return
}
head := list.Head
fmt.Println(head.Info)
for i := 1; i < list.Len; i++ {
head = head.Next
fmt.Println(head.Info)
}
}
func main() {
list := List{}
list.Add(1)
list.Add(8)
list.Add(4)
list.Add(3)
list.Add(5)
list.Travers()
}
func (list *List) Add(ele int) {
node := &Node{Info: ele, Next: nil}
if list.Len == 0 {
list.Head = node
list.Tail = node
} else {
list.Tail.Next = node
list.Tail = node
}
list.Len += 1
}
链表的应用:LRU缓存淘汰
·LRU(Least Recently Used)最近最少使用思路:缓存的kev放到链表中,头部的元素表示最近刚使用如果命中缓存,从链表中找到对应的key,移到链表头部
如果没命中缓存:
如果缓存容量没超,放入缓存,并把key放到链表头部.
如果超出缓存容量,删除链表尾部元素,再把key放到链表头部
LRU和超时缓存
循环链表
环
RING添加元素
RING的应用:基于滑动窗口的统计
最近100次接口调用的平均耗时、最近10笔订单的平均值最近30个交易日股票的最高点
ring的容量即为滑动窗口的大小,把待观察变量按时间顺序不停地写入ring即可
2.栈
先进后出
3.堆
4.Trie树
根节点是总入口,不存储字符
对于英文,第个节点有26个子节点,子节点可以存到数组里;中文由于汉字很多,用数组存子节点太浪费内存,可以用map存子节点从根节点到叶节点的完整路径是一个term从根节点到某个中间节点也可能是一个term,即一个term可能是另一个term的前缀
斐波那契数列
递归造成计算浪费
用栈解除递归
斐波那契数列非递归实现
堆的应用
堆的底层表示
堆是一棵二叉树。大根堆:任意节点的值都大于等于其子节点。反之为小根堆。用数组来表示堆,下标为i的结点的父结点下标为(i-1)/2,其左右子结点分别为(2i+ 1)、(2i+ 2)。
构建堆
每当有元素调整下来时,要对以它为父节点的三角形区域进行调整
删除堆顶
向下调整
用堆实现超时缓存
把超时的元素从缓存中删除
1.按key的到期时间把key插入小根堆中
2周期扫描堆顶元素,如果它的到期时间早于当前时刻,则从堆和缓存中删除,然后向下调整堆
求集合中最大的K个元素
1.用集合的前K个元素构建小根堆
2.逐一遍历集合的其他元素,如果比堆顶小直接丢弃;否则替换掉堆顶,然后向下调整堆
把超时的元素从缓存中删除
1.按key的到期时间把key插入小根堆中
2.周期扫描堆顶元素,如果它的到期时间早于当前时刻,则从堆和缓存中删除,然后向下调整堆
堆的实现
Trie树
根节点是总入口,不存储字符
对于英文,第个节点有26个子节点,子节点可以存到数组里;中文由于汉字很多,用数组存子节点太浪费内存,可以用map存子节点从根节点到叶节点的完整路径是一个term从根节点到某个中间节点也可能是一个term,即一个term可能是另一个term的前缀
1.实现数字签名
2.用map和链表实现LRU缓存
3.用map和堆表实现超时缓存
并发编程
go并发模型
并发合并文件
channel
并发安全性
多路复用
web开发
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简化版
demo后端开发
demo IOC版
demo后端开发
RPC入门
rpc入门
protobuf编解码
proto3语法入门
grpc入门
框架支持grpc
cmdb api
云资源provider
云凭证管理
云资源同步api
用户中心
用户中心
登录认证
权限判定
审计中心
消息队列kafka
审计中心
web入门
javascript基础
web基础html
web基础css
web基础浏览器
vue入门
vue基础
vue路由与状态管理
项目前端框架
登陆页面
项目404页面
项目导航页面
项目前端
主机列表页面
cmdb主机页面
cmdb搜索页面
cmdb同步页面
总线和缓存
web和prometheus二次开发
prometheus概念
exporter开发
kubernetes二次开发
Kubernetes 简介与client-go使用
基于client-go 的多集群管理平台
Kubernetes Operator 开发
kubernetes二次开发
API Server开发
API Server pipeline开发
pipeline调度器开发
informer和服务注册
订阅SCM事件
调度器controller开发
step调度器开发和node开发
api server订阅scm事件
更多推荐
所有评论(0)