2

搭建可维护的 Golang 开发环境

 1 year ago
source link: https://soulteary.com/2022/07/04/build-a-maintainable-golang-development-environment.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

搭建可维护的 Golang 开发环境

2022年07月04日阅读Markdown格式8072字17分钟阅读

本篇文章将聊聊如何快速搭建 Linux 环境中的 Golang 开发环境。

《基础篇》的内容中,我们聊过了如何基于 Ubuntu 22.04 搭建基础的 Linux 学习环境。接下来的文章里,我们先来聊聊如何在 Linux 环境中,快速安装配置各种可维护的语言环境。

在开始聊如何做之前,我们首先要了解为什么要这么做。

我知道有不少同学有安装好环境之后,然后持续使用一个环境,并“天长地久”的用下去的习惯。这样做的好处是“看起来简单省心”,但实际上却埋藏了许多隐患,举几个常见的例子:

  • 同事说代码、程序有问题,跑不起来,你说程序在你这边是正常的,你觉得他的使用方式不对,他觉得你的程序“兼容性”/“健壮性”有问题。
  • 当你想把半年、一年前的项目跑起来,发现运行的时候多了非常多的“警告”,甚至是“报错”,尤其是当你换了一台电脑的时候。
  • 项目扩充人手,你的同事也需要配置一套环境,你和他折腾的半天,虽然有着重重困难,什么版本不对,配置散落在系统的各个“犄角旮旯”等等,但是你们最终克服了困难,并加深了革命友情。
  • 项目遇到大版本升级,因为一些历史原因,你需要同时使用两个不同的语言版本来做调试,不同版本存在兼容性问题(包括依赖兼容性问题),你的本地环境是升级还是不升呢。

解决这个问题的最佳方案有两个:

  • 尽可能简化你的环境,简化环境依赖(因为项目的多样性和复杂性,这有一些难)。
  • 尽可能参考“基础架构即代码(IaC)”的思想去维护我们自己的开发环境,让我们所使用的内容,尽可能配置化,透明化,可复现

它除了能够完成 golang 开发环境的快速安装之外,还能够保障多个版本的 golang 共存,不同版本的软件依赖包都保持正常工作。并且,它的实现和社区大名鼎鼎的 nvm-sh/nvm 、shyiko/jabba 是一致的,都是由 BASH 编写,和所需要管理的 Runtime 语言无关,能够更稳定的完成“管理工作”。

Golang 环境安装和配置使用

关于 Golang 的多版本管理和安装,我曾经写过两篇相关的内容,一篇是半年前的内容,分享如何对 “Golang 进行多版本管理”,另外一篇则是这篇的补充内容,分享如何针对 Mac M1 这类 ARM 设备使用 Golang 版本管理工具:《M1 芯片 Mac 上更好的 Golang 使用方案》

如果你希望了解本章节之外的实践内容,或者过程中的思考,可以翻阅上面两篇内容。本篇文章的重点在于如何快速安装和配置,所以就不再展开相关“折腾安装工具”的细节啦。

安装 Golang 版本管理工具:soulteary/gvm

关于 Golang 的安装和版本升降级,因为老牌开源软件 GVM (Go Version Manager)“年久失修”,所以我做了一个修正版:https://github.com/soulteary/gvm

想要正常使用这个工具,我们需要先完成工具的基础依赖的安装:

sudo apt install -y binutils bison gcc make

接着,执行下面的命令,通过网络获取安装脚本,然后在本地执行脚本完成安装。(如果你因为网络或其他原因,无法执行这条命令,可以使用下文中的替代方案):

curl -sSL https://github.com/soulteary/gvm/raw/master/binscripts/gvm-installer | bash

不出意外的话,你将会看到类似下面的日志输出,意味着工具就此刻已经安装好啦。

Cloning from https://github.com/soulteary/gvm.git to /home/soulteary/.gvm
No existing Go versions detected
Installed GVM v1.0.24

Please restart your terminal session or to get started right away run
 `source /home/soulteary/.gvm/scripts/gvm`

