Go 语言进阶与依赖管理 | 青训营笔记

89 阅读4分钟

0003.Go 语言进阶与依赖管理 | 青训营笔记

[TOC]

一、语言进阶

1.并发 VS 并行

1多线程程序在一个核的cpu上运行 2多线程程序在多个核的cpu上运行

==Go 可以充分发挥多核优势,高效运行==

2.Goroutine

goroutine是Go并发设计的核心,也叫协程,它比线程更加轻量,因此可以同时运行成千上万个并发任务。

不仅如此,Go语言内部已经实现了goroutine之间的内存共享,它比线程更加易用、高效和轻便。

来看看线程与协程的区别:

==协程==:用户态,轻量级线程,==栈 KB级别== ==线程==:内核态,线程跑多个协程,==栈MB级别==

在 Go 语言中,有一个关键字 go 可以创建一个新的协程。你可以在任意一个可执行的 Go 函数前加上 go 关键字即可创建一个新协程。

2.CSP (Communicating Sequentisl Procesees)

提倡通过==通信共享内存==而不是通过共享内存而实现通信

3.Channel

make(chan 元素类型,[缓冲大小])

  • 无缓冲通道make(chan int)
  • 有缓冲通道make(chan int,2)

4.并发安全 Lock

5.WaitGroup

==计数器== 开启协程+1: 执行结束-1;主协程阻塞直到计数器为0.

6.Goroutine 小结

优点:

1、开销小

POSIX的thread API虽然能够提供丰富的API,例如配置自己的CPU亲和性,申请资源等等,线程在得到了很多与进程相同的控制权的同时,开销也非常的大,在Goroutine中则不需这些额外的开销,所以一个Golang的程序中可以支持10w级别的Goroutine。

每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少(goroutine:2KB ,线程:8MB)

2、调度性能好

在Golang的程序中,操作系统级别的线程调度,通常不会做出合适的调度决策。例如在GC时,内存必须要达到一个一致的状态。在Goroutine机制里,Golang可以控制Goroutine的调度,从而在一个合适的时间进行GC。

在应用层模拟的线程,它避免了上下文切换的额外耗费,兼顾了多线程的优点。简化了高并发程序的复杂度。

缺点:

协程调度机制无法实现公平调度。

二、依赖管理

1.背景

  • 工程项目不可能基于标准库0-1编码搭建
  • 管理依赖库

2.Go依赖管理演进

GOPATH→Go Vendor→Go Module

  • 不同环境 (项目)依赖的版本不同
  • 控制依赖库的版本

3.GOPATH

环境变量$GOPATH

  • 项目代码直接依赖 src下的代码
  • go get 下载最新版本的包到 src目录下

在 GOPATH 的 $GOPATH/src 下进行 .go 文件或源代码的存储,我们可以称其为 GOPATH 的模式,这个模式拥有一些弊端。

  • A. 无版本控制概念. 在执行go get的时候,你无法传达任何的版本信息的期望,也就是说你也无法知道自己当前更新的是哪一个版本,也无法通过指定来拉取自己所期望的具体版本。
  • B.无法同步一致第三方版本号. 在运行 Go 应用程序的时候,你无法保证其它人与你所期望依赖的第三方库是相同的版本,也就是说在项目依赖库的管理上,你无法保证所有人的依赖版本都一致。
  • C.无法指定当前项目引用的第三方版本号. 你没办法处理 v1、v2、v3 等等不同版本的引用问题,因为 GOPATH 模式下的导入路径都是一样的,都是github.com/foo/bar。

4.Go Vendo

  • 项目目录下增加 vendor 文件,所有依赖包副本形式放在 $ProjectRoot/vendor
  • 依赖寻址方式: vendor => GOPATH

通过每个项目引入一份依赖的副本解决了多个项目需要同一个 package依赖的冲突问题。

Go Vendor的弊端:

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

5. GoModule

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

终极目标: 定义版本规则和管理项目依赖关系

6.依赖管理三要素

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

7.依赖配置- go.mod

module example/project/dpp   依赖管理基本单元
go 1.16         原生库
reguire(        单元依赖
example/libl v1.0.2
example/lib2 v1.0.0 // indirect
example/lib3 v0.1.0-20190725025543-5a5fe074e612
example/lib4 v0.0.0-20180306012644-bacd9crefldd /y indirect
example/lib5/v3 v3.0.2
example/lib6 v3.2.0+incompatible
)


语义化版本
${MAJOR}.${MINOR}.${PATCH}
V1.3.0
V2.3.0

基于 commit 伪版本
wx.0.0-yyyymmddhhmmss-abcdefgh1234
v0.0.0-20220401081311-c38fb59326b7
v0.0.0-20201130134442-10cb98267c6c

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

依赖配置- indirect

依赖配置- incompatible

8.依赖分发

回源:

  • 无法保证构建稳定性 增加/修改/删除软件版本

  • 无法保证依赖可用性 删除软件

  • 增加第三方压力 代码托管平台负载问题

依赖分发-Proxy

依赖分发-变量 GOPROXY GOPRQXY="httpe://proxy1.cn, proxy2.cn ,direct" 服务站点URL列表,“direct"表示源站

9.工具

工具-go get:

go get example.org/pkg

  • 默认 @update
  • 删除依赖 @none
  • tg版本,语义版本 @v1.1.2
  • 特定的commit @23dfdd5
  • 分支的最新commit @master

工具-go mod:

go mod

  • 初始化,创建go.mod文件 init
  • 下载模块到本地缓存 download
  • 增加需要的依赖,删除不需要的依赖 tidy