我正在编写一个API客户端(库),它可以访问JSON端点并填充内存缓存。
到目前为止:
然而,在init()中开始一个time.Ticker感觉不太对劲:我还没有看到任何其他的lib这样做。但是我想避免软件包用户为了从少数JSON端点获取数据而不得不做大量的工作。
我的公共API看起来像这样:
// Example usage:
// rt := api.NewRT()
// err := rt.GetLatest
// tmpl.ExecuteTemplate(w, "my_page.tmpl", M{"results": rt.Data})
func (rt *RealTime) GetLatest() error {
rt = realtimeCache.Cached
if rt == nil {
return errors.New("No cached response is available.")
}
return nil
}
内部抓取器如下:
func fetchLatest() error {
log.Println("Fetching latest RT results.")
resp, err := http.Get(realtimeEndpoint)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// Lock our cache from writes
realtimeCache.Lock()
defer realtimeCache.Unlock()
var rt *RealTime
err = json.Unmarshal(body, &rt)
if err != nil {
return err
}
// Update the cache
realtimeCache.Cached = rt
return nil
}
func init() {
// Populate the cache on start-up
fetchLatest()
fetchHistorical()
// Refresh the cache every minute (default)
ticker := time.NewTicker(time.Second * interval)
go func() {
for _ = range ticker.C {
fetchLatest()
fetchHistorical()
}
}()
}
API的其他部分也有类似的功能(我将模块化,但我开始时很简单),但这是它的要点。
是否有更好的方法让后台工作人员获取仍然用户友好的结果?
答案 0 :(得分:3)
恕我直言,在init()
函数上启动计时器是一个坏主意,原因很简单,因为API的用户应该决定是否以及何时进行提取/缓存/更新。< / p>
我建议使用NewRT()
函数中的任一选项或程序包范围的布尔值(api.AutoUpdate
,api.Caching
)来选择数据的缓存和自动更新。< / p>
在您的访问者的呼叫中,您可以采取适当的行动:
NewRT()
函数中启动)将为您处理数据这样您就无法在用户需要之前开始检索任何内容,但可以灵活地让用户决定是否需要其他功能。
请注意,您应该确保在删除相应的结构后不会保留不必要的计时器。
答案 1 :(得分:1)
就像Elwinar说的那样,在init
中启动计时器是一个坏主意,但是你有一个构造函数,所以任何“对象构造”都应该在其中发生,这里是一个简短的example:
(查看游乐场的完整代码)
func NewRT(interval int) (rt *realTime) {
rt = &realTime{
tk: time.NewTicker(time.Second * time.Duration(interval)),
}
go func() {
rt.fetch()
for _ = range rt.tk.C {
rt.fetch()
}
}()
return
}
func (rt *realTime) fetch() {
rt.Lock()
defer rt.Unlock()
rt.fetchLatest()
rt.fetchHistory()
}
...
func (rt *realTime) GetLatest() error {
rt.RLock()
defer rt.RUnlock()
if rt.cached == nil || len(rt.cached) == 0 {
return ErrNoCachedResponse
}
return nil
}
func (rt *realTime) Stop() {
rt.Lock()
defer rt.Unlock()
rt.tk.Stop()
}