这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记。
包含前两天的课程笔记,如有错误还请指正。【有船启航】
Day01
第一天的内容比较零散,所以以点来记,而不使用课程大纲。
一、基础知识点
-
变量和常量名首字母大写时为可被包外调用的,类似java的
public关键字 -
switch-case用法更多变,可以在
switch后省略,case后写条件 -
make()造切片。但append()必须返还给原切片,切片的构成为长度、容量、底层数组指针,所以切片应该是值传递。copy()用来从两个切片中传递拷贝 -
容量不够会发生扩容,扩容与底层数组的长度有关
-
类似Python的切片操作:语法:
slice[i:j]第一个参数为开始索引,第二个为结束索引,左闭右开区间。注意,两个参数都可以省略,当省略开始索引时,默认值为0,省略结束索引时默认值为切片长度 -
可以通过
make()函数造map,map还是类似java的,同为键值对。语法:map[key type]value type -
map的读取也和java中类似。
- 注:
- ①可以通过返回值ok(bool类型)判断某key在map中是否存在,存在为true,否则为false;
- ②Go中一个函数的返回值类型可以有多个,具体就如同上述ok
-
map的读取也和java中类似,注意:①可以通过返回值ok(bool类型)判断某key在map中是否存在,存在为true,否则为false;②Go中一个函数的返回值类型可以有多个,具体就如同上述ok
-
可以通过
delete()函数删除键值对 -
range对于array的两个参数分别返还为索引和当前值,对于map则为键和值 -
解决值传递时,Go可以通过指针,即在写函数时在形参类型和变量前添加
*,在调用函数时,传入实参时前面添加& -
结构体同样可以使用指针
-
结构体方法类似于java中的类方法,需要在方法名前添加接收类型,并用括号包围,调用时就可以使用接收类型的变量调用了。同样可以使用指针。
-
错误处理:写函数时,返回值类型中添加error,在可能出错函数返回中new一个;调用函数时,可以用error类型接收,并进行判断和处理。
-
字符串操作和字符串格式化,建议直接查文档中的
strings包和fmt包,Go里面的字符串可能类似java中的StringBuffer和StringBuilder(还不确定!我还得看看文档。英文过关的可以用zeal软件,下载离线文档) -
json处理,公开字段(首字母大写)可以使用
json包的MarshalIndent()函数进行序列化,返回byte数组,再通过json的Unmarshal()函数反序列化 -
json的风格可以在结构体字段后修改
-
time包的时间格式是固定时间:
"2006-01-02 15:04:05",而非"yyyy-MM-dd hh:mm:ss" -
字符转换,通过
strconv包下的函数
二、实战
随机数问题:
v1,需要随机数种子,不然每次都是一样的数(可以利用时间戳初始化随机数种子)
v2,用户输入的读取,取只读的流,读取流中的字符,将字符串中的换行符去除,将字符串转换为数字
v3,判断逻辑
v4,实现循环,直至猜对时break
命令行词典:
(发送Http请求,解析json)
代码生成的网址:curlconverter.com/#go
解析响应体网址:oktools.net/json2go
- 要复制curl(bash)
- 返回的响应同样是流,要先检测确认是否出错,为了避免资源浪费,需要手动关闭(珍惜资源同java里的操作)
- 然后构造结构体,与json的字段一一对应
代理问题:
Day02
第二天的内容涉及到项目结构,老老实实按老师的大纲来了。(绝不是因为老师语速快)(引用了课件,若不允许,联系删改)
1.语言进阶
1.1并行并发
并发:一个CPU上 ,应用程序可能不会在同一时间完成多个任务,但在应用程序内部一次完成多个任务。要同时在多个任务上取得进展,CPU会在执行期间在不同的任务之间切换。
并行:多个 CPU 或 CPU 内核,并同时在多个任务上取得进展。但是,并行执行并不是指与并行性相同的现象 。
Go提高了并发调度模型,发挥多核计算机的优势。
协程:用户态,轻量级线程,栈KB级别。(PPT有误)
线程:内核态,线程跑多个协程,栈MB级别。
- 注:主协程如果执行完毕了,那么子协程会终止,需让主协程等待或接收子协程的执行运作。
1.2通道(类似传输队列,保证收发数据的顺序)
Go除去通过通信共享内存,也保留通过共享内存实现通信。但通过共享内存实现通信需要互斥量的加速,也就是需要获取临界区的权限,但会发生数据竞态,影响性能。注:最好通过Lock加锁,保证对于统一数据的操作安全(并发安全)。
语法:make(chan 元素类型,缓冲大小)
- 无缓冲通道 如
make(chan int):同步通道,会导致发收的gorountine同步化, - 有缓冲通道 如
make(chan int,2):类似于队列、生产消费模型,解决生产消费的效率不均问题
2.管理包
2.1.1 GOPATH
直接依赖src下的代码,通过go get下载最新版本的包道src目录下
缺点:无法实现package的多版本控制
2.1.2 Go Vendor
依赖优先从vendor文件获取,找不到再回GOPATH找。解决多个项目需要同一个package的冲突问题。
缺点:当两个轮子依赖了另一个轮子A的不同版本,则会产生冲突。即无法控制依赖的版本;更新项目又可能出现依赖冲突,导致编译出错。原因是依赖项目源码,并不能清晰的解决依赖的版本。
2.1.3 Go Module
通过go.mod文件管理依赖包版本 通过go get/go mod指令工具管理依赖包
2.2 依赖管理三要素
-
配置文件,描述依赖 go.mod indirect 直接依赖 incompatiable 不兼容
-
中心仓库管理依赖库 Proxy 依赖分发 变量GOPROXY->go mod direct 若镜像网站没有依赖,就回官网
-
本地工具 go get/mod
3.测试
3.1 回归测试 v 集成测试 v 单元测试
从左到右,覆盖率逐层变大,成本逐层降低
回归测试:质量保证员通过终端测试主流;基于自动化的回归测试,集中功能维度;面对测试开发阶段,对单独函数模块进行测试。
3.2 规范
所有测试文件以_test.go结尾 func TestXxx(*testing.T) 初始化逻辑放到TestMain中,入参为m testing.M(可*指针),然后用m.Run(),执行该package下的所有单元测试 运行:go test [flags] [packages]
3.3 代码覆盖率
通过 go test *_test.go *.go --cover 执行单测并计算代码覆盖率
-
注:
-
①一般覆盖率:50%~60%,较高覆盖率80%+。
-
②测试分支相互独立、全面覆盖。
-
③测试单元粒度足够小 -> 函数需要小,函数单一职责。
3.4 依赖
单元测试的目标:稳定性、幂等性
单元测试不稳定 -> Mock机制(开源mock包:monkey : github.com/bouk/monkey)
快速Mock函数: 原理:一个(打桩)函数替代另一个(原)函数
- 注:记得将打桩函数卸载,可通过defer关键字
3.5 基准测试(压力测试,性能分析)
-
规范:func BenchmarkXxx(b *testing.B)
-
细节刨除非测试代码的损耗:通过计时器重置
4 项目实战(实现一个Web服务)
4.1 需求设计
4.1.1社区话题页面——掘金回帖
- 话题展示和回帖列表
- 实现一个web服务,不实现前端
- 话题和回帖数据用文件存储
4.1.2 用例分析
4.1.3 分层结构(此处就不多说了,java的同学应该很熟悉SpringMVC的流程)
4.2 代码开发
DAO层:
- 设计Topic和Post两个实体的属性
- 初始化数据索引
sync.Once:是 Go 中使方法只执行一次的对象实现,适用于高并发场景下的只执行一次的情况 - 提供查询函数
Service层:业务逻辑
业务逻辑:参数校验-->准备数据-->组装实体
- 注:
- ①准备数据:案例的数据都依赖于Id,两个流程没有前后顺序,可以使用并行执行,提高效率
- ②使用子协程,要确保主协程在子协程执行完后结束
- ③注意err,包括控制流程的退出,使得页面信息正常返回
Controller层:流程控制
- 构建View对象,通过code、msg打包业务状态信息,用data承载业务实体信息
- 处理业务错误码
通过gin搭建web框架:实现接口
- 初始化数据索引,遍历数据文件,生成索引map
- 初始化引擎配置,将path映射到具体controller,通过 path变量 传递 话题Id
- 构建路由
- 启动服务