这里选择执行或者不执行 source /home/soulteary/.gvm/scripts/gvm 这条命令都可以(注意调整路径中的用户名),因为在接下来的文章中,我们将使用更靠谱的方式来将命令注册到我们所使用的 SHELL 环境中。

使用国内镜像来安装:soulteary/gvm

为了让安装过程更加顺利,我们可以使用从国内镜像下载包含安装脚本的仓库代码,然后直接执行安装脚本,来完成 gvm 这个开源软件的安装。

先使用 git clone 下载完整的软件仓库:

git clone https://gitcode.net/soulteary/gvm.git

指定 SRC_REPO 参数为国内镜像地址,然后运行安装脚本:

SRC_REPO=https://gitcode.net/soulteary/gvm.git bash gvm/binscripts/gvm-installer

当脚本运行完毕,我们将会看到上文中提到过的日志输出,此刻 gvm 就安装完毕啦。

Cloning from https://gitcode.net/soulteary/gvm.git to /home/soulteary/.gvm
No existing Go versions detected
Installed GVM v1.0.24

Please restart your terminal session or to get started right away run
 `source /home/soulteary/.gvm/scripts/gvm`

为了更方便的使用 gvm,我们还需要进行一些配置。

配置 gvm 加速 Golang 下载/切换

gvm 支持使用两种方式来下载 “Golang”,然而不论是“下载源码编译安装”,还是下载适合当前操作系统的“预编译好的二进制文件”,我们都需要访问官方地址。

为了避免下载过程中因为网络问题,出现下载慢,或者无法下载的情况,节约我们的时间,我们需要对 gvm 进行一些简单的配置。

我们可以在当前使用的 “SHELL” 的 “rc” 文件中(比如.bashrc 或者 .zshrc),添加下面的内容,来在当前的环境中让 gvm 命令生效,同时,让我们能够使用更快的下载源来下载我们所需要的 “Golang”:

