「Go 语言上手-工程实践」

2,737 阅读4分钟

语言进阶

了解go高性能的本质以及golang为什么这么快

并发 vs 并行

  • 并发
    • 多线程程序在单核CPU上的执行 ,通过CPU时间片的快速切换去达到程序同时执行的效果。
  • 并行
    • 多线程程序在多核CPU上的执行,不行的程序运行在不同的核上,是正真意义上的并行(同时执行),并行是实现并发的一种手段。

go协程goroutine

较线程相比,协程是用户态的一种资源,创建代价比较小,协程栈在KB级别,协程的创建由go语言本身去实现;相反线程是内核态的一种资源,其创建和销毁的代价比较大,线程栈在MB级别。

协程通信

golang中提倡通过通信来共享内存,而不是通过共享内存来实现通信。如果要用共享内存实现通信的话就需要对其进行互斥操作。

image.png

Channel/通道

  • 创建通道的方式
// type表示元素类型,buffer_size表示缓冲区大小
make(chan type,[buffer_size])

//创建无缓冲通道
make(chan int)

//创建有缓冲通道
make(chan int, 4)
  • 无缓冲通道 vs 有缓冲通道

    • 无缓冲的通道又称为阻塞的通道,无缓冲的通道只有在有人接收值的时候才能发送值。就像你住的小区没有快递柜和代收点,快递员给你打电话必须要把这个物品送到你的手中,简单来说就是无缓冲的通道必须有接收才能发送。
    • 使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道。

    • 有缓冲通道就像你小区的快递柜只有那么个多格子,格子满了就装不下了,就阻塞了,等到别人取走一个快递员就能往里面放一个。

WaitGroup

在代码中生硬的使用time.Sleep肯定是不合适的,因为我们不能精确的知道程序执行的一个时间。go语言中可以使用sync.WaitGroup来实现并发任务的同步。

sync.WaitGroup有以下几个方法:

方法名功能
(wg * WaitGroup) Add(delta int)计数器+delta
(wg *WaitGroup) Done()计数器-1
(wg *WaitGroup) Wait()阻塞直到计数器变为0

sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。

依赖管理

  1. 不同环境/项目依赖的版本不同
  2. 控制依赖库的版本

GOPATH

image.png
  • bin:项目编译的二进制文件
  • pkg:项目编译的中间产物,可加速编译

GOPATH弊端

image.png

如上图所示,A和B依赖某一pkg的不同版本,存在的问题:无法实现pkg多版本控制

GO Vendor

在项目目录下增加vendor文件夹,所有依赖包副本形式放在$project/vendor下,优先从该文件夹下去获取依赖

Go Vendor弊端

image.png

如上图所示,项目A同时依赖package B和package C,package B 和package C同时依赖package D(D有V1和V2两个版本),利用vendor的方式就不能控制V1和V2的选择(本质上是因为vendor还是依赖的项目的源码),于是就是出现下面问题:

  • 无法控制依赖的版本
  • 更新项目又可能出现依赖冲突,导致编译出错

Go Module

定义版本规则和管理项目依赖关系

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

依赖管理三要素

  • 配置文件,描述依赖 go.mod
  • 中心仓库管理依赖库 Proxy
  • 本地工具 go get/mod

go.mod

image.png
依赖标识:
[Module Path][Version/Pseudo-version]

依赖配置-version

  • 语义化版本 ${MAJOR}.${MINOR}.${PATCH}

MAJOR:大版本,不同的MAJOR是可以不兼容的 MINOR:新增一些功能函数,在大版本基础上是兼容的 PATCH:代码bug修复

v1.3.0

v2.3.0

  • 基于commit伪版本

版本前缀(和语义化版本一致)-时间戳(提交本次commit的时间戳)-提交commit12位的hash码

v0.0.0-20180306012644-bacd9c7ef1dd

依赖配置-indirect

image.png

存在: A->B->C

A直接依赖B,A间接依赖C,在go.mod中对于没有直接依赖的标识为indirect,如上图所示

依赖配置-incompatible

image.png

依赖分发-Proxy

表示项目依赖应该去哪里下载

image.png

直接使用第三方平台的话会存在以下问题:

  • 无法保证构建稳定性,比如存在增加、修改、删除软件版本的话,后续构建可能会存在依赖找不到了,导致构建失败
  • 无法保证依赖的可用性,比如依赖仓库被删除了
  • 增加第三方压力

为了解决以上问题,就有了GOPROXY

image.png

GOPROXY是一个服务站点,会缓存源站中的软件内容,缓存的软件版本不会改变,并且源站软件删除之后依然可用,从而实现稳定和可靠的依赖分发。


// 项目依赖查找顺序proxy1->proxy2->direct
// 如果在前面都没有找见,则直接到源站查找
GOPROXY="https://proxy1.cn,https://proxy2.cn,direct"

工具 go mod

go mod init //初始化,创建go.mod文件

go mod download //下载模块到本地缓存

go mod tidy // 增加需要的依赖,删除不需要的依赖

工具 go get


go get example.org/pkg

@update //获取默认的依赖包
@none //删除依赖
@v1.2.2 //获取对应语义版本的依赖包
@23dfff3 //获取对应commit的依赖
@master //获取最新分支的依赖包

//比如
go get code.byted.org/tmq/tmq@v1.5.30

本文正在参加技术专题18期-聊聊Go语言框架