Golang具有一套可以构建和处理go源代码的程序,作为命令行工具,这些程序也并非直接运行,而是由go程序调用。运行这些程序最常见的方式是作为go程序的子命令,例如 go fmt,该命令的运行方式是由go程序使用适合于包级处理的参数调用底层二进制文件,对go源代码的完整包进行操作;这些程序也可以作为独立的二进制文件运行,使用go tool子命令(如go tool cgo)使用未修改的参数;某些命令(如pprof)只能通过go tool子命令访问。go命令行作为日常开发的工具,能大大方便编译、调试、诊断程序性能等工作,本文是对常见go命令行工具使用的汇总


1 go命令

go命令管理go源代码并运行此处列出的其他命令。在终端输入go,会打印出如下信息

Go实战之常用命令行工具_环境变量

常用命令详解

​go bug​

编译包和依赖项

编译go文件

build编译由导入路径命名的包及其依赖项,但不安装编译结果。如果build的参数是来自单个目录的.go文件列表,则build会将它们视为指定单个包的源文件列表;编译包时,build将忽略以"_test.go"结尾的文件

​go build ed.go rx.go​​go build unix/sam​​.exe​

Go实战之常用命令行工具_json_02

删除对象文件和缓存文件​ 用法如下

​go build​

Go实战之常用命令行工具_json_03

go clean

展示包或符号的文档​ 用法

Go实战之常用命令行工具_环境变量_04

go doc

打印go环境信息​ 用法

默认情况下,env将信息打印为shell脚本(在windows上,是批处理文件)。如果一个或多个变量名作为参数给定,env将在其自己的行上打印每个命名变量的值

Go实战之常用命令行工具_json_05

go env

更新包以使用新的APIs​ 使用方法

fix对由导入路径命名的包运行go fix命令。fix查找使用旧api的go程序,并重写它们以使用新的api; 更新到新的go版本后,fix将帮助您对程序进行必要的更改。使用

如果没有显式路径,fix将读取标准输入并将结果写入标准输出; 如果命名路径是一个文件,fix会就地重写命名文件; 如果命名路径是一个目录,fix重写该目录中的所有.go文件树

gofmt(重新格式化)源码包

类似于C中的lint, 统一代码风格和排版

常用命令如: go fmt

​go fmt -l -w​

-n标志打印将要执行的命令; -x标志在执行命令时打印命令

​go doc cmd/gofmt​

通过处理源码生成go文件​ 使用方法

生成(必须显示运行)由现有文件中的指令描述的运行命令。这些命令可以运行任何进程,但其意图是创建或更新go源文件。它还接受标准的构建标志,包括-v,-n和-x: -v标志在处理过程中打印包和文件的名称; -n标志打印将要执行的命令; -x标志在执行命令时打印命令

将依赖项添加到当前模块并安装它们

​git repo​

常用命令如: go get -u github.com/go-sql-driver/mysql (从github上获

取mysq|的driver并安装至本地)

get解析并向当前开发模块添加依赖项,然后构建并安装它们

第一步,要解决添加哪些依赖项 对于每个命名的包或包模式,get必须决定使用相应模块的哪个版本。默认情况下,get会查找最新的标记版本,如v0.4.5或v1.2.3; 如果没有标记的发布版本,get将查找最新的标记的预发布版本,如v0.0.1-pre1; 如果根本没有标记的版本,get将查找最新的已知提交。如果在更高版本(例如,比最新版本更新的预发行版)中还不需要该模块,则get将使用它查找的版本。否则,get将使用当前所需的版本

第二步是下载(如果需要)、构建和安装命名包 如果参数命名的是模块而不是包(因为模块的根目录中没有go源代码),则会跳过该参数的安装步骤,而不会导致构建失败

Go实战之常用命令行工具_环境变量_06

go get

编译安装包和依赖项

也是编译,与build最大的区别是编译后会将输出文件打包成库放在pkg下

