12 Packages và Libraries của Go cực kì mạnh mẽ mà bạn cần phải biết

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 : %s\n", 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