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}$
// 邮箱格式
// [email protected]
// 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, "[email protected]")) // 可以匹配
fmt.Println(regexp.MatchString(pattern, "[email protected]")) // 不匹配
fmt.Println(regexp.MatchString(pattern, "[email protected]")) // 不匹配
fmt.Println(regexp.MatchString(pattern, "a@bxedu")) // 不匹配
fmt.Println(regexp.MatchString(pattern, "我是[email protected]")) // 不匹配
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
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基础