Golang检测飞行中的请求

时间:2015-06-27 16:51:19

标签: http caching concurrency go request

我想知道是否已经有一个图书馆可以做到这一点,或者可能建议以哪种方式解决以下问题:

客户端A请求资源A,这是一个长时间运行的请求,因为资源A很昂贵,导致缓存未命中。在此期间,客户端B请求资源A,现在它仍然是高速缓存未命中,因为客户端A的请求尚未返回并填充高速缓存。因此,客户端B应该阻止并在客户端A的请求完成并填充缓存时得到通知,而不是发出新的请求来生成资源A.

我认为群组缓存库有这样的东西,但我还没有能够浏览代码来弄清楚他们是如何做到的,我也不想将实现与它结合起来并将其用作依赖。

到目前为止我唯一的解决方案是pub-sub类型的东西,我们有一个全局的当前飞行请求地图,其中reqID为关键。当req1到来时,它在地图中设置它的ID,req2来了并检查它的id是否在地图中,因为它请求相同的资源,所以我们阻塞了通知器通道。当req1完成时它会做3件事:

  1. 从地图中删除其ID
  2. 将条目保存在缓存中
  3. 将带有ID的广播发送到通知程序通道 req2从缓存中接收通知,取消阻止和提取。
  4. 由于go并没有内置对广播的支持,因此可能会收听广播频道,然后保留每个请求广播的订阅者列表,或者我们可能会将地图更改为reqId =&gt ;列表(broadcastChannelSubscribers)。这些方面的东西。

    如果您认为使用Go的原语有更好的方法,那么任何输入都会受到赞赏。困扰我的唯一一个解决方案是这个全球地图,被锁包围,我认为它很快就会成为瓶颈。如果你有一些非锁定的想法,即使它们是概率性的,我很高兴听到它们。

4 个答案:

答案 0 :(得分:2)

这让我想起了一个人正在实施类似事情的问题:

Coalescing items in channel

我给出了一个实现这样一个中间层的例子。我认为这符合您的想法:定期跟踪对同一资源的请求,并防止它们被并行重新计算。

如果您有一个单独的例程负责接收请求和管理对缓存的访问,那么您不需要显式锁定(虽然有一个隐藏在通道中)。无论如何,我不知道你的应用程序的细节,但考虑到你需要检查缓存(可能已锁定)和(偶尔)执行一个昂贵的计算缺失条目 - 锁定地图查找似乎不是一个巨大的问题给我。如果您认为这会有所帮助,您也可以总是跨越更多这样的中间层例程,但是您需要一种确定性的方式来路由请求(因此每个缓存条目都由一个例程管理)。

很抱歉没有为您提供一个银弹解决方案,但听起来您无论如何都能解决您的问题。

答案 1 :(得分:2)

缓存和性能问题总是很棘手,你应该总是做一个基本的解决方案来确保你的假设是正确的。但是,如果我们知道瓶颈是获取资源并且缓存将带来显着回报,那么您可以使用Go的渠道来实现排队。假设response是您资源的类型。

type request struct {
     back chan *response
}

func main() {
    c := make(chan request,10) // non-blocking
    go func(input chan request){
        var cached *response
        for _,i := range input {
            if cached == nil { // only make request once
                cached = makeLongRunningRequest()
            }
            i.back <- cached
        }
    }(c)

    resp := make(chan *response)

    c <- request{resp} // cache miss
    c <- request{resp} // will get queued
    c <- request{resp} // will get queued

    for _,r := range resp {
        // do something with response
    }
}

这里我们只提取一个资源,但您可以为要获取的每个资源启动一个goroutine。 Goroutines很便宜,所以除非你需要数百万资源同时缓存,否则你应该没问题。你当然也可以在一段时间后杀死你的goroutines。

要跟踪哪个资源ID属于哪个频道,我会使用地图

map[resourceId]chan request

使用互斥锁。同样,如果获取资源是瓶颈,那么锁定地图的成本应该可以忽略不计。如果锁定地图是个问题,请考虑使用sharded map

总的来说,你似乎很顺利。我建议尽可能简化设计,尽可能使用通道而不是锁。它们可以防止可怕的并发错误。

答案 2 :(得分:1)

一个解决方案是并发非阻塞缓存,详见The Go Programming Language第9章。

code samples非常值得一看,因为作者会带您浏览几个版本(memo1,memo2等),说明竞争条件的问题,使用互斥锁保护地图,以及仅使用频道的版本。< / p>

同时考虑https://blog.golang.org/context,因为它有类似的概念,并处理取消飞行请求。

将内容复制到此答案中是不切实际的,因此希望链接有用。

答案 3 :(得分:0)

Golang已将其作为功能func main() { http.HandleFunc("/github", func(w http.ResponseWriter, r *http.Request) { var key = "facebook" var requestGroup singleflight.Group // Search The Cache, if found in cache return from cache, else make single flight request if res, err := searchCache(); err != nil{ return res } // Cache Miss-> Make Single Flight Request, and Cache it v, err, shared := requestGroup.Do(key, func() (interface{}, error) { // companyStatus() returns string, error, which statifies interface{}, error, so we can return the result directly. if err != nil { return interface{}, err } return companyStatus(), nil }) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } //Set the Cache Here setCache(key, v) status := v.(string) log.Printf("/Company handler requst: status %q, shared result %t", status, shared) fmt.Fprintf(w, "Company Status: %q", status) }) http.ListenAndServe("127.0.0.1:8080", nil) } // companyStatus retrieves Comapny's API status func getCompanyStatus() (string, error) { log.Println("Making request to Some API") defer log.Println("Request to Some API Complete") time.Sleep(1 * time.Second) resp, err := http.Get("Get URL") if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode != 200 { return "", fmt.Errorf("Upstream response: %s", resp.Status) } r := struct{ Status string }{} err = json.NewDecoder(resp.Body).Decode(&r) return r.Status, err } 提供。

对于您的用例,只需在单趟航班上使用一些额外的逻辑即可。考虑下面的代码片段:

<root>
    <p id="111">5tw5t5et</p>
    <p id="111">4qvtq3</p>
    <p id="222">qv34tqv3</p>
    <j>qv43tvq</j>
    <p id="333">qv43tvq</p>
    <p id="333">q34tvq43tvq</p>
    <p id="333">q3434t3tvq43tvq</p>
</root>

我希望代码段能自我解释,您可以参考Single Flight Official Docs来深入研究单机飞行。