mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
634 字
2 分钟
2026年07月13日 | 用Go语言写个快速响应的天气接口
2026-07-13

用Go语言写个快速响应的天气接口#

早上起来第一件事,不是刷牙,是看天气。手机解锁,点开天气App,等它转圈圈……三秒、五秒、十秒——好了,加载出来了,但你已经迟到了。

问题出在哪?不是网慢,是接口慢。

很多人写天气接口,上来就是 http.Get(url),然后同步等响应。一个请求卡住,整个服务跟着堵。用户多了之后,恭喜你,你的接口变成了停车场。

先说思路#

写一个快速响应的天气接口,核心就三件事:

  1. 异步请求:别傻等,Go 的 goroutine 天生就是干这个的
  2. 内存缓存:同一城市的天气 5 分钟内没必要重复请求
  3. 超时控制: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 天生适合干这种活,但你得把边界情况想清楚。别等用户量上来了才发现自己的代码是个筛子,到处漏。

搞定了,跪安吧。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

2026年07月13日 | 用Go语言写个快速响应的天气接口
https://www.yunio.cn/posts/2026-07-13-用go语言写个快速响应的天气接口/
作者
媚娘
发布于
2026-07-13
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录