对于一个像我这样没有go背景的java程序员来说,使用go开发一个可用的程序的速度是肉眼可见的缓慢。
其难点不在于go语言本身,而是搭建整个工程链路的过程,即所谓的“配环境”。
本文主要讲述如何配出一个适合go开发的环境,以免有同事陷入困境时对我使出“您配吗”之类的大招。
安装brew:/bin/bash-c"$(curl-fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
安装完成后可以执行 brew--version 查看brew是否安装成功。
安装go编译器和调试器
brew install go
这将安装go的最新版本。在发文的这一刻,go的最新版本是1.22。
如果想要安装某个特定的go历史版本,可以使用brew install go@1.21
这将安装go1.21。
可以使用brew search go来查看当前brew仓库中可用的go版本列表。
安装完成后可以执行 go version 查看go是否安装成功。
brew install delvebrew install gdbdlv version 查看delve是否安装成功,执行 gdb --version 查看gdb是否安装成功。export GOFLAGS="-ldflags=-compressdwarf=false"export GOPROXY=https://goproxy.io,direct
export GOPRIVATE="你的私有gitlab地址.com/*,另外的私有gitlab地址.com/*" ,不要带http或https前缀
其次推荐idea安装go插件,但是要求idea是Ultimate版本。
不想花钱的话可以用vscode。Go 认准Microsoft出品。
your_app_name/├── cmd/│ ├── your_app_name/ # 应用程序的入口点,包含main包│ │ └── main.go # 应用程序的主函数所在│ └── ... # 如果有多个可执行文件,可以在这里添加├── internal/ # 可选,存放仅供本应用内部使用的包│ └── ... # 内部包结构,比如业务逻辑、工具函数等├── config/ # 配置文件或配置加载逻辑│ └── ... # 如yaml, toml, env等配置文件├── scripts/ # 构建、部署脚本等│ └── ... ├── tests/ # 单元测试、集成测试等│ └── ... ├── README.md # 项目说明文档├── go.mod # Go模块定义文件├── go.sum # Go模块依赖的校验和文件├── .gitignore # Git忽略规则文件└── ... # 其他必要的文件,如Dockerfile、Makefile等
your_library_name/├── internal/ # 可选,用于存放不希望对外暴露的包│ └── ... # 内部使用的包├── pkg/ # 存放库的公开对外接口。pkg 这一层可以省略,直接把源代码放在/your_library_name 目录下│ └── your_library_name/│ ├── your_library.go│ └── ... # 库的各个源文件├── tests/ # 单元测试、集成测试等│ └── ... ├── README.md # 项目说明文档,包括安装、使用方法等├── go.mod # Go模块定义文件├── go.sum # Go模块的校验和文件├── .gitignore # Git忽略文件列表└── ... # 其他必要的文件,如Dockerfile、Makefile等
go的依赖项通过 go.mod 文件描述,类似于java的 pom.xml 或py的 requirements.txt
go是源代码依赖,没有制品的概念。可以理解为每次编译时,go都会从git上面把所有的依赖项的源代码全部拉下来,放在一起编译。官方的源代码仓库就是github。
一个典型的go.mod示例如下:
module github.com/dapr/dapr^^^ 声明该模块的名字,类似于 groupId:artifactId 强烈建议在这里填写这个工程的实际可访问的git地址,否则会带来无穷无尽的麻烦go 1.21^^^ 指定要求的最低 go 版本,类似于 <java.version>1.8</java.version>require (^^^ 这里列出所有直接依赖项,类似于 <dependencies> contrib.go.opencensus.io/exporter/prometheus v0.4.2 github.com/PaesslerAG/jsonpath v0.1.1 github.com/PuerkitoBio/purell v1.2.1)require (^^^ 这一大片后面带有 // indirect 的是 go 自动生成的所有间接依赖,类似 mvn dependency:tree 的产出。 千万不要手工修改这部分。 cloud.google.com/go v0.110.10 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/datastore v1.15.0 // indirect cloud.google.com/go/iam v1.1.5 // indirect)replace (^^^ 这一段用于模块的名称和实际 git 地址不匹配的情况。 上面提到的无穷无尽的麻烦就是指,所有引用你的包的项目都需要使用 replace 来将你的包指向实际的 git 地址 github.com/toolkits/concurrent => github.com/niean/gotools v0.0.0-201512 replace github.com/dapr/components-contrib => ../components-contrib ^^^ 也可以将模块的路径指向本地,以便于二方包开发过程中进行调试)export GOPRIVATE="你的私有gitlab地址.com/*,另外的私有gitlab地址.com/*",这在第1节中已经讲过。
在代码中引用二方包
github.com/groupId/artifactIdgithub.com/apache/dubbo-go,在域名后面只有两段(groupId 和 artifactId)github.com/apache/dubbo-go/remotinggithub.com/apache/dubbo-go 部分是模块名,后面的 /remoting 表示这个包所处的具体git路径。import ( "github.com/go-playground/validator/v10")...func foo() { validator.Xxx()}
引用这个包时需要依赖包的路径
("github.com/go-playground/validator/v10"),但是在具体使用的时候使用的包名是validator,这中间看不出任何对应关系,只能去翻源代码。
个人建议:除了标准库的包之外,对所有import的包都自己定义一个别名,例如:
import ( validator "github.com/go-playground/validator/v10")
所以如果临时注释掉一条语句,再回头把注释去掉时,很有可能因为import语句被删掉了而编译不过。
个人建议:当临时需要禁用掉某些代码时,不要直接注释,要使用if false {} 给包起来。
if false { validator.Xxx() // 不要直接注掉,免得ide把对应的import语句也给删了}
go mod tidy,否则会编译失败。go不会在编译时帮你更新依赖。
顺便,受上一条规则影响,所有的import都必须是实际被使用的。如果代码中去掉了某个import,而这一行import是整个工程中唯一引用某个依赖的位置,那么就意味着这个依赖不再被需要。此时需要从go.mod中去掉该依赖并执行 go mod tidy,否则会导致编译不通过。
个人建议:直接把go mod tidy命令放在编译脚本中,每次编译时都执行一下。下面【编译】一节会详细解释。
go mod tidy 之后都要记得检查一下go.mod文件,以免被go mod tidy命令改坏。gitlab.mycompany.com/${groupId}/${artifactId}
require gitlab.mycompany.com/${groupId}/${artifactId} v$tagrequire gitlab.mycompany.com/shop/libaaa v0.3.6-rc9require gitlab.mycompany.com/${groupId}/${artifactId} v0.0.0-notexistreplace gitlab.mycompany.com/${groupId}/${artifactId} => gitlab.mycompany.com/${groupId}/${artifactId} v0.0.0-${branch}
例如:require gitlab.mycompany.com/shop/libaaa v0.0.0-notexistreplace gitlab.mycompany.com/shop/libaaa => gitlab.mycompany.com/shop/libaaa v0.0.0-develop2go get -u(类似于mvn -U命令)更新所有snapshot。
gitlab.mycompany.com/${groupId}/${artifactId}沧海月明珠有泪,蓝田日暖玉生烟。此情可待成追忆,只是当时已惘然。 —— (唐)李商隐go build。它有很多参数,非常灵活,但是每次编译都把完整命令全部敲一遍的话,精神方面可能就有点问题了。这个时候我们需要用make来简化构建工作,这又是一个大坑。validate compile package testdeploy 之类的阶段,直接敲 mvn clean build 即可让 maven 完成预先定义的动作。make clean build 之后要执行什么操作都需要在Makefile中手工声明。这份Makefile应当放在工程根目录下,文件名即为 Makefile 八个字母,注意大小写。
# 定义一些常量APPNAME := your_application_nameBINDIR := /build/binGOBIN := $(shell go env GOPATH)/binexport GO111MODULE := on# 目标: 清理clean: @rm -rf $(BINDIR)/* @echo "Cleanup completed."# 目标: 构建build: go_mod_tidy compile# 执行go mod tidygo_mod_tidy: @go mod tidy @echo "go mod tidy completed."# 编译项目compile: @mkdir -p $(BINDIR) @go build -o $(BINDIR)/${APPNAME} @echo "Build completed."# 使用帮助信息help: @echo "Usage:" @echo "make build - 整理模块依赖,编译项目,将产出物放到 /build/bin 目录下" @echo "make clean - 清理构建产物".PHONY: clean build go_mod_tidy compile help
调试
{ "version": "0.2.0", "configurations": [ { "name": "Launch Go Program with Args", "type": "go", "request": "launch", "mode": "debug", "program": "${fileDirname}", // 当前文件所在的目录作为工作目录,如果你需要指定特定的可执行文件路径,可以改为该路径 "args": ["arg1", "arg2", "--option=value"], // 在这里添加你的命令行参数 "env": {"env1":"env_value1", "env2":"env_value2"}, // 可以在这里添加环境变量,如果需要 "showLog": true, // 是否在调试控制台显示Delve的日志,默认为false "trace": "verbose" // 设置为"verbose"可以获取更详细的调试信息,根据需要调整 } ]}
-ldflags=-compressdwarf=false,这个参数用于在编译产物中保留debug信息。dlv exec xxxx -- 要传给xxx的命令行参数,要记得它调试的是编译出来的可执行文件。
此时可以敲命令与程序进行交互,常用的命令如下:
dlv debug xxx.go 的语法,可以直接指定源文件,此时它会调用go编译器把这个文件编译出来之后再去调试。对于特别小的工程来说较为方便。
打包
面向ChatGPT老师教程
Q: 我需要以《面向chatgpt老师编程》为题写一篇几百字的短文,向同事们介绍如何借助您的知识迅速掌握go的编程技能,请帮我提供一份草稿
A: 《面向ChatGPT老师编程:快速掌握Go语言技能》
在技术日新月异的今天,Go语言以其简洁的语法、高效的并发模型和卓越的性能,在众多编程语言中脱颖而出,成为众多开发者的新宠。但对于初学者而言,如何高效、系统地学习Go,快速掌握其精髓,却是一个值得探讨的话题。在此,我们引入一位特别的“老师”——ChatGPT,来指导我们如何在编程之旅中乘风破浪,迅速提升Go语言技能。
第一步:基础搭建,从Hello World开始
与ChatGPT老师的对话,不妨就从最经典的“Hello, World!”程序开始。通过编写这个简单的程序,ChatGPT不仅能帮你熟悉Go的基本语法,还能深入浅出地解释背后的工作原理,比如包的导入、函数定义等基础概念。
第二步:深入理解并发模型
Go语言的一大亮点是其内置的goroutine和channel机制,这对于构建高性能的并发服务至关重要。通过向ChatGPT提出具体场景的问题,如“如何使用goroutine处理大量并发请求?”或“如何利用channel实现数据安全共享?”,ChatGPT能以实例结合理论的方式,让你轻松掌握并发编程的核心技巧。
第三步:实战演练,项目驱动学习
理论知识虽好,但没有实践的支撑,就如同空中楼阁。不妨向ChatGPT请教一些小型实战项目,如编写一个简易的Web服务器、实现一个文件上传下载服务等。ChatGPT不仅会提供代码示例,还会引导你思考如何优化代码结构、提高执行效率,让你在实践中深刻理解Go的高级特性。
第四步:调试与优化,进阶之道
遇到bug怎么办?性能瓶颈如何突破?ChatGPT在这些方面同样能给予帮助。你可以询问如何使用Go的调试工具(如delve)进行代码调试,或是如何利用pprof进行性能分析。通过ChatGPT的指导,你将学会如何高效地定位问题并进行性能调优,让代码更加健壮、高效。
第五步:持续学习,紧跟Go生态发展
Go语言的生态系统不断发展壮大,新的库和框架层出不穷。ChatGPT可以帮助你追踪最新的Go语言动态,推荐优秀的开源项目和文章,让你的学习之路永不止步。
总之,ChatGPT不仅是你的编程助手,更是你学习Go语言道路上的良师益友。它以无尽的知识库、耐心的解答态度,帮助你快速成长,从Go语言新手进化为高手。在这个过程中,记得多提问、多实践,与ChatGPT老师一起探索Go语言的无限可能。
微信扫一扫
关注该公众号