Go语言工程实践 | 青训营

67 阅读5分钟

前言

本篇笔记主要记录整理后端青训营Go语言工程实践篇的课程内容。

正文

语言进阶(并发编程)

首先需要搞清楚并发与并行的概念。并发指多个程序在单核CPU上在某个时间段内均被运行,并行指多个程序在多核CPU上在某个时间点同时运行。Go语言可以充分发挥CPU多核优势,高效运行。

线程运行在内核态,栈空间占用为MB级别,一个线程可以包括多个协程,协程运行在用户态,栈空间占用为KB级别。Go语言对协程的支持使得其对高并发有良好的支持和极高的性能。

协程的使用:

  1. 使用func(虚参){方法体}(实参)定义好处理逻辑后,在func前加上关键字go可以为该方法启动一个协程;
  2. Go语言推荐通过通信共享内存,使用channel可以进行协程间通信(通信过程有序),定义语句为make(chan 元素类型[, 缓冲大小])。
  3. 并发安全问题可以通过Lock加锁或WaitGroup计数器控制阻塞解决。

依赖管理

背景

稍微具备一定规模的工程项目不可能纯粹基于标准库进行搭建,无论开发者的编码水平与编码态度如何,项目规模达到一定程度时,引入经过多用户使用与测试过的质量有一定保证的开源库实现部分程序逻辑是有助于提高项目代码质量与稳定性的。由于开发者会对开源库进行内容更新与版本更迭,不同项目对外部库的依赖版本不同,此时需要依赖管理工具对项目依赖统一进行管理。

Go依赖管理演进

发展历程

从GOPATH到Go Vendor到Go Module。

  1. GOPATH。

    • 需要配置环境变量GOPATH,该目录下包括bin、pkg和src三个目录。bin目录中存储项目编译的二进制文件;pkg目录中存储项目编译的中间产物,用于加速编译;src目录存储项目源码。
    • go get命令可以下载最新版本的包到src目录下。
    • 无法实现package的多版本控制,当不同项目依赖某外部库的不同版本时,GOPATH无法协调。
  2. Go Vendor。

    • 项目目录下增加vendor文件,所有依赖包副本形式放在项目根目录下的vendor文件夹中。依赖包优先在vendor目录下寻找,未找到的情况下去GOPATH目录下寻找。通过每个项目引入一份依赖副本的方式解决了多个项目需要同一外部库不同版本的依赖冲突问题。
    • 无法控制依赖的版本,更新项目可能出现依赖冲突。
  3. Go Module。

    • 通过go.mod文件管理依赖包版本,通过go get/ go mod指令工具管理依赖包。

依赖管理三要素

  1. 描述依赖的配置文件
  2. 管理全部依赖库的中心仓库
  3. 本地依赖的管理工具

若项目间接依赖了另一项目的不同版本,编译时会选择能兼容使用的最低版本。

依赖分发

  1. Go Proxy站点。直接从代码托管平台获取依赖包可能会因作者增加/修改/删除软件版本、删除软件或代码平台负载等问题导致依赖可用性以及项目稳定性受到威胁。Go Proxy是一个代理站点,从代码托管平台中缓存依赖库的不同版本,保证依赖的可用性与项目稳定性。
  2. 变量GOPROXY。Go Module通过配置GOPROXY环境变量指定依赖的下载地址,多个地址以英文逗号分隔,从前往后尝试访问,direct代表依赖代码的源站。

测试

单元测试

基础

  1. 规则。所有测试文件以_test.go结尾,方法名定义为func TestXXX(t *testing.T), 初始化逻辑放到TestMain方法中。

    func TestMain(m *testing.M) {
        // 测试前置工作
        code := m.Run()
        // 测试收尾工作
    }
    
  2. 运行命令:go test XXX_test.go。

  3. assert。将给定值与预期值进行对比,不符合预期时抛出错误。

  4. 覆盖率。代码覆盖率是以测试中执行到的代码行数与总代码行数的比值。

  5. 测试分支需要相互独立,全面覆盖。测试单元粒度需要满足函数单一职责。

Mock测试

复杂项目一般会存在文件、数据库、缓存等外部依赖,单元测试需要保证稳定性和幂等性,实现这一目的需要用到mock机制。可以对特定函数进行mock,屏蔽其对外部文件的依赖。通过开源mock测试库monkey可以快速实现这一功能,在运行时替换被调用的函数地址。

基准测试

基准测试可以通过分析找出需要优化的点,Go语言内置的测试框架提供了进行基准测试的能力。

基准测试以Benchmark开头,入参是testing.B, 用b中的N值反复递增循环测试。

项目实战

按照课程内容,使用本地文件存储话题和回帖信息,使用Gin实现Web请求响应。

本地数据为json格式。

  1. topic.json

    [
          {
            "Id": 1,
            "Title": "青",
            "Content": "出发",
            "CreateTime": "2023-07-23T00:00:00Z"
          },
          {
            "Id": 2,
            "Title": "青训营",
            "Content": "青训营出发",
            "CreateTime": "2023-07-24T00:00:00Z"
          }
    ]
    
  1. post.json

    [
        {
          "Id": 1,
          "TopicId": 2,
          "Content": "加油",
          "CreateTime": "2023-07-25T00:00:00Z"
        },
        {
          "Id": 2,
          "TopicId": 2,
          "Content": "坚持",
          "CreateTime": "2023-07-26T00:00:00Z"
        }
    ]
    

使用postman测试接口结果如下。

  1. 访问全部数据

访问全部数据.png

  1. 访问单个话题

访问单个话题(成功).png

访问单个话题(失败).png

结语

当下的依赖管理与测试工具已经较为完善。根据课程最后一部分内容,本文实现了基于读取本地文件到内存中后通过Web访问的话题浏览功能,在实现过程中实际上让我感受更深的是数据库管理系统在处理关系型数据时的优越性。通过文件管理系统对该类数据进行管理与操作要麻烦许多。