Go la một ngôn ngữ lập trình vô cùng tuyệt vời và tiềm năng, với phong cách đơn giản hóa. Được thể hiện khá rõ với standard library, vốn chứa tất cả mọi thứ cần thiết, nhưng không hề dư thừa.
Thật may, Go có một cộng đồng khá sôi động với nhiều libraries của “third-party” được chia sẻ tại đây. Trong bài hướng dẫn này, tôi sẽ giới thiệu cho bạn 12 package và libraries tốt nhất của Go. Trải dài từ qui mô nhỏ thích hợp cho mọi project cho đến các hệ thống xử lí cực lớn và phức tạp.
Awesome Go
Trước khi nhảy vào tìm hiểu về libraries, tôi muốn giới thiệu các bạn về Awesome Go, một list danh sách các Go libraries và những nguồn khác, khá chính xác và luôn được cập nhật thường xuyên. Nên hãy bỏ công vào xem nhé.
Golang-Set
Go có arrays, slices và maps, nhưng nó lại không có một set data structure. bạn có thể bước trước một set với một map của bools, nhưng nó vẫn sẽ luôn tốt hơn nếu ta có data type thật với operations đúng. Đó chính là mục đích của golang-set. Sau đây là một ví dụ tạo ra một set mới, thêm items và testing cho membership:
package main import ( "fmt" "github.com/deckarep/golang-set" ) func main() { basicColors := mapset.NewSet() basicColors.Add("Red") basicColors.Add("Blue") basicColors.Add("Green") if basicColors.Contains("Green") { fmt.Println("Yay! 'Green' is a basic color") } else { fmt.Println("What a disappointment! 'Green' is not a basic color") } if basicColors.Contains("Yellow") { fmt.Println("Yay! 'Yellow' is a basic color") } else { fmt.Println("What a disappointment! 'Yellow' is not a basic color") } } Output: Yay! 'Green' is a basic color What a disappointment! 'Yellow' is not a basic color
Lưu ý rằng do package có tên là “mapset”. Ngoài mấy tính năng cơ bản ra, bạn có thể thực hiện được tất cả các loại set operations như union, intersection, và difference. Bạn cũng lặp được set các giá trị:
package main import ( "fmt" "github.com/deckarep/golang-set" ) func main() { basicColors := mapset.NewSet() basicColors.Add("Red") basicColors.Add("Blue") basicColors.Add("Green") otherColors := mapset.NewSetFromSlice([]interface{}{"Orange", "Yellow", "Indigo", "Violet"}) rainbowColors := basicColors.Union(otherColors) for color := range rainbowColors.Iterator().C { fmt.Println(color) } }
Color
Hãy tiếp tục với color theme. Khi bạn đang viết command-line programs, việc có thể highlight những messages quan trọng, hay phân biệt giữa errors, successes, và warnings là cực kì hữu ích.
color package cho bạn khả năng thêm nhiều màu sắc khác nhau cho chương trình của mình. Nó dùng ANSII escape codes và supports cả hệ điều hành Windows. Sau đây là một ví dụ đơn giản:
package main import ( "github.com/fatih/color" ) func main() { color.Red("Roses are red") color.Blue("Violets are blue") }
Color package hỗ trợ tính năng trộn và kết hợp màu với màu của background, style như bold hoặc italic, dùng màu với non-color output.
package main import ( "github.com/fatih/color" "fmt" ) func main() { minion := color.New(color.FgBlack).Add(color.BgYellow).Add(color.Bold) minion.Println("Minion says: banana!!!!!!") m := minion.PrintlnFunc() m("I want another banana!!!!!") slantedRed := color.New(color.FgRed, color.BgWhite, color.Italic).SprintFunc() fmt.Println("I've made a huge", slantedRed("mistake")) }
Color package còn có những tính năng hữu ích khác mà bạn nên khám phá thêm.
Now
Now là mộ package khá đơn giản với tính năng cung cấp một wrapper cho standard time package và nó rất dễ dùng với ngày và thời gian khác nhau xung quanh thời điểm hiện tại.
Ví dụ như bạn có thể bắt đầu với thời điểm hiện tại hoặc tại cuối ngày chủ nhật cho đến thời điểm hiện tại. Sau đây là cách dùng cho thời điểm là “ngay bây giờ”:
package main import ( "github.com/jinzhu/now" "fmt" ) func main() { fmt.Println("All the beginnings...") fmt.Println(now.BeginningOfMinute()) fmt.Println(now.BeginningOfHour()) fmt.Println(now.BeginningOfDay()) fmt.Println(now.BeginningOfWeek()) fmt.Println(now.BeginningOfMonth()) fmt.Println(now.BeginningOfQuarter()) fmt.Println(now.BeginningOfYear()) } Output: All the beginnings... 2017-06-04 16:59:00 -0700 PDT 2017-06-04 16:00:00 -0700 PDT 2017-06-04 00:00:00 -0700 PDT 2017-06-04 00:00:00 -0700 PDT 2017-06-01 00:00:00 -0700 PDT 2017-04-01 00:00:00 -0700 PDT 2016-12-31 23:00:00 -0800 PST
Bạn còn có thể parse thời gian và thêm vào format của riêng bạn. Now
type sẽ in vào time.Time
, nên bạn có thể dùng tất cả cách thức time.Time
trực tiếp lên Now
objects.
Gen
gen tool tạo ra code cho bạn – chính xác là chúng cho ra code thuộc type-aware với tính năng giảm bớt khoảng cách không có templates hoặc generics trong Go.
Bạn cần chú thích types của mình với comment đặc biệt. cũng như các gen generates source files mà bạn dùng trong project của mình. Sau đây là một cách để dùng annotated type:
// +gen slice:"Where,Count,GroupBy[int]" type Person struct { Name string Age int }
Chạy gen
sẽ tạo ra person_slice.go
:
// Generated by: gen // TypeWriter: slice // Directive: +gen on Person package main // PersonSlice is a slice of type Person. Use it where you would use []Person. type PersonSlice []Person // Where returns a new PersonSlice whose elements return true for func. See: http://clipperhouse.github.io/gen/#Where func (rcv PersonSlice) Where(fn func(Person) bool) (result PersonSlice) { for _, v := range rcv { if fn(v) { result = append(result, v) } } return result } // Count gives the number elements of PersonSlice that return true for the passed func. See: http://clipperhouse.github.io/gen/#Count func (rcv PersonSlice) Count(fn func(Person) bool) (result int) { for _, v := range rcv { if fn(v) { result++ } } return } // GroupByInt groups elements into a map keyed by int. See: http://clipperhouse.github.io/gen/#GroupBy func (rcv PersonSlice) GroupByInt(fn func(Person) int) map[int]PersonSlice { result := make(map[int]PersonSlice) for _, v := range rcv { key := fn(v) result[key] = append(result[key], v) } return result }
Code sẽ cung cấp những phương thức tương tự như LINQ để vận hành trên PersonSlice
type. Nó khá dễ hiểu cũng như dễ lưu trữ.
Bạn có thể dùng nó với cách sau: Trong main function, một PersonSlice
đã được defined. age()
function chọn ra tuổi thích hợp trong Person
argument của nó. GroupByInt()
function được tạo ra và nó sẽ lấy giá trị của age()
function và trả về với kết quả là nhóm những người với độ tuổi giống nhau:
package main import ( "fmt" ) // +gen slice:"Where,Count,GroupBy[int]" type Person struct { Name string Age int } func age(p Person) int { return p.Age } func main() { people := PersonSlice { {"Jim", 34}, {"Jane", 23}, {"Kyle", 23}, } groupedByAge := people.GroupByInt(age) fmt.Println(groupedByAge) } Output: map[34:[{Jim 34}] 23:[{Jane 23} {Kyle 23}]]
Gorm
Go được biết tới với khả năng cực kì mạnh mẽ. Lập trình Database cũng không phải là ngoại lệ. Phần lớn các DB libraries dành cho Go vẫn còn khá đơn sơ. Gorm mang tới object-relational mapping cho Go với những tính năng như:
- Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism)
- Callbacks (Before/After Create/Save/Update/Delete/Find)
- Preloading (eager loading)
- Transactions
- Composite Primary Key
- SQL Builder
- Auto Migrations
- Logger
- Mở rộng được, viết Plugins dựa trên GORM callbacks
Nhưng nó không phải là làm được hết mọi thứ. Nếu bạn chuyển qua từ Python thì đừng mong chờ vào SQLAlchemy. Sau đây là cách dùng Gorm với sqlite. Lưu ý gorm.Model
được đặt trong Product struct.
package main import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/sqlite" ) type Product struct { gorm.Model Code string Price uint } func main() { db, err := gorm.Open("sqlite3", "test.db") if err != nil { panic("failed to connect database") } defer db.Close() // Migrate the schema db.AutoMigrate(&Product{}) // Create db.Create(&Product{Code: "L1212", Price: 1000}) // Read var product Product db.First(&product, 1) // find product with id 1 db.First(&product, "code = ?", "L1212") // Update - update product's price to 2000 db.Model(&product).Update("Price", 2000) // Delete - delete product db.Delete(&product)
Goose
Một trong những tasks quan trọng nhất khi làm việc với relational databases là quản lí schema. Modifying DB schema được xem là thay đổi khá đáng sợ đối với nhiều người. goose package cho phép bạn thực hiện các thay đổi với schema và cả di chuyển dữ liệu nếu cần. Bạn có thể goose up
và goose down
để di chuyển về trước hoặc lui lại. Nhưng nhớ là đừng để data của bạn bị mất hoặc corrupted.
Goose hoạt động bằng cách phân biệt schema ra từng phiên bản và dùng các files tùy theo từng schema. Các files đó có thể là SQL commands hoặc Go commands. Sau đây là một ví dụ SQL migration file thêm một table mới:
-- +goose Up CREATE TABLE person ( id int NOT NULL, name text, age int, PRIMARY KEY(id) ); -- +goose Down DROP TABLE person;
-- +goose up
& -- +goose down
comment sẽ cho goose biết nên upgrade hay downgrade schema.
Glide
Glide là một package manager dành cho Go. Với GOPATH
, bạn có thể sẽ có nhiều chương trình bị xung đột dependencies. Giải pháp cho vấn đề trên là cho từng program quản lí vendor directory của package dependencies. Đây chính là lúc Glide thể hiện sức mạnh của nó.
Một vài tính năng nổi bật của Glide:
- Support versioning packages bao gồm Semantic Versioning 2.0.0 support.
- Support aliasing packages (e.g. để làm với Github).
- Remove the need for munging import statements.
- Tương thích với toàn bộ go tools.
- Support tất cả VCS tools được Go supports (git, bzr, hg, svn).
- Support custom local và global plugins.
- Repository caching và data caching để cải thiện hiệu năng.
- Flatten dependencies, giải quyết việc khác phiên bản và tránh đưa vào một package nhiều lần.
- Quản lí và cài đặt dependencies theo yêu cầu trong version control system.
Các dependencies sẽ được lưu trữ trong glide.yaml, và glide cung cấp một số commands để quản lí dependencies:
create, init Initialize a new project, creating a glide.yaml file config-wizard, cw Wizard that makes optional suggestions to improve config in a glide.yaml file. get Install one or more packages into `vendor/` and add dependency to glide.yaml. remove, rm Remove a package from the glide.yaml file, and regenerate the lock file. import Import files from other dependency management systems. name Print the name of this project. novendor, nv List all non-vendor paths in a directory. rebuild Rebuild ('go build') the dependencies install, i Install a project's dependencies update, up Update a project's dependencies tree (Deprecated) Tree prints the dependencies of this project as a tree. list List prints all dependencies that the present code references. info Info prints information about this project cache-clear, cc Clears the Glide cache. about Learn about Glide mirror Manage mirrors help, h Shows a list of commands or help for one command
Ginkgo
Ginkgo là một BDD (Behavior Driven Development) testing framework. Nó cho phép bạn viết các bài tests trong một syntax tương tự như English mà người xem dù có ít kinh nghiệm vẫn có thể hiểu được cũng như kiểm tra nó có phù hợp với yêu cầu của họ không.
Một số developers cũng rất thích test kiểu này. Nó được tích hợp với Go’s built-in testing package và thường được kết hợp với Gomega. Sau đây là một ví dụ với Ginkgo + Gomega test:
actual, err := foo() Ω(err).Should(BeNil()) Ω(actual).ShouldNot(BeNil()) Ω(actual.result).Should(Equal(100))
Etcd
Etcd là distributed Key-Value store rất đáng tin cậy. Server được implemented vào Go, và Go client tương tác với nó thông qua gRPC.
Etcd tập trung vào những điều sau:
- Đơn giản: Thiết kế gọn, user-facing API (gRPC).
- An toàn: automatic TLS vớioptional client cert authentication.
- Nhanh: benchmarked 10,000 writes/sec.
- Tin cậy: được Distributed sử dụng Raft.
Sau đây là một ví dụ với kết nối server, thêm và lấy giá trị, bao gồm timeouts và cleanup.
func test_get() { cli, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() _, err = cli.Put(context.TODO(), "foo", "bar") if err != nil { log.Fatal(err) } ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) resp, err := cli.Get(ctx, "foo") cancel() if err != nil { log.Fatal(err) } for _, ev := range resp.Kvs { fmt.Printf("%s : %sn", ev.Key, ev.Value) } // Output: foo : bar }
NSQ
NSQ là một distributed queue. Tôi từng dùng nó cho primary building block của distributed systems với qui mô lớn. NSQ thật sự rất tuyệt vời những tính năng như:
- Hỗ trợ distributed topologies với no SPOF.
- Horizontally scalable (không cần brokers, dể dàng thêm nodes vào cluster).
- Low-latency push based message delivery (performance).
- Kết hợp load-balanced và multicast style message routing.
- Cực kì mạnh mẽ với streaming (high-throughput) và job oriented (low-throughput) workloads.
- Primarily in-memory
- Runtime discovery service cho consumers để tìm producers (nsqlookupd).
- Transport layer security (TLS).
- Data format agnostic.
- Ít dependencies (dễ deploy) và một default configuration dễ dùng.
- TCP protocol supporting client libraries cực kì tối giản với nhiều ngôn ngữ khác nhau.
- HTTP interface cho stats, admin actions, và producers ( không cần client library để publish).
- Integrates với statsd cho real-time instrumentation.
- Robust cluster administration interface (nsqadmin).
Sau đây là cách chúng ta đưa message tới NSQ:
package main import ( "github.com/bitly/go-nsq" ) func main() { config := nsq.NewConfig() p, _ := nsq.NewProducer("127.0.0.1:4150", config) p.Publish("topic", []byte("message")) p.Stop() }
Còn dưới đây là cách consume:
package main import ( "sync" "fmt" "github.com/bitly/go-nsq" ) func main() { wg := &sync.WaitGroup{} wg.Add(1) config := nsq.NewConfig() q, _ := nsq.NewConsumer("topic", "channel", config) handler := nsq.HandlerFunc(func(message *nsq.Message) error { fmt.Printf("Got a message: %v", message) wg.Done() return nil }) q.AddHandler(handler) q.ConnectToNSQD("127.0.0.1:4150") wg.Wait() }
Docker
Bạn có thể không hề biết về Docker vốn được tích hợp trong Go. Bạn thường không dùng Docker trong Code của mình, tuy vậy Docker vẫn được xem là một Go project lớn và khá thành công.
Kubernetes
Kubernetes là open-source container orchestration platform dành cho cloud-native applications. Có thể nói cũng là một con quái vật distributed system được tích hợp vào Go. Tôi có viết một cuốn sách Mastering Kubernetes, giải thích khá cận kẽ những thế mạnh của Kubernetes. Đến từ một Go developer, tôi nghĩ rằng Kubernetes cực kì linh hoạt, cho phép bạn mở rộng cũng như customize với plugins.
Lời kết
Go là một ngôn ngữ lập trình tuyệt vời. Với phong cách thiết kế tối giản và thân thiện với người dùng. Kèm theo đó library của Go không qua deep như những ngôn ngữ khác.
Mặt khác, cộng đồng Go được xem là một trong những community tích cực trong thế giới lập trình, cung cấp cho bạn rất nhiều library chất lượng để sử dụng cho project của mình.
Nguồn: code.tutplus.com