Skip to content

Module

Jede moderne Sprache verfügt über ein ausgereiftes Abhängigkeitsverwaltungstool, wie z.B. Gradle für Java, Pip für Python oder Npm für NodeJs. Ein gutes Abhängigkeitsverwaltungstool kann Entwicklern viel Zeit sparen und die Entwicklungseffizienz steigern. In frühen Jahren hatte Go jedoch keine ausgereifte Lösung für das Abhängigkeitsmanagement. Damals wurde der gesamte Code im GOPATH-Verzeichnis gespeichert, was für Softwareprojekte sehr unpraktisch war -Versionsverwirrung und schwer zu verwaltende Abhängigkeiten waren die Folge. Um dieses Problem zu lösen, entstanden verschiedene Lösungen in der Entwickler-Community, was zu einer chaotischen Situation führte. Einige Lösungen wie Vendor erwiesen sich als vielversprechend. Schließlich führte Go 1.11 offiziell Go Mod ein, das die vorherige chaotische Situation beendete und in späteren Updates kontinuierlich verbessert wurde, wobei ältere Tools überflüssig wurden. Heute, zum Zeitpunkt der Erstellung dieses Artikels, ist Go bei Version 1.20 angekommen, und fast alle Go-Projekte verwenden Go Mod. Daher wird in diesem Artikel ausschließlich Go Mod behandelt. Das offizielle Team hat auch eine sehr detaillierte Dokumentation zu Go-Modulen erstellt: Go Modules Reference.

Module schreiben

Go Module basieren im Wesentlichen auf VCS (Versionskontrollsystemen). Beim Herunterladen von Abhängigkeiten werden tatsächlich VCS-Befehle wie git ausgeführt. Wenn Sie eine von Ihnen geschriebene Bibliothek teilen möchten, müssen Sie nur drei Dinge sicherstellen:

  • Das Quellcode-Repository ist öffentlich zugänglich und verwendet eines der folgenden VCS:
    • git
    • hg (Mercurial)
    • bzr (Bazaar)
    • svn
    • fossil
  • Es ist ein konformes go mod Projekt
  • Es folgt der semantischen Versionierung

Daher können Sie ganz normal mit VCS entwickeln und Ihren spezifischen Versionen standardkonforme Tags geben. Andere können Ihre Bibliothek dann über den Modulnamen herunterladen. Im Folgenden werden die Schritte zur Modulentwicklung anhand eines Beispiels demonstriert.

Beispiel-Repository: 246859/hello: say hello (github.com)

Vorbereitung

Stellen Sie vor dem Start sicher, dass Ihre Version go mod vollständig unterstützt (go >= 1.17) und Go Module aktiviert ist. Mit folgendem Befehl können Sie überprüfen, ob es aktiviert ist:

bash
$ go env GO111MODULE

Falls nicht aktiviert, aktivieren Sie Go Module mit folgendem Befehl:

bash
$ go env -w GO111MODULE=on

Erstellen

Zunächst benötigen Sie ein öffentlich zugängliches Quellcode-Repository. Es gibt viele Möglichkeiten, ich empfehle Github. Erstellen Sie dort ein neues Projekt namens hello. Für den Repository-Namen gibt es zwar keine besonderen Einschränkungen, es wird jedoch empfohlen, keine Sonderzeichen zu verwenden, da diese den Modulnamen beeinflussen können.

Nach der Erstellung können Sie sehen, dass die URL des Repositories https://github.com/246859/hello lautet, und der entsprechende Go-Modulname ist github.com/246859/hello.

Klonen Sie es dann lokal und initialisieren Sie das Modul mit dem Befehl go mod init:

bash
$ git clone git@github.com:246859/hello.git
Cloning into 'hello'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (5/5), done.

$ cd hello && go mod init github.com/246859/hello
go: creating new go.mod: module github.com/246859/hello

Schreiben

Nun können Sie mit der Entwicklungsarbeit beginnen. Die Funktionalität ist sehr einfach, es gibt nur eine Funktion:

go
// hello.go
package hello

import "fmt"

// Hello returns hello message
func Hello(name string) string {
        if name == "" {
                name = "world"
        }
        return fmt.Sprintf("hello %s!", name)
}

Schreiben Sie auch eine Testdatei für Unit-Tests:

go
// hello_test.go
package hello_test

import (
        "testing"
        "fmt"
        "github.com/246859/hello"
)

func TestHello(t *testing.T) {
        data := "jack"
        expected := fmt.Sprintf("hello %s!", data)
        result := hello.Hello(data)

        if result != expected {
                t.Fatalf("expected result %s, but got %s", expected, result)
        }

}

Schreiben Sie als Nächstes ein Kommandozeilenprogramm zur Ausgabe von hello. Die Funktionalität ist ebenfalls sehr einfach. Für Kommandozeilenprogramme werden diese gemäß Konvention im Verzeichnis cmd/app_name/ erstellt, daher werden die Dateien für das hello-Kommandozeilenprogramm im Verzeichnis cmd/hello/ gespeichert:

go
// cmd/hello/main.go
package main

import (
  "flag"
  "github.com/246859/hello"
  "os"
)

var name string

func init() {
  flag.StringVar(&name, "name", "world", "name to say hello")
}

func main() {
  flag.Parse()
  msg := hello.Hello(name)
  _, err := os.Stdout.WriteString(msg)
  if err != nil {
    os.Stderr.WriteString(err.Error())
  }
}

Testen

Formatieren Sie nach dem Schreiben den Quellcode und führen Sie Tests durch:

