数据结构与 方法(增删改查)
-
安装goland,注意版本是2024.1.1,不是2024.2.1,软件下载地址也在链接中提供了
-
‘go’ 不是内部或外部命令,也不是可运行的程序
或批处理文件。
在 Windows 搜索栏中输入“环境变量”,然后选择“编辑系统环境变量”或“编辑环境变量”。
在系统属性窗口中,点击“环境变量”按钮。
在“系统变量”区域,找到并选择“Path”变量,然后点击“编辑”。
在弹出的窗口中,点击“新建”,然后输入 Go 的 bin 目录的路径,例如 C:\Go\bin。
点击“确定”保存更改。
-
枚举用 iota自动就依次排号变成枚举了,不用你赋值了。枚举就是变量代替正整数
有类型的枚举的话,就不能 float64+ int了,因为有严格的类型校验,没指明类型的可以 -
字符串拼接可以用 +
数据不可变,它的值改变不了
字符串不可变: 意味着你不能改变字符串内部的单个字符。
变量可变: 变量本身可以被重新赋值,指向另一个字符串。
var newName string
newName = name + " " + "day"
fmt.Println(newName)
newName += "s"
fmt.Println(newName)
从行为上看,newName 的内容变了,但这是因为你指向了一个全新的字符串。在 Go 中,这种操作不是修改原有字符串,而是创建了一个新的字符串,并让 newName 指向这个新的字符串。原有的字符串 “lucky day” 仍然存在于内存中,直到 Go 的垃圾收集器决定回收它。
- 结构体就是复合类型的聚合
指针,如果p = &s(结构体) ,通过取地址运算符取到指针, 那么p就能像 s一样点出来所有内含的字段 - 数组
长度固定,存储同一类型的数据。数组是值传递的,不是引用传递,每次作为参数都是值传递,数组太大,复制会引起性能损耗。解决方案就是使用切片
切片 【指针,len, cap容量 cap>=len】
可以动态改变长度(每次大于容量再append元素,容量是翻倍的涨,比如1,2,4,8),一旦扩容,切片就会和原数组解绑,然后绑定到一个新数组上。频繁扩容耗费性能。所以初始化的时候,len可以设置为0,但是容量最好设置一个预估的值
len就是切片里元素的个数,
切片是对底层数组的描述的意思是当改动切片中的某个值,从切片用中括号截取的切片对应的值也会改变,它从根上就变化了。
容量cap就是你把原切片切出一部分,容量就是原容量减去中括号前面切去的就是现在的容量
- golang是编译语言,python是脚本语言
脚本语言:适用于快速开发、自动化任务、Web开发和应用程序扩展。它们的价值在于提高开发效率和灵活性。
编译语言:适用于高性能应用、系统编程、大型企业应用和安全性要求高的场景。它们的价值在于提供高效的执行速度和对底层系统的控制。
-
make 只能用于内置的切片、映射和通道类型,不能用于用户定义的类型或其他内置类型(如整型、浮点型等)。对于这些类型,你需要使用其他方式(如直接初始化或使用 new 函数)来创建和初始化变量。
-
map 哈希表或者字典,k-v。
遍历map的时候是无序的。map也是有容量的,如果频繁扩容也会造成性能消耗 -
切片(slice)、映射(map)和通道(channel)是引用类型,其他的是值类型
-
这里num的作用域就是if 函数内。err经常用这一招
-
for循环重复执行一段代码,直到不满足条件为止
for range用来遍历 切片 map 数组,string 等用的,下面是range和传统形式两种
[]string{“lucky”,“nike”,“allen”}中括号里没东西就是切片,有数值就是数组
-
包就是import那个,
package中 首字母大写的函数是可以被导出的,首字母小写的就是内部使用的
构建就是 go build xx.go,xx.go就是你的那个package xx脚本。然后就可以在命令行直接执行 ./xx 跟 go run xx.go一样的效果
如果有两个errors包(不需要go build的,脚本形式就行),只需要前面加上来源,比如github.com/pkg/errors,并且给它取个别名来解决包冲突
包名一般跟顶层文件夹名保持一致
交叉编译(Cross-Compilation)是指在一种平台上编译生成能够在另一种平台上运行的程序。也就是说,编译代码的机器和运行该代码的目标机器使用不同的操作系统或硬件架构。交叉编译通常用于开发需要在多个平台(如 Linux、Windows、macOS,或不同的处理器架构如 ARM、x86)上运行的程序。
Go 语言内置了对交叉编译的强大支持。Go 的编译器允许开发者通过设置两个环境变量GOOS
(目标操作系统)和GOARCH
(目标硬件架构)来进行交叉编译。
例如,想要在 Linux 上编译一个能在 Windows 运行的程序,开发者可以这样做:
GOOS=windows GOARCH=amd64 go build -o myapp.exe
同理,想要编译一个能在 ARM 架构的 Linux 设备上运行的 Go 程序,可以设置:
GOOS=linux GOARCH=arm64 go build -o myapp
-
函数
func init(){}
func 函数名(参数)(返回值){
函数体
defer 函数:当前函数结束时候调用 //主要用于释放资源。因为中间函数发生错误,return掉了,这时就没法执行关闭资源的代码,这时候defer就能派上用场了。比如ioutil.ReadAll(file)的时候,需要 file.Close()
} -
方法
相较于函数,多了一个 接收者 receiver,用来连接方法与类型,比如结构体的方法,不能是基础类型,int,string这些
receiver参数可以是值类型(用于只读),也可以是指针类型(读写)。
-
接口,里面有没有实现的方法。我开放了我的系统,但你实现要符合我的规则,也就是接口
接口组合就是 接口套接口或者 结构体里面套接口 -
并发与并行
同时运行多个任务,交替进行,不一定在同一时刻都在运行的是并发。 都在同一时刻运行的是并行。并行需要多核cpu或者分布式计算系统
并行那么棒,为啥需要并发
1)并发可以让 I/O 操作等待的时间里去做其他事情(其他协程或者线程切进来工作)。比如在等朋友的时候,你还可以玩玩手机,不是干等。
2)防止死锁
协程
用了协程,干一件事用10ms,干5件事也是10ms
创建协程,使用WaitGroup 等待协程结束(防止协程还没执行,main函数先执行完了,就都结束了)
解决并发同时访问一个资源不安全的问题,用mutex.Lock() 互斥锁。访问的时候变成了串行。锁的位置要放准,锁太多执行效率会下降
- channel 两个协程通信,生产者 消费者模型
数据写进channel,然后从channel里面读出来
ch:=make(chan int, 3) 这个3就使 channel变成有缓冲的了,这样有空间就往里面写。否则只能写一个,读一个,变成串行了,有缓冲就并发,处理快得多
如果无缓冲,又没人读,就变成死锁,报错,解决就是用 select。它监听多个channel,谁有数据时,就执行对应 case 分支
收发都有select。
channel 还可以执行上报心跳
-
error 处理 可预期的、可以处理的错误
透明错误 – 什么问题报什么
哨兵错误 – 比如搜数据库没有这条数据,就写入,用户看不到的,后台就把这个错误解决了
自定义错误 – error 中包含 错误码,追踪 id 等等
行为错误 – 给错误分组。比如网络错误,连接错误。对应分组有相应的应对方式 -
panic 适用于 不可恢复的错误
二、project
- Gin web框架(启动个后端,监听8080端口),在GitHub上有 安装和简单示例
postman可以测试它的接口 - 路由
Gin提供了 增删改查(PUT PATCH POST…) 等 用于处理 HTTP
CMS 内容管理
对应/api/cms/各种路由,api会单独拎出来
main函数加载路由表,route对应的url,而 services对应的是url对应的处理方法
-
中间件 剥洋葱
在请求被处理前后做一些额外的操作
比如 session 鉴权,就是postman带个session id -
模型绑定与验证 自动转换
将json转换为我们在go语言中定义的结构体
结构体的字段与json中的字段一一映射。这个往往是请求和响应两个结构体成对出现的,和客户端有来有回 -
注册接口
后端定义POST 注册接口 + handle 处理逻辑
密码加密 避免暴力破解
1)生成随机盐值,纵使输入相同的密码,得到的哈希值也是不同的
2)内部多次哈希的迭代次数就是工作因子
3)这样通过密码 存储的盐值和工作因子,就能验证密码是否正确
database tool
goland自带的工具,连接数据库,操作数据库
user.sql里面包含着表要创建成什么样子,就是sql语句的合集
在 Go 语言中,DAO(Data Access Object)模式是一种设计模式,主要用于将应用程序与底层数据存储(例如数据库)的交互逻辑分离。DAO 层负责处理与数据库的所有操作,如 CRUD(创建、读取、更新、删除)等。
GORM
就是go 的 ORM (对象关系映射)。数据库操作用的
用结构体映射数据库中的表,元素等。跟前端那个映射类似
先连上表,再增删改查
分表用重写TableName
分页查询用链式调用。比如 db.Where().Offset().Limit().Find()
model里面就是结构体们
- 登录接口
登录成功后 返回session id,并且session id持久化
redis
github上有 go-redis
也可以通过命令行redis-cli 登进去看
redis存了两个,一个是 userid : sessionid. 另一个是session_auth:sessionid : time.Now().Unix(),这个是鉴权用的
session id
用 go get github.com/google/uuid 这个库生成 session id := uuid.New().String()
鉴权
带着sessionID去访问,如果session_auth还在的话,就能通过鉴权,不用输入用户名和密码,直接进去
- 内容库
goland编辑器右键自带单元测试。验证写得函数是否正确,省的每次还得开关整个服务
查询的时候涉及到翻页逻辑
比如查哪个id,作者,title,查找范围是第几页的前多少个数据,这是前端传过去的。后端就会返回回来具体内容
- 加工流
在 Go 语言中,DAG(Directed Acyclic Graph, 有向无环图) 是一种数据结构,用于表示具有方向性的、无环的图形。它常被用于表示依赖关系、任务调度、数据流等问题,特别适合在分布式系统、工作流调度、数据处理流水线等场景中。
GoFlow 是 Go 语言中的一个框架,它利用 DAG 来处理数据流问题。它的主要功能是通过 DAG 来定义和执行一系列具有依赖关系的任务,使得数据能够沿着这些任务进行流动,帮助开发者轻松管理复杂的依赖关系和任务调度。
go get github.com/s8sg/goflow
Input —> 累加 1到10 —> 累加 100到200 —> output。
redis用于工作流临时存储
上面是简单的,下面是个更复杂的加工流
做两次累加(它们是并行,异步执行的,不一定谁先),需要都完成之后,再执行聚合的逻辑。直接调现成的方法完成聚合
也提供了条件分支现成的方法
- 自己项目的dag加工流
thumbnail是缩略图
加工流和内容创建是如何结合起来的呢?
三、微服务
大应用拆分成一组小型、自治的微服务
服务用http,rpc 通信