Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go 在处理来自RabbitMQ的消息时限制并发性_Go - Fatal编程技术网

Go 在处理来自RabbitMQ的消息时限制并发性

Go 在处理来自RabbitMQ的消息时限制并发性,go,Go,我正在尝试从队列(RabbitMQ)读取URL,并进行有限数量的并发HTTP请求,即,有10个工作线程池对从队列接收的URL进行并发请求(永远) 到目前为止,我已经按照RabbitMQ教程实现了一个消费者: 并从网络上发现的示例中尝试了许多方法,以下面的示例结束: 不幸的是,我当前的代码运行大约一分钟,然后无限期冻结。我尝试过添加/移动围棋套路,但似乎无法让它按预期工作(我是新手) 当前代码: package main import ( "fmt" "log" "n

我正在尝试从队列(RabbitMQ)读取URL,并进行有限数量的并发HTTP请求,即,有10个工作线程池对从队列接收的URL进行并发请求(永远)

到目前为止,我已经按照RabbitMQ教程实现了一个消费者:

并从网络上发现的示例中尝试了许多方法,以下面的示例结束:

不幸的是,我当前的代码运行大约一分钟,然后无限期冻结。我尝试过添加/移动围棋套路,但似乎无法让它按预期工作(我是新手)

当前代码:

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/Xide/bloom"
    "github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
    if err != nil {
        log.Fatalf("%s: %s", msg, err)
        panic(fmt.Sprintf("%s: %s", msg, err))
    }
}

var netClient = &http.Client{
    Timeout: time.Second * 10,
}

func getRequest(url string) {
    //resp, err := http.Get(string(url))
    resp, err := netClient.Get(string(url))
    if err != nil {
        log.Printf("HTTP request error: %s", err)
        return
    }
    fmt.Println("StatusCode:", resp.StatusCode)
    fmt.Println(resp.Request.URL)
}

