German (Deutsch) translation by Tatsiana Bochkareva (you can also view the original English article)
Go ist eine erstaunliche Sprache mit viel Schwung und konzentriert sich auf Einfachheit. Dieser Ansatz zeigt sich in seiner Standardbibliothek, die alle wesentlichen, aber nicht viel mehr enthält.
Glücklicherweise hat Go eine lebendige Community, die viele Bibliotheken von Drittanbietern erstellt und gemeinsam nutzt. In diesem Tutorial stelle ich Ihnen 12 der besten Pakete und Bibliotheken von Go vor. Einige von ihnen haben einen relativ engen Umfang und können zu jedem Projekt hinzugefügt werden, während andere große Projekte sind, die Sie in massive, große verteilte Systeme integrieren können.
Awesome Go
Bevor ich in die Bibliotheken selbst eintauche, möchte ich Ihnen Awesome Go vorstellen, eine sehr aktive und kuratierte Liste von Go-Bibliotheken und anderen Ressourcen. Sie sollten ab und zu besuchen und prüfen, was es Neues gibt.
1. Golang-Set
Go hat Arrays, Slices und Maps, aber keine festgelegte Datenstruktur. Sie können einen Satz mit einer Karte von Bools nachahmen, aber es ist schön, einen tatsächlichen Datentyp mit den richtigen Operationen und der richtigen Semantik zu haben. Hier kommt das Golang-Set ins Spiel. Hier ist ein grundlegendes Beispiel für das Erstellen eines neuen Sets, das Hinzufügen von Elementen und das Testen der Mitgliedschaft:
1 |
package main |
2 |
|
3 |
import ( |
4 |
"fmt" |
5 |
"github.com/deckarep/golang-set" |
6 |
) |
7 |
|
8 |
|
9 |
func main() { |
10 |
basicColors := mapset.NewSet() |
11 |
basicColors.Add("Red") |
12 |
basicColors.Add("Blue") |
13 |
basicColors.Add("Green") |
14 |
|
15 |
if basicColors.Contains("Green") { |
16 |
fmt.Println("Yay! 'Green' is a basic color") |
17 |
} else { |
18 |
fmt.Println("What a disappointment! 'Green' is not a basic color") |
19 |
} |
20 |
|
21 |
|
22 |
if basicColors.Contains("Yellow") { |
23 |
fmt.Println("Yay! 'Yellow' is a basic color") |
24 |
} else { |
25 |
fmt.Println("What a disappointment! 'Yellow' is not a basic color") |
26 |
} |
27 |
} |
28 |
|
29 |
Output: |
30 |
|
31 |
Yay! 'Green' is a basic color |
32 |
What a disappointment! 'Yellow' is not a basic color |
Beachten Sie, dass der Paketname "mapset" lautet. Zusätzlich zu den Grundlagen führen Sie alle festgelegten Operationen wie Vereinigung, Schnittmenge und Differenz aus. Sie können die festgelegten Werte auch durchlaufen:
1 |
package main |
2 |
|
3 |
import ( |
4 |
"fmt" |
5 |
"github.com/deckarep/golang-set" |
6 |
) |
7 |
|
8 |
|
9 |
func main() { |
10 |
basicColors := mapset.NewSet() |
11 |
basicColors.Add("Red") |
12 |
basicColors.Add("Blue") |
13 |
basicColors.Add("Green") |
14 |
|
15 |
otherColors := mapset.NewSetFromSlice([]interface{}{"Orange", "Yellow", "Indigo", "Violet"}) |
16 |
rainbowColors := basicColors.Union(otherColors) |
17 |
|
18 |
for color := range rainbowColors.Iterator().C { |
19 |
fmt.Println(color) |
20 |
} |
21 |
} |
2. Farbe
Gehen wir mit dem FarbTheme fort. Beim Schreiben von Befehlszeilenprogrammen ist es hilfreich, Farben zu verwenden, um wichtige Meldungen hervorzuheben oder zwischen Fehlern, Erfolgen und Warnungen zu unterscheiden.
Das Farbpaket bietet eine einfache Möglichkeit, Ihren Programmen Farbe hinzuzufügen (Sehen Sie, was ich dort gemacht habe?). Es verwendet ANSII-Escape-Codes und unterstützt auch Windows! Hier ist ein kurzes Beispiel:
1 |
package main |
2 |
|
3 |
import ( |
4 |
"github.com/fatih/color" |
5 |
) |
6 |
|
7 |
func main() { |
8 |
color.Red("Roses are red") |
9 |
color.Blue("Violets are blue") |
10 |
} |
Das Farbpaket unterstützt das Mischen von Farben mit Hintergrundfarben, Stilen wie Fett oder Kursiv und das Besprühen von Farben mit nicht farbiger Ausgabe.
1 |
package main |
2 |
|
3 |
import ( |
4 |
"github.com/fatih/color" |
5 |
"fmt" |
6 |
) |
7 |
|
8 |
func main() { |
9 |
minion := color.New(color.FgBlack).Add(color.BgYellow).Add(color.Bold) |
10 |
minion.Println("Minion says: banana!!!!!!") |
11 |
|
12 |
m := minion.PrintlnFunc() |
13 |
m("I want another banana!!!!!") |
14 |
|
15 |
slantedRed := color.New(color.FgRed, color.BgWhite, color.Italic).SprintFunc() |
16 |
fmt.Println("I've made a huge", slantedRed("mistake")) |
17 |
} |
Das Farbpaket bietet weitere nützliche Funktionen. Gehen Sie voran und erkunden Sie mehr.
3. Now
Now ist ein sehr einfaches Paket, das einen praktischen Wrapper für das Standardzeitpaket bietet und die Arbeit mit verschiedenen Datums- und Zeitkonstrukten zur aktuellen Zeit vereinfacht.
Sie können den Beginn der aktuellen Minute oder das Ende des Sonntags abrufen, der deraktuellen Zeit am nächsten liegt. So verwenden Sie "now":
1 |
package main |
2 |
|
3 |
import ( |
4 |
"github.com/jinzhu/now" |
5 |
"fmt" |
6 |
) |
7 |
|
8 |
func main() { |
9 |
|
10 |
fmt.Println("All the beginnings...") |
11 |
fmt.Println(now.BeginningOfMinute()) |
12 |
fmt.Println(now.BeginningOfHour()) |
13 |
fmt.Println(now.BeginningOfDay()) |
14 |
fmt.Println(now.BeginningOfWeek()) |
15 |
fmt.Println(now.BeginningOfMonth()) |
16 |
fmt.Println(now.BeginningOfQuarter()) |
17 |
fmt.Println(now.BeginningOfYear()) |
18 |
|
19 |
} |
20 |
|
21 |
Output: |
22 |
|
23 |
All the beginnings... |
24 |
2017-06-04 16:59:00 -0700 PDT |
25 |
2017-06-04 16:00:00 -0700 PDT |
26 |
2017-06-04 00:00:00 -0700 PDT |
27 |
2017-06-04 00:00:00 -0700 PDT |
28 |
2017-06-01 00:00:00 -0700 PDT |
29 |
2017-04-01 00:00:00 -0700 PDT |
30 |
2016-12-31 23:00:00 -0800 PST |
Sie können auch Zeiten analysieren und sogar Ihre eigenen Formate hinzufügen (für die die bekannten Formate aktualisiert werden müssen). Der Now
-Typ bettet time.Time
ein, sodass Sie die gesamte time.Time
-Methode direkt für Now
-Objekte verwenden können.
4. Gen
Das gen-Werkzeug generiert Code für Sie - insbesondere typenbezogenen Code, der versucht, die Lücke zu schließen, in der keine Vorlagen oder Generika in Go vorhanden sind.
Sie kommentieren Ihre Typen mit einem speziellen Kommentar und gen generiert Quelldateien, die Sie in Ihr Projekt aufnehmen. Keine Laufzeitmagie. Sehen wir uns ein Beispiel an. Hier ist ein kommentierter Typ.
1 |
// +gen slice:"Where,Count,GroupBy[int]" |
2 |
type Person struct { |
3 |
Name string |
4 |
Age int |
5 |
} |
Wenn Sie gen
ausführen (stellen Sie sicher, dass es sich in Ihrem Pfad befindet), wird person_slice.go
generiert:
1 |
// Generated by: gen |
2 |
// TypeWriter: slice |
3 |
// Directive: +gen on Person |
4 |
|
5 |
package main |
6 |
|
7 |
// PersonSlice is a slice of type Person. Use it where you would use []Person. |
8 |
type PersonSlice []Person |
9 |
|
10 |
// Where returns a new PersonSlice whose elements return true for func. See: https://clipperhouse.github.io/gen/#Where |
11 |
func (rcv PersonSlice) Where(fn func(Person) bool) (result PersonSlice) { |
12 |
for _, v := range rcv { |
13 |
if fn(v) { |
14 |
result = append(result, v) |
15 |
} |
16 |
} |
17 |
return result |
18 |
} |
19 |
|
20 |
// Count gives the number elements of PersonSlice that return true for the passed func. See: http://clipperhouse.github.io/gen/#Count |
21 |
func (rcv PersonSlice) Count(fn func(Person) bool) (result int) { |
22 |
for _, v := range rcv { |
23 |
if fn(v) { |
24 |
result++ |
25 |
} |
26 |
} |
27 |
return |
28 |
} |
29 |
|
30 |
// GroupByInt groups elements into a map keyed by int. See: http://clipperhouse.github.io/gen/#GroupBy |
31 |
func (rcv PersonSlice) GroupByInt(fn func(Person) int) map[int]PersonSlice { |
32 |
result := make(map[int]PersonSlice) |
33 |
for _, v := range rcv { |
34 |
key := fn(v) |
35 |
result[key] = append(result[key], v) |
36 |
} |
37 |
return result |
38 |
} |
Der Code bietet LINQ-ähnliche Methoden für die Bearbeitung des PersonSlice
-Typs. Es ist einfach zu verstehen und gut dokumentiert.
So verwenden Sie es. In der Hauptfunktion wird eine PersonSlice
definiert. Die Funktion age()
wählt das Altersfeld aus dem Argument Person
aus. Die generierte GroupByInt()
-Funktion übernimmt die age()
-Funktion und gibt die Personen aus dem Slice zurück, die nach ihrem Alter gruppiert sind (34 ist nur Jim, 23 hat sowohl Jane als auch Kyle).
1 |
package main |
2 |
|
3 |
import ( |
4 |
"fmt" |
5 |
) |
6 |
|
7 |
// +gen slice:"Where,Count,GroupBy[int]" |
8 |
type Person struct { |
9 |
Name string |
10 |
Age int |
11 |
} |
12 |
|
13 |
func age(p Person) int { |
14 |
return p.Age |
15 |
} |
16 |
|
17 |
func main() { |
18 |
people := PersonSlice { |
19 |
{"Jim", 34}, |
20 |
{"Jane", 23}, |
21 |
{"Kyle", 23}, |
22 |
} |
23 |
|
24 |
groupedByAge := people.GroupByInt(age) |
25 |
|
26 |
fmt.Println(groupedByAge) |
27 |
} |
28 |
|
29 |
|
30 |
Output: |
31 |
|
32 |
map[34:[{Jim 34}] 23:[{Jane 23} {Kyle 23}]] |
5. Gorm
Go ist bekannt für seine spartanische Natur. Die Datenbankprogrammierung ist nicht anders. Die beliebtesten DB-Bibliotheken für Go sind ziemlich niedrig. Gorm bringt die Welt der objektrelationalen Zuordnung mit den folgenden Funktionen in Go:
- Assoziationen (Has One, Has Many, Belongs To, Many To Many, Polymorphism)
- Rückrufe (Before/After Create/Save/Update/Delete/Find)
- Vorladen (eifriges Laden)
- Transaktionen
- Zusammengesetzter Primärschlüssel
- SQL Builder
- Automatische Migrationen
- Logger
- Erweiterbare Schreib-Plugins basierend auf GORM-Rückrufen
Aber es deckt nicht alles ab. Wenn Sie aus Python kommen, erwarten Sie keine SQLAlchemy-Magie. Für ausgefallenere Sachen musst du ein niedrigeres Level gehen. Hier ist ein Beispiel für die Verwendung von Gorm mit SQLite. Beachten Sie das eingebettete Modell gorm.Model
in der Produktstruktur.
1 |
package main |
2 |
|
3 |
import ( |
4 |
"github.com/jinzhu/gorm" |
5 |
_ "github.com/jinzhu/gorm/dialects/sqlite" |
6 |
) |
7 |
|
8 |
type Product struct { |
9 |
gorm.Model |
10 |
Code string |
11 |
Price uint |
12 |
} |
13 |
|
14 |
func main() { |
15 |
db, err := gorm.Open("sqlite3", "test.db") |
16 |
if err != nil { |
17 |
panic("failed to connect database") |
18 |
} |
19 |
defer db.Close() |
20 |
|
21 |
// Migrate the schema |
22 |
db.AutoMigrate(&Product{}) |
23 |
|
24 |
// Create |
25 |
db.Create(&Product{Code: "L1212", Price: 1000}) |
26 |
|
27 |
// Read |
28 |
var product Product |
29 |
db.First(&product, 1) // find product with id 1 |
30 |
db.First(&product, "code = ?", "L1212") |
31 |
|
32 |
// Update - update product's price to 2000 |
33 |
db.Model(&product).Update("Price", 2000) |
34 |
|
35 |
// Delete - delete product |
36 |
db.Delete(&product) |
6. Goose
Eine der wichtigsten Aufgaben bei der Arbeit mit relationalen Datenbanken ist die Verwaltung des Schemas. Das Ändern des DB-Schemas wird in einigen Organisationen als "beängstigende" Änderung angesehen. Mit dem goose-Paket können Sie bei Bedarf Schemaänderungen und sogar Datenmigrationen durchführen. Sie können goose up
und goose down
, um hin und her zu gehen. Achten Sie jedoch auf Ihre Daten und stellen Sie sicher, dass sie nicht verloren gehen oder beschädigt werden.
Goose versioniert Ihr Schema und verwendet Migrationsdateien, die jedem Schema entsprechen. Die Migrationsdateien können SQL-Befehle oder Go-Befehle sein. Hier ist ein Beispiel für eine SQL-Migrationsdatei, die eine neue Tabelle hinzufügt:
1 |
-- +goose Up |
2 |
CREATE TABLE person ( |
3 |
id int NOT NULL, |
4 |
name text, |
5 |
age int, |
6 |
PRIMARY KEY(id) |
7 |
); |
8 |
|
9 |
-- +goose Down |
10 |
DROP TABLE person; |
Die Kommentare -- +goose up
und -- +goose down
geben goose an, was zu tun ist, um das Schema zu aktualisieren oder herunterzustufen.
7. Glide
Glide ist ein Paketmanager für Go. Unter einem einzigen GOPATH
haben Sie möglicherweise viele Programme mit widersprüchlichen Abhängigkeiten. Die Lösung besteht darin, dass jedes Programm sein eigenes Herstellerverzeichnis mit Paketabhängigkeiten verwaltet. Glide hilft bei dieser Aufgabe.
Hier sind die Merkmale des Gleitens:
- Unterstützt Versionspakete, einschließlich Semantic Versioning 2.0.0.
- Unterstützt Aliasing-Pakete (z. B. zum Arbeiten mit Github-Gabeln).
- Entfernen Sie keine Munging-Importanweisungen mehr.
- Arbeiten Sie mit allen Go-Werkzeugs.
- Unterstützt alle von Go unterstützten VCS-Werkzeugs (git, bzr, hg, svn).
- Unterstützt benutzerdefinierte lokale und globale Plugins.
- Repository-Caching und Daten-Caching für verbesserte Leistung.
- Reduzieren Sie Abhängigkeiten, lösen Sie Versionsunterschiede und vermeiden Sie die mehrfache Aufnahme eines Pakets.
- Verwalten und installieren Sie Abhängigkeiten nach Bedarf oder in Ihrem Versionskontrollsystem.
Die Abhängigkeiten werden in glide.yaml gespeichert, und glide bietet verschiedene Befehle zum Verwalten von Abhängigkeiten:
1 |
create, init Initialize a new project, creating a |
2 |
glide.yaml file |
3 |
config-wizard, cw Wizard that makes optional suggestions |
4 |
to improve config in a glide.yaml file. |
5 |
get Install one or more packages into |
6 |
`vendor/` and add dependency to |
7 |
glide.yaml. |
8 |
remove, rm Remove a package from the glide.yaml |
9 |
file, and regenerate the lock file. |
10 |
import Import files from other dependency |
11 |
management systems. |
12 |
name Print the name of this project. |
13 |
novendor, nv List all non-vendor paths in a |
14 |
directory. |
15 |
rebuild Rebuild ('go build') the dependencies |
16 |
install, i Install a project's dependencies |
17 |
update, up Update a project's dependencies |
18 |
tree (Deprecated) Tree prints the |
19 |
dependencies of this project as a tree. |
20 |
list List prints all dependencies that the |
21 |
present code references. |
22 |
info Info prints information about this |
23 |
project |
24 |
cache-clear, cc Clears the Glide cache. |
25 |
about Learn about Glide |
26 |
mirror Manage mirrors |
27 |
help, h Shows a list of commands or help for |
28 |
one command |
8. Ginkgo
Ginkgo ist ein BDD-Testframework (Behavior Driven Development). Sie können Ihre Tests in einer Syntax schreiben, die Englisch ähnelt, und weniger technischen Mitarbeitern ermöglichen, Tests (und deren Ausgabe) zu überprüfen und zu überprüfen, ob sie den Geschäftsanforderungen entsprechen.
Einige Entwickler mögen diese Art der Testspezifikation auch. Es lässt sich in das integrierte Testpaket von Go integrieren und wird häufig mit Gomega kombiniert. Hier ist ein Beispiel für einen Ginkgo + Gomega-Test:
1 |
actual, err := foo() |
2 |
Ω(err).Should(BeNil()) |
3 |
Ω(actual).ShouldNot(BeNil()) |
4 |
Ω(actual.result).Should(Equal(100)) |
9. Etcd
Etcd ist ein zuverlässiger verteilter Schlüsselwertspeicher. Der Server ist in Go implementiert und der Go-Client interagiert mit ihm über gRPC.
Es konzentriert sich auf Folgendes:
- Einfach: gut definierte, benutzerbezogene API (gRPC).
- Sicher: Automatisches TLS mit optionaler Clientzertifizierungsauthentifizierung.
- Schnell: Benchmarking von 10.000 Schreibvorgängen/Sek.
- Zuverlässig: Mit Raft richtig verteilt.
Hier ist ein Beispiel für das Herstellen einer Verbindung zum Server, das Eingeben und Abrufen eines Werts, einschließlich Zeitüberschreitungen und Bereinigung.
1 |
func test_get() { |
2 |
cli, err := clientv3.New(clientv3.Config{ |
3 |
Endpoints: endpoints, |
4 |
DialTimeout: dialTimeout, |
5 |
}) |
6 |
if err != nil { |
7 |
log.Fatal(err) |
8 |
} |
9 |
defer cli.Close() |
10 |
|
11 |
_, err = cli.Put(context.TODO(), "foo", "bar") |
12 |
if err != nil { |
13 |
log.Fatal(err) |
14 |
} |
15 |
|
16 |
ctx, cancel := context.WithTimeout(context.Background(), |
17 |
requestTimeout) |
18 |
resp, err := cli.Get(ctx, "foo") |
19 |
cancel() |
20 |
if err != nil { |
21 |
log.Fatal(err) |
22 |
} |
23 |
for _, ev := range resp.Kvs { |
24 |
fmt.Printf("%s : %s\n", ev.Key, ev.Value) |
25 |
} |
26 |
// Output: foo : bar |
27 |
} |
10. NSQ
NSQ ist eine großartige verteilte Warteschlange. Ich habe es erfolgreich als primären Baustein für verteilte Großsysteme verwendet. Hier sind einige seiner Funktionen:
- Unterstützt verteilte Topologien ohne SPOF.
- Horizontal skalierbar (keine Broker, fügen Sie dem Cluster nahtlos weitere Knoten hinzu).
- Push-basierte Nachrichtenübermittlung mit geringer Latenz (Leistung).
- Kombination aus lastausgeglichenem und Multicast-Nachrichtenrouting.
- Excel sowohl bei Streaming-Workloads (hoher Durchsatz) als auch bei joborientierten Workloads (niedriger Durchsatz).
- Hauptsächlich im Speicher (Nachrichten, die über eine Hochwassermarke hinausgehen, werden transparent auf der Festplatte gespeichert).
- Runtime Discovery Service für Verbraucher, um Produzenten zu finden (nsqlookupd).
- Transport Layer Security (TLS).
- Datenformat agnostisch.
- Nur wenige Abhängigkeiten (einfach bereitzustellen) und eine vernünftige, begrenzte Standardkonfiguration.
- Einfaches TCP-Protokoll, das Client-Bibliotheken in jeder Sprache unterstützt.
- HTTP-Schnittstelle für Statistiken, Administratoraktionen und Produzenten (zum Veröffentlichen ist keine Clientbibliothek erforderlich).
- Integriert in statsd für Echtzeitinstrumentierung.
- Robuste Cluster-Verwaltungsschnittstelle (nsqadmin).
Wie man eine Nachricht in NSQ veröffentlicht (Fehlerbehandlung entfällt):
1 |
package main |
2 |
|
3 |
import ( |
4 |
"github.com/bitly/go-nsq" |
5 |
) |
6 |
|
7 |
func main() { |
8 |
config := nsq.NewConfig() |
9 |
p, _ := nsq.NewProducer("127.0.0.1:4150", config) |
10 |
|
11 |
p.Publish("topic", []byte("message")) |
12 |
p.Stop() |
13 |
} |
Und so konsumieren Sie:
1 |
package main |
2 |
|
3 |
import ( |
4 |
"sync" |
5 |
"fmt" |
6 |
"github.com/bitly/go-nsq" |
7 |
) |
8 |
|
9 |
func main() { |
10 |
wg := &sync.WaitGroup{} |
11 |
wg.Add(1) |
12 |
|
13 |
config := nsq.NewConfig() |
14 |
q, _ := nsq.NewConsumer("topic", "channel", config) |
15 |
handler := nsq.HandlerFunc(func(message *nsq.Message) error { |
16 |
fmt.Printf("Got a message: %v", message) |
17 |
wg.Done() |
18 |
return nil |
19 |
}) |
20 |
q.AddHandler(handler) |
21 |
q.ConnectToNSQD("127.0.0.1:4150") |
22 |
wg.Wait() |
23 |
} |
11. Docker
Docker ist heute ein bekannter Name (wenn Ihre Familienmitglieder hauptsächlich DevOps-Leute sind). Möglicherweise wissen Sie nicht, dass Docker in Go implementiert ist. Normalerweise verwenden Sie Docker nicht in Ihrem Code, aber es ist ein bedeutendes Projekt und verdient es, als äußerst erfolgreiches und beliebtes Go-Projekt anerkannt zu werden.
12. Kubernetes
Kubernetes ist eine Open-Source-Container-Orchestrierungsplattform für Cloud-native Anwendungen. Es ist ein weiteres in Go implementiertes Monster-Distributed-System. Ich habe kürzlich ein Buch mit dem Titel Mastering Kubernetes geschrieben, in dem ich die fortschrittlichsten Aspekte von Kubernetes ausführlich erläutere. Aus Sicht des Go-Entwicklers ist Kubernetes sehr flexibel und kann über Plugins erweitert und angepasst werden.
Schlussfolgerung
Go ist eine großartige Sprache. Seine Designphilosophie ist es, eine einfache und ansprechbare Sprache zu sein. Die Standardbibliothek ist nicht so umfassend wie einige andere Sprachen wie Python.
Die Go-Community wurde erweitert, und es gibt viele hochwertige Bibliotheken, die Sie in Ihren Programmen verwenden können. In diesem Artikel habe ich 12 Bibliotheken vorgestellt. Ich empfehle Ihnen, nach anderen Bibliotheken zu suchen, bevor Sie alles von Grund auf neu implementieren.