如何使用Go协程实现内存缓存并进行定时更新?
嘿,这个用内存缓存+Go协程定时更新的思路太靠谱了,刚好我之前做过类似的天气API代理场景,给你梳理下把getWeather移到main并启动协程之后,需要重点关注的几个关键点和代码示例:
核心实现要点
1. 线程安全的缓存容器
因为协程在后台更新缓存,同时你的API请求在读取缓存,必须用线程安全的存储结构,不然会出现数据竞争问题。最常用的是带读写互斥锁的结构体,比sync.Map更直观:
import ( "sync" "time" "net/http" "encoding/json" "log" ) // 假设这是从第三方API返回的天气数据结构 type WeatherData struct { City string `json:"city"` Temp float64 `json:"temp"` Humidity int `json:"humidity"` } type WeatherCache struct { mu sync.RWMutex // 读写锁,读多写少场景更高效 data WeatherData }
2. 定时更新的协程实现
用time.Ticker来实现每秒更新比time.Sleep循环更可靠,它能保证固定间隔触发,不会因为单次API调用耗时导致间隔偏移:
func getWeather() (WeatherData, error) { // 这里是调用第三方天气API的逻辑,返回数据或错误 // 示例模拟返回数据 return WeatherData{ City: "Beijing", Temp: 22.5, Humidity: 60, }, nil } func main() { var cache WeatherCache // 先初始化缓存,避免第一个请求拿到空数据 initialData, err := getWeather() if err == nil { cache.mu.Lock() cache.data = initialData cache.mu.Unlock() } // 启动定时更新协程 ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() // 程序退出时停止ticker go func() { for range ticker.C { newData, err := getWeather() if err != nil { log.Printf("Failed to fetch weather data: %v", err) continue // 出错时不覆盖旧缓存,保持可用数据 } cache.mu.Lock() cache.data = newData cache.mu.Unlock() log.Println("Weather cache updated successfully") } }() // 启动你的API服务,这里用标准库net/http示例 http.HandleFunc("/weather", func(w http.ResponseWriter, r *http.Request) { cache.mu.RLock() defer cache.mu.RUnlock() // 读操作用读锁,支持并发读 w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(cache.data); err != nil { http.Error(w, "Failed to encode response", http.StatusInternalServerError) } }) log.Println("Server starting on :8080...") log.Fatal(http.ListenAndServe(":8080", nil)) }
3. 关键细节优化
- 缓存初始化:启动服务时先主动拉取一次数据,避免第一个请求返回空值;
- 错误容错:第三方API报错时不覆盖现有缓存,保证你的API始终能返回有效数据;
- 读写锁效率:用
sync.RWMutex而不是普通sync.Mutex,因为读请求远多于写请求,读写锁支持多个并发读,性能更好; - 资源清理:用
defer ticker.Stop()确保程序退出时释放ticker资源,避免内存泄漏。
如果后续需要扩展,比如支持多城市缓存、持久化缓存(重启不丢失),可以在这个基础上调整缓存结构,比如把WeatherData改成map[string]WeatherData存储多城市数据,或者加上Redis作为二级缓存。
内容的提问来源于stack exchange,提问作者Ahmed Ashraf
相关产品推荐
相关产品推荐

