用Go语言写个快速响应的天气接口
早上起来第一件事,不是刷牙,是看天气。手机解锁,点开天气App,等它转圈圈……三秒、五秒、十秒——好了,加载出来了,但你已经迟到了。
问题出在哪?不是网慢,是接口慢。
很多人写天气接口,上来就是 http.Get(url),然后同步等响应。一个请求卡住,整个服务跟着堵。用户多了之后,恭喜你,你的接口变成了停车场。
先说思路
写一个快速响应的天气接口,核心就三件事:
- 异步请求:别傻等,Go 的 goroutine 天生就是干这个的
- 内存缓存:同一城市的天气 5 分钟内没必要重复请求
- 超时控制:API 挂了不能拖着你的服务一起死
代码结构
package main
import ( "encoding/json" "fmt" "net/http" "sync" "time")
// 天气数据结构type Weather struct { City string `json:"city"` Temp float64 `json:"temp"` Weather string `json:"weather"` Update int64 `json:"update"`}
// 简单的内存缓存var ( cache = make(map[string]*Weather) cacheMu sync.RWMutex)
// 从和风天气API拉数据func fetchWeather(city string) (*Weather, error) { client := &http.Client{Timeout: 5 * time.Second} url := fmt.Sprintf("https://devapi.qweather.com/v7/weather/now?location=%s&key=YOUR_KEY", city)
resp, err := client.Get(url) if err != nil { return nil, fmt.Errorf("请求失败: %w", err) } defer resp.Body.Close()
var result struct { Now struct { Temp string `json:"temp"` Text string `json:"text"` } `json:"now"` }
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("解析失败: %w", err) }
return &Weather{ City: city, Temp: parseFloat(result.Now.Temp), Weather: result.Now.Text, Update: time.Now().Unix(), }, nil}
// 带缓存的获取func getWeather(city string) (*Weather, error) { // 先查缓存 cacheMu.RLock() if w, ok := cache[city]; ok && time.Now().Unix()-w.Update < 300 { cacheMu.RUnlock() return w, nil } cacheMu.RUnlock()
// 缓存没有,去请求 w, err := fetchWeather(city) if err != nil { return nil, err }
// 写缓存 cacheMu.Lock() cache[city] = w cacheMu.Unlock()
return w, nil}
func weatherHandler(w http.ResponseWriter, r *http.Request) { city := r.URL.Query().Get("city") if city == "" { city = "guangzhou" }
result, err := getWeather(city) if err != nil { http.Error(w, err.Error(), http.StatusBadGateway) return }
w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(result)}
func main() { http.HandleFunc("/weather", weatherHandler) fmt.Println("天气接口启动,:8080") http.ListenAndServe(":8080", nil)}几个坑,踩过才知道
坑1:HTTP 超时不设
有些人写代码连 http.Client 都不自定义,默认超时是永不超时。API 那边挂了,你的 goroutine 就一直挂着,内存慢慢吃满,最后 OOM。5 秒超时是底线,别省。
坑2:缓存用 sync.Map 不一定好
很多人一听”并发安全”就上 sync.Map,但天气这种场景,读多写少,普通的 map + RWMutex 效率更高。sync.Map 的优势在 key 不固定的场景,别无脑用。
坑3:不处理 JSON 解析错误
API 返回的格式变了、字段缺了、甚至是空响应,你都得处理。不然一个空指针就能让你的服务崩掉。
坑4:goroutine 泄漏
每个请求都起一个 goroutine 去调 API,但如果你没有超时控制,这些 goroutine 可能永远不会结束。时间一长,goroutine 数量爆炸,服务就凉了。
部署建议
别直接裸跑 go run main.go,用 systemd 或者 Docker 管一下进程。重启策略、日志收集、健康检查,该有的都得有。
Nginx 反代一层也行,加个 gzip 压缩,响应体能小一半。
最后
写接口不难,写一个靠谱的接口难。Go 天生适合干这种活,但你得把边界情况想清楚。别等用户量上来了才发现自己的代码是个筛子,到处漏。
搞定了,跪安吧。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时