bash
$ go fmt && go vet ./...

$ go test -v .
=== RUN   TestHello
--- PASS: TestHello (0.00s)
PASS
ok      github.com/246859/hello 0.023s

Führen Sie das Kommandozeilenprogramm aus:

bash
$ go run ./cmd/hello -name jack
hello jack!

Dokumentation

Zuletzt sollten Sie eine prägnante README für diese Bibliothek schreiben, damit andere Entwickler auf einen Blick wissen, wie sie verwendet wird:

markdown
# hello

just say hello

## Install

import code

```bash
go get github.com/246859/hello@latest
```

install cmd

```bash
go install github.com/246859/hello/cmd/hello@latest
```

## Example

Here's a simple example as follows:

```go
package main

import (
  "fmt"
  "github.com/246859/hello"
)

func main() {
  result := hello.Hello("jack")
  fmt.Println(result)
}
```

Dies ist ein sehr einfaches README-Dokument, das Sie nach Belieben erweitern können.

Hochladen

Wenn der gesamte Code geschrieben und getestet ist, können Sie die Änderungen committen und in das Remote-Repository pushen:

bash
$ git add go.mod hello.go hello_test.go cmd/ example/ README.md

$ git commit -m "chore(mod): mod init" go.mod
[main 5087fa2] chore(mod): mod init
 1 file changed, 3 insertions(+)
 create mode 100644 go.mod

$ git commit -m "feat(hello): complete Hello func" hello.go
[main 099a8bf] feat(hello): complete Hello func
 1 file changed, 11 insertions(+)
 create mode 100644 hello.go

$ git commit -m "test(hello): complete hello testcase" hello_test.go
[main 76e8c1e] test(hello): complete hello testcase
 1 file changed, 17 insertions(+)
 create mode 100644 hello_test.go

$ git commit -m "feat(hello): complete hello cmd" cmd/hello/
[main a62a605] feat(hello): complete hello cmd
 1 file changed, 22 insertions(+)
 create mode 100644 cmd/hello/main.go

$ git commit -m "docs(example): add hello example" example/
[main 5c51ce4] docs(example): add hello example
 1 file changed, 11 insertions(+)
 create mode 100644 example/main.go

$ git commit -m "docs(README): update README" README.md
[main e6fbc62] docs(README): update README
 1 file changed, 27 insertions(+), 1 deletion(-)

Insgesamt sechs Commits - nicht viele. Nach dem Committen erstellen Sie einen Tag für den neuesten Commit:

bash
$ git tag v1.0.0

$ git tag -l
v1.0.0

$ git log --oneline
e6fbc62 (HEAD -> main, tag: v1.0.0, origin/main, origin.HEAD) docs(README): update README
5c51ce4 docs(example): add hello example
a62a605 feat(hello): complete hello cmd
76e8c1e test(hello): complete hello testcase
099a8bf feat(hello): complete Hello func
5087fa2 chore(mod): mod init
1f422d1 Initial commit

Pushen Sie schließlich in das Remote-Repository:

bash
$ git push --tags
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 16 threads
Compressing objects: 100% (17/17), done.
Writing objects: 100% (21/21), 2.43 KiB | 1.22 MiB/s, done.
Total 21 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), done.
To github.com:246859/hello.git
   1f422d1..e6fbc62    main -> main
  * [new tag]         v1.0.0 -> v1.0.0

Nach dem Pushen erstellen Sie noch ein Release (ein Tag reicht aus, ein Release entspricht nur den GitHub-Konventionen):

Damit ist das Schreiben des Moduls abgeschlossen. Dies ist der grundlegende Prozess der Modulentwicklung. Andere Entwickler können nun Code über den Modulnamen importieren oder Kommandozeilentools installieren.

Referenzierung

Referenzieren Sie die Bibliothek mit go get:

bash
$ go get github.com/246859/hello@latest
go: downloading github.com/246859/hello v1.0.0
go: added github.com/246859/hello v1.0.0

Installieren Sie das Kommandozeilenprogramm mit go install:

bash
$ go install github.com/246859/hello/cmd/hello@latest && hello -name jack
hello jack!

Oder führen Sie es direkt mit go run aus:

bash
$ go run -mod=mod github.com/246859/hello/cmd/hello -name jack
hello jack!

Wenn eine Bibliothek referenziert wurde, erstellt Go Package automatisch eine Seite dafür. Dieser Prozess erfolgt automatisch, ohne dass Entwickler etwas tun müssen. Die hello-Bibliothek hat beispielsweise eine eigene Dokumentationsseite:

Weitere Informationen zum Hochladen von Modulen finden Sie unter Add a package.

Informationen zum Löschen von Modulen finden Sie unter Removing a package.

Proxy einrichten

Go hat zwar kein zentrales Repository wie Maven Repo, PyPi oder NPM, aber es gibt ein offizielles Proxy-Repository: Go modules services (golang.org), das Module basierend auf Version und Modulname zwischenspeichert. Da die Server jedoch im Ausland stehen, ist die Zugriffsgeschwindigkeit für Nutzer in China nicht optimal. Daher müssen wir die Standard-Proxy-Adresse ändern. In China gibt es derzeit mehrere gute Optionen:

Wählen Sie hier den Proxy von Qiniu Cloud. Führen Sie folgenden Befehl aus, um den Go-Proxy zu ändern. direct bedeutet, dass bei fehlgeschlagenem Proxy-Download direkt auf das Quellcode-Repository zugegriffen wird:

