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

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

tcp\server.go

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

func main() {
	addr := "0.0.0.0:9999"
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
	defer listener.Close()
	fmt.Println("Listen: ", addr)

	for {
		client, err := listener.Accept()
		if err == nil {
			client.Write([]byte(time.Now().Format("2006-01-02 15:04:05")))
			client.Close()
		}
	}

}
package 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" />
我叫 &lt;img src=&#34;xxxx&#34; /&gt;

Go语言操作Mysql数据库

Mysql的基本概念和操作方法

数据库基础

库、表、SQL

Mysql操作

1. 安装

2. 创建新用户
    create user golang identified by "1234";
    grant all privileges on database.table to name@addr identified by password;
    addr %
    database.table *.* database.*

    grant all privileges on *.* to golang @'%' identified by "1234";

    flush privileges;
root/1234 禁止远程访问root用户

3. 库
    database

    创建:
        create database name default charset utf8mb4;
    查询:
        show databases;
    查看:
        show create database name;
    删除:
        drop database name;


4. 表
    use database;
    创建
        create table name (
            colname coltype 修饰,

            index indexname (colname, colname1, colname2)
        ) engine=innodb default charset=utf8mb4;

        colname: 列名 小写英文字母,数字, _组成
        coltype: 数值类型
                    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

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


outercondition = innercondition and

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


total, totalFilter

管理员
    total  select count(*) from user

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

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

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


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


第二页 10 10


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

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

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

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

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

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

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

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

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

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

用户管理平台cmdb

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

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

用户管理

        登陆验证

        管理页加载

        用户数据加载

        增/删/改/锁定/解锁

云主机管理

主机资源监控

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

redis保存session

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

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

day12

自述

MVC:

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

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

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

客户端渲染: http渲染页面

day13

controller逻辑图

自述

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


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

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


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

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

    用户鉴权


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

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

            controller: DelSession(key)
            DestorySession()

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


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


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

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

day14

自述

静态资源下载在本地

static

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

用户管理

// 任务管理

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


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

day15

代码结构

 部署 

自述

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

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

        /*

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

                                                    不是 =>

                        session => uid => loginuser

        */

        controller => 加载视图

        views/tpl

        rounter(url=>controller)

    b. => 提交数据

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

        Form

        parseForm

       检查旧密码

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

        新旧不能一样

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

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

[x]2. CSRF

    网络攻击

    扩展请求伪造

    a. 配置

    开启CSRF防护

    配置Token => key

    过期时间

    b. 打开页面生成TOken

        从Controller生成 传递到页面

    c. 提交数据提交Token

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

        csrf_token =>

[x]3. Flash

    处理成功后将消息存储 cookie

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

    页面使用后端模板

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

    存储

    flash := beego.NewFlash

    flash.Set(key, value)

    flash.Store(&c.Controller)

    // 获取

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

    // readFromRequest     c.Data[key] = value

    flash.Data

[x]4. 错误处理

[x]5. 验证

[x]6. 日志

[x]7. Layout&LayoutSection

[x]13. Cache

    缓存

8. 用户权限

    jd

        万志强 => user

        kk => user

        管理侧 => 添加商品

                  维护价格

                 用户管理

    角色 不同角色 不同功能

         相同角色 不同数据

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

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

                      B => 2, 3, 4

    Todolist

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

    修改密码

    Task管理

    用户管理

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

            UserController

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

                            是管理员 => 继续访问

            TaskController

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

            Task => User属性

            TaskController

                Query => user == current.ID

                Add => current.ID => db

                Detail => id, user => data

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

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

    如何限制普通用户操作UserController

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

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

        session => User 有 放行

                        无 跳转到登录页面

    role {

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

        2 : [controller1.Action1, controller1.Action2]

    }

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

                    GetControllerAndAction

        在 actions => 有权限

        不在 => 无权限

9. 过滤器

[x]10. 部署

[x]11. HTTPS

[x]12. cobra

    beego orm --db --force --verbose

    main web

         db

         init

name {

    attr: value;

}

html->head

<style type="text/css">

</style>

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

User

type userService struct {

}

Add

Find/Query

Detail

Delete

UserService = new(userService)

A UserService.Add

B UserServiceInstance

day16

csrf

自述

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


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

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


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

管理员
    用户管理
    任务管理

普通用户
    任务管理

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

hasPerssion(.user, "key")


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

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

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


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

    c.GetControllerAndAction() in [Controller.Action]

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

