分类 技术志 下的文章

从DevOps到日常脚本:聊聊Go语言的多面性

本文永久链接 – https://tonybai.com/2024/10/08/go-languages-versatility-from-devops-to-daily-scripts

2024年初,TIOBE编程语言排行榜上,Go再次进入了前十,并在之后又成功冲高至第七名

Go语言的排名上升,至少在Reddit Go论坛上帖子数量和在线人数上得到了体现,尽管目前与Rust热度仍有差距,但可见Go的关注度在提升:


2024年国庆节假期某天下午的实时在线数对比

随着Go语言人气的上升,论坛中的问题也变得愈发多样化。许多Gopher常常问及为何Go是DevOps语言Go适合用作脚本语言吗等问题,这些都反映了Go语言的多面性。

从最初的系统编程语言,到如今在DevOps领域的广泛应用,再到一些场合被探索用作脚本语言,Go展现出了令人惊叹的灵活性和适应性。在本篇文章中,我们将聚焦于Go语言在DevOps领域的应用以及它作为脚本替代语言的潜力,聊聊其强大多面性如何满足这些特定场景的需求。

1. Go在DevOps中的优势

随着DevOps的发展,平台工程(Platform Engineering)这一新兴概念逐渐兴起。在自动化任务、微服务部署和系统管理中,编程语言的作用变得愈发重要。Go语言凭借其高性能、并发处理能力以及能够编译成单一二进制文件的特点,越来越受到DevOps领域开发人员的青睐,成为开发DevOps工具链的重要组成部分。

首先,Go的跨平台编译能力使得DevOps团队可以在一个平台上编译,然后在多个不同的操作系统和架构上运行,结合编译出的单一可执行文件的能力,大大简化了部署流程,这也是很多Go开发者认为Go适合DevOps的第一优势:

$GOOS=linux GOARCH=amd64 go build -o myapp-linux-amd64 main.go
$GOOS=linux GOARCH=arm64 go build -o myapp-linux-arm64 main.go
$GOOS=darwin GOARCH=amd64 go build -o myapp-darwin-amd64 main.go
$GOOS=windows GOARCH=amd64 go build -o myapp-windows-amd64.exe main.go

其次,Go的标准库仿佛“瑞士军刀”,开箱即用,为DevOps场景提供了所需的丰富的网络、加密和系统操作功能库,大幅降低对外部的依赖,即便不使用第三方包生态系统,也可以满足大部分的DevOps功能需求。

此外,Go的goroutines和channels为处理高并发任务提供了极大便利,这在DevOps中也尤为重要。例如,以下代码展示了如何使用goroutines并发检查多个服务的健康状态:

func checkServices(services []string) {
    var wg sync.WaitGroup
    for _, service := range services {
        wg.Add(1)
        go func(s string) {
            defer wg.Done()
            if err := checkHealth(s); err != nil {
                log.Printf("Service %s is unhealthy: %v", s, err)
            } else {
                log.Printf("Service %s is healthy", s)
            }
        }(service)
    }
    wg.Wait()
}

并且,许多知名的DevOps基础设施、中间件和工具都是用Go编写的,如Docker、Kubernetes、Prometheus等,集成起来非常丝滑。这些工具的成功进一步证明了Go在DevOps领域的适用性。

2. Go作为脚本语言的潜力

在传统的DevOps任务中,Python和Shell脚本长期以来都是主力军,它们(尤其是Python)以其简洁的语法和丰富的生态系统赢得了DevOps社区的广泛青睐。然而,传统主力Python和Shell脚本虽然灵活易用,但在处理大规模数据或需要高性能的场景时往往力不从心。此外,它们的动态类型系统可能导致运行时错误,增加了调试难度。

随着Go的普及,它的“超高性价比”逐渐被开发运维人员所接受:既有着接近于脚本语言的较低的学习曲线与较高的生产力(也得益于Go超快的编译速度),又有着静态语言的高性能,还有单一文件在部署方面的便利性