sh
$ go env -w GOPROXY=https://goproxy.cn,direct

Nach erfolgreicher Änderung des Proxys wird das Herunterladen von Abhängigkeiten in Zukunft sehr schnell sein.

Abhängigkeiten herunterladen

Nachdem der Proxy geändert wurde, installieren wir eine Drittanbieter-Abhängigkeit. Go hat eine offizielle Website für die Abhängigkeitssuche: Go Packages.

Code-Referenzierung

Suchen Sie dort nach dem bekannten Web-Framework Gin:

Es werden viele Suchergebnisse angezeigt. Bei der Verwendung von Drittanbieter-Abhängigkeiten sollten Sie die Anzahl der Referenzierungen und die Aktualisierungszeit berücksichtigen, um zu entscheiden, ob Sie die Abhängigkeit verwenden möchten. Wählen Sie hier direkt das erste Ergebnis:

Auf der entsprechenden Seite sehen Sie, dass es sich um eine Dokumentationsseite für diese Abhängigkeit handelt, mit vielen detaillierten Informationen. Sie können auch später hier nach Dokumentation suchen:

Kopieren Sie einfach die Adresse und verwenden Sie dann den Befehl go get im zuvor erstellten Projekt:

sh
$ go get github.com/gin-gonic/gin

Während des Prozesses werden viele Abhängigkeiten heruntergeladen. Solange keine Fehler auftreten, war der Download erfolgreich:

sh
$ go get github.com/gin-gonic/gin
go: added github.com/bytedance/sonic v1.8.0
go: added github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
go: added github.com/gin-contrib/sse v0.1.0
go: added github.com/gin-gonic/gin v1.9.0
go: added github.com/go-playground/locales v0.14.1
go: added github.com/go-playground/universal-translator v0.18.1
go: added github.com/go-playground/validator/v10 v10.11.2
go: added github.com/goccy/go-json v0.10.0
go: added github.com/json-iterator/go v1.1.12
go: added github.com/klauspost/cpuid/v2 v2.0.9
go: added github.com/leodido/go-urn v1.2.1
go: added github.com/mattn/go-isatty v0.0.17
go: added github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
go: added github.com/modern-go/reflect2 v1.0.2
go: added github.com/pelletier/go-toml/v2 v2.0.6
go: added github.com/twitchyliquid64/golang-asm v0.15.1
go: added github.com/ugorji/go/codec v1.2.9
go: added golang.org/x/arch v0.0.0-20210923205945-b76863e36670
go: added golang.org/x/crypto v0.5.0
go: added golang.org/x/net v0.7.0
go: added golang.org/x/sys v0.5.0
go: added golang.org/x/text v0.7.0
go: added google.golang.org/protobuf v1.28.1
go: added gopkg.in/yaml.v3 v3.0.1

Überprüfen Sie nach Abschluss die go.mod-Datei:

sh
$ cat go.mod
module golearn

go 1.20

require github.com/gin-gonic/gin v1.9.0

require (
  github.com/bytedance/sonic v1.8.0 // indirect
  github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
  github.com/gin-contrib/sse v0.1.0 // indirect
  github.com/go-playground/locales v0.14.1 // indirect
  github.com/go-playground/universal-translator v0.18.1 // indirect
  github.com/go-playground/validator/v10 v10.11.2 // indirect
  github.com/goccy/go-json v0.10.0 // indirect
  github.com/json-iterator/go v1.1.12 // indirect
  github.com/klauspost/cpuid/v2 v2.0.9 // indirect
  github.com/leodido/go-urn v1.2.1 // indirect
  github.com/mattn/go-isatty v0.0.17 // indirect
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
  github.com/modern-go/reflect2 v1.0.2 // indirect
  github.com/pelletier/go-toml/v2 v2.0.6 // indirect
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
  github.com/ugorji/go/codec v1.2.9 // indirect
  golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
  golang.org/x/crypto v0.5.0 // indirect
  golang.org/x/net v0.7.0 // indirect
  golang.org/x/sys v0.5.0 // indirect
  golang.org/x/text v0.7.0 // indirect
  google.golang.org/protobuf v1.28.1 // indirect
  gopkg.in/yaml.v3 v3.0.1 // indirect
)

Sie werden feststellen, dass im Vergleich zu vorher viel mehr Inhalte hinzugekommen sind. Außerdem werden Sie bemerken, dass eine neue Datei namens go.sum im Verzeichnis erstellt wurde:

sh
$ ls
go.mod  go.sum  main.go

Ändern Sie die Datei main.go wie folgt:

go
package main

import (
  "github.com/gin-gonic/gin"
)

func main() {
  gin.Default().Run()
}

Führen Sie das Projekt erneut aus:

sh
$ go run golearn
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

So wurde mit nur einer Zeile Code ein einfacher Webserver gestartet. Wenn Sie eine Abhängigkeit nicht mehr benötigen, können Sie sie mit dem Befehl go get löschen. Hier am Beispiel von Gin:

sh
$ go get github.com/gin-gonic/gin@none
go: removed github.com/gin-gonic/gin v1.9.0

Wenn Sie @none nach der Abhängigkeitsadresse hinzufügen, wird die Abhängigkeit gelöscht. Das Ergebnis zeigt, dass die Löschung erfolgreich war. Wenn Sie die go.mod-Datei erneut überprüfen, werden Sie feststellen, dass die Gin-Abhängigkeit nicht mehr vorhanden ist.

sh
$ cat go.mod | grep github.com/gin-gonic/gin