func main() {
    bf := bloom.NewDefaultScalable(0.1)

    conn, err := amqp.Dial("amqp://127.0.0.1:5672/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()

    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    q, err := ch.QueueDeclare(
        "urls",            // name
        true,              // durable
        false,             // delete when unused
        false,             // exclusive
        false,             // no-wait
        nil,               // arguments
    )
    failOnError(err, "Failed to declare a queue")

    err = ch.Qos(
        1,     // prefetch count
        0,     // prefetch size
        false, //global
    )
    failOnError(err, "Failed to set Qos")

    msgs, err := ch.Consume(
        q.Name, // queue
        "",     // consumer
        false,  // auto-ack
        false,  // exclusive
        false,  // no-local
        false,  // no-wait
        nil,    // args
    )
    failOnError(err, "Failed to register a consumer")

    forever := make(chan bool)

    concurrency := 10
    sem := make(chan bool, concurrency)
    go func() {
        for d := range msgs {
            sem <- true
            url := string(d.Body)
            if bf.Match(url) == false {
                bf.Feed(url)
                log.Printf("Not seen: %s", d.Body)
                go func(url string) {
                    defer func() { <-sem }()
                    getRequest(url)
                }(url)
            } else {
                log.Printf("Already seen: %s", d.Body)
            }
            d.Ack(false)
        }
        for i := 0; i < cap(sem); i++ {
            sem <- true
        }
    }()

    log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
    <-forever
}
主程序包
进口(
“fmt”
“日志”
“net/http”
“时间”
“github.com/Xide/bloom”
“github.com/streadway/amqp”
)
func failOnError(错误,消息字符串){
如果错误!=零{
log.Fatalf(“%s:%s”,消息,错误)
死机(fmt.Sprintf(“%s:%s”,消息,错误))
}
}
var netClient=&http.Client{
超时:时间。秒*10,
}
func getRequest(url字符串){
//resp,err:=http.Get(字符串(url))
resp,err:=netClient.Get(字符串(url))
如果错误!=零{
Printf(“HTTP请求错误:%s”,错误)
返回
}
fmt.Println(“状态代码:”,对应状态代码)
fmt.Println(resp.Request.URL)
}
func main(){
bf:=bloom.NewDefaultScalable(0.1)
连接,错误:=amqp.拨号(“amqp://127.0.0.1:5672/")
FailOneError(错误,“未能连接到RabbitMQ”)
延迟连接关闭()
通道,错误:=连接通道()
FailOneError(错误,“无法打开通道”)
延迟关闭
q、 错误:=ch.QueueDeclare(
“URL”,//名称
正确,//持久
false,//未使用时删除
false,//独占
false,//没有等待
nil,//参数
)
FailOneError(错误,“未能声明队列”)
err=ch.Qos(
1,//预取计数
0,//预取大小
false,//全局
)
FailOneError(错误,“设置Qos失败”)
msgs,err:=ch.Consume(
q、 名称,//队列
“”,//消费者
false,//自动确认
false,//独占
false,//没有本地
false,//没有等待
nil,//args
)
FailOneError(错误,“未能注册消费者”)
永远:=制造(陈波)
并发性:=10
sem:=make(chan bool,并发)
go func(){
对于d:=范围msgs{

sem您没有正确处理HTTP响应,导致越来越多的开放连接。请尝试以下操作:

func getRequest(url string) {
    resp, err := netClient.Get(string(url))
    if err != nil {
        log.Printf("HTTP request error: %s", err)
        return
    }
    // Add this bit:
    defer func() {
        io.Copy(ioutil.Discard, resp.Body)
        resp.Body.Close()
    }()
    fmt.Println("StatusCode:", resp.StatusCode)
    fmt.Println(resp.Request.URL)
}
在您读完该频道的消息后,这似乎是不必要的,并且可能存在问题:

    for i := 0; i < cap(sem); i++ {
        sem <- true
    }

根据,
Fatalf
已经存在,因此将永远不会调用
panic
。如果您想登录并
panic
,请尝试,这是专门为此设计的。

当您收到消息时,您将添加到
sem
,但只有在您没有看到url时才从
sem
中删除

因此,一旦你“已经看到”10个URL,你的应用程序就会锁定。
因此,您需要添加
您是否可以将日志输出添加到问题中,这将帮助人们了解使用
-race
标志运行程序的情况,这可能有助于您进行调试:当并发设置为10时,它会发出大约60个HTTP请求(逐渐变慢)然后冻结。Building with-race不提供任何信息。说“客户端在完成响应体后必须关闭响应体:”我在代码中找不到关闭响应体的位置。所以我猜想,所有这些连接都会不确定地保持打开状态(但只有60个调用,这应该已经不是问题了。)如果我没记错的话,如果您没有完全阅读响应的主体,那么也会有问题,但是我找不到指向它的文档。但是我记得我做过类似于
io.Copy(resp.body,ioutil.Discard)的事情
或其他什么。可能这是迷信。上面的示例存在:
panic:sync:negative WaitGroup counter
@david budworth更新示例时,我忽略了使用工作进程数(并发)初始化WaitGroup。我无法实际运行该应用程序,因为我没有任何提交项目,因此您可能需要进行一些调整。重点更在于展示另一种方式,并解释解决方案挂起的原因。
var wg sync.Waitgroup
=>
var wg sync.Waitgroup
和添加
wg.add(并发性)
@Cui是的,这更直接,只是
wg.Add(并发)
,但我通常更喜欢在
wg.Add(1)
wg.Done()
之间有一个明显的联系。另外,如果我后来(错误地)将for循环更改为

if err != nil {
    log.Fatalf("%s: %s", msg, err)
    panic(fmt.Sprintf("%s: %s", msg, err))
}
package main

import (
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/Xide/bloom"
    "github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
    if err != nil {
        log.Fatalf("%s: %s", msg, err)
    }
}

var netClient = &http.Client{
    Timeout: time.Second * 10,
}

func getRequest(url string) {
    //resp, err := http.Get(string(url))
    resp, err := netClient.Get(string(url))
    if err != nil {
        log.Printf("HTTP request error: %s", err)
        return
    }
    resp.Body.Close()
    fmt.Println("StatusCode:", resp.StatusCode)
    fmt.Println(resp.Request.URL)
}
func main() {
    bf := bloom.NewDefaultScalable(0.1)

    conn, err := amqp.Dial("amqp://127.0.0.1:5672/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()

    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    q, err := ch.QueueDeclare(
        "urls", // name
        true,   // durable
        false,  // delete when unused
        false,  // exclusive
        false,  // no-wait
        nil,    // arguments
    )
    failOnError(err, "Failed to declare a queue")

    err = ch.Qos(
        1,     // prefetch count
        0,     // prefetch size
        false, //global
    )
    failOnError(err, "Failed to set Qos")

    msgs, err := ch.Consume(
        q.Name, // queue
        "",     // consumer
        false,  // auto-ack
        false,  // exclusive
        false,  // no-local
        false,  // no-wait
        nil,    // args
    )
    failOnError(err, "Failed to register a consumer")

    concurrency := 10
    var wg sync.Waitgroup              // used to coordinate when they are done, ie: if rabbit conn was closed
    for x := 0; x < concurrency; x++ { // spawn 10 goroutines, all reading from the rabbit channel
        wg.Add(1)
        go func() {
            defer wg.Done() // signal that this goroutine is done
            for d := range msgs {
                url := string(d.Body)
                if bf.Match(url) == false {
                    bf.Feed(url)
                    log.Printf("Not seen: %s", d.Body)
                    getRequest(url)
                } else {
                    log.Printf("Already seen: %s", d.Body)
                }
                d.Ack(false)
            }
            log.Println("msgs channel closed")
        }()
    }

    log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
    wg.Wait() // when all goroutine's exit, the app exits
}