API库中的背景提取

时间:2014-06-23 22:34:21

标签: json rest go

我正在编写一个API客户端(库),它可以访问JSON端点并填充内存缓存。

到目前为止:

  • 我启动了一段时间。库中的init()函数中的循环循环每分钟都会访问API,这会刷新缓存(嵌入JSON结构和时间戳的结构)。
  • 图书馆中面向公众的函数调用只是从catch中获取,因此不需要担心自己的速率限制,但可以检查时间戳是否要确认数据的新鲜度

然而,在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的其他部分也有类似的功能(我将模块化,但我开始时很简单),但这是它的要点。

是否有更好的方法让后台工作人员获取仍然用户友好的结果?

2 个答案:

答案 0 :(得分:3)

恕我直言,在init()函数上启动计时器是一个坏主意,原因很简单,因为API的用户应该决定是否以及何时进行提取/缓存/更新。< / p>

我建议使用NewRT()函数中的任一选项或程序包范围的布尔值(api.AutoUpdateapi.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()
}