Wenn Sie auf die neueste Version aktualisieren möchten, können Sie das Suffix @latest hinzufügen oder selbst nach verfügbaren Release-Versionen suchen:

sh
$ go get -u github.com/gin-gonic/gin@latest

Kommandozeilentools installieren

Der Befehl go install lädt Drittanbieter-Abhängigkeiten lokal herunter und kompiliert sie in Binärdateien. Dank der Kompilierungsgeschwindigkeit von Go dauert dieser Prozess normalerweise nicht lange. Go speichert sie dann im Verzeichnis $GOPATH/bin oder $GOBIN, damit die Binärdatei systemweit ausgeführt werden kann (vorausgesetzt, Sie haben diese Pfade zu den Umgebungsvariablen hinzugefügt).

TIP

Bei Verwendung des install-Befehls muss die Versionsnummer angegeben werden.

Laden Sie beispielsweise den in Go geschriebenen Debugger delve:

bash
$ go install github.com/go-delve/delve/cmd/dlv@latest
go: downloading github.com/go-delve/delve v1.22.1
go: downloading github.com/cosiner/argv v0.1.0
go: downloading github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d
go: downloading github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62
go: downloading github.com/google/go-dap v0.11.0
go: downloading github.com/hashicorp/golang-lru v1.0.2
go: downloading golang.org/x/arch v0.6.0
go: downloading github.com/cpuguy83/go-md2man/v2 v2.0.2
go: downloading go.starlark.net v0.0.0-20231101134539-556fd59b42f6
go: downloading github.com/cilium/ebpf v0.11.0
go: downloading github.com/mattn/go-runewidth v0.0.13
go: downloading github.com/russross/blackfriday/v2 v2.1.0
go: downloading github.com/rivo/uniseg v0.2.0
go: downloading golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2

$ dlv -v
Error: unknown shorthand flag: 'v' in -v
Usage:
  dlv [command]

Available Commands:
  attach      Attach to running process and begin debugging.
  completion  Generate the autocompletion script for the specified shell
  connect     Connect to a headless debug server with a terminal client.
  core        Examine a core dump.
  dap         Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
  debug       Compile and begin debugging main package in current directory, or the package specified.
  exec        Execute a precompiled binary, and begin a debug session.
  help        Help about any command
  test        Compile test binary and begin debugging program.
  trace       Compile and begin tracing program.
  version     Prints version.

Additional help topics:
  dlv backend    Help about the --backend flag.
  dlv log        Help about logging flags.
  dlv redirect   Help about file redirection.

Use "dlv [command] --help" for more information about a command.

Modulverwaltung

Alle oben genannten Inhalte behandeln nur die grundlegende Verwendung von Go Mod. Tatsächlich reicht dies nicht aus, um Go Mod vollständig zu beherrschen. Die offizielle Definition eines Moduls lautet: eine Menge von Paketen, die mit Versionen markiert sind. In dieser Definition sollten Pakete ein vertrautes Konzept sein, während Versionen der semantischen Versionierung folgen müssen, definiert als: v(major).(minor).(patch) Format. Zum Beispiel ist die Go-Versionsnummer v1.20.1, wobei die Hauptversionsnummer 1 ist, die Nebenversion 20 und die Patch-Version 1, zusammen also v1.20.1. Hier ist eine detailliertere Erklärung:

  • major: Wenn sich die Hauptversion ändert, bedeutet dies, dass das Projekt inkompatible Änderungen erfahren hat. Projekte, die von einer alten Version auf die neue Version aktualisieren, werden wahrscheinlich nicht mehr normal funktionieren.
  • minor: Wenn sich die Nebenversion ändert, bedeutet dies, dass das Projekt neue Funktionen hinzugefügt hat, basierend auf der vorherigen Version mit nur neuen Features.
  • patch: Wenn sich die Patch-Version ändert, bedeutet dies, dass nur Bugs behoben wurden, ohne neue Funktionen hinzuzufügen.

Häufige Befehle

BefehlBeschreibung
go mod downloadAbhängigkeiten des aktuellen Projekts herunterladen
go mod editgo.mod-Datei bearbeiten
go mod graphModulabhängigkeitsgraph ausgeben
go mod initgo mod im aktuellen Verzeichnis initialisieren
go mod tidyProjektmodule bereinigen
go mod verifyLegitimität der Projektabhängigkeiten verifizieren
go mod whyErklären, wo Abhängigkeiten im Projekt verwendet werden
go clean -modcacheProjektmodul-Abhängigkeitscache löschen
go list -mModule auflisten

Weitere Informationen zu den Befehlen finden Sie unter go mod cmd

Modulspeicherung

Bei der Verwendung von Go Mod für das Projektmanagement wird der Modul-Cache standardmäßig im Verzeichnis $GOPATH/pkg/mod gespeichert. Sie können auch $GOMODCACHE ändern, um einen anderen Speicherort anzugeben:

sh
$ go env -w GOMODCACHE=Ihr-Modul-Cache-Pfad

Alle Go Module-Projekte auf demselben Computer teilen sich den Cache in diesem Verzeichnis. Der Cache hat keine Größenbeschränkung und wird nicht automatisch gelöscht. Die entpackten Abhängigkeitsquelldateien im Cache sind schreibgeschützt. Um den Cache zu leeren, führen Sie folgenden Befehl aus:

sh
$ go clean -modcache

Im Verzeichnis $GOMODCACHE/cache/download werden die Originaldateien der Abhängigkeiten gespeichert, einschließlich Hash-Dateien, Original-Archive usw.:

