Go一些演讲课的听课笔记,内容包含了channel与并发产生的问题、设计模式、go开发规范、开发模式、go开发核心功能设计、Go并发原语综述、网络以及一道课后题目
1. channel与并发产生的问题
1.1 阻塞问题
- 读的时候,没数据
- 写的时候,写不进去
- 读写nil的channel,也就是未初始化的channel
1.2 panic问题
- 关闭不恰当,会导致panic
- 使用不当则是阻塞goroutine,导致goroutine泄露,goroutine泄露一般都伴随着内存泄漏
1.3 goroutine泄露与内存泄漏
- 当goroutine泄露之后,存在于goroutine栈上的对象,以及这些对象运用的对象,都不会被回收。
- 如果goroutine运行的是闭包,那么闭包绑定的上下文中对象同样不会被回收。
2. 设计模式
- option模式:最典型的函数式编程引入的设计模式
- 责任链模式:通过函数式编程来实现
- 工厂模式:函数式编程设计的工厂模式和抽象工厂模式
3.go开发规范
- 开源规范
- 日志规范
- 目录规范
- 错误规范
- 代码规范
- 接口规范
- 接口规范
- 版本规范
- 文档规范
- 提交规范
3.1 目录规范
目录结构通常是指我们的代码由哪些目录构成,每个目录下存放什么文件,实现什么功能,以及各个目录间的依赖关系。
3.2 日志规范
3.2.1 在何处打印日志
- 在分支语句处打印日志
- 写操作处打印日志
- 在循环中打印日志要慎重
- 在错误产生的最原始位置打印日志
3.2.2 在合适的级别记录日志
- Debug级别
- Error级别
- Info级别
- Panic级别
- Warn级别
- Fatal级别
3.2.3 合理的记录日志
- 在记录日志时,不要输出一些敏感信息,例如密码、密钥等
- 统一日志记录格式。例如:①成功日志一律为 动词 + 一些事情 ②失败日志 fail to 动词 + 一些事情 ③日志内容:以大写字母开头,例如:log.Info(“Update user function called”)
- 根据需要,日志最好包含三个个信息,请求ID(requestID) ,用户(user),行为(Action)
- 不要将日志记录在健康的日志级别上
3.2.4 日志规范
- 开发调试、现网故障排障时,不要遗忘一件事情:根据排障的过程优化日志打印
- 打印日志要 “不多不少”,避免打印没有作用的日志,也不要遗漏关键的日志信息
- 支持动态开关Debug日志
- 总是将日志记录在本地文件
- 集中化日志存储处理
- 结构日志记录
3.3 开发规范
- 有业务code码标识
- 考虑到安全,希望能够对外内分别展示不同的错误信息
返回错误信息时:
- 在肯定要求时用must,在否定要求时用Must not
- 指定不等式时,若使用文字而不是符号
- 当引用另一个字段名称时,在反引号中指明名称
must be greater than 'request'
- 引用文字字符串值时,请在单引号中指明文字
3.4 提交规范
3.5 版本规范
语义化版本规范格式化:
- 主版本号.次版本号.修订号(X.Y.Z),其中X,Y,Z为非负的整数,且禁止在数字前方补0
- 主版本号(MAJOR):当做不兼容的API修改
- 次版本号(MINOR):当做了向下兼容的功能性新增及修改,这里有个不成文的约定需要注意,偶数为稳定版本,奇数为开发版本
- 修订号(PATCH):当做了向下兼容的问题修正
- 先行版本号:意味着该版本不稳定,可能存在不兼容问题,格式为:X.Y.Z
- 编译版本号:一般是编译器在编译该过程中自动生成的,我们只定义其格式,并不进行人为控制.
如何确定版本号?
- 在实际开发的时候,建议使用0.1.0作为第一个开发版本号,并在后续的每次发行时递增次序号
- 当我们的版本是一个稳定版本,并且第一次对外发布时,版本号可以定为1.0.0
- 当我们严格按照 fix类型的commit,可以将修订号+1,feat类型的commit可以将次版本号+1,带有BRAKING CHANGE的commit可以将主版本号+1
3.6 文档规范
文档包括:
- API文档
- SDK文档
- 安装文档
- 功能介绍文档
- 操作指南文档
4.开发模式
- 迭代模式:研发任务被切分为一系列轮次,每一轮次都是一个迭代,每一次迭代都是一个从设计到实践完整过程,它不要求每一个阶段的任务都做到最完美,而是先把主要功能搭建起来,然后通过客户的反馈信息不断完善
- 敏捷模式:把一个人的需求分成多个,可分阶段完成的小迭代,每个迭代交付的都是一个可使用的软件,在开发过程中,软件要一直处于可使用的状态。
5.go开发核心功能设计
- 应用管理
- 基础功能
- 应用功能
- web服务
- 应用测试
- 应用部署
5.1 应用管理
- 代码格式化
- 镜像制作
- 版权声明
- 代码部署
- 生成swagger文档
- 工具安装
- 编译
- 单元测试
- 代码生成
- CA证书制作
- 静态代码检查
5.2 基础功能
- 日志包设计: 排障、数据分析、注重功能、性能、规范、日志告警
- 错误包设计:大型项目中,需要和业务码适配,并具有trace功能
- 错误码设计:需要考虑到业务码、HTTP Status Code,内网Message,Reference
- 应用框架构建:框架通用性、易用性。通常包括:命令行程序、配置文件解析、命令行参数解析
- 数据库:稳定性、易用性、功能性
5.3 应用功能
5.3.1 API服务
- 以Restful或者RPC接口的方式对外提供服务
- 管控流通常为Restful接口
- 数据流通常为RPC接口
5.3.2 GO SDK
- 通常包含了跟软件相关的库、文档、使用示例、封装好的API接口调用工具
- 可以极大的提高开发者的并发效率和体验
- 对于一个企业级的项目,最好提供SDK
5.3.3 命令行工具
- 一般为xxxctl,例如:kubectl,ectdctl,istioctl
- 实现自动化,提高调用效率
5.3.4分布式作业
- 异步任务处理
5.3.5 异步数据处理
- 通常用于数据分析、监控告警、运营、数据展示等
- 对采集效率要求很高
- 分为同步采集与异步采集
5.3.6 Web服务
- Restful
- JSON
- 路由
- Request ID
- HTTP/HTTPS
- 认证/授权
- gRPC
- Protobuf
- 中间件
- 跨域
- 优雅关停
- 参数解析
- 参数校验
- 逻辑处理
- 返回结果
5.3.7 应用测试
- 单元测试
- 性能分析
- 测试框架
- 性能测试
- 覆盖率
- Mock工具
5.3.8 应用部署
- 裸金属部署
- 容器化部署
- 服务编排
- CICD
- 高可用
- 负载均衡
- 弹性伸缩
- 安全
6.GO并发原语综述
- Mutex
- RWMutex
- Once
- WaitGroup
- Cond
- channel
- Map
- Pool
- Context
- Timer
- atomic
- Semaphore
- SingleFlight
- CyclicBarrier
- ReentrantLock
- Future/Promise
- Locker
- Leader选举
- Barrier、DoubleBarrier
- Queue
- 同步控制
- 任务编排
- 消息传递、goroutine控制
7. 网络
7.1 tcp各层协议与数据包
7.2 tcp为何需要三次握手、四次挥手
- 三次握手最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的。
- 因为连接是全双工的,双方必须都收到对方的fin包及确认才可关闭。
7.3 为什么会出现大量close_wait
- close_wait一般会出现在被动关闭方
- 并发请求太多导致
- 被动关闭方未及时释放端口资源
7.4 tcp为啥需要流量控制
- 由于通讯双方,网速不同,通讯方任一方发送过快都会导致对方消息处理不过来,所以就需要把数据放到缓冲区中。
- 如果缓冲区满了,发送方还在疯狂发送,那接收方只能把数据包丢弃,因此我们需要控制发送速率
- 我们缓冲区剩余大小称之为接收窗口,用变量win表示,如果win=0,则发送方停止发送
7.5 tcp为啥需要拥塞控制
- 流量控制与拥塞控制是两个概念,拥塞控制是调节网络的负载
- 接收方网络资源繁忙,因未及时相应ACK导致发送方重传大量数据,这样将会导致网络更加拥堵
- 拥塞控制是动态调整win大小,不只是依赖缓冲区大小确定窗口大小