下面是一个简单的文件处理脚本,用于向大家展示Go的简单易学:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    file, err := os.Open("input.txt")
    if err != nil {
        fmt.Println("Error opening file:", err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        line := scanner.Text()
        if strings.Contains(line, "ERROR") {
            fmt.Println(line)
        }
    }
}

这个示例虽然要比同等功能的Python或shell代码行数要多,但由于Go的简单和直观,多数人都很容易看懂这段代码。

此外,Go的静态强类型系统可以在编译时捕获更多错误,避免在运行时的调试,提高了脚本在运行时的可靠性。

开发运维人员眼中的脚本语言,如Shell脚本和Python脚本,通常是直接基于源代码进行解释和运行的。实际上,Go语言同样可以实现这一点,而其关键工具就是go run命令。这个命令允许开发者快速执行Go代码,从而使Go源码看起来更像是“脚本”,下面我们就来看看go run。

3. go run:桥接编译型语言与脚本语言的利器

我们知道go run命令实际上是编译和运行的组合,它首先编译源代码,然后立即执行生成的二进制文件。这个过程对用户来说是透明的,使得Go程序可以像脚本一样方便地运行。这一命令也大大简化了Go程序的开发流程,使Go更接近传统的脚本语言工作流。可以说,通过go run,Go语言向脚本语言的使用体验更靠近了一步。

此外,go run与go build在编译阶段的行为并不完全相同:

  • go run在运行结束后,不保留编译后的二进制文件;而go build生成可执行文件并保留。

  • go run编译时默认不包含调试信息,以减少构建时间;而go build则保留完整的调试信息。

  • go run可以使用-exec标志指定运行环境,比如:

$go run -exec="ls" main.go
/var/folders/cz/sbj5kg2d3m3c6j650z0qfm800000gn/T/go-build1742641170/b001/exe/main

我们看到,如果设置了-exec标志,那么go run -exec=”prog” main.go args编译后的命令执行就变为了”prog a.out args”。go run还支持跨平台模拟执行,当GOOS或GOARCH与系统默认值不同时,如果在\$PATH路径下存在名为”go_\$GOOS_\$GOARCH_exec”的程序,那么go run就会执行:

$go_$GOOS_$GOARCH_exec a.out args

比如:go_js_wasm_exec a.out args
  • go run通常用于运行main包,在go module开启的情况下,go run使用的是main module的上下文。go build可以编译多个包,对于非main包时只检查构建而不生成输出

  • go run还支持运行一个指定版本号的包

当指定了版本后缀(如@v1.0.0或@latest)时,go run会进入module-aware mode(模块感知模式),并忽略当前目录或上级目录中的go.mod文件。这意味着,即使你当前的项目中存在依赖管理文件go.mod,go run也不会影响或修改当前项目的依赖关系,下面这个示例展示了这一点:

$go run golang.org/x/example/hello@latest

go: downloading golang.org/x/example v0.0.0-20240925201653-1a5e218e5455
go: downloading golang.org/x/example/hello v0.0.0-20240925201653-1a5e218e5455
Hello, world!

这个功能特别适合在不影响主模块依赖的情况下,临时运行某个工具或程序。例如,如果你只是想测试某个工具的特定版本,或者快速运行一个远程程序包,而不希望它干扰你正在开发的项目中的依赖项,这种方式就很实用。

不过有一点要注意的是:go run的退出状态并不等于编译后二进制文件的退出状态,看下面这个示例:

// main.go成功退出
$go run main.go
Hello from myapp!
$echo $?
0

// main.go中调用os.Exit(2)退出
$go run main.go
Hello from myapp!
exit status 2
$echo $?
1

go run使用退出状态1来表示其运行程序的异常退出状态,但这个值和真实的exit的状态值不相等。

到这里我们看到,go run xxx.go可以像bash xxx.sh或python xxx.py那样,以“解释”方式运行一个Go源码文件。这使得Go语言在某种程度上具备了脚本语言的特性。然而,在脚本语言中,例如Bash或Python等,用户可以通过将源码文件设置为可执行,并在文件的首行添加适当的解释器指令,从而直接运行脚本,而无需显式调用解释器。这种灵活性使得脚本的执行变得更加简便。那么Go是否也可以做到这一点呢?我们继续往下看。