bash
$ ls $(go env GOMODCACHE)/cache/download/github.com/246859/hello/@v -1
list
v1.0.0.info
v1.0.0.lock
v1.0.0.mod
v1.0.0.zip
v1.0.0.ziphash

Die entpackten Abhängigkeiten sind wie folgt organisiert - es ist der Quellcode des angegebenen Moduls:

bash
$ ls $(go env GOMODCACHE)/github.com/246859/hello@v1.0.0 -1
LICENSE
README.md
cmd/
example/
go.mod
hello.go
hello_test.go

Versionsauswahl

Bei der Versionsauswahl für Abhängigkeiten folgt Go dem Prinzip der minimalen Versionauswahl. Hier ist ein Beispiel von der offiziellen Website: Das Hauptmodul referenziert Version 1.2 von Modul A und Version 1.2 von Modul B. Modul A Version 1.2 referenziert Version 1.3 von Modul C, Modul B Version 1.2 referenziert Version 1.4 von Modul C. Beide Versionen von Modul C (1.3 und 1.4) referenzieren Version 1.2 von Modul D. Nach dem Prinzip der minimalen Versionauswahl wird Go schließlich die Versionen A1.2, B1.2, C1.4 und D1.2 auswählen. Die hellblau markierten werden von der go.mod-Datei geladen, die eingerahmten sind die schließlich ausgewählten Versionen.

Die offizielle Website enthält weitere Beispiele mit ähnlicher Bedeutung.

go.mod

Bei jedem Go Mod-Projekt wird eine go.mod-Datei erstellt. Es ist daher sehr wichtig, die go.mod-Datei zu verstehen. In den meisten Fällen muss die go.mod-Datei jedoch nicht manuell bearbeitet werden.

module golearn

go 1.20

require github.com/gin-gonic/gin v1.9.0

require (
   github.com/bytedance/sonic v1.8.0 // indirect
   github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
   github.com/gin-contrib/sse v0.1.0 // indirect
   github.com/go-playground/locales v0.14.1 // indirect
   github.com/go-playground/universal-translator v0.18.1 // indirect
   github.com/go-playground/validator/v10 v10.11.2 // indirect
   github.com/goccy/go-json v0.10.0 // indirect
   github.com/json-iterator/go v1.1.12 // indirect
   github.com/klauspost/cpuid/v2 v2.0.9 // indirect
   github.com/leodido/go-urn v1.2.1 // indirect
   github.com/mattn/go-isatty v0.0.17 // indirect
   github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
   github.com/modern-go/reflect2 v1.0.2 // indirect
   github.com/pelletier/go-toml/v2 v2.0.6 // indirect
   github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
   github.com/ugorji/go/codec v1.2.9 // indirect
   golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
   golang.org/x/crypto v0.5.0 // indirect
   golang.org/x/net v0.7.0 // indirect
   golang.org/x/sys v0.5.0 // indirect
   golang.org/x/text v0.7.0 // indirect
   google.golang.org/protobuf v1.28.1 // indirect
   gopkg.in/yaml.v3 v3.0.1 // indirect
)

In der Datei werden Sie feststellen, dass die meisten Abhängigkeitsadressen github und ähnliche Begriffe enthalten. Dies liegt daran, dass Go kein öffentliches Abhängigkeits-Repository hat und die meisten Open-Source-Projekte auf GitHub gehostet werden. Einige haben eigene Repositories, wie z.B. google.golang.org/protobuf oder golang.org/x/crypto. Normalerweise ist diese URL-Zeichenfolge auch der Modulname des Go-Projekts. Dies führt zu einem Problem: URLs unterscheiden nicht zwischen Groß- und Kleinschreibung, aber die Ordner, in denen Abhängigkeiten gespeichert werden, schon. Daher referenzieren go get github.com/gin-gonic/gin und go get github.com/gin-gonic/Gin dieselbe Abhängigkeit, werden aber lokal unter unterschiedlichen Pfaden gespeichert. In solchen Fällen wandelt Go Großbuchstaben nicht direkt in Speicherpfade um, sondern escapet sie als !Kleinbuchstabe. Zum Beispiel wird github.com\BurntSushi schließlich zu github.com\!burnt!sushi.

module

Das Schlüsselwort module deklariert den Modulnamen des aktuellen Projekts. In einer go.mod-Datei darf das Schlüsselwort module nur einmal erscheinen. Im Beispiel:

module golearn

bedeutet dies, dass der aktuelle Modulname golearn ist. Wenn Sie beispielsweise die go.mod-Datei der Gin-Abhängigkeit öffnen, sehen Sie ihren module-Namen:

module github.com/gin-gonic/gin

Der Modulname von Gin ist die Adresse, die beim Herunterladen der Abhängigkeit verwendet wird. Dies ist auch das allgemein empfohlene Format für Modulnamen: Domain/Benutzer/Repository-Name.

TIP

Ein wichtiger Punkt: Wenn die Hauptversion größer als 1 ist, muss die Hauptversionsnummer im Modulnamen erscheinen. Zum Beispiel:

github.com/my/example

Wenn die Version auf v2.0.0 aktualisiert wird, muss der Modulname wie folgt geändert werden:

github.com/my/example/v2

Wenn ein bestehendes Projekt die alte Version referenziert und die neue Version nicht unterscheidet, können Benutzer beim Referenzieren der Abhängigkeit nicht zwischen inkompatiblen Änderungen unterscheiden, die durch Versionsänderungen entstehen, was zu Programmfehlern führen kann.