可执行文件安装在由GOBIN环境变量命名的目录中,如果未设置GOPATH环境变量,则默认为[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9JI2pY2J-1605846310736)(https://static.studygolang.com/191006/b46ed6e425e622ef35f3996ab983ba8d)]HOME/go/bin; [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9YSEUri9-1605846310739)(https://static.studygolang.com/191006/51c9c2363e64aa6a344033bacaebae24)]GOROOT/bin或[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ys8tIYlG-1605846310740)(https://static.studygolang.com/191006/7a85f308b8441edf74327ac6772316fa)]GOBIN中

禁用module-aware模式时,其他软件包将安装在目录[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0oJ2heSZ-1605846310741)(https://static.studygolang.com/191006/39934a7eaf8a988547a51602765b9fad)]GOOS_$GOARCH中; 启用module-aware模式时,将构建并缓存其他包,但不安装。-i标志还安装命名包的依赖项

列出包或模块 使用方法

list列出了命名包,每行一个。最常用的标志是-f和-json,它们控制为每个包打印的输出的形式。-f标志

使用包模板的语法指定列表的备用格式,传递给模板的结构形如

下面记录的其他列表标志控制更具体的细节

Go实战之常用命令行工具_json_07

go list

模块维护​ go mod提供对模块操作的访问,使用方法如下所示

注意,所有go命令都内置了对模块的支持,而不仅仅是“go mod”。例如,日常添加、删除、升级和降级依赖项都应该使用“go get”完成。command列表如下

Go实战之常用命令行工具_json_08

go mod

下载模块到本地缓存 使用方法如下

下载命名模块,可以是选择主模块依赖项的模块模式,也可以是path@version格式的模块查询。如果没有参数,download将应用于主模块的所有依赖项

go命令将在正常执行期间根据需要自动下载模块。“go mod download”命令主要用于预填充本地缓存或计算go模块代理

从工具或脚本编辑go.mod​ 使用方法如下

编辑提供了一个用于编辑go.mod的命令行接口,主要用于工具或脚本。它只读取go.mod,不查找有关模块的信息。默认情况下,edit读取和写入主模块的go.mod文件,但可以在编辑标志后指定其他目标文件。编辑标志指定一些列编辑操作

Go实战之常用命令行工具_环境变量_09

go mod edit

打印模块需求图​ 使用方法如下

图以文本形式打印模块需求图(应用的替换)。输出中的每一行都有两个空格分隔的字段:一个模块及其一个需求。每个模块都被标识为path@version格式的字符串,但主模块没有@version后缀

在当前目录下初始化新的模块​ 使用方法

init初始化并将新go.mod写入当前目录,实际上创建一个新的模块,该模块以当前目录为根

添加缺失的模块并移除未使用的模块​ 使用方法如下

tidy确保go.mod与模块中的源代码匹配。它增加了构建当前模块的包和依赖项所需的任何缺少的模块,并且移除未使用的模块。它还将添加任何缺少项的go.sum并删除任何不必要的项

-v标志导致tidy将有关已删除模块的信息打印到标准错误

生成依赖项的自动生成副本​ 使用说明

vendor重置主模块的vendor目录,以包含构建和测试所有主模块包所需的所有包。它不包括vendored的测试代码

-v标志将vendor提供的模块和包的名称打印为标准错误

验证依赖项是否具有预期内容​ 使用说明

验证检查当前模块(存储在本地下载的源缓存中)的依赖项在下载后是否未被修改。如果所有模块都未修改,验证打印“all modules verified.”,否则它报告哪些模块已被更改,并导致“go mod”以非零状态退出

解释为什么需要包或模块​ 使用说明

why在导入图中显示从主模块到列出的每个包的最短路径。如果给定了-m标志,why将参数视为模块列表,并在每个模块中找到指向任何包的路径。默认情况下,why查询与“go list all”匹配的包的graph,其中包括可访问包的测试。-vendor标志导致排除依赖项测试的原因

编译并运行go程序​ 使用说明

run编译并运行名为main的go包。通常,包被指定为来自单个目录的.go源文件列表,但它也可能是与单个已知包匹配的导入路径、文件系统路径或模式,如“go run.”或“go run my/cmd”

​xprog a.out arguments...​

测试包

运行当前包目录下的tests

​go test​​go test -v​
​go test​​*_test.go​​_​​_test.go​​.​
​xxx_ test.go​
​xxx _test.go​


Go并未强制xxx的部分必须是要测试的文件名,只是最佳实践。


​_test​​testdata​

go test的两种不同运行模式


  • 本地目录模式​,在没有包参数(例如,“go test”或“go test-v”)的情况下调用go test时发生。在此模式下,go test编译当前目录中包的源码和测试文件,然后运行生成的测试二进制文件。在此模式下,将禁用缓存。在包测试完成后,go测试打印一个显示测试状态的“摘要行”(“OK”或“FAIL”)、包名和测试的时间
  • 包列表模式​,在使用显式包参数(例如“go test math”、“go test ./…”、“go test”)调用go test时发生。在此模式下,go test编译并测试命令行中列出的每个包。如果包测试通过,则go测试只打印最终的“OK”摘要行。如果包测试失败,go test将打印完整的测试输出。如果使用-bench或-v标志调用,go测试打印完整的输出,甚至通过传递包测试,以便显示请求的基准测试结果或冗长日志记录。所有列出的包测试完成并打印输出后,如果存在任何一个包测试失败,go test将打印最终“FAIL”状态

仅在包列表模式下,go test缓存成功的包测试结果,以避免不必要的重复运行测试。当测试结果可以从缓存中恢复时,go test将重新显示以前的输出,而不是再次运行测试二进制文件。当发生这种情况时,go测试打印(缓存)代替汇总行中测试花费的时间

Go实战之常用命令行工具_环境变量_10



​TestXxx​


test case的入参为t *testing.T或b *testing.B



t.Errorf为打印错误信息,并且当前test case会被跳过



Go的test不会保证多个TestXxx是顺序执行,但是通常会按顺序执行。推荐使用 subTest
Go实战之常用命令行工具_二进制文件_11
使用t.Run来执行subtests可以做到控制test输出以及test的顺序



TestMain 入口 testcase
使用TestMain作为初始化test,并且使用m.Run()来调用其他tests可
以完成一些需 要初始化操作的testing,比如数据库连接,文件打开,
REST服务登录等
如果没有在TestMain中调用m.Run()则除了TestMain以外的其他tests
都不会被执行
Go实战之常用命令行工具_二进制文件_12



Test 之benchmark


  • benchmark函数一般以Benchmark开头
  • benchmark的case一般会跑b.N次,而且每次执行都会如此
  • 在执行过程中会根据实际case的执行时间是否稳定会增加b.N的次数
    以达到稳态

运行特定的go工具

tool运行由参数标识的go tool命令,-n标志不带参数地打印已知工具的列表

go版本信息​ 使用说明

go version报告用于构建每个可执行文件的go版本。如果命令行中没有命名文件,则go version将打印其自己的版本信息; 如果目录被命名,go version将遍历该目录,递归地查找识别的go二进制文件并报告它们的版本。默认情况下,go version不会报告在目录扫描期间发现的无法识别的文件

-v标志 报告无法识别的文件

-m标志 go version在可用时打印每个可执行文件的嵌入模块版本信息。在输出中,模块信息由版本行后面的多行组成,每行由一个前导制表符缩进

报告包中可能出现的错误​ 使用方法说明

vet对由导入路径命名的包运行go vet命令

2 构建模式

====

​go build​​go install​

Go实战之常用命令行工具_环境变量_13

build modes

3 环境变量

====

​go env ​​go env -w =

通用环境变量

Go实战之常用命令行工具_json_14

go env general-purpose

用于cgo的环境变量

Go实战之常用命令行工具_二进制文件_15

go env cgo

特定于体系结构的环境变量

Go实战之常用命令行工具_二进制文件_16

go env arch

特殊用途的环境变量

Go实战之常用命令行工具_二进制文件_17

go env special

​go env​

Go实战之常用命令行工具_json_18

go env additional

4 逻辑和性能诊断工具

go生态系统提供了大量的API和工具来诊断go程序中的逻辑和性能问题,这一节是对这些可用工具的总结

诊断解决方案可以分为以下几类


  • 性能分析​ 这类工具用于分析go程序的复杂性和成本,例如通过它的内存使用和频繁调用的函数来标识go程序的开销部分
  • 追踪​ 是一种在调用或用户请求的整个生命周期中检测代码以分析延迟的方法,它提供了每个组件对系统的总延迟概述,可以跨越多个go进程进行
  • 调试​ 允许我们暂停go程序并检查其执行。程序状态和流程可以通过调试来验证
  • 运行时统计和事件​ 运行时统计和事件的收集和分析为go程序的健康提供了高层次的概述。尖峰/度量的监控指标有助于识别吞吐量、利用率和性能的变化

Tips 一些诊断工具可能相互干扰。例如,精准内存剖析可能影响CPU性能分析的准确性、goroutine阻塞分析会影响调度器跟踪。因此,单独使用工具获取更精确的信息

性能分析​ 对于识别昂贵的或频繁调用的代码段非常有用。go运行时以pprof可视化工具提供所期望格式的性能分析数据。在测试期间,也可以通过go test或net/http/pprof包中提供的endpoints来收集性能分析数据

由runtime/pprof预定义配置文件


  • cpu 决定了程序在actively状态(而不是在sleeping或waiting I/O)时花费的cpu时间
  • heap 报告内存分配示例;用于监视当前和历史内存使用情况,并检查内存泄漏
  • threadcreate 报告程序中引导创建新线程的部分
  • goroutine 报告当前所有goroutine的堆栈跟踪
  • block 显示goroutine阻塞等待同步原语(包括timer channels),block profile默认为未启用状态,使用runtime.SetBlockProfileRate可以启用它
  • mutex 报告锁争用。当您认为由于互斥争用导致CPU未充分利用时,请使用此profile。默认情况下mutex profile处于未启用状态,通过runtime.SetMutexProfileFraction可以启用它

其他分析器​ 在Linux上,可以使用perf工具分析go程序,perf可以配置和解开cgo/SWIG代码和内核,因此可以深入了解native/kernel性能瓶颈; 在MacOS上,可以使用Instruments分析go程序

生产服务性能分析​ 分析生产中的程序是安全的,但是启用某些profile(例如CPU profile)会增加成本。您可能想要定期分析生产服务性能问题,特别是在具有单个进程的多个副本的系统中,周期性地随机挑选一个副本是安全的选择。选择一个生产进程,每隔Y秒分析并保存它的结果以进行可视化分析;然后定期重复。可以手动和/或自动检查结果以发现问题。性能收集可能相互干扰,因此建议每次只收集单个概要文件

​go tool pprof​
​/custom_debug_path/profile​

追踪​ 这是一种在调用链的整个生命周期中检测代码以分析时延的方法,go提供golang.org/x/net/trace包作为每个go节点的最小跟踪后端,并用一个简单的dashboard提供一个最小的检测库,go还提供了一个可执行的Tracer来追踪间隔期间内运行时事件。追踪可以为我们提供


  • 检测并分析go进程中的应用程序延迟
  • 在一个很长的调用链中测量特定调用的成本
  • 找出利用率并改进性能。没有跟踪数据,瓶颈并不总是显而易见

在单体系统中,从程序的构建块收集诊断数据相对容易,所有模块都在一个进程中,并共享公共资源来报告日志、错误和其他诊断信息。一旦系统由单体进程扩展到分布式微服务,就很难定位从前端Web服务器到所有后台的调用,以及响应返回给用户。这也是分布式追踪在测试和分析生产系统方面发挥重要作用的地方

分布式追踪是一种在用户请求的整个生命周期中检测代码以分析延迟的方法。当系统是分布式的,并且传统的分析和调试工具无法扩展时,您可能希望使用分布式跟踪工具来分析用户请求和RPC的性能。分布式追踪系统是我们能够


  • 在大型系统中检测并分析应用程序延迟
  • 跟踪用户请求生命周期内的所有RPC,并查看仅在生产中可见的集成问题
  • 找出可以应用于我们系统的性能改进。在跟踪数据收集之前,许多瓶颈是不明显的

go生态系统为每个追踪系统提供了不同的分布式跟踪库和对后端透明的库

调试 调试是识别程序错误行为的过程,调试器允许我们了解程序的执行流程和当前状态。go用户主要使用以下调试器


  • **Delve ** 作为go编程语言的调试器,它支持go的运行时概念和内置类型。Delve正试图成为一个功能齐全的可靠的go程序调试器
  • GDB​ go通过标准go编译器和gccgo提供GDB支持。堆栈管理、线程和运行时包含不同于执行模型的方面,GDB可能混淆调试器,即使程序是用gccgo编译的。尽管GDB可以用来调试go程序,但它并不理想,可能会造成混乱

运行时统计和事件​ 运行时提供用户内部事件的统计和报告,以便在运行时级别诊断性能和使用问题。用户可以监视这些统计信息,以便更好地了解go程序的整体健康和性能。一些经常监视的统计数据和状态


  • runtime.ReadMemStats 报告与堆分配和垃圾收集相关的度量指标。内存统计对于监视进程消耗多少内存资源、进程是否可以充分利用内存以及捕获内存泄漏是有用的
  • debug.ReadGCStats 读取垃圾收集的统计信息。查看gc暂停上花费了多少资源是很有用的。它还报告了垃圾收集器暂停和暂停时间百分比的时间线
  • debug.Stack 返回当前堆栈跟踪,堆栈跟踪对于查看当前正在运行的goroutine的数量、它们正在执行的操作以及它们是否被阻止非常有用
  • debug.WriteHeapDump 暂停所有goroutine的执行并允许您将堆转储到文件。堆转储是在给定时间内go进程内存的快照。它包含所有分配的对象以及goroutine、finalizers等
  • runtime.NumGoroutine 返回当前goroutine的数目。可以监视该值以查看是否有足够的goroutine供使用,或检测goroutine泄漏

go附带运行时Execution Tracer来捕获大量运行时事件。调度、SysCall、垃圾回收、堆大小和其他事件由运行时收集,并可用于go工具跟踪可视化。Tracer可用于


  • 了解goroutine如何执行
  • 了解一些核心运行时事件,例如GC
  • 识别并行性差的执行

小结

go提供了丰富的命令行和工具,在日常运维开发中熟练使用命令/工具能够大大提升效率。本文是关于命令行和常用工具的简单总结,希望能对您有所帮助

参考

  • https://studygolang.com/articles/23764