Ghép hoàn chỉnh + chạy thử
💻 Code hoàn chỉnh
package main
import (
"context"
"fmt"
"sync"
"time"
)
// 1. INTERFACE
type Notifier interface {
SendAlert(message string) error
}
// 2. THỰC THỂ NOTIFIER
type TelegramBot struct{ ChannelID string }
func (t TelegramBot) SendAlert(message string) error {
fmt.Printf("🚨 [Telegram %s]: %s\n", t.ChannelID, message)
return nil
}
// 3. STRUCT DỮ LIỆU
type Server struct {
Name string
IP string
IsUp bool
}
func (s *Server) SetStatus(status bool) { s.IsUp = status }
// 4. HÀM LOGIC CỐT LÕI
func checkServerHealth(ctx context.Context, wg *sync.WaitGroup, s *Server,
results chan<- string, notifier Notifier) {
defer wg.Done()
var processingTime time.Duration
if s.Name == "DB-Server" {
processingTime = 3 * time.Second
} else {
processingTime = 1 * time.Second
}
select {
case <-time.After(processingTime):
s.SetStatus(true)
results <- fmt.Sprintf("✅ %s (%s) ĐANG HOẠT ĐỘNG.", s.Name, s.IP)
case <-ctx.Done():
s.SetStatus(false)
err := fmt.Errorf("không thể kết nối %s: %w", s.Name, ctx.Err())
results <- fmt.Sprintf("❌ LỖI: %s (%s) bị treo!", s.Name, s.IP)
notifier.SendAlert(err.Error())
}
}
// 5. MAIN
func main() {
fmt.Println("=== KHỞI ĐỘNG HỆ THỐNG GIÁM SÁT SERVER ===")
servers := []*Server{
{Name: "Web-Server-01", IP: "192.168.1.10"},
{Name: "Web-Server-02", IP: "192.168.1.11"},
{Name: "DB-Server", IP: "10.0.0.5"},
}
alertSystem := TelegramBot{ChannelID: "#sysadmin-alerts"}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
var wg sync.WaitGroup
results := make(chan string, len(servers))
for _, srv := range servers {
wg.Add(1)
go checkServerHealth(ctx, &wg, srv, results, alertSystem)
}
wg.Wait()
close(results)
fmt.Println("\n--- BÁO CÁO TRẠNG THÁI ---")
for res := range results {
fmt.Println(res)
}
fmt.Println("===========================================")
}
🚀 Cách chạy
# Bước 1: Tạo file main.go, dán code vào
# Bước 2: Khởi tạo module (chỉ lần đầu)
go mod init monitor
# Bước 3: Chạy
go run main.go
📤 Output mong đợi
=== KHỞI ĐỘNG HỆ THỐNG GIÁM SÁT SERVER ===
🚨 [Telegram #sysadmin-alerts]: không thể kết nối DB-Server: context deadline exceeded
--- BÁO CÁO TRẠNG THÁI ---
✅ Web-Server-01 (192.168.1.10) ĐANG HOẠT ĐỘNG.
✅ Web-Server-02 (192.168.1.11) ĐANG HOẠT ĐỘNG.
❌ LỖI: DB-Server (10.0.0.5) bị treo!
===========================================
Tổng thời gian: ~2 giây (đúng bằng timeout, chứ không phải 5 giây tuần tự).
🎓 Tổng kết tư duy 6 bước
1. DỮ LIỆU trước → struct (khuôn mẫu)
2. HÀNH VI phụ → interface + implementation
3. KHUNG main → dựng "sân khấu"
4. LOGIC đơn giản → chạy tuần tự, đảm bảo ĐÚNG
5. SONG SONG → goroutine + WaitGroup + channel
6. TIMEOUT + LỖI → context + select + error wrap
Bảng khái niệm Go trong bài này
| Khái niệm | Vai trò | Ví dụ trong bài |
|---|---|---|
struct |
Nhóm dữ liệu | Server{Name, IP, IsUp} |
interface |
Hợp đồng hành vi | Notifier |
* (con trỏ) |
Sửa được giá trị gốc | *Server, &wg |
func với receiver |
Method gắn với struct | (s *Server) SetStatus |
go |
Chạy ngầm | go checkServerHealth(...) |
sync.WaitGroup |
Đợi nhiều goroutine | wg.Add/Done/Wait |
chan |
Truyền dữ liệu giữa goroutine | results chan<- string |
select |
Chờ nhiều sự kiện | case <-time.After / case <-ctx.Done |
context |
Tín hiệu huỷ/timeout | context.WithTimeout |
defer |
Hoãn thực thi đến khi hàm thoát | defer wg.Done(), defer cancel() |
fmt.Errorf(%w) |
Bọc lỗi | err := fmt.Errorf("... %w", ...) |
🧭 7 nguyên tắc vàng khi học Go
- Viết dữ liệu (struct) trước, logic sau.
- Chạy tuần tự cho đúng, rồi mới làm song song.
- Interface = hợp đồng, đừng lạm dụng — chỉ dùng khi cần thay thế linh hoạt.
- Luôn
defernhững gì cần dọn dẹp:wg.Done(),cancel(),close(file). wg.Add()phải TRƯỚCgo, không được Add trong goroutine.close(channel)sauwg.Wait(), không bao giờ trước.- Mọi hàm mạng/IO phải nhận
context.Context— thói quen chuyên nghiệp.
🏋️ Bài tập tự luyện
- Thêm
SlackBot(cũng implementNotifier) — bằng chứng của đa hình. - Cho phép cấu hình timeout qua biến (thay vì cứng 2s).
- Thêm retry: nếu server lỗi thì thử lại 3 lần trước khi cảnh báo.
- Ghi kết quả ra file thay vì in ra màn hình (dùng
os.Create+defer file.Close()).
🎯 Bạn đã hoàn thành Phần 1 — nền tảng qua 1 dự án thực tế!
Bài này gói gọn ~80% khái niệm Go quan trọng nhất. Hiểu bài này = sẵn sàng đọc code Kubernetes, Docker, và các dự án Go lớn. Phần 2 (20% còn lại) đợi bạn ở mục lục — đọc tự do, không cần theo thứ tự, tra cứu khi cần.