Deprecation

Kommentieren Sie Deprecated am Anfang der Zeile über module, um anzuzeigen, dass das Modul veraltet ist, zum Beispiel:

// Deprecated: use example.com/mod/v2 instead.
module example.com/mod

go

Das Schlüsselwort go gibt die Go-Version an, mit der das aktuelle Projekt entwickelt wurde. Die Versionsnummer muss den semantischen Regeln folgen. Je nach Go-Version verhält sich Go Mod unterschiedlich. Hier ist ein einfaches Beispiel. Informationen zu verfügbaren Go-Versionen finden Sie in der offiziellen Dokumentation.

go 1.20

require

Das Schlüsselwort require gibt an, dass eine externe Abhängigkeit referenziert wird, zum Beispiel:

require github.com/gin-gonic/gin v1.9.0

Das Format ist require Modulname Versionsnummer. Bei mehreren Referenzen können Klammern verwendet werden:

require (
   github.com/bytedance/sonic v1.8.0 // indirect
)

Der Kommentar // indirect bedeutet, dass die Abhängigkeit nicht direkt vom aktuellen Projekt referenziert wird. Möglicherweise hat eine direkt referenzierte Abhängigkeit diese Abhängigkeit referenziert, sodass sie für das aktuelle Projekt eine indirekte Referenz darstellt. Wie zuvor erwähnt, muss bei Änderung der Hauptversion diese im Modulnamen erscheinen. Module, die dieser Regel nicht folgen, werden als nicht konforme Module bezeichnet und erhalten bei require den Kommentar incompatible:

require example.com/m v4.1.2+incompatible

Pseudoversionen

In der oben gezeigten go.mod-Datei werden Sie feststellen, dass einige Abhängigkeitsversionen keine semantischen Versionsnummern sind, sondern kryptische Zeichenfolgen. Dies sind tatsächlich die CommitIDs der entsprechenden Versionen. Semantische Versionen beziehen sich normalerweise auf ein bestimmtes Release. Pseudoversionen können auf einen bestimmten Commit verfeinern. Das übliche Format ist vx.y.z-yyyyMMddHHmmss-CommitId. Da vx.y.z möglicherweise nicht wirklich existiert, wird es als Pseudoversion bezeichnet. Zum Beispiel existiert v0.0.0 im folgenden Beispiel nicht, wirklich relevant sind die nachfolgenden 12 Zeichen der CommitID.

// CommitID wird normalerweise auf die ersten 12 Zeichen gekürzt
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect

Entsprechend können Sie beim Herunterladen von Abhängigkeiten die CommitID anstelle der semantischen Versionsnummer angeben:

go get github.com/chenzhuoyu/base64x@fe3a3abad311

exclude

Das Schlüsselwort exclude gibt an, dass eine bestimmte Version einer Abhängigkeit nicht geladen werden soll. Wenn require gleichzeitig dieselbe Version referenziert, wird diese ebenfalls ignoriert. Dieses Schlüsselwort ist nur im Hauptmodul wirksam. Zum Beispiel:

exclude golang.org/x/net v1.2.3

exclude (
    golang.org/x/crypto v1.4.5
    golang.org/x/text v1.6.7
)

replace

replace ersetzt eine bestimmte Version einer Abhängigkeit. Sie können Modulpfad und Version oder plattformspezifische Dateipfade verwenden. Beispiele:

text
replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5

replace (
    golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
    golang.org/x/net => example.com/fork/net v1.4.5
    golang.org/x/net v1.2.3 => ./fork/net
    golang.org/x/net => ./fork/net
)

Nur die Version links von => wird ersetzt; andere Versionen derselben Abhängigkeit können weiterhin normal verwendet werden. Unabhängig davon, ob Sie lokale Pfade oder Modulpfade für den Ersatz angeben, muss bei einem Ersatzmodul mit go.mod-Datei die module-Anweisung mit dem ersetzten Modulpfad übereinstimmen.

retract

Die Anweisung retract gibt an, dass der Version oder dem Versionsbereich, der von retract angegeben wird, nicht gefolgt werden sollte. Zum Beispiel, wenn nach der Veröffentlichung einer neuen Version ein schwerwiegendes Problem entdeckt wird, kann die retract-Anweisung verwendet werden.

Einige Versionen zurückziehen:

text
retract (
    v1.0.0 // Published accidentally.
    v1.0.1 // Contains retractions only.
)

Versionsbereich zurückziehen:

text
retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
    v1.0.0
    [v1.0.0, v1.9.9]
)

go.sum

Die go.sum-Datei existiert bei der Erstellung eines Projekts zunächst nicht. Sie wird erst generiert, wenn tatsächlich externe Abhängigkeiten referenziert werden. Die go.sum-Datei ist nicht für Menschen lesbar und es wird nicht empfohlen, sie manuell zu bearbeiten. Ihre Hauptaufgabe besteht darin, das Problem der konsistenten Erstellung zu lösen, d.h. sicherzustellen, dass verschiedene Personen in verschiedenen Umgebungen bei der Erstellung desselben Projekts genau dieselben Abhängigkeitspakete verwenden. Dies kann nicht allein durch eine go.mod-Datei gewährleistet werden.

Schauen wir uns an, was Go beim Herunterladen einer Abhängigkeit von Anfang bis Ende tut. Verwenden Sie zunächst folgenden Befehl, um eine Abhängigkeit herunterzuladen:

