取消用户特定的goroutine [英] Cancelling user specific goroutines

查看:49
本文介绍了取消用户特定的goroutine的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序(网络应用程序),允许用户使用twitter oauth登录并提供自动推文删除功能.用户登录到Web应用程序后,我将为每个用户启动一个goroutines(通过REST api),这将删除用户tweet列表.

假设有100位用户,每位用户有500 ++条推文:

  • 如何在删除过程中停止删除go例程.

    例如:用户30在启动删除过程2分钟后请求停止删除tweet(这应该通过对我的应用程序的API调用来完成).

  • 考虑到http请求和twitter API限制,创建go例程以最大化应用程序性能的最佳实践是什么.我应该为每个用户创建go例程还是实现工作者池?

信息:我在Twitter客户端后端使用 anaconda


我找到了一种使用带有上下文的map来实现此目的的方法.这是供参考的代码.归功于 https://gist.github.com/montanaflynn/020e75c6605dbe2c726e410020a7a974

 程序包主要进口 (语境""fmt"日志""net/http"同步"时间")//通过嵌入sync.Mutex获得并发的安全映射类型输入cancelMap struct {同步内部地图[string] context.CancelFunc}func newCancelMap()* cancelMap {返回和取消地图{内部:make(map [string] context.CancelFunc),}}func(c * cancelMap)Get(关键字字符串)(值上下文.CancelFunc,确定,布尔){钟()结果,好的:= c.internal [key]c.Unlock()返回结果,确定}func(c * cancelMap)设置(关键字字符串,值context.CancelFunc){钟()c.internal [key] =值c.Unlock()}func(c * cancelMap)Delete(键字符串){钟()删除(c.内部,密钥)c.Unlock()}//使用取消功能创建全局作业图var作业= newCancelMap()//假装的工人将被包裹在这里//https://siadat.github.io/post/contextfunc work(ctx context.Context,id字符串){为了 {选择 {情况< -ctx.Done():fmt.Printf(取消作业ID%s \ n",ID)返回情况< -time.After(time.Second):fmt.Printf(正在执行的作业ID为%s \ n",ID)}}}func startHandler(w http.ResponseWriter,r * http.Request){//从查询参数获取作业ID和名称id:= r.URL.Query().Get("id")//检查作业映射中是否已存在该作业如果_,好的:= jobs.Get(id);好的 {fmt.Fprintf(w,已开始的作业ID:%s \ n",ID)返回}//使用取消为作业创建新的上下文ctx,取消:= context.WithCancel(context.Background())//将其保存在全局作业图中jobs.Set(id,取消)//实际上开始运行作业上班(ctx,id)//传回200的讯息fmt.Fprintf(w,作业ID:%s已启动\ n",ID)}func stopHandler(w http.ResponseWriter,r * http.Request){//从查询参数获取作业ID和名称id:= r.URL.Query().Get("id")//检查作业地图中的取消功能取消,找到:= jobs.Get(id)如果!找到{fmt.Fprintf(w,作业ID:%s未运行\ n",ID)返回}//取消工作取消()//从工作图删除工作Jobs.Delete(id)//传回200的讯息fmt.Fprintf(w,作业ID:%s已取消\ n",ID)}func main(){http.HandleFunc("/start",startHandler)http.HandleFunc("/stop",stopHandler)log.Fatal(http.ListenAndServe(:8080",nil))} 

解决方案

您无法从外部停止goroutine,该goroutine必须支持取消操作.有关详细信息,请参见:在Go中取消阻止操作.支持取消的常见方法是渠道和 context 软件包.>

关于哪个更适合您,这太广泛了.这取决于很多事情,但是对于示例/参考,标准库的HTTP服务器在其自己的goroutine中为每个传入的HTTP请求提供服务,并且具有不错的性能.

如果您的请求率很高,则可能值得创建和使用goroutine池(或使用执行此操作的第三方库/路由器),但这实际上取决于您的实际代码,您应该对应用进行评估/配置决定是否需要它.

通常,我们可以说,如果每个goroutine所做的工作与创建/调度goroutine所需的开销相比是大"的,通常只使用一个新的goroutine更为干净.与启动goroutine相比,在诸如Twitter API之类的goroutine中访问第三方服务可能会比启动goroutine拥有更多数量级的工作和更多延迟,因此您应该为每个功能启动一个goroutine(不会造成性能损失).

