golang-backend-development by manutej/luxor-claude-marketplace
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill golang-backend-development一项全面的技能,用于使用 Go 构建生产级后端系统。掌握 goroutine、channel、Web 服务器、数据库集成、微服务架构以及可扩展、并发后端应用的部署模式。
在以下场景中使用此技能:
Go 擅长于:
Goroutine 是由 Go 运行时管理的轻量级线程。它们能以最小的开销实现并发执行。
关键特性:
基本 Goroutine 模式:
func main() {
// 启动并发计算
go expensiveComputation(x, y, z)
anotherExpensiveComputation(a, b, c)
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
go 关键字启动一个新的 goroutine,允许 expensiveComputation 与 anotherExpensiveComputation 并发运行。这是 Go 并发模型的基础。
常见用例:
Channel 在 goroutine 之间提供类型安全的通信,在许多场景中消除了对显式锁的需求。
Channel 类型:
// 无缓冲 channel - 同步通信
ch := make(chan int)
// 缓冲 channel - 在缓冲区大小内异步
ch := make(chan int, 100)
// 只读 channel
func receive(ch <-chan int) { /* ... */ }
// 只写 channel
func send(ch chan<- int) { /* ... */ }
使用 Channel 进行同步:
func computeAndSend(ch chan int, x, y, z int) {
ch <- expensiveComputation(x, y, z)
}
func main() {
ch := make(chan int)
go computeAndSend(ch, x, y, z)
v2 := anotherExpensiveComputation(a, b, c)
v1 := <-ch // 阻塞直到结果可用
fmt.Println(v1, v2)
}
此模式确保两个计算都在继续之前完成,channel 同时提供了通信和同步功能。
Channel 模式:
select 语句支持多路复用多个 channel 操作,类似于 channel 的 switch 语句。
超时实现:
timeout := make(chan bool, 1)
go func() {
time.Sleep(1 * time.Second)
timeout <- true
}()
select {
case <-ch:
// 从 ch 读取成功
case <-timeout:
// 操作超时
}
基于 Context 的取消:
select {
case result := <-resultCh:
return result
case <-ctx.Done():
return ctx.Err()
}
context.Context 接口跨 API 边界管理截止时间、取消信号和请求范围的值。
Context 接口:
type Context interface {
// Done 返回一个 channel,当工作应被取消时关闭
Done() <-chan struct{}
// Err 返回 context 被取消的原因
Err() error
// Deadline 返回工作应被取消的时间
Deadline() (deadline time.Time, ok bool)
// Value 返回请求范围的值
Value(key any) any
}
创建 Context:
// Background context - 永不取消
ctx := context.Background()
// 带取消功能
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 带超时功能
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 带截止时间
deadline := time.Now().Add(10 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// 带值
ctx = context.WithValue(parentCtx, key, value)
最佳实践:
func DoSomething(ctx context.Context, ...)defer cancel()ctx.Done()sync.WaitGroup 等待一组 goroutine 完成。
基本模式:
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// 执行工作
}(i)
}
wg.Wait() // 阻塞直到所有 goroutine 完成
常见用例:
当需要共享状态时,使用 sync.Mutex 或 sync.RWMutex 进行保护。
Mutex 模式:
var (
service map[string]net.Addr
serviceMu sync.Mutex
)
func RegisterService(name string, addr net.Addr) {
serviceMu.Lock()
defer serviceMu.Unlock()
service[name] = addr
}
func LookupService(name string) net.Addr {
serviceMu.Lock()
defer serviceMu.Unlock()
return service[name]
}
用于读密集型工作负载的 RWMutex:
var (
cache map[string]interface{}
cacheMu sync.RWMutex
)
func Get(key string) interface{} {
cacheMu.RLock()
defer cacheMu.RUnlock()
return cache[key]
}
func Set(key string, value interface{}) {
cacheMu.Lock()
defer cacheMu.Unlock()
cache[key] = value
}
Go 处理并发连接的标准模式:
for {
rw := l.Accept()
conn := newConn(rw, handler)
go conn.serve() // 并发处理每个连接
}
每个接受的连接都在其自己的 goroutine 中处理,允许服务器高效地扩展到数千个并发连接。
简单 HTTP 服务器:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe("localhost:8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello!")
}
处理器函数:
func handler(w http.ResponseWriter, r *http.Request) {
// 读取请求
method := r.Method
path := r.URL.Path
query := r.URL.Query()
// 写入响应
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"message": "success"}`)
}
处理器结构体:
type APIHandler struct {
db *sql.DB
logger *log.Logger
}
func (h *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 访问依赖项
h.logger.Printf("Request: %s %s", r.Method, r.URL.Path)
// 处理请求
}
日志中间件:
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
// 用法
http.Handle("/api/", loggingMiddleware(apiHandler))
认证中间件:
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if !isValidToken(token) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
链式中间件:
handler := loggingMiddleware(authMiddleware(corsMiddleware(apiHandler)))
http.Handle("/api/", handler)
带 Context 的 HTTP 请求:
func handleSearch(w http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 检查查询参数
query := req.FormValue("q")
if query == "" {
http.Error(w, "missing query", http.StatusBadRequest)
return
}
// 使用 context 执行搜索
results, err := performSearch(ctx, query)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 渲染结果
renderTemplate(w, results)
}
支持 Context 的 HTTP 请求:
func httpDo(ctx context.Context, req *http.Request,
f func(*http.Response, error) error) error {
c := &http.Client{}
// 在 goroutine 中运行请求
ch := make(chan error, 1)
go func() {
ch <- f(c.Do(req))
}()
// 等待完成或取消
select {
case <-ctx.Done():
<-ch // 等待 f 返回
return ctx.Err()
case err := <-ch:
return err
}
}
自定义路由器:
type Router struct {
routes map[string]http.HandlerFunc
}
func (r *Router) Handle(pattern string, handler http.HandlerFunc) {
r.routes[pattern] = handler
}
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if handler, ok := r.routes[req.URL.Path]; ok {
handler(w, req)
} else {
http.NotFound(w, req)
}
}
RESTful API 结构:
// GET /api/users
func listUsers(w http.ResponseWriter, r *http.Request) { /* ... */ }
// GET /api/users/:id
func getUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
// POST /api/users
func createUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
// PUT /api/users/:id
func updateUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
// DELETE /api/users/:id
func deleteUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
流水线通过由 channel 连接的多个阶段处理数据。
生成器阶段:
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
处理阶段:
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
流水线用法:
func main() {
// 设置流水线
c := gen(2, 3)
out := sq(c)
// 消费输出
for n := range out {
fmt.Println(n) // 先 4 后 9
}
}
缓冲生成器(无需 Goroutine):
func gen(nums ...int) <-chan int {
out := make(chan int, len(nums))
for _, n := range nums {
out <- n
}
close(out)
return out
}
将工作分配给多个工作器并合并结果。
扇出:多个工作器:
func main() {
in := gen(2, 3, 4, 5)
// 扇出:将工作分配给两个 goroutine
c1 := sq(in)
c2 := sq(in)
// 扇入:合并结果
for n := range merge(c1, c2) {
fmt.Println(n)
}
}
合并函数(扇入):
func merge(cs ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
// 为每个输入 channel 启动输出 goroutine
output := func(c <-chan int) {
for n := range c {
out <- n
}
wg.Done()
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
// 所有输出完成后关闭 out
go func() {
wg.Wait()
close(out)
}()
return out
}
使用 Done Channel 取消:
func sq(done <-chan struct{}, in <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range in {
select {
case out <- n * n:
case <-done:
return
}
}
}()
return out
}
广播取消:
func main() {
done := make(chan struct{})
defer close(done) // 广播给所有 goroutine
in := gen(done, 2, 3, 4)
c1 := sq(done, in)
c2 := sq(done, in)
// 处理部分结果
out := merge(done, c1, c2)
fmt.Println(<-out)
// 返回时 done 关闭,取消所有流水线阶段
}
带取消的合并:
func merge(done <-chan struct{}, cs ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
output := func(c <-chan int) {
defer wg.Done()
for n := range c {
select {
case out <- n:
case <-done:
return
}
}
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
固定数量的工作器:
func handle(queue chan *Request) {
for r := range queue {
process(r)
}
}
func Serve(clientRequests chan *Request, quit chan bool) {
// 启动处理器
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests)
}
<-quit // 等待退出
}
信号量模式:
var sem = make(chan int, MaxOutstanding)
func handle(r *Request) {
sem <- 1 // 获取
process(r)
<-sem // 释放
}
func Serve(queue chan *Request) {
for req := range queue {
go handle(req)
}
}
限制 Goroutine 创建:
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1 // 在创建 goroutine 前获取
go func() {
process(req)
<-sem // 释放
}()
}
}
查询多个源并返回第一个结果:
func Query(conns []Conn, query string) Result {
ch := make(chan Result)
for _, conn := range conns {
go func(c Conn) {
select {
case ch <- c.DoQuery(query):
default:
}
}(conn)
}
return <-ch
}
串行 MD5 计算:
func MD5All(root string) (map[string][md5.Size]byte, error) {
m := make(map[string][md5.Size]byte)
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
m[path] = md5.Sum(data)
return nil
})
return m, err
}
带流水线的并行 MD5:
type result struct {
path string
sum [md5.Size]byte
err error
}
func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) {
c := make(chan result)
errc := make(chan error, 1)
go func() {
defer close(c)
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
// 为每个文件启动 goroutine
go func() {
data, err := ioutil.ReadFile(path)
select {
case c <- result{path, md5.Sum(data), err}:
case <-done:
}
}()
// 检查是否提前取消
select {
case <-done:
return errors.New("walk canceled")
default:
return nil
}
})
select {
case errc <- err:
case <-done:
}
}()
return c, errc
}
func MD5All(root string) (map[string][md5.Size]byte, error) {
done := make(chan struct{})
defer close(done)
c, errc := sumFiles(done, root)
m := make(map[string][md5.Size]byte)
for r := range c {
if r.err != nil {
return nil, r.err
}
m[r.path] = r.sum
}
if err := <-errc; err != nil {
return nil, err
}
return m, nil
}
高效的缓冲区重用:
var freeList = make(chan *Buffer, 100)
func server() {
for {
b := <-serverChan // 等待工作
process(b)
// 尝试重用缓冲区
select {
case freeList <- b:
// 缓冲区在空闲列表中
default:
// 空闲列表已满,GC 将回收
}
}
}
数据库连接池:
import "database/sql"
func initDB(dataSourceName string) (*sql.DB, error) {
db, err := sql.Open("postgres", dataSourceName)
if err != nil {
return nil, err
}
// 配置连接池
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(10 * time.Minute)
// 验证连接
if err := db.Ping(); err != nil {
return nil, err
}
return db, nil
}
单行查询:
func getUser(db *sql.DB, userID int) (*User, error) {
user := &User{}
err := db.QueryRow(
"SELECT id, name, email FROM users WHERE id = $1",
userID,
).Scan(&user.ID, &user.Name, &user.Email)
if err == sql.ErrNoRows {
return nil, fmt.Errorf("user not found")
}
if err != nil {
return nil, err
}
return user, nil
}
多行查询:
func listUsers(db *sql.DB) ([]*User, error) {
rows, err := db.Query("SELECT id, name, email FROM users")
if err != nil {
return nil, err
}
defer rows.Close()
var users []*User
for rows.Next() {
user := &User{}
if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil {
return nil, err
}
users = append(users, user)
}
if err := rows.Err(); err != nil {
return nil, err
}
return users, nil
}
带 Context 的插入/更新:
func createUser(ctx context.Context, db *sql.DB, user *User) error {
query := "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id"
err := db.QueryRowContext(ctx, query, user.Name, user.Email).Scan(&user.ID)
return err
}
func transferFunds(ctx context.Context, db *sql.DB, from, to int, amount decimal.Decimal) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback() // 如果未提交则回滚
// 从账户扣款
_, err = tx.ExecContext(ctx,
"UPDATE accounts SET balance = balance - $1 WHERE id = $2",
amount, from)
if err != nil {
return err
}
// 向账户存款
_, err = tx.ExecContext(ctx,
"UPDATE accounts SET balance = balance + $1 WHERE id = $2",
amount, to)
if err != nil {
return err
}
return tx.Commit()
}
func insertUsers(db *sql.DB, users []*User) error {
stmt, err := db.Prepare("INSERT INTO users (name, email) VALUES ($1, $2)")
if err != nil {
return err
}
defer stmt.Close()
for _, user := range users {
_, err := stmt.Exec(user.Name, user.Email)
if err != nil {
return err
}
}
return nil
}
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
// 用法
if email == "" {
return &ValidationError{Field: "email", Message: "required"}
}
import "fmt"
func processData(data []byte) error {
err := validateData(data)
if err != nil {
return fmt.Errorf("process data: %w", err)
}
return nil
}
// 解包
if errors.Is(err, ErrValidation) {
// 处理验证错误
}
if errors.As(err, &validationErr) {
// 访问 ValidationError 字段
}
var (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidInput = errors.New("invalid input")
)
// 用法
if errors.Is(err, ErrNotFound) {
http.Error(w, "Resource not found", http.StatusNotFound)
}
func TestGetUser(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
user, err := getUser(db, 1)
if err != nil {
t.Fatalf("getUser failed: %v", err)
}
if user.Name != "John Doe" {
t.Errorf("expected name John Doe, got %s", user.Name)
}
}
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{"valid email", "user@example.com", false},
{"missing @", "userexample.com", true},
{"empty string", "", true},
{"missing domain", "user@", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("validateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}
func BenchmarkConcurrentMap(b *testing.B) {
m := make(map[string]int)
var mu sync.Mutex
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
mu.Lock()
m["key"]++
mu.Unlock()
}
})
}
func TestHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/api/users", nil)
w := httptest.NewRecorder()
handler(w, req)
resp := w.Result()
if resp.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %d", resp.StatusCode)
}
body, _ := ioutil.ReadAll(resp.Body)
// 断言正文内容
}
func main() {
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
// 在 goroutine 中启动服务器
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
// 带超时的优雅关闭
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exited")
}
type Config struct {
ServerPort int `env:"PORT" envDefault:"8080"`
DBHost string `env:"DB_HOST" envDefault:"localhost"`
DBPort int `env:"DB_PORT" envDefault:"5432"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
Timeout time.Duration `env:"TIMEOUT" envDefault:"30s"`
}
func loadConfig() (*Config, error) {
cfg := &Config{}
if err := env.Parse(cfg); err != nil {
return nil, err
}
return cfg, nil
}
import "log/slog"
func setupLogger() *slog.Logger {
return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
}
func handler(w http.ResponseWriter, r *http.Request) {
logger := slog.With(
"method", r.Method,
"path", r.URL.Path,
"remote", r.RemoteAddr,
)
logger.Info("handling request")
// 处理请求
logger.Info("request completed", "status", 200)
}
func healthHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 检查数据库
if err := db.Ping(); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
json.NewEncoder(w).Encode(map[string]string{
"status": "unhealthy",
"error": err.Error(),
})
return
}
// 检查其他依赖项...
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "healthy",
})
}
}
import "golang.org/x/time/rate"
func rateLimitMiddleware(limiter *rate.Limiter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
}
// 用法
limiter := rate.NewLimiter(rate.Limit(10), 20) // 10 次请求/秒,突发 20 次
handler := rateLimitMiddleware(limiter)(apiHandler)
func safelyDo(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Println("work failed:", err)
}
}()
do(work)
}
func server(workChan <-chan *Work) {
for work := range workChan {
go safelyDo(work)
}
}
type UserService struct {
db *sql.DB
cache *redis.Client
logger *slog.Logger
}
func NewUserService(db *sql.DB, cache *redis.Client, logger *slog.Logger) *UserService {
return &UserService{
db: db,
cache: cache,
logger: logger,
}
}
func (s *UserService) GetUser(ctx context.Context, userID string) (*User, error) {
// 先检查缓存
if user, err := s.getFromCache(ctx, userID); err == nil {
return user, nil
}
// 查询数据库
user, err := s.getFromDB(ctx, userID)
if err != nil {
return nil, err
}
// 更新缓存
go s.updateCache(context.Background(), user)
return user, nil
}
type server struct {
pb.UnimplementedUserServiceServer
db *sql.DB
}
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
user := &pb.User{}
err := s.db.QueryRowContext(ctx,
"SELECT id, name, email FROM users WHERE id = $1",
req.GetId(),
).Scan(&user.Id, &user.Name, &user.Email)
if err != nil {
return nil, status.Errorf(codes.NotFound, "user not found")
}
return user, nil
}
type ServiceRegistry struct {
services map[string][]string
mu sync.RWMutex
}
func (r *ServiceRegistry) Register(name, addr string) {
r.mu.Lock()
defer r.mu.Unlock()
r.services[name] = append(r.services[name], addr)
}
func (r *ServiceRegistry) Discover(name string) (string, error) {
r.mu.RLock()
defer r.mu.RUnlock()
addrs := r.services[name]
if len(addrs) == 0 {
return "", fmt.Errorf("service %s not found", name)
}
// 简单轮询
return addrs[rand.Intn(len(addrs))], nil
}
type CircuitBreaker struct {
maxFailures int
timeout time.Duration
failures int
lastFailure time.Time
state string // closed, open, half-open
mu sync.Mutex
}
func (cb *CircuitBreaker) Call(fn func() error) error {
cb.mu.Lock()
if cb.state == "open" {
if time.Since(cb.lastFailure) > cb.timeout {
cb.state = "half-open"
} else {
cb.mu.Unlock()
return errors.New("circuit breaker open")
}
}
cb.mu.Unlock()
err := fn()
cb.mu.Lock()
defer cb.mu.Unlock()
if err != nil {
cb.failures++
cb.lastFailure = time.Now()
if cb.failures >= cb.maxFailures {
cb.state = "open"
}
return err
}
cb.failures = 0
cb.state = "closed"
return nil
}
反模式:
for _, v := range values {
go func() {
fmt.Println(v) // 所有 goroutine 共享同一个 v
}()
}
正确做法:
for _, v := range values {
go func(val string) {
fmt.Println(val) // 每个 goroutine 获得自己的副本
}(v)
}
select 与 default 结合使用以实现非阻塞操作A comprehensive skill for building production-grade backend systems with Go. Master goroutines, channels, web servers, database integration, microservices architecture, and deployment patterns for scalable, concurrent backend applications.
Use this skill when:
Go excels at:
Goroutines are lightweight threads managed by the Go runtime. They enable concurrent execution with minimal overhead.
Key Characteristics:
Basic Goroutine Pattern:
func main() {
// Launch concurrent computation
go expensiveComputation(x, y, z)
anotherExpensiveComputation(a, b, c)
}
The go keyword launches a new goroutine, allowing expensiveComputation to run concurrently with anotherExpensiveComputation. This is fundamental to Go's concurrency model.
Common Use Cases:
Channels provide type-safe communication between goroutines, eliminating the need for explicit locks in many scenarios.
Channel Types:
// Unbuffered channel - synchronous communication
ch := make(chan int)
// Buffered channel - asynchronous up to buffer size
ch := make(chan int, 100)
// Read-only channel
func receive(ch <-chan int) { /* ... */ }
// Write-only channel
func send(ch chan<- int) { /* ... */ }
Synchronization with Channels:
func computeAndSend(ch chan int, x, y, z int) {
ch <- expensiveComputation(x, y, z)
}
func main() {
ch := make(chan int)
go computeAndSend(ch, x, y, z)
v2 := anotherExpensiveComputation(a, b, c)
v1 := <-ch // Block until result available
fmt.Println(v1, v2)
}
This pattern ensures both computations complete before proceeding, with the channel providing both communication and synchronization.
Channel Patterns:
The select statement enables multiplexing multiple channel operations, similar to a switch for channels.
Timeout Implementation:
timeout := make(chan bool, 1)
go func() {
time.Sleep(1 * time.Second)
timeout <- true
}()
select {
case <-ch:
// Read from ch succeeded
case <-timeout:
// Operation timed out
}
Context-Based Cancellation:
select {
case result := <-resultCh:
return result
case <-ctx.Done():
return ctx.Err()
}
The context.Context interface manages deadlines, cancellation signals, and request-scoped values across API boundaries.
Context Interface:
type Context interface {
// Done returns a channel closed when work should be canceled
Done() <-chan struct{}
// Err returns why context was canceled
Err() error
// Deadline returns when work should be canceled
Deadline() (deadline time.Time, ok bool)
// Value returns request-scoped value
Value(key any) any
}
Creating Contexts:
// Background context - never canceled
ctx := context.Background()
// With cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// With deadline
deadline := time.Now().Add(10 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// With values
ctx = context.WithValue(parentCtx, key, value)
Best Practices:
func DoSomething(ctx context.Context, ...)defer cancel() immediately after creating cancelable contextctx.Done() in long-running operationssync.WaitGroup waits for a collection of goroutines to finish.
Basic Pattern:
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// Do work
}(i)
}
wg.Wait() // Block until all goroutines complete
Common Use Cases:
When shared state is necessary, use sync.Mutex or sync.RWMutex for protection.
Mutex Pattern:
var (
service map[string]net.Addr
serviceMu sync.Mutex
)
func RegisterService(name string, addr net.Addr) {
serviceMu.Lock()
defer serviceMu.Unlock()
service[name] = addr
}
func LookupService(name string) net.Addr {
serviceMu.Lock()
defer serviceMu.Unlock()
return service[name]
}
RWMutex for Read-Heavy Workloads:
var (
cache map[string]interface{}
cacheMu sync.RWMutex
)
func Get(key string) interface{} {
cacheMu.RLock()
defer cacheMu.RUnlock()
return cache[key]
}
func Set(key string, value interface{}) {
cacheMu.Lock()
defer cacheMu.Unlock()
cache[key] = value
}
Go's standard pattern for handling concurrent connections:
for {
rw := l.Accept()
conn := newConn(rw, handler)
go conn.serve() // Handle each connection concurrently
}
Each accepted connection is handled in its own goroutine, allowing the server to scale to thousands of concurrent connections efficiently.
Simple HTTP Server:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe("localhost:8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello!")
}
Handler Functions:
func handler(w http.ResponseWriter, r *http.Request) {
// Read request
method := r.Method
path := r.URL.Path
query := r.URL.Query()
// Write response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"message": "success"}`)
}
Handler Structs:
type APIHandler struct {
db *sql.DB
logger *log.Logger
}
func (h *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Access dependencies
h.logger.Printf("Request: %s %s", r.Method, r.URL.Path)
// Handle request
}
Logging Middleware:
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
// Usage
http.Handle("/api/", loggingMiddleware(apiHandler))
Authentication Middleware:
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if !isValidToken(token) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
Chaining Middleware:
handler := loggingMiddleware(authMiddleware(corsMiddleware(apiHandler)))
http.Handle("/api/", handler)
HTTP Request with Context:
func handleSearch(w http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Check query parameter
query := req.FormValue("q")
if query == "" {
http.Error(w, "missing query", http.StatusBadRequest)
return
}
// Perform search with context
results, err := performSearch(ctx, query)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Render results
renderTemplate(w, results)
}
Context-Aware HTTP Request:
func httpDo(ctx context.Context, req *http.Request,
f func(*http.Response, error) error) error {
c := &http.Client{}
// Run request in goroutine
ch := make(chan error, 1)
go func() {
ch <- f(c.Do(req))
}()
// Wait for completion or cancellation
select {
case <-ctx.Done():
<-ch // Wait for f to return
return ctx.Err()
case err := <-ch:
return err
}
}
Custom Router:
type Router struct {
routes map[string]http.HandlerFunc
}
func (r *Router) Handle(pattern string, handler http.HandlerFunc) {
r.routes[pattern] = handler
}
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if handler, ok := r.routes[req.URL.Path]; ok {
handler(w, req)
} else {
http.NotFound(w, req)
}
}
RESTful API Structure:
// GET /api/users
func listUsers(w http.ResponseWriter, r *http.Request) { /* ... */ }
// GET /api/users/:id
func getUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
// POST /api/users
func createUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
// PUT /api/users/:id
func updateUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
// DELETE /api/users/:id
func deleteUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
Pipelines process data through multiple stages connected by channels.
Generator Stage:
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
Processing Stage:
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
Pipeline Usage:
func main() {
// Set up pipeline
c := gen(2, 3)
out := sq(c)
// Consume output
for n := range out {
fmt.Println(n) // 4 then 9
}
}
Buffered Generator (No Goroutine Needed):
func gen(nums ...int) <-chan int {
out := make(chan int, len(nums))
for _, n := range nums {
out <- n
}
close(out)
return out
}
Distribute work across multiple workers and merge results.
Fan-Out: Multiple Workers:
func main() {
in := gen(2, 3, 4, 5)
// Fan out: distribute work across two goroutines
c1 := sq(in)
c2 := sq(in)
// Fan in: merge results
for n := range merge(c1, c2) {
fmt.Println(n)
}
}
Merge Function (Fan-In):
func merge(cs ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
// Start output goroutine for each input channel
output := func(c <-chan int) {
for n := range c {
out <- n
}
wg.Done()
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
// Close out once all outputs are done
go func() {
wg.Wait()
close(out)
}()
return out
}
Cancellation with Done Channel:
func sq(done <-chan struct{}, in <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range in {
select {
case out <- n * n:
case <-done:
return
}
}
}()
return out
}
Broadcasting Cancellation:
func main() {
done := make(chan struct{})
defer close(done) // Broadcast to all goroutines
in := gen(done, 2, 3, 4)
c1 := sq(done, in)
c2 := sq(done, in)
// Process subset of results
out := merge(done, c1, c2)
fmt.Println(<-out)
// done closed on return, canceling all pipeline stages
}
Merge with Cancellation:
func merge(done <-chan struct{}, cs ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
output := func(c <-chan int) {
defer wg.Done()
for n := range c {
select {
case out <- n:
case <-done:
return
}
}
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
Fixed Number of Workers:
func handle(queue chan *Request) {
for r := range queue {
process(r)
}
}
func Serve(clientRequests chan *Request, quit chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests)
}
<-quit // Wait to exit
}
Semaphore Pattern:
var sem = make(chan int, MaxOutstanding)
func handle(r *Request) {
sem <- 1 // Acquire
process(r)
<-sem // Release
}
func Serve(queue chan *Request) {
for req := range queue {
go handle(req)
}
}
Limiting Goroutine Creation:
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1 // Acquire before creating goroutine
go func() {
process(req)
<-sem // Release
}()
}
}
Query multiple sources and return first result:
func Query(conns []Conn, query string) Result {
ch := make(chan Result)
for _, conn := range conns {
go func(c Conn) {
select {
case ch <- c.DoQuery(query):
default:
}
}(conn)
}
return <-ch
}
Serial MD5 Calculation:
func MD5All(root string) (map[string][md5.Size]byte, error) {
m := make(map[string][md5.Size]byte)
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
m[path] = md5.Sum(data)
return nil
})
return m, err
}
Parallel MD5 with Pipeline:
type result struct {
path string
sum [md5.Size]byte
err error
}
func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) {
c := make(chan result)
errc := make(chan error, 1)
go func() {
defer close(c)
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
// Start goroutine for each file
go func() {
data, err := ioutil.ReadFile(path)
select {
case c <- result{path, md5.Sum(data), err}:
case <-done:
}
}()
// Check for early cancellation
select {
case <-done:
return errors.New("walk canceled")
default:
return nil
}
})
select {
case errc <- err:
case <-done:
}
}()
return c, errc
}
func MD5All(root string) (map[string][md5.Size]byte, error) {
done := make(chan struct{})
defer close(done)
c, errc := sumFiles(done, root)
m := make(map[string][md5.Size]byte)
for r := range c {
if r.err != nil {
return nil, r.err
}
m[r.path] = r.sum
}
if err := <-errc; err != nil {
return nil, err
}
return m, nil
}
Efficient buffer reuse:
var freeList = make(chan *Buffer, 100)
func server() {
for {
b := <-serverChan // Wait for work
process(b)
// Try to reuse buffer
select {
case freeList <- b:
// Buffer on free list
default:
// Free list full, GC will reclaim
}
}
}
Database Connection Pool:
import "database/sql"
func initDB(dataSourceName string) (*sql.DB, error) {
db, err := sql.Open("postgres", dataSourceName)
if err != nil {
return nil, err
}
// Configure connection pool
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(10 * time.Minute)
// Verify connection
if err := db.Ping(); err != nil {
return nil, err
}
return db, nil
}
Single Row Query:
func getUser(db *sql.DB, userID int) (*User, error) {
user := &User{}
err := db.QueryRow(
"SELECT id, name, email FROM users WHERE id = $1",
userID,
).Scan(&user.ID, &user.Name, &user.Email)
if err == sql.ErrNoRows {
return nil, fmt.Errorf("user not found")
}
if err != nil {
return nil, err
}
return user, nil
}
Multiple Row Query:
func listUsers(db *sql.DB) ([]*User, error) {
rows, err := db.Query("SELECT id, name, email FROM users")
if err != nil {
return nil, err
}
defer rows.Close()
var users []*User
for rows.Next() {
user := &User{}
if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil {
return nil, err
}
users = append(users, user)
}
if err := rows.Err(); err != nil {
return nil, err
}
return users, nil
}
Insert/Update with Context:
func createUser(ctx context.Context, db *sql.DB, user *User) error {
query := "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id"
err := db.QueryRowContext(ctx, query, user.Name, user.Email).Scan(&user.ID)
return err
}
func transferFunds(ctx context.Context, db *sql.DB, from, to int, amount decimal.Decimal) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback() // Rollback if not committed
// Debit from account
_, err = tx.ExecContext(ctx,
"UPDATE accounts SET balance = balance - $1 WHERE id = $2",
amount, from)
if err != nil {
return err
}
// Credit to account
_, err = tx.ExecContext(ctx,
"UPDATE accounts SET balance = balance + $1 WHERE id = $2",
amount, to)
if err != nil {
return err
}
return tx.Commit()
}
func insertUsers(db *sql.DB, users []*User) error {
stmt, err := db.Prepare("INSERT INTO users (name, email) VALUES ($1, $2)")
if err != nil {
return err
}
defer stmt.Close()
for _, user := range users {
_, err := stmt.Exec(user.Name, user.Email)
if err != nil {
return err
}
}
return nil
}
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
// Usage
if email == "" {
return &ValidationError{Field: "email", Message: "required"}
}
import "fmt"
func processData(data []byte) error {
err := validateData(data)
if err != nil {
return fmt.Errorf("process data: %w", err)
}
return nil
}
// Unwrapping
if errors.Is(err, ErrValidation) {
// Handle validation error
}
if errors.As(err, &validationErr) {
// Access ValidationError fields
}
var (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidInput = errors.New("invalid input")
)
// Usage
if errors.Is(err, ErrNotFound) {
http.Error(w, "Resource not found", http.StatusNotFound)
}
func TestGetUser(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
user, err := getUser(db, 1)
if err != nil {
t.Fatalf("getUser failed: %v", err)
}
if user.Name != "John Doe" {
t.Errorf("expected name John Doe, got %s", user.Name)
}
}
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{"valid email", "user@example.com", false},
{"missing @", "userexample.com", true},
{"empty string", "", true},
{"missing domain", "user@", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("validateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}
func BenchmarkConcurrentMap(b *testing.B) {
m := make(map[string]int)
var mu sync.Mutex
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
mu.Lock()
m["key"]++
mu.Unlock()
}
})
}
func TestHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/api/users", nil)
w := httptest.NewRecorder()
handler(w, req)
resp := w.Result()
if resp.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %d", resp.StatusCode)
}
body, _ := ioutil.ReadAll(resp.Body)
// Assert body content
}
func main() {
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
// Start server in goroutine
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// Wait for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exited")
}
type Config struct {
ServerPort int `env:"PORT" envDefault:"8080"`
DBHost string `env:"DB_HOST" envDefault:"localhost"`
DBPort int `env:"DB_PORT" envDefault:"5432"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
Timeout time.Duration `env:"TIMEOUT" envDefault:"30s"`
}
func loadConfig() (*Config, error) {
cfg := &Config{}
if err := env.Parse(cfg); err != nil {
return nil, err
}
return cfg, nil
}
import "log/slog"
func setupLogger() *slog.Logger {
return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
}
func handler(w http.ResponseWriter, r *http.Request) {
logger := slog.With(
"method", r.Method,
"path", r.URL.Path,
"remote", r.RemoteAddr,
)
logger.Info("handling request")
// Process request
logger.Info("request completed", "status", 200)
}
func healthHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Check database
if err := db.Ping(); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
json.NewEncoder(w).Encode(map[string]string{
"status": "unhealthy",
"error": err.Error(),
})
return
}
// Check other dependencies...
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "healthy",
})
}
}
import "golang.org/x/time/rate"
func rateLimitMiddleware(limiter *rate.Limiter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
}
// Usage
limiter := rate.NewLimiter(rate.Limit(10), 20) // 10 req/sec, burst 20
handler := rateLimitMiddleware(limiter)(apiHandler)
func safelyDo(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Println("work failed:", err)
}
}()
do(work)
}
func server(workChan <-chan *Work) {
for work := range workChan {
go safelyDo(work)
}
}
type UserService struct {
db *sql.DB
cache *redis.Client
logger *slog.Logger
}
func NewUserService(db *sql.DB, cache *redis.Client, logger *slog.Logger) *UserService {
return &UserService{
db: db,
cache: cache,
logger: logger,
}
}
func (s *UserService) GetUser(ctx context.Context, userID string) (*User, error) {
// Check cache first
if user, err := s.getFromCache(ctx, userID); err == nil {
return user, nil
}
// Query database
user, err := s.getFromDB(ctx, userID)
if err != nil {
return nil, err
}
// Update cache
go s.updateCache(context.Background(), user)
return user, nil
}
type server struct {
pb.UnimplementedUserServiceServer
db *sql.DB
}
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
user := &pb.User{}
err := s.db.QueryRowContext(ctx,
"SELECT id, name, email FROM users WHERE id = $1",
req.GetId(),
).Scan(&user.Id, &user.Name, &user.Email)
if err != nil {
return nil, status.Errorf(codes.NotFound, "user not found")
}
return user, nil
}
type ServiceRegistry struct {
services map[string][]string
mu sync.RWMutex
}
func (r *ServiceRegistry) Register(name, addr string) {
r.mu.Lock()
defer r.mu.Unlock()
r.services[name] = append(r.services[name], addr)
}
func (r *ServiceRegistry) Discover(name string) (string, error) {
r.mu.RLock()
defer r.mu.RUnlock()
addrs := r.services[name]
if len(addrs) == 0 {
return "", fmt.Errorf("service %s not found", name)
}
// Simple round-robin
return addrs[rand.Intn(len(addrs))], nil
}
type CircuitBreaker struct {
maxFailures int
timeout time.Duration
failures int
lastFailure time.Time
state string // closed, open, half-open
mu sync.Mutex
}
func (cb *CircuitBreaker) Call(fn func() error) error {
cb.mu.Lock()
if cb.state == "open" {
if time.Since(cb.lastFailure) > cb.timeout {
cb.state = "half-open"
} else {
cb.mu.Unlock()
return errors.New("circuit breaker open")
}
}
cb.mu.Unlock()
err := fn()
cb.mu.Lock()
defer cb.mu.Unlock()
if err != nil {
cb.failures++
cb.lastFailure = time.Now()
if cb.failures >= cb.maxFailures {
cb.state = "open"
}
return err
}
cb.failures = 0
cb.state = "closed"
return nil
}
Anti-pattern:
for _, v := range values {
go func() {
fmt.Println(v) // All goroutines share same v
}()
}
Correct:
for _, v := range values {
go func(val string) {
fmt.Println(val) // Each goroutine gets its own copy
}(v)
}
select with default for non-blocking operationsfmt.Errorf("%w", err)sync.Pool for frequently allocated objectsgo test -bench . -cpuprofile=cpu.profsync.Map for concurrent map access patternsproject/
├── cmd/
│ └── server/
│ └── main.go # Application entry point
├── internal/
│ ├── api/ # HTTP handlers
│ ├── service/ # Business logic
│ ├── repository/ # Data access
│ └── middleware/ # HTTP middleware
├── pkg/
│ └── utils/ # Public utilities
├── migrations/ # Database migrations
├── config/ # Configuration files
└── docker/ # Docker files
t.Helper() for test helper functionshttptest for HTTP handler testingProblem:
var service map[string]net.Addr
func RegisterService(name string, addr net.Addr) {
service[name] = addr // RACE CONDITION
}
func LookupService(name string) net.Addr {
return service[name] // RACE CONDITION
}
Solution:
var (
service map[string]net.Addr
serviceMu sync.Mutex
)
func RegisterService(name string, addr net.Addr) {
serviceMu.Lock()
defer serviceMu.Unlock()
service[name] = addr
}
Problem:
func process() {
ch := make(chan int)
go func() {
ch <- expensive() // Blocks forever if no receiver
}()
// Returns without reading from ch
}
Solution:
func process() {
ch := make(chan int, 1) // Buffered channel
go func() {
ch <- expensive() // Won't block
}()
}
Receivers need to know when no more values are coming:
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out) // IMPORTANT: close when done
}()
return out
}
// This will deadlock
ch := make(chan int)
ch <- 1 // Blocks forever - no receiver
v := <-ch
Use buffered channels or separate goroutines.
c := make(chan struct{})
// RACE CONDITION
go func() { c <- struct{}{} }()
close(c)
Ensure happens-before relationship with proper synchronization.
go test -racego tool pprofgo test -benchgo vet, staticcheckSkill Version : 1.0.0 Last Updated : October 2025 Skill Category : Backend Development, Systems Programming, Concurrent Programming Prerequisites : Basic programming knowledge, understanding of HTTP, familiarity with command line Recommended Next Skills : docker-deployment, kubernetes-orchestration, grpc-microservices
Weekly Installs
540
Repository
GitHub Stars
44
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
opencode486
gemini-cli473
codex471
github-copilot450
cursor415
claude-code354
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
140,500 周安装