go get github.com/bytedance/sonic v1.8.0

Der Befehl go get lädt das Abhängigkeitspaket zunächst in das lokale Cache-Verzeichnis herunter, normalerweise $GOMODCACHE/cache/download/. Dieses Verzeichnis unterteilt Abhängigkeitspakete verschiedener Websites nach Domainnamen, sodass Sie möglicherweise folgende Verzeichnisstruktur sehen:

sh
$ ls
cloud.google.com/      go.opencensus.io/     gopkg.in/          nhooyr.io/
dmitri.shuralyov.com/  go.opentelemetry.io/  gorm.io/           rsc.io/
github.com/            go.uber.org/          honnef.co/         sumdb/
go.etcd.io/            golang.org/           lukechampine.com/
go.mongodb.org/        google.golang.org/    modernc.org/

Der Speicherpfad der im obigen Beispiel heruntergeladenen Abhängigkeit ist:

$GOMODCACHE/cache/download/github.com/bytedance/sonic/@v/

Eine mögliche Verzeichnisstruktur mit mehreren versionsbenannten Dateien:

sh
$ ls
list         v1.8.0.lock  v1.8.0.ziphash  v1.8.3.mod
v1.5.0.mod   v1.8.0.mod   v1.8.3.info     v1.8.3.zip
v1.8.0.info  v1.8.0.zip   v1.8.3.lock     v1.8.3.ziphash

Normalerweise gibt es in diesem Verzeichnis eine list-Datei, die die bekannten Versionsnummern der Abhängigkeit enthält. Für jede Version gibt es folgende Dateien:

  • zip: Komprimiertes Archiv des Abhängigkeitsquellcodes
  • ziphash: Hash-Wert, der aus dem Abhängigkeitsarchiv berechnet wurde
  • info: Versionsmetadaten im JSON-Format
  • mod: Die go.mod-Datei dieser Version
  • lock: Temporäre Datei, offiziell nicht näher erläutert

Normalerweise berechnet Go die Hash-Werte der Archivdatei und der go.mod-Datei und fragt dann den von GOSUMDB angegebenen Server (standardmäßig sum.golang.org) nach dem Hash-Wert des Abhängigkeitspakets ab. Wenn die lokal berechneten Hash-Werte nicht mit den abgefragten Ergebnissen übereinstimmen, wird die Ausführung nicht fortgesetzt. Wenn sie übereinstimmen, wird die go.mod-Datei aktualisiert und zwei Datensätze in die go.sum-Datei eingefügt, etwa wie folgt:

github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=

TIP

Wenn GOSUMDB deaktiviert ist, schreibt Go die lokal berechneten Hash-Werte direkt in die go.sum-Datei. Dies wird im Allgemeinen nicht empfohlen.

Normalerweise hat jede Abhängigkeit zwei Datensätze: der erste ist der Hash-Wert des Archivs, der zweite der Hash-Wert der go.mod-Datei des Abhängigkeitspakets. Das Datensatzformat ist Modulname Versionsnummer Algorithmusname:Hash-Wert. Einige ältere Abhängigkeitspakete haben möglicherweise keine go.mod-Datei, daher gibt es keinen zweiten Hash-Datensatz. Wenn dieses Projekt in einer anderen Umgebung erstellt wird, berechnet Go die Hash-Werte basierend auf den lokalen Abhängigkeiten, die in go.mod angegeben sind, und vergleicht sie mit den in go.sum aufgezeichneten Hash-Werten. Wenn die Hash-Werte nicht übereinstimmen, bedeutet dies, dass die Abhängigkeitsversionen unterschiedlich sind, und der Build wird abgelehnt. In diesem Fall könnten sowohl die lokalen Abhängigkeiten als auch die go.sum-Datei geändert worden sein. Da go.sum jedoch über GOSUMDB abgefragt wurde, wird eher der go.sum-Datei vertraut.

Private Module

Die meisten Go Mod-Tools sind auf Open-Source-Projekte ausgerichtet, aber Go bietet auch Unterstützung für private Module. Für private Projekte müssen normalerweise folgende Umgebungsvariablen konfiguriert werden:

  • GOPROXY: Sammlung von Proxy-Servern für Abhängigkeiten
  • GOPRIVATE: Liste von Mustern für Modulpfad-Präfixe privater Module. Wenn der Modulname der Regel entspricht, wird das Modul als privat behandelt. Das Verhalten entspricht GONOPROXY und GONOSUMDB.
  • GONOPROXY: Liste von Mustern für Modulpfad-Präfixe, die nicht über den Proxy heruntergeladen werden. Wenn die Regel zutrifft, wird beim Herunterladen des Moduls nicht GOPROXY verwendet, sondern versucht, direkt vom Versionskontrollsystem herunterzuladen.
  • GONOSUMDB: Liste von Mustern für Modulpfad-Präfixe, die nicht über die öffentliche GOSUMDB-Verifizierung erfolgen. Wenn die Regel zutrifft, wird beim Herunterladen des Moduls nicht die öffentliche Checksum-Datenbank verwendet.
  • GOINSECURE: Liste von Mustern für Modulpfad-Präfixe, die über HTTP und andere unsichere Protokolle abgerufen werden können.

Workspace

Zuvor wurde erwähnt, dass die go.mod-Datei die replace-Anweisung unterstützt, die es ermöglicht, vorübergehend lokale Änderungen zu verwenden, die noch nicht veröffentlicht wurden:

replace (
  github.com/246859/hello v1.0.1 => ./hello
)