export GO_BINARY_BASE_URL=https://golang.google.cn/dl/
[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"
export GOROOT_BOOTSTRAP=$GOROOT

在“rc” 文件中添加了上述内容后,需要重启终端会话,才能够让会话生效。你可以使用 CTRL+D 退出登录,然后再重新使用 SSH 进行终端连接或者直接在本地创建一个新的会话(具体怎么做,取决于你是如何开启的会话)。

为了让配置过程清晰透明,上面的三条命令,我们来依次看看上面的命令都“做了什么事情”。

export GO_BINARY_BASE_URL=https://golang.google.cn/dl/

命令中的 GO_BINARY_BASE_URL 变量,定义了我们将从何处下载 Golang 的二进制文件或源码压缩包进行安装。当然,你也可以将其替换为下面的任意一个。

# 官方地址
https://go.dev/dl/
# 官方国内镜像地址
https://golang.google.cn/dl/
# 阿里云镜像
https://mirrors.aliyun.com/golang/
# 中科大镜像
http://mirrors.ustc.edu.cn/golang/

接下来,我们来看看三条命令中看似最复杂的命令:

[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"

这条命令,是根据软件的实际安装情况来选择性加载 gvm。相比较前文中安装完毕 gvm 日志输出内容推荐我们直接使用 source 命令加载 gvm,这样可以更安全的执行命令,当且仅当 ~/.gvm 存在的时候才会加载程序,将 gvm 注册到你当前的 SHELL 环境中。

export GOROOT_BOOTSTRAP=$GOROOT

最后一条命令,则是为了确保 Golang 使用源码编译安装时,不会出错(golang 1.14后需要 ),感兴趣可以围观官方开源项目中的这个 issue

gvm 简明实用教程

gvm 是一个特别简单的命令,我们日常使用中其实只需要记得两个命令就好,第一个是 gvm install,第二个是 gvm use

假设我们想安装 Golang 最新版本 1.18.3,那么只需要执行下面的命令:

gvm install go1.18.3 -B

在执行完毕命令之后,稍等片刻,当我们看到 Installing go1.18.3 from binary source 这条日志输出结果后,就意味着 Golang 已经被下载完毕了。如果你希望使用编译源码的方式安装 Golang 的话,可以去掉上面命令中的-B 参数:

gvm install go1.18.3

虽然我们已经完成了 Golang 1.18.3 的安装,但是目前我们还不能直接使用它,需要再执行一条命令,将这个版本的 Golang “激活”:

gvm use go1.18.3 --default

在执行完命令之后,我们能够立刻看到类似 Now using version go1.18.3 的日志输出结果,接下来我们就可以随意的使用 go 这个命令了。

我们可以使用 go version 来验证刚刚下载的程序是否符合我们的诉求:

go version
go version go1.18.3 linux/amd64

未来如果 Golang 推出了新版本,我们想升级只需要按照上面的玩法,调整版本号,然后再执行一遍 installuse 命令就好了,是不是很简单!

当然,如果你只是想临时性的使用某个版本,比如 Golang 1.17 这个旧版本,可以稍微调整一下上面的命令,去掉 use 命令中的 --default 参数,只在当前 SHELL 会话中,让这个版本的 Golang 生效,随着我们关闭终端会话,Golang 的版本也会恢复到我们指定的默认版本,再也不需要担心系统环境混乱的问题啦。

gvm install go1.17 -B
gvm use go1.17

# 再次执行查看版本,可以看到版本号已经变化了
go version
go version go1.17 linux/amd64

配置 Golang 软件包镜像

在日常开发和学习过程中,我们更多的是使用 Golang 来初始化项目和下载必要的软件包依赖。所以如何快速的下载到各种软件包也很重要,好在 Golang 提供了软件包代理配置选项 GOPROXY,我们可以通过在 “SHELL” 的 “rc” 文件中配置这个参数来完成下载提速:

export GO111MODULE=on
export GOPROXY="https://goproxy.cn"

和上文中配置 gvm 一样,我们将上面的内容添加到所使用的 SHELL 的 “rc” 配置后,需要重新创建一个终端会话,让配置生效。

当然,你也可以将上面命令中使用的“镜像源”替换为下面任意一个:

# 由七牛云赞助的项目
goproxy.cn
# 阿里云
https://mirrors.aliyun.com/goproxy/
# 华为云
https://repo.huaweicloud.com/repository/goproxy/
# “一家的”
goproxy.io
proxy.golang.com.cn

这里有一个题外话,初见“goproxy”的两个域名的时候,觉得域名十分相似,一番搜索,发现这两个域名虽然归属不同的开发者在维护,但是它们之间确实有一段缘分:“goproxy.io 和 goproxy.cn 的关系”

Golang 环境验证:GoJieba

在完成环境配置之后,我们使用一个比较实用的 Golang 项目(https://github.com/yanyiwu/gojieba),来验证环境是否“好用”。

随便创建一个程序目录,然后在其中创建一个名为 main.go 的文件,引用 “gojieba”,并对一些句子和词汇进行处理:

package main

import (
	"fmt"
	"strings"

	"github.com/yanyiwu/gojieba"
)

func main() {
	var s string
	var words []string
	use_hmm := true
	x := gojieba.NewJieba()
	defer x.Free()

	s = "北京西站南广场东"
	words = x.CutAll(s)
	fmt.Println(s)
	fmt.Println("全模式:", strings.Join(words, "/"))

	words = x.Cut(s, use_hmm)
	fmt.Println(s)
	fmt.Println("精确模式:", strings.Join(words, "/"))
	s = "向量数据库"
	words = x.Cut(s, use_hmm)
	fmt.Println(s)
	fmt.Println("精确模式:", strings.Join(words, "/"))

	x.AddWord("向量数据库")
	s = "向量数据库"
	words = x.Cut(s, use_hmm)
	fmt.Println(s)
	fmt.Println("添加词典后,精确模式:", strings.Join(words, "/"))

	s = "前门到了,请您后门下车"
	words = x.Cut(s, use_hmm)
	fmt.Println(s)
	fmt.Println("新词识别:", strings.Join(words, "/"))

	s = "小明先去了北京西站南广场东,然后又去了南京东路北大街西"
	words = x.CutForSearch(s, use_hmm)
	fmt.Println(s)
	fmt.Println("搜索引擎模式:", strings.Join(words, "/"))

	s = "朝阳区三里屯优衣库"
	words = x.Tag(s)
	fmt.Println(s)
	fmt.Println("词性标注:", strings.Join(words, ","))

	s = "元宇宙"
	words = x.Tag(s)
	fmt.Println(s)
	fmt.Println("词性标注:", strings.Join(words, ","))

	s = "长江大桥"
	words = x.CutForSearch(s, !use_hmm)
	fmt.Println(s)
	fmt.Println("搜索引擎模式:", strings.Join(words, "/"))

	wordinfos := x.Tokenize(s, gojieba.SearchMode, !use_hmm)
	fmt.Println(s)
	fmt.Println("Tokenize:(搜索引擎模式)", wordinfos)

	wordinfos = x.Tokenize(s, gojieba.DefaultMode, !use_hmm)
	fmt.Println(s)
	fmt.Println("Tokenize:(默认模式)", wordinfos)

	keywords := x.ExtractWithWeight(s, 5)
	fmt.Println("Extract:", keywords)
}

在准备好程序文件之后,我们先执行 go mod init main,完成 Go 项目的初始化:

go: creating new go.mod: module main
go: to add module requirements and sums:
	go mod tidy

在执行完上面的命令后,我们的目录中将会多出来 “go.mod” 和 “go.sum” 两个文件,接着,我们来执行 go mod tidy 命令,让程序完成相关依赖的下载:

go: finding module for package github.com/yanyiwu/gojieba
go: found github.com/yanyiwu/gojieba in github.com/yanyiwu/gojieba v1.1.2

因为我们配置了软件包镜像,所以应该在几秒内就能够完成项目的初始化。

在完成了项目初始化之后,我们执行 go run main.go 来验证下程序是否能运行,不出意外,将看到类似下面的输出结果:

北京西站南广场东
全模式: 北京/北京西/北京西站/京西/西站/南/广场/东
北京西站南广场东
精确模式: 北京西站/南/广场/东
向量数据库
精确模式: 向量/数据库
向量数据库
添加词典后,精确模式: 向量数据库
前门到了,请您后门下车
新词识别: 前门/到/了/,/请/您/后门/下车
小明先去了北京西站南广场东,然后又去了南京东路北大街西
搜索引擎模式: 小明/先去/了/北京/京西/西站/北京西/北京西站/南/广场/东/,/然后/又/去/了/南京/京东/东路/南京东路/北大/大街/北大街/西
朝阳区三里屯优衣库
词性标注: 朝阳区/ns,三里屯/ns,优衣库/x
元宇宙
词性标注: 元/m,宇宙/n
长江大桥
搜索引擎模式: 长江/大桥/长江大桥
长江大桥
Tokenize:(搜索引擎模式) [{长江 0 6} {大桥 6 12} {长江大桥 0 12}]
长江大桥
Tokenize:(默认模式) [{长江大桥 0 12}]
Extract: [{长江大桥 11.1926274509}]

当然,除了 run 之外,我们最常用的命令还有 testbuild,本篇文章暂时不聊如何写单元测试,所以我们就先只验证 build 命令,执行 go build .,我们将在程序目录得到一个名为 main 的可执行文件。

手动执行命令 ./main,不出意外,将得到和上面 run 一样的输出结果。至此,Golang 环境验证也就结束啦。

目前为止,我们已经聊完了“基础 Linux 环境搭建”、“Docker 环境安装和配置”、“Golang 的开发环境搭建”。

接下来的文章中,我会继续完成上篇文章中提到的几种不同的 K8S “发行版”的安装和配置,以及当今世界上流行的编程语言的环境配置。

希望对你有帮助。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK