有人问我Go怎么上手,干脆写一篇,省得每次都重复回答。
问题背景
用 Go 重写了一个 Python 爬虫服务,内存从 2GB 降到 80MB,QPS 从 500 涨到 8000。关键是协程比线程轻太多了,10万并发也就几百MB内存。
先确认环境没问题
# 安装 Go
wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
go version排查过程
按这个顺序排查,大部分问题都能定位:
- 第1步:go mod tidy 经常跑一下,清理没用的依赖
- 第2步:err != nil 一定要处理,别用 _ 忽略,出了 bug 找不到
- 第3步:sync.Pool 复用对象,GC 压力能小不少
- 第4步:pprof 是性能调优神器:import _ "net/http/pprof"
修复后的代码
// HTTP 服务 + 优雅关闭
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok","time":"%s"}`, time.Now().Format(time.RFC3339))
})
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
// 优雅关闭
go func() {
if err := srv.ListenAndServe(); 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("正在关闭服务器...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("服务器强制关闭: %v", err)
}
log.Println("服务器已优雅关闭")
}常见问题
goroutine 泄漏怎么查?
runtime.NumGoroutine() 打印数量。如果一直在涨就是泄漏了。用 go tool pprof http://localhost:8080/debug/pprof/goroutine 看goroutine栈。常见原因:channel 没关、context 没 cancel。
Go 的包管理怎么搞?
go mod init 初始化,go get 加依赖,go mod tidy 清理。别再用 GOPATH 模式了,都 2026 了。私有仓库配 GOPRIVATE 环境变量。
并发写 map panic 怎么办?
sync.Map 适合读多写少。通用场景用 sync.RWMutex 保护,或者按 key 分片用多个 map + 各自的锁。
这篇会持续更新,有新的踩坑经验就加上来。
0 条评论 欢迎参与讨论