Beim Kompilieren verwendet Go dann das lokale hello-Modul. Nach der Veröffentlichung einer neuen Version wird dies entfernt.

Wenn Sie jedoch die replace-Anweisung verwenden, wird der Inhalt der go.mod-Datei geändert. Diese Änderung könnte versehentlich in das Remote-Repository committet werden, was nicht erwünscht ist, da das von der replace-Anweisung angegebene Target ein Dateipfad und keine Netzwerk-URL ist. Ein Pfad, der auf diesem Computer funktioniert, funktioniert möglicherweise auf einem anderen Computer nicht. Dateipfade können auch ein plattformübergreifendes Problem darstellen. Um solche Probleme zu lösen, wurde der Workspace eingeführt.

Workspace ist eine neue Lösung für das Multi-Modul-Management, die Go in Version 1.18 eingeführt hat. Sie soll die lokale Multi-Modul-Entwicklung erleichtern. Im Folgenden wird dies anhand eines Beispiels erläutert.

Beispiel-Repository: 246859/work: go work example (github.com)

Beispiel

Das Projekt enthält zunächst zwei unabhängige Go-Module: auth und user:

bash
$ ls -1
LICENSE
README.md
auth
go.work
user

Das auth-Modul ist von der Struktur User des user-Moduls abhängig. Der Inhalt lautet:

go
package auth

import (
  "errors"
  "github.com/246859/work/user"
)

// Verify user credentials if is ok
func Verify(user user.User) (bool, error) {
  password, err := query(user.Name)
  if err != nil {
    return false, err
  }
  if password != user.Password {
    return false, errors.New("authentication failed")
  }
  return true, nil
}

func query(username string) (string, error) {
  if username == "jack" {
    return "jack123456", nil
  }
  return "", errors.New("user not found")
}

Der Inhalt des user-Moduls lautet:

go
package user

type User struct {
  Name     string
  Password string
  Age      int
}

In diesem Projekt können wir die go.work-Datei wie folgt schreiben:

go 1.22

use (
  ./auth
  ./user
)

Der Inhalt ist sehr einfach zu verstehen. Mit der use-Anweisung geben Sie an, welche Module an der Kompilierung teilnehmen. Führen Sie dann den Code im auth-Modul aus:

go
// auth/example/main.go
package main

import (
  "fmt"
  "github.com/246859/work/auth"
  "github.com/246859/work/user"
)

func main() {
  ok, err := auth.Verify(user.User{Name: "jack", Password: "jack123456"})
  if err != nil {
    panic(err)
  }
  fmt.Printf("%v", ok)
}

Führen Sie folgenden Befehl aus. Das Ergebnis zeigt, dass das Modul erfolgreich importiert wurde:

bash
$ go run ./auth/example
true

In früheren Versionen gab es für diese beiden unabhängigen Module nur zwei Möglichkeiten, wenn das auth-Modul den Code des user-Moduls verwenden wollte:

  1. Die Änderungen des user-Moduls committen und in das Remote-Repository pushen, eine neue Version veröffentlichen und dann die go.mod-Datei auf die angegebene Version ändern
  2. Die go.mod-Datei ändern, um die Abhängigkeit auf eine lokale Datei umzuleiten

Beide Methoden erfordern die Änderung der go.mod-Datei. Die Existenz des Workspaces dient dazu, andere Module importieren zu können, ohne die go.mod-Datei zu ändern. Es ist jedoch zu verstehen, dass die go.work-Datei nur während der Entwicklung verwendet wird. Sie existiert nur, um die lokale Entwicklung bequemer zu machen, nicht für das Abhängigkeitsmanagement. Sie ermöglicht es Ihnen vorübergehend, den Prozess des Committens und Veröffentlichens zu überspringen, sodass Sie neue Änderungen des user-Moduls sofort verwenden können, ohne warten zu müssen. Nachdem das user-Modul getestet wurde, muss dennoch eine neue Version veröffentlicht werden, und das auth-Modul muss schließlich die go.mod-Datei ändern, um die neueste Version zu referenzieren (dieser Prozess kann mit dem Befehl go work sync durchgeführt werden). Daher sollte go.work im normalen Go-Entwicklungsprozess nicht in das VCS committet werden (die go.work im Beispiel-Repository dient nur Demonstrationszwecken), da ihr Inhalt von lokalen Dateien abhängt und ihre Funktionalität auf die lokale Entwicklung beschränkt ist.

Befehle

Hier sind einige Workspace-Befehle:

BefehlBeschreibung
editgo.work bearbeiten
initEinen neuen Workspace initialisieren
syncModulabhängigkeiten des Workspaces synchronisieren
useEin neues Modul zur go.work hinzufügen
vendorAbhängigkeiten im Vendor-Format kopieren

Weitere Informationen zu den Befehlen finden Sie unter go work cmd

Anweisungen

Der Inhalt der go.work-Datei ist sehr einfach und enthält nur drei Anweisungen:

  • go: Go-Version angeben
  • use: Zu verwendende Module angeben
  • replace: Zu ersetzende Module angeben

Außer der use-Anweisung entsprechen die anderen beiden im Wesentlichen den Anweisungen in go.mod, mit dem Unterschied, dass die replace-Anweisung in go.work auf alle Module wirkt. Eine vollständige go.work sieht wie folgt aus:

tex
go 1.22

use(
  ./auth
  ./user
)

repalce github.com/246859/hello v1.0.0 => /home/jack/code/hello

Golang by www.golangdev.cn edit