最简http服务器只需两行核心代码:导入net/http和fmt包,调用http.handlefunc注册根路径处理器并输出"hello, world!",再用http.listenandserve(":8080", nil)启动监听;但该写法无超时控制、无panic恢复、无结构化日志,仅适用于本地验证。
用 net/http 起一个最简 http 服务器
go 写服务器,net/http 是标准库首选,不用装第三方包就能跑起来。它不是“框架”,而是轻量、可控的底层构建块——适合 api、静态服务、代理或嵌入式 http 接口。
最简写法就是两行:
package main
<p>import (
"fmt"
"net/http"
)</p><p>func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
})
http.ListenAndServe(":8080", nil)
}</p>
运行后访问 http://localhost:8080 就能看到响应。注意:http.ListenAndServe 默认不处理 TLS,端口小于 1024 需 root 权限;nil 表示用默认的 http.DefaultServeMux 路由器。
为什么不能直接在 HandleFunc 里写业务逻辑?
因为 HandleFunc 的 handler 函数签名是固定的:func(http.ResponseWriter, *http.Request),没有上下文、无中间件支持、无法提前终止、错误难统一捕获。真实项目里很快会遇到这些问题:
- 需要解析 JSON 请求体,但
r.Body只能读一次,重复调用json.Decode会失败 - 想加日志或鉴权,得在每个 handler 开头手动写,容易漏或不一致
- 某个 handler panic 会导致整个服务器崩溃(Go 默认不 recover)
解决方案不是换框架,而是封装自己的 http.Handler 实现,或用 http.ServeMux + 中间件函数链。例如,加一层 panic 捕获:
func recoverMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
然后用 http.ListenAndServe(":8080", recoverMiddleware(myMux)) 包裹路由。
http.Server 结构体必须显式配置超时吗?
必须。Go 的 http.ListenAndServe 底层用的是 http.Server{} 默认值,其中 ReadTimeout、WriteTimeout、IdleTimeout 全为 0(即无限等待)。线上环境极易因客户端慢速攻击、网络卡顿或 bug 导致连接堆积、goroutine 泄露。
正确做法是显式初始化 http.Server:
srv := &http.Server{
Addr: ":8080",
Handler: myMux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
}
<p>// 启动后监听 os.Interrupt 信号做优雅关闭
go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
}()</p>
注意:ReadTimeout 从连接建立开始计时,包括 TLS 握手和请求头读取;IdleTimeout 控制 keep-alive 空闲时间,对 HTTP/2 更关键。
静态文件服务用 http.FileServer 为什么常返回 404?
常见原因是路径映射没对齐。比如你想把当前目录下的 public/ 映射到 /static/,却写了:
http.Handle("/static/", http.FileServer(http.Dir("public"))) // ❌ 错误
这会导致访问 /static/logo.png 时,FileServer 去找 public/static/logo.png(自动拼了前缀),而实际文件在 public/logo.png。正确写法是剥离 URL 前缀:
fs := http.FileServer(http.Dir("public"))
http.Handle("/static/", http.StripPrefix("/static/", fs)) // ✅
另外注意:http.Dir 路径必须是绝对路径或相对于执行目录(不是源码目录),建议用 filepath.Abs("public") 避免歧义;Windows 下路径分隔符不用手动处理,http.Dir 内部已适配。
真正麻烦的从来不是“怎么启动一个服务器”,而是连接管理、错误分类、日志结构化、健康检查端点、配置热加载这些细节。它们不会出现在 hello world 例子里,但上线第一天就会撞上。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











