第二节Go 语言上手 - 工程实践 | 青训营笔记

94 阅读9分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记

讲师介绍

image.png

课程目录

image.png

语言进阶

image.png 并发:一个核,通过时间片轮转
并行:使用多核进行多线程运行
广义的并发可以理解为系统对外的一个特征 并行是实现并发的一个手段
go语言为并发而生,go的机制实现并发调度模型, image.png go中的机制体现为协程 理解为用户级的线程 由go语言调度 线程上比较消耗资源,可以并发的跑多个协程,ppt写错了,上面写反了kb和mb
image.png 注意是快速,开了多个协程去打印,用了sleep做阻塞,后续协程同步会优化这一点,右边是输出乱序的 image.png 通过通信共享内存,主要通过通信来共享内存,需要用到通道的概念 通过临界区共享内存处理交换,必须是要用互斥量进行加锁来获取权限,容易产生数据静态
所以go提倡通过通信来协程 image.png 无缓冲通道会导致两个gorountine同步化
通道容量代表了存储的元素,可以类比货架的格子,由阻塞存在,理解为生产消费模型 image.png 生产的数字发送到src的通道,完成了a协程的功能,b协程通过range遍历src的数据,这样src实现了a协程和b协程的通信,最终主协程来打印 通过src和dest可以实现顺序性和并发安全的,这样打印和计算平方就可以分开来了
设计思考:消费者的消费速度会比打印慢一些,生产者的速度会快一点,用带缓冲的队列就不会因为消费者的消费速度问题影响生产者的执行效率,带缓冲的channel解决效率问题,需要注意用defer关闭 image.png 这里操作一个变量,对比加锁和不加锁
加锁lock获取临界区资源然后进行释放,without方法是直接操作,不加锁输出8382,加锁会输出预期的值,并发安全的问题就产生了
解决问题方法:需要对临界区进行控制来保证安全,需要注意对共享内存进行非并发的读写操作 image.png 用sleep解决阻塞是不太好的,需要创建一个计数器来进行并发任务的同步,waitGroup暴露了三个方法,计数器来增加或减少,例如我们启动n个并发任务的话可以讲计数器增加n,每个任务完成时用done方法,将计数器减一,最后调用wait方法来阻塞等待所有并发任务解决。 image.png image.png 创建五个计数器,每个协程执行之后用done方法,最后通过wait进行阻塞 image.png 设计到以上三个方面,go提倡通过通信共享内存,最后是sync包下的关键字来进行并发操作和同步

依赖管理

image.png image.png image.png 实际的开发工程需要管理依赖库,实际开发过程需要关注业务逻辑的实现,其他的依赖框架,日志,driver和集合需要通过sdk的方式引入,需要对依赖包进行管理 image.png 三个阶段,现在需要掌握Go Module image.png GOPATH是项目的工作区,注意bin,pkg和src,go get是下载最新版本包到src GOPATH实现的逻辑,把项目代码的依赖放到src下,通过goget下载最新版本的包到src目录下 image.png 对于本地项目来说,两个project不能实现版本的依赖控制 image.png vendor,vendor存放了当前项目的副本依赖包,项目的依赖会优先从vendor目录下寻找,每个项目引入一份依赖版本就能解决了。 image.png 如果两个项目依赖的包不兼容就会不能很好控制v1和v2的版本问题 image.png go.mod在1.16默认开启了
通过go.mod 文件管理依赖包版本 通过go get/go mod 指令工具管理依赖包 image.png 可以类比java maven的含义 image.png go.mod文件标识了一个模块,表示可以从哪里找到模块,如果前缀是github,那么依赖在GitHub上了 如果很多包需要引用可以创建很多go.mod image.png 第一种是语义版本,包括了三个部分,MAJOR可以是不兼容的,MINOR做的新增函数,需要兼容,PATCH是代码修复 git会重点讲解
基于commit伪版本
版本,时间戳,还有hash前缀,每次提交go都会生成伪版本号 image.png image.png image.png image.png image.png 答案是b,因为要保证1.3和1.4的是兼容,如果由1.5,选择最低兼容版本即可 image.png image.png 依赖哪里分发 image.png 稳定性:只要作者修改也不会发现
可用性:作者可以对仓库删除
压力:第三方代码管理系统一直拉会产生大流量的场景 image.png go proxy可以解决这个问题,proxy可以保障依赖的稳定性,可以类比需求,需求下游的接口不一定满足,可以建立一个适配器的方案来解决问题 image.png 逐渐寻找 image.png go get 会拉取 image.png 这里面就需要初始化和下载拉到缓存,然后增加需要的依赖
大家每次提交代码需要执行go mod tidy,减少构建整个项目的时间 image.png image.png