I have an apps(web apps) that let user sign in using twitter oauth and provide automatic tweet deletion functionalities. After the user logged in into the web app I will spin up a goroutines (via REST api) for each user that will delete list of users tweets.

let say there will be 100 users with 500++ tweets per user:

  • how do I stop the deletion go routines in the middle of deletion process.

    eg: user 30 is requesting to stop deletion of tweets after initiating the delete process for 2mins (this should be done via an API call to my apps).

  • what is the best practices of creating go routines in order to maximize the performance of the apps considering the http request and twitter API limit. should I create go routines per user or implement worker pool?

info: I am using anaconda for the twitter client backend


Edit:

I have found a way to implement this using map with context. Here's the code for reference. credit to https://gist.github.com/montanaflynn/020e75c6605dbe2c726e410020a7a974

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "sync"
    "time"
)

// a concurrent safe map type by embedding sync.Mutex
type cancelMap struct {
    sync.Mutex
    internal map[string]context.CancelFunc
}

func newCancelMap() *cancelMap {
    return &cancelMap{
        internal: make(map[string]context.CancelFunc),
    }
}

func (c *cancelMap) Get(key string) (value context.CancelFunc, ok bool) {
    c.Lock()
    result, ok := c.internal[key]
    c.Unlock()
    return result, ok
}

func (c *cancelMap) Set(key string, value context.CancelFunc) {
    c.Lock()
    c.internal[key] = value
    c.Unlock()
}

func (c *cancelMap) Delete(key string) {
    c.Lock()
    delete(c.internal, key)
    c.Unlock()
}

// create global jobs map with cancel function
var jobs = newCancelMap()

// the pretend worker will be wrapped here
// https://siadat.github.io/post/context
func work(ctx context.Context, id string) {

    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Cancelling job id %s\n", id)
            return
        case <-time.After(time.Second):
            fmt.Printf("Doing job id %s\n", id)
        }
    }
}

func startHandler(w http.ResponseWriter, r *http.Request) {

    // get job id and name from query parameters
    id := r.URL.Query().Get("id")

    // check if job already exists in jobs map
    if _, ok := jobs.Get(id); ok {
        fmt.Fprintf(w, "Already started job id: %s\n", id)
        return
    }

    // create new context with cancel for the job
    ctx, cancel := context.WithCancel(context.Background())

    // save it in the global map of jobs
    jobs.Set(id, cancel)

    // actually start running the job
    go work(ctx, id)

    // return 200 with message
    fmt.Fprintf(w, "Job id: %s has been started\n", id)
}

func stopHandler(w http.ResponseWriter, r *http.Request) {

    // get job id and name from query parameters
    id := r.URL.Query().Get("id")

    // check for cancel func from jobs map
    cancel, found := jobs.Get(id)
    if !found {
        fmt.Fprintf(w, "Job id: %s is not running\n", id)
        return
    }

    // cancel the jobs
    cancel()

    // delete job from jobs map
    jobs.Delete(id)

    // return 200 with message
    fmt.Fprintf(w, "Job id: %s has been canceled\n", id)
}

func main() {
    http.HandleFunc("/start", startHandler)
    http.HandleFunc("/stop", stopHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

解决方案

You can't stop a goroutine from the outside, the goroutine has to support the cancellation operation. For details, see: cancel a blocking operation in Go. Common means to support cancellation is channels and the context package.

As to which is better for you, that's too broad. That depends on many things, but for an example / reference, the standard lib's HTTP server serves each incoming HTTP request in its own goroutine, and has a decent performance.

If you have a high request rate, it might worth creating and using a goroutine pool (or use a 3rd party lib / router that does this), but it really depends on your actual code, you should measure / profile your app to decide if it's needed or if it's worth it.

Generally we can say that if the work that each goroutine does is "big" compared to the overhead of the creation / scheduling a goroutine requires, it is usually cleaner to just use a new goroutine for it. Accessing 3rd party services in a goroutine such as the Twitter API will likely have orders of magnitude more work and delay than launching a goroutine, so you should be fine launching a goroutine for each of those (without a performance penalty).

这篇关于取消用户特定的goroutine的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