0

9 kỹ thuật Go giúp code hiệu quả và "cao tay"

Dev Go senior hiếm khi dùng "chiêu thức hoa mỹ". Thứ làm họ khác biệt là kiểm soát chi tiết ngôn ngữ. Thường chỉ cần chỉnh 1 dòng code, có thể tránh memory leak ẩn hoặc tăng throughput gấp đôi.

Trước khi đi sâu vào code, nói trước: môi trường tốt là nửa chiến thắng. Go update nhanh – project cũ còn 1.16, project mới đã nhắm 1.23. Quản lý local environment thủ công dễ kill động lực.

image.png

ServBay giải quyết gọn: install Go environment with one clickrun multiple version of python (cùng các ngôn ngữ khác) song song. Mỗi project dùng version riêng, quản lý service chỉ 1 click thay vì vật lộn config file. Giao môi trường cho tool, focus code.

Có môi trường ngon, giờ vào 9 kỹ thuật Go thực chiến "4 lạng拨千 cân".

image.png

1. defer: Không chỉ cleanup, mà là safety net

defer chạy ngay trước khi function return. Newbie chủ yếu dùng close file, nhưng sức mạnh thật là co-located resource acquire/release.

Đặt defer ngay sau khi lấy resource, bạn không cần track từng return branch để chắc lock/file được free. Đây cũng là nền tảng error handling và panic/recover của Go.

func CopyFile(srcName, dstName string) error {
    src, err := os.Open(srcName)
    if err != nil {
        return err
    }
    // Release resource dù exit kiểu gì
    defer src.Close()

    dst, err := os.Create(dstName)
    if err != nil {
        return err
    }
    defer dst.Close()

    _, err = io.Copy(dst, src)
    return err
}

Cleanup logic tập trung, tránh leak tinh vi.


2. Worker Pool kiểm soát Goroutine bùng nổ

Goroutine rẻ, không miễn phí. Spawn hàng ngàn không kiểm soát gây memory bloat, scheduler overhead, overload downstream.

Dùng worker pool có context.Context để bounded concurrency và graceful cancel.

func taskProcessor(ctx context.Context, jobs <-chan int, results chan<- int) {
    for {
        select {
        case <-ctx.Done():
            return
        case job, ok := <-jobs:
            if !ok {
                return
            }
            results <- job * 2
        }
    }
}

func RunPool() {
    jobs := make(chan int, 10)
    results := make(chan int, 10)

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // Giới hạn 3 worker
    for w := 0; w < 3; w++ {
        go taskProcessor(ctx, jobs, results)
    }

    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 0; a < 5; a++ {
        fmt.Println(<-results)
    }
}

Production dùng pattern này an toàn hơn "fire-and-forget" goroutine nhiều.


3. strings.Builder: Vũ khí bí mật nối chuỗi

Đừng dùng + nối chuỗi trong loop. String immutable, mỗi + alloc buffer mới + copy data cũ, đập allocator và GC.

strings.Builder làm việc trực tiếp trên byte buffer.

func BuildLog(parts []string) string {
    var sb strings.Builder
    // Optional: pre-grow nếu estimate được size
    // sb.Grow(len(parts) * 10)

    for _, p := range parts {
        sb.WriteString(p)
    }
    return sb.String()
}

Heavy concat path (log, JSON, template) nhanh gấp vài lần +.


4. Tránh string ↔ []byte qua lại

I/O, network, file thường làm việc với []byte. Convert sang string cho tiện rồi convert lại tạo garbage và alloc thừa.

Làm trực tiếp trên []byte:

// Tránh string conversion overhead
func FilterSensitiveBytes(data [][]byte) []byte {
    var result []byte
    for _, chunk := range data {
        result = append(result, chunk...)
    }
    return result
}

HTTP body, log stream, binary protocol giữ memory và GC pressure thấp.


5. interface{}/any + Type Switch cho dynamic data

Xử lý semi-dynamic data (JSON mixed type), interface{}/any + type switch nhanh và safe hơn reflection.

func handleConfig(cfg interface{}) {
    switch v := cfg.(type) {
    case string:
        fmt.Printf("Config là path: %s\n", v)
    case int:
        fmt.Printf("Config là timeout: %d\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

Explicit, branch real type, tránh reflection phức tạp.


6. Wrap blocking work bằng Context + timeout

Concurrency model Go xoay quanh context.Context. Không chỉ cancel, mà tránh goroutine leak và request hang mãi.

Mọi blocking/network op đều phải nhận Context và enforce timeout.

func heavyWork(ctx context.Context) error {
    select {
    case <-time.After(2 * time.Second):
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    if err := heavyWork(ctx); err != nil {
        fmt.Println("Work failed:", err)
    }
}

Production service bắt buộc pattern này.


7. sync.Pool: Object reuse đúng cách

High freq alloc, short-lived large object (buffer), sync.Pool giảm alloc đáng kể. Nhưng phải handle slice growth đúng.

var bufPool = sync.Pool{
    New: func() any {
        b := make([]byte, 0, 4096) // 4KB buffer
        return &b
    },
}

func ProcessStream(data []byte) {
    bufPtr := bufPool.Get().(*[]byte)

    buf := *bufPtr
    buf = buf[:0]

    buf = append(buf, data...)

    // QUAN TRỌNG: write back nếu append realloc
    *bufPtr = buf

    bufPool.Put(bufPtr)
}

Không write grown slice về pointer, sẽ reuse backing array quá nhỏ mãi.


8. Mutex bảo vệ state, Channel cho data flow

Go mantra "don't communicate by sharing memory" không cấm lock. State đơn giản (counter, cache), sync.Mutex rõ ràng và nhanh hơn Channel.

type SafeStats struct {
    mu    sync.Mutex
    count int
}

func (s *SafeStats) Increment() {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.count++
}

func (s *SafeStats) Get() int {
    s.mu.Lock()
    defer s.mu.Unlock()
    return s.count
}

Rule: Channel cho data flow giữa goroutine, mutex cho shared state.


9. goimports tự động format + import

Đừng manual reorder import hay format tay. Config editor chạy goimports khi save:

  • Format code (như gofmt).
  • Add missing import.
  • Remove unused import.

VD:

  • VS Code: bật editor.formatOnSave, tool Go format = goimports.
  • GoLand: bật "Optimize imports" + "Reformat code" khi save.

Style consistent, focus logic thay vì whitespace.


Kết

Go advocate simplicity, nhưng simple không phải naive. Với những kỹ thuật nhỏ nhưng mạnh này – kết hợp tool cho phép install Go environment with one clickrun multiple version of python cùng Go service – code robust hơn, service performant hơn, dev experience mượt mà hơn.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.