测试

image.png image.png image.png 集成:功能自动化测试 单元:面对测试开发阶段,对单纯的函数进行测试 image.png image.png 编写Test测试规则且连接第一个是要大写的 image.png image.png 测试不符,指令go test -v image.png 开源assert包来比较函数 image.png 标明覆盖度 image.png 用 go test 测试文件名 文件名 -cover 表明测试代码覆盖率 一共三行,通过了两行,所以是66.7% image.png 这样代码就都覆盖了 image.png 覆盖率对于金融类技术比较高,测试成本会变高 image.png 幂等就是结果都是一样的,稳定性是指单元测试能够相互隔离的,然后函数都可以独立运行 image.png 文件处理操作,对每一行进行遍历,对第一行包含11的替换成00,然后执行单元测试,一旦文件被人篡改了,特定场景下不能运行了,就需要对mock,屏蔽对文件的依赖 image.png mock组件有很多,可以实现对method进行打桩,用函数a替换函数b,a是打桩函数,b表示为原函数,replace为打桩的函数,target为原函数
unpatch是卸载打桩的
在运行时讲函数地址替换,实现了mock的功能 image.png 通过mock实现了不对文件名是强依赖,可以直接实行 image.png 基准测试是测试一段程序的运行性能和耗费cpu的程度 image.png 十个服务器,随机选择一个服务器,然后对select做基本测试 image.png 以benchmark开头,做串行的压力测试,对b.n做循环,红框是耗时
结果rand函数,由于有锁所以并行变弱 image.png 可以看到字节的框架,fastrand牺牲了数列的一致性,原始包随机的场景在高并发场景会导致性能问题 image.png 依赖用mock,基准用本地测试

项目实践

image.png image.png 项目是有参考价值的 image.png 功能包括:话题详情,回帖列表,支持回帖,点赞和回帖回复,这次需求开发一个涉及的服务端小功能 image.png image.png 设计到页面的展示,还有一个回帖的列表 两个实体:话题和回帖,可以思考结构体 image.png ER图提供了思路,注意关联了一个topic-id image.png 数据层就是这里的model,主要封装了外部数据的增删改查,数据层面向逻辑层,对service层透明,屏蔽下游数据差异,不管下游文件是上面,service层都是不变的 Service逻辑层处理核心业务逻辑,计算打包业务实体entity,对应我们的需求,就是话题页面,上送给视图层 control视图层包装数据格式,通过api方式访问
image.png 用gin框架搭建web服务器,更着走,处理开发思路 image.png 要实现通过话题id的查询 image.png 通过全扫描的方式,但是不是高校的,这里引出索引的概念,索引就像书的目录,可以引导我们快速查找我们要的结果 image.png 打开文件,初始化scanner,通过迭代器方式遍历数据行,转化为结构体存储到内存map里
有了索引就可以查询了
image.png sync.Once 高并发只执行一次的,减少存储的浪费,单例模式 image.png 逻辑层做一个实体的封装,有postlist的数组,service的数组就是为了构建页面信息的结构体 下面就是逻辑 image.png 这个就是service的流程编排,通过err控制流程会退出,正常返回页面信息,err为nil image.png 主要用来获取查询方法的发帖数据和回帖数据,他们都依赖topic id,不依赖可以用并行实现,创建两个计数器用两个方法来查询,最后从reposity返回 image.png 具体是如图 image.png control层定义了业务状态信息,用data承载业务实体信息 string转化int,对pagedata打包,control层就ok image.png image.png 最后通过gin配置服务器,流程首先初始化数据索引,访问文件初始化索引
然后初始化gin引擎配置
构建路由,用url路由使用的方法,通过gin以http方式暴露出去
path映射具体的control,通过path遍历传递话题id image.png 完成后测试运行,首先启动服务,直接请求get2 image.png curl --location --request GET 'http://0.0.0.0:8080/community/page/get/2' curl http://localhost:8080/community/page/get/2 不行试试这个 image.png image.png image.png 问题: 线程和协程的区别? 实现登录什么时候用session cookie方式?什么时候JWT? sC涉及后台解密加数据查询操作,jwt不需要依赖存储,直接服务端解密
端上用sc,实际项目中和服务端http交互会用jwt方式
卡夫卡之类消息队列解决的问题是做到隔离,类似生产消费模型,如果是强依赖不能用消息队列,非强依赖就可以用到消息队列做到了解耦的功能
对于分层结构看文档