这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
讲师介绍
课程目录
语言进阶
并发:一个核,通过时间片轮转
并行:使用多核进行多线程运行
广义的并发可以理解为系统对外的一个特征
并行是实现并发的一个手段
go语言为并发而生,go的机制实现并发调度模型,
go中的机制体现为协程 理解为用户级的线程 由go语言调度
线程上比较消耗资源,可以并发的跑多个协程,ppt写错了,上面写反了kb和mb
注意是快速,开了多个协程去打印,用了sleep做阻塞,后续协程同步会优化这一点,右边是输出乱序的
通过通信共享内存,主要通过通信来共享内存,需要用到通道的概念
通过临界区共享内存处理交换,必须是要用互斥量进行加锁来获取权限,容易产生数据静态
所以go提倡通过通信来协程
无缓冲通道会导致两个gorountine同步化
通道容量代表了存储的元素,可以类比货架的格子,由阻塞存在,理解为生产消费模型
生产的数字发送到src的通道,完成了a协程的功能,b协程通过range遍历src的数据,这样src实现了a协程和b协程的通信,最终主协程来打印 通过src和dest可以实现顺序性和并发安全的,这样打印和计算平方就可以分开来了
设计思考:消费者的消费速度会比打印慢一些,生产者的速度会快一点,用带缓冲的队列就不会因为消费者的消费速度问题影响生产者的执行效率,带缓冲的channel解决效率问题,需要注意用defer关闭
这里操作一个变量,对比加锁和不加锁
加锁lock获取临界区资源然后进行释放,without方法是直接操作,不加锁输出8382,加锁会输出预期的值,并发安全的问题就产生了
解决问题方法:需要对临界区进行控制来保证安全,需要注意对共享内存进行非并发的读写操作
用sleep解决阻塞是不太好的,需要创建一个计数器来进行并发任务的同步,waitGroup暴露了三个方法,计数器来增加或减少,例如我们启动n个并发任务的话可以讲计数器增加n,每个任务完成时用done方法,将计数器减一,最后调用wait方法来阻塞等待所有并发任务解决。
创建五个计数器,每个协程执行之后用done方法,最后通过wait进行阻塞
设计到以上三个方面,go提倡通过通信共享内存,最后是sync包下的关键字来进行并发操作和同步
依赖管理
实际的开发工程需要管理依赖库,实际开发过程需要关注业务逻辑的实现,其他的依赖框架,日志,driver和集合需要通过sdk的方式引入,需要对依赖包进行管理
三个阶段,现在需要掌握Go Module
GOPATH是项目的工作区,注意bin,pkg和src,go get是下载最新版本包到src
GOPATH实现的逻辑,把项目代码的依赖放到src下,通过goget下载最新版本的包到src目录下
对于本地项目来说,两个project不能实现版本的依赖控制
vendor,vendor存放了当前项目的副本依赖包,项目的依赖会优先从vendor目录下寻找,每个项目引入一份依赖版本就能解决了。
如果两个项目依赖的包不兼容就会不能很好控制v1和v2的版本问题
go.mod在1.16默认开启了
通过go.mod 文件管理依赖包版本
通过go get/go mod 指令工具管理依赖包
可以类比java maven的含义
go.mod文件标识了一个模块,表示可以从哪里找到模块,如果前缀是github,那么依赖在GitHub上了
如果很多包需要引用可以创建很多go.mod
第一种是语义版本,包括了三个部分,MAJOR可以是不兼容的,MINOR做的新增函数,需要兼容,PATCH是代码修复 git会重点讲解
基于commit伪版本
版本,时间戳,还有hash前缀,每次提交go都会生成伪版本号
答案是b,因为要保证1.3和1.4的是兼容,如果由1.5,选择最低兼容版本即可
依赖哪里分发
稳定性:只要作者修改也不会发现
可用性:作者可以对仓库删除
压力:第三方代码管理系统一直拉会产生大流量的场景
go proxy可以解决这个问题,proxy可以保障依赖的稳定性,可以类比需求,需求下游的接口不一定满足,可以建立一个适配器的方案来解决问题
逐渐寻找
go get 会拉取
这里面就需要初始化和下载拉到缓存,然后增加需要的依赖
大家每次提交代码需要执行go mod tidy,减少构建整个项目的时间
测试
集成:功能自动化测试
单元:面对测试开发阶段,对单纯的函数进行测试
编写Test测试规则且连接第一个是要大写的
测试不符,指令go test -v
开源assert包来比较函数
标明覆盖度
用 go test 测试文件名 文件名 -cover 表明测试代码覆盖率
一共三行,通过了两行,所以是66.7%
这样代码就都覆盖了
覆盖率对于金融类技术比较高,测试成本会变高
幂等就是结果都是一样的,稳定性是指单元测试能够相互隔离的,然后函数都可以独立运行
文件处理操作,对每一行进行遍历,对第一行包含11的替换成00,然后执行单元测试,一旦文件被人篡改了,特定场景下不能运行了,就需要对mock,屏蔽对文件的依赖
mock组件有很多,可以实现对method进行打桩,用函数a替换函数b,a是打桩函数,b表示为原函数,replace为打桩的函数,target为原函数
unpatch是卸载打桩的
在运行时讲函数地址替换,实现了mock的功能
通过mock实现了不对文件名是强依赖,可以直接实行
基准测试是测试一段程序的运行性能和耗费cpu的程度
十个服务器,随机选择一个服务器,然后对select做基本测试
以benchmark开头,做串行的压力测试,对b.n做循环,红框是耗时
结果rand函数,由于有锁所以并行变弱
可以看到字节的框架,fastrand牺牲了数列的一致性,原始包随机的场景在高并发场景会导致性能问题
依赖用mock,基准用本地测试
项目实践
项目是有参考价值的
功能包括:话题详情,回帖列表,支持回帖,点赞和回帖回复,这次需求开发一个涉及的服务端小功能
设计到页面的展示,还有一个回帖的列表 两个实体:话题和回帖,可以思考结构体
ER图提供了思路,注意关联了一个topic-id
数据层就是这里的model,主要封装了外部数据的增删改查,数据层面向逻辑层,对service层透明,屏蔽下游数据差异,不管下游文件是上面,service层都是不变的
Service逻辑层处理核心业务逻辑,计算打包业务实体entity,对应我们的需求,就是话题页面,上送给视图层
control视图层包装数据格式,通过api方式访问
用gin框架搭建web服务器,更着走,处理开发思路
要实现通过话题id的查询
通过全扫描的方式,但是不是高校的,这里引出索引的概念,索引就像书的目录,可以引导我们快速查找我们要的结果
打开文件,初始化scanner,通过迭代器方式遍历数据行,转化为结构体存储到内存map里
有了索引就可以查询了
sync.Once 高并发只执行一次的,减少存储的浪费,单例模式
逻辑层做一个实体的封装,有postlist的数组,service的数组就是为了构建页面信息的结构体
下面就是逻辑
这个就是service的流程编排,通过err控制流程会退出,正常返回页面信息,err为nil
主要用来获取查询方法的发帖数据和回帖数据,他们都依赖topic id,不依赖可以用并行实现,创建两个计数器用两个方法来查询,最后从reposity返回
具体是如图
control层定义了业务状态信息,用data承载业务实体信息
string转化int,对pagedata打包,control层就ok
最后通过gin配置服务器,流程首先初始化数据索引,访问文件初始化索引
然后初始化gin引擎配置
构建路由,用url路由使用的方法,通过gin以http方式暴露出去
path映射具体的control,通过path遍历传递话题id
完成后测试运行,首先启动服务,直接请求get2
curl --location --request GET 'http://0.0.0.0:8080/community/page/get/2'
curl http://localhost:8080/community/page/get/2 不行试试这个
问题:
线程和协程的区别?
实现登录什么时候用session cookie方式?什么时候JWT?
sC涉及后台解密加数据查询操作,jwt不需要依赖存储,直接服务端解密
端上用sc,实际项目中和服务端http交互会用jwt方式
卡夫卡之类消息队列解决的问题是做到隔离,类似生产消费模型,如果是强依赖不能用消息队列,非强依赖就可以用到消息队列做到了解耦的功能
对于分层结构看文档