day19

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

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

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

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

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

    队列
        db
        rabbitmq/kafka
        redis
        file

redis:
    key value 基于内存的存储

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

    key value => map key value=>type

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

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

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

            放入元素PUSH
                 LPUSH/RPUSH

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

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

            队列
                先入先出
                    LPUSH RPOP
                    RPUSH LPOP

                阻塞
                    RPOP => 无元素返回nil

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


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

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

            sinter
            sdiff
            sunion
        zset 有序集合
            zadd

            优先级任务
            权重 任务ID

            zrange key 0 0
            zrevrange key 0 0

            zrem member


            task:id hash

        地理位置
        bitmap
        ...

    默认无认证
        require

    go 操作

开发流程

创建项目结构

修改配置文件

编写启动文件

登陆

        用户/Token模型定义

        登陆页面加载

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

        验证结果处理

用户管理

        登陆验证

        管理页加载

        用户数据加载

        增/删/改/锁定/解锁

        Token查看/生成

gocmdb

第二版 

go install github.com/GoAdminGroup/adm@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>&copy; All Rights Reserved. GoAdmin</small>
            </p>
        </div>
    </div>

滑动验证码_PC

<script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
<script>

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

    function submitData() {
        captcha.show()
    }
</script>

主机资源监控与实战

syncd 代码部署

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

容器云k8s与二次开发

day20-0829

docker基本使用

k8s基本概念和环境搭建

k8s基本操作

k8s dashboard

k8s client创建资源

k8s client获取资源

k8s client修改资源和删除资源

k8s web管理

k8s ingress

1. k8s搭建

kubeadm

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

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


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

API与SDK的介绍与使用

配置/证书下载

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

kubeadm部署

- 准备环境

安装docker

 k8s

 kubeadm安装

dashboard安装

二进制部署 

kubectl命令 

资源配置文件YAML 

docker

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

安装

架构

技术

镜像

容器

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

挂载

网络

dockerfile 

docker-compose 

 

自建私有镜像仓库

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

k8s client获取资源

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

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01EZ3lPVEEwTURBeE9Wb1hEVE13TURneU56QTBNREF4T1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUNQCjdQdmdidFp5b3FDRVh1dVMrZFZTNThaUlZXTVN4VkRmRkFXR1F2cEVLb2tuNGZRMWNUVmcvaGt1L0xwTE9NeEsKWll2TWRETFRnVWFjZGlJOTlFM216eWNKZ2pKbVN0VWJMZWlqbEFaNDZzYVEvZHJlaUxIYSt0R2Q3bVpCcmZXMgoxWG5jSUYyMm5Oa0pMaS9JNloyalZqYkMvaytwS2FoSDhPeU9HU2kwdWVUZ2lhWjRMeElwT1k5U2dONmQvREdrCkVWSmRXa1hkRTRZV1B4MHpQcXFIUW9XN1VVdFlzcnBTUXR0NTE2bkNEdTlacmwvc2tlb2dRbDdOTWVUSkR3RWEKZTI5NVFsY1g5RlVBTHh2eVBsbmpLWjRTK3lueVdXR1VmTTBzOGJ3bWIycWdTV2hqSHdRalY2ZVJiODdyYVdpVQpOZW91dG9najJzeEFxL3FUdEtrQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFEdHROK1hydUZwbkFNdmxZRStFb3BVL0J0SUMKM0VSMFZacG5idk9yNm16ZDM4UHNFS24zWGlQcGFKeUpMRENUeHcyT3c5RXRIRHV0QlFlQStHNkhqRlJBUEZSZApSZC9hdXkvUkwyK3YycU1XNEt0YUxvWDdXaXhiQ1JBNEs2dk83UVhGeCtab1RETDR0K3VjL2ZlanQ4dkdyc1V2CjYzaXB1MHp5U3NWZGIwbmJlRlpTa3VGOHM2eUZPVlIxdlhxa0FjVGQrek9lcUs0MkVzRlRBNVFaZ080Nm5UclAKcHdiK1lZVnlodTZsWUc4c2tDZ0NjSWdnMW41bC9FNXpiQUo1YzVwdjN3S200dGZEVVJON0xmbVVPa01iUlAwQQprakcwOHNUOGNmOEJEdTNiZWN1b3FQNW9ZVTRVcUFGZUFETEhlSFFNdklpRXVQdlBDbU5WNzZiVFRiMD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
    server: https://192.168.204.130: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事件

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