4. Go脚本化的实现方式

下面是通过一些技巧或第三方工具实现Go脚本化的方法。对于喜欢使用脚本的人来说,最熟悉的莫过于shebang(即解释器指令)。在许多脚本语言中,通过在文件的第一行添加指定的解释器路径,可以直接运行脚本,而无需显式调用解释器。例如,在Bash或Python脚本中,通常会看到这样的行:

#!/usr/bin/env python3

那么Go语言支持shebang吗? 是否可以实现实现类似的效果呢?我们下面来看看。

4.1 使用“shebang(#!)”运行Go脚本

很遗憾,Go不能直接支持shebang,我们看一下这个示例main.go:

#!/usr/bin/env go run 

package main

import (
    "fmt"
    "os"
)

func main() {
    s := "world"
    if len(os.Args) > 1 {
        s = os.Args[1]
    }
    fmt.Printf("Hello, %v!\n", s)
}

这一示例的第一行就是一个shebang解释器指令,我们chmod u+x main.go,然后执行该Go“脚本”:

$./main.go
main.go:1:1: illegal character U+0023 '#'

这个执行过程中,Shell可以正常识别shebang,然后调用go run去运行main.go,问题就在于go编译器视shebang这一行为非法语法!

常规的shebang写法行不通,我们就使用一些trick,下面是改进后的示例:

//usr/bin/env go run $0 $@; exit

package main

import (
    "fmt"
    "os"
)

func main() {
    s := "world"
    if len(os.Args) > 1 {
        s = os.Args[1]
    }
    fmt.Printf("Hello, %v!\n", s)
}

这段代码则可以chmod +x 后直接运行:

$./main.go
Hello, world!
$./main.go gopher
Hello, gopher!

这是因为它巧妙地结合了shell脚本和Go代码的特性。我们来看一下第一行:

//usr/bin/env go run $0 $@; exit

这一行看起来像是Go的注释,但实际上是一个shell命令。当文件被执行时,shell会解释这一行,/usr/bin/env用于寻找go命令的路径,go run \$0 \$@ 告诉go命令运行当前脚本文件(\$0)以及所有传递给脚本的参数(\$@),当go run编译这个脚本时,又会将第一行当做注释行而忽略,这就是关键所在。最后的exit确保shell在Go程序执行完毕后退出。如果没有exit,shell会执行后续Go代码,那显然会导致报错!

除了上述trick外,我们还可以将Go源码文件注册为可执行格式(仅在linux上进行了测试),下面就是具体操作步骤。

4.2 在Linux系统中注册Go为可执行格式

就像在Windows上双击某个文件后,系统打开特定程序处理对应的文件一样,我们也可以将Go源文件(xxx.go)注册为可执行格式,并指定用于处理该文件的程序。实现这一功能,我们需要借助binfmt_misc。binfmt_misc是Linux内核的一个功能,允许用户注册新的可执行文件格式。这使得Linux系统能够识别并执行不同类型的可执行文件,比如脚本、二进制文件等。

我们用下面命令将Go源文件注册到binfmt_misc中:

echo ':golang:E::go::/usr/local/bin/gorun:OC' | sudo tee /proc/sys/fs/binfmt_misc/register

简单解释一下上述命令:

  • :golang::这是注册的格式的名称,可以自定义。
  • E:::表示执行文件的魔数(magic number),在这里为空,表示任何文件类型。
  • go:::指定用于执行的解释器,这里是go命令。
  • /usr/local/bin/gorun:指定用于执行的程序路径,这里是一个自定义的gorun脚本
  • :OC:表示这个格式是可执行的(O)并且支持在运行时创建(C)。

当你执行一个Go源文件时,Linux内核会检查文件的类型。如果文件的格式与注册的格式匹配,内核会调用指定的解释器(在这个例子中是gorun)来执行该文件。

gorun脚本是我们自己编写的,源码如下:

#!/bin/bash

# 检查是否提供了源文件
if [ -z "$1" ]; then
  echo "用法: gorun <go源文件> [参数...]"
  exit 1
fi

# 检查文件是否存在
if [ ! -f "$1" ]; then
  echo "错误: 文件 $1 不存在"
  exit 1
fi

# 将第一个参数作为源文件,剩余的参数作为执行参数
GO_FILE="$1"
shift  # 移除第一个参数,剩余的参数将会被传递

# 使用go run命令执行Go源文件,传递其余参数
go run "$GO_FILE" "$@"

将gorun脚本放置带/usr/local/bin下,并chmod +x使其具有可执行权限。

接下来,我们就可以直接执行不带有”shebang”的正常go源码了:

// main.go
package main

import (
    "fmt"
    "os"
)

func main() {
      s := "world"
      if len(os.Args) > 1 {
          s = os.Args[1]
      }
      fmt.Printf("Hello, %v!\n", s)
}

直接执行上述源文件:

$ ./main.go
Hello, world!
$ ./main.go gopher
Hello, gopher!

4.3 第三方工具支持

Go社区也有一些将支持将Go源文件视为脚本的解释器工具,比如:traefik/yaegi等。

$go install github.com/traefik/yaegi/cmd/yaegi@latest
go: downloading github.com/traefik/yaegi v0.16.1
$yaegi main.go
Hello, main.go!

yaegi还可以像python那样,提供Read-Eval-Print-Loop功能,我们可以与yaegi配合进行交互式“Go脚本”编码:

$ yaegi
> 1+2
: 3
> import "fmt"
: 0xc0003900d0
> fmt.Println("hello, golang")
hello, golang
: 14
>

类似的提供REPL功能的第三方Go解释器还包括:cosmos72/gomacrox-motemen/gore等,这里就不深入介绍了,感兴趣的童鞋可以自行研究。

5. 小结

在本文中,我们探讨了Go语言在DevOps和日常脚本编写中的多面性。首先,Go语言因其高性能、并发处理能力及跨平台编译特性,成为DevOps领域的重要工具,助力于自动化任务和微服务部署。其次,随着Go语言的普及,其作为脚本语言的潜力逐渐被开发运维人员认识,Go展现出了优于传统脚本语言的高效性和可靠性。

我们还介绍了Go脚本的实现方式,包括使用go run命令,它使得Go程序的执行更像传统脚本语言,同时也探讨了一些技巧和工具,帮助开发者将Go源码文件作为可执行脚本直接运行。通过这些探索,我们可以看到Go语言在现代开发中的灵活应用及其日益增长的吸引力。

随着AI能力的飞速发展,使用Go编写一个日常脚本就是分分钟的事情,但Go的特性让这样的脚本具备了传统脚本语言所不具备的并发性、可靠性和性能优势。我们有理由相信,Go在DevOps和脚本编程领域的应用将会越来越广泛,为开发者带来更多的可能性和便利。

6. 参考资料


Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily
  • Gopher Daily Feed订阅 – https://gopherdaily.tonybai.com/feed

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

Go项目中使用Git Submodule,还有这个必要吗?

本文永久链接 – https://tonybai.com/2024/10/05/using-git-submodules-in-go-projects

在软件开发中,依赖管理一直是一个重要的议题,特别是在像Go这样的编程语言中,随着项目的扩展,如何有效管理依赖变得至关重要。Git Submodule作为Git的一个重要功能,允许在一个Git仓库中嵌入另一个仓库,从而方便地管理跨项目的代码共享。然而,Go语言引入的Go Module机制似乎已经解决了依赖管理的问题,那么在Go项目中,是否还有使用Git Submodule的必要呢?本文将简单探讨一下Go项目中Git Submodule的使用方法,并分析它是否还值得使用。

1. Git Submodule是什么?

Git Submodule是Git版本管理工具提供的一个功能,允许你将一个Git仓库作为另一个Git仓库(主仓库)的子目录。主仓库通过记录Submodule的URL和commit hash来追踪Submodule。当你克隆一个包含Submodule的仓库时,需要额外的步骤来初始化和更新Submodule。

下面是一个将github.com/rsc/pdf仓库作为git submodule的示例。

我们先建立主仓库:

$mkdir main-project
$cd main-project
$go mod init main-project
$git init
$git add -A
$git commit -m"initial import" .
[master (root-commit) 8227e65] initial import
 1 file changed, 3 insertions(+)
 create mode 100644 go.mod

接下来,我们来添加submodule:

$git submodule add https://github.com/rsc/pdf.git
Cloning into '/Users/tonybai/Test/Go/submodule/main-project/pdf'...
remote: Enumerating objects: 48, done.
remote: Counting objects: 100% (30/30), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 48 (delta 21), reused 21 (delta 21), pack-reused 18 (from 1)
Unpacking objects: 100% (48/48), done.

$git commit -m "Add rsc/pdf as a submodule"
[master 2778170] Add rsc/pdf as a submodule
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 pdf

git submodule在主仓库的顶层目录下创建一个.gitmodules文件:

$cat .gitmodules
[submodule "pdf"]
    path = pdf
    url = https://github.com/rsc/pdf.git

pdf子目录下的.git不再是目录而是一个文件,其内容指示了pdf仓库的git元数据目录的位置,即主仓库下的.git/modules/pdf下:

$cat pdf/.git
gitdir: ../.git/modules/pdf

git submodule这种机制的主要用途是当多个项目之间有共享代码时,避免将共享的代码直接复制到每个项目中,而是通过Submodule来引用外部仓库。这种方式使得共享代码的版本控制更加明确和独立,也方便了项目之间的更新、管理与版本控制。

通过git submodule status可以查看主仓库下各个submodule的当前状态:

$git submodule status
 c47d69cf462f804ff58ca63c61a8fb2aed76587e pdf (v0.1.0-1-gc47d69c)

通过git submodule update还可以更新各个submodule到最新版本。 但通常在主仓库中会锁定Submodule的特定版本,通过锁定Submodule的版本,可以确保主仓库使用的是经过测试和验证的Submodule代码,这减少了因Submodule更新而导致的意外问题。同时,锁定版本还可以确保所有开发者和构建环境都使用完全相同版本的Submodule,这对于保证构建的一致性和可重现性至关重要。版本锁定让你还可以精确控制何时更新Submodule,你可以在准备好处理潜在的变更和进行必要的测试时,有计划地更新Submodule版本。submodule的版本锁定可以通过下面命令组合实现:

cd path/to/submodule
git checkout <specific-commit-hash>
cd -
git add path/to/submodule
git commit -m "Lock submodule to specific version"

这个提交会更新主仓库中记录的Submodule版本,其他克隆主仓库的人在初始化和更新Submodule时,就会自动获取到这个特定版本。

在以Git为版本管理工具的项目中,Submodule在以下一些场景中还是很有用的:

  • 在多项目依赖场景下,我们可以使用Submodule共享公共库;
  • 在大型单一仓库中,Submodule有助于我们模块化管理各个子项目;
  • 统一对Submodule的版本进行严格管理,避免在更新时引入未测试的新代码。

submodule虽然可以解决一些问题,但由于增加了项目管理复杂度以及学习成本,应用算不上广泛,但也不乏一些知名的开源项目在使用,比如git项目自身、opensslqemu等。

不过,对于Go项目而言,Go Modules是Go在Go 1.11引入的新的官方依赖管理机制,它通过go.mod文件声明依赖关系,通过go.sum文件确保依赖的完整性,实现了构建的可重现性。那么,在Go项目中还有必要引入sub modules吗?

这里我们先不下结论,而是先来看看Go项目引入submodule后该如何使用呢。

2. Go项目的Git Submodule使用方法

在前面我们在本地建立了一个main-project,然后将rsc/pdf作为submodule导入到了main-project中,main-project是一个Go项目,它的go.mod如下:

// main-project/go.mod

module main-project

go 1.23.0

我们现在就继续使用这个示例来看看Go项目中git submodule的使用方法。

我们先来看一种错误的使用方法:使用相对路径

我们在main-project下建立一个main.go的源文件:

// main-project/main.go

package main

import (
    _ "./pdf"
)

func main() {
    println("ok")
}

建完后,整个main-project的目录布局如下:

$tree -F
.
├── go.mod
├── main.go
└── pdf/
    ├── LICENSE
    ├── README.md
    ├── lex.go
    ├── name.go
    ├── page.go
    ├── pdfpasswd/
    │   └── main.go
    ├── ps.go
    ├── read.go
    └── text.go

在第一版main.go中,我们期望使用相对路径来导入submomdule中的pdf包,运行main.go,我们得到下面结果:

$go run main.go
main.go:4:2: "./pdf" is relative, but relative import paths are not supported in module mode

我们看到:在go module构建模式下,Go已经不再支持以相对路径导入Go包了!但是如果我们直接通过rsc.io/pdf这个路径导入,那显然使用的就不是submodule中的pdf包了。

下面我们试试第二种方法,即将pdf目录看成main-project的子目录,将pdf包看成是main-project这个module下的一个包,这样pdf包在main-project这个module下的导入路径就变成了main-project/pdf:

// main-project/main.go
package main

import (
    _ "main-project/pdf"
)

func main() {
    println("ok")
}

这次构建和运行main.go,我们将得到正确的预期结果。

到这里,我们似乎又找到了go module之外go项目依赖管理的新方法,并且这种方法特别适合当某些依赖项目尚未发布,还无法直接通过Go Module导入的库,甚至是一些永远不会发布的内部库或私有库。这种方法让pdf看起来是main-project的一部分,但实际上pdf包的版本却是需要开发人员自己通过git submodule命令管理的,pdf包的版本无法用go.mod(和go.sum)控制,因为它被视为是main-project的一部分了,而不是外部依赖包

如果你不想将其视为main-project的一部分,还想将其以外部依赖的方式管理起来,那就需要利用到go module的replace或go.work了。不过这种方法的前提是submodule下必须是一个go module,即有自己的go.mod。rsc.io/pdf包是一个legacy package,还没有自己的go.mod,我们先在本地pdf目录下为其添加一个go.mod:go mod init rsc.io/pdf。

接下来,我们先来简单看看用replace如何实现导入pdf包,我们需要修改一下main-project/go.mod:

// main-project/go.mod

module main-project

go 1.23.0

require rsc.io/pdf v0.1.1

replace rsc.io/pdf => ./pdf

这里我们用replace指示符将rsc.io/pdf替换为本地pdf目录下的go module,这样修改后,我们运行main.go也会得到正确的结果。

另外我们还可以使用go.work来导入pdf,下面命令初始化一个go.work:

$go work init .

编辑go.work,添加workspace包含的路径:

go 1.23.0

use (
    .
    ./pdf
)

这样go编译器会默认在当前目录和pdf目录下搜索rsc.io/pdf模块,运行main.go也是ok的。

相对于将pdf包看成是main-project module下的一个包并用main-project/pdf这个内部依赖的包导入路径的方法,使用replace或go.work的好处在于一旦pdf包得以发布,main.go可以无需修改pdf包导入路径,并可以基于go.mod精确管理pdf包的版本。

3. 小结

那么我们在Go项目中到底是否有必要使用sub modules呢?我们来小结一下。

总的来说,在大多数情况下,Go Modules确实已经覆盖了Git Submodule在Go项目中的主要功能,甚至做的更好,比如:Go Modules提供了更细粒度的版本控制,能自动解析和下载依赖,并也可以确保了构建的可重现性。因此,对于大多数Go项目而言,使用Go Modules已经足够满足依赖管理需求,而无需再使用git submodule。 并且,在Go项目以及Go社区的实践中,应对类似共享未发布的依赖包的场景(git submodule适用的场景),使用replace或go.work是比较主流的实践,或者说go.work以及replace就是为了这种情况而添加的。

当然如果组织/公司内部尚未构建可以很好地支持内部Go项目间依赖包获取、导入和管理的基础设施,那么git submodule不失为一种可以在内部Go项目中实施的可行的依赖版本管理和控制方案。

最后,无论选择使用Git Submodule、Go Modules,还是两者结合,最重要的是要确保项目结构清晰,依赖关系明确,以便于团队协作和项目维护。


Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily
  • Gopher Daily Feed订阅 – https://gopherdaily.tonybai.com/feed

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats