Golang之消息队列 |
您所在的位置:网站首页 › go语言channel在项目中怎么用 › Golang之消息队列 |
消息队列的使用场景
异步处理 异步是指不用一次性执行完,可以先去执行别的,等这边回应了再做处理。这里我就拿一个网上用烂的例子:用户注册。用户在正确填写信息后点击注册,这时会发起网络请求到后端去做数据合法性等的验证,一旦验证通过则会将数据写入数据库并返回注册成功类的信息。但现在很多的应用都会有短信、邮箱等其他媒介的额外通知,这些又是在什么时候实现的呢?我们先说说同步的处理方式,数据写入数据库之后随即调用发短信、邮箱的接口,发完这些信息之后再给前端返回注册成功,这个就是同步的方式。但这里面有一个问题,那就是效率的问题,可能当前计算机暂时腾不出资源发短信和邮件,即暂时阻塞了,那么前端就必须等到后端成功发送短信邮件了才能收到注册成功的信息,从用户体验来说是非常差的。如果采用异步的处理方式就不一样了,在数据写入数据库之后我们可以将这个请求或者相关标志信息写入队列,然后返回注册成功的字样。这样一来用户很就能得知自己注册成功了,至于短信和邮件其实是次要的,晚一点收到也无所谓,所以在后端程序有空闲的时候可以从消息队列中取出这些信息并发送短信或邮件。这个就是异步,即不必把响应信息和发送短信邮件一次性做完,可以先在消息队列中做记录,然后先返回响应信息,发短信邮件的后续再做。 流量削峰 这个流量削峰说得通俗一点就是并发请求的控制,像大型电商网站搞什么限时秒杀、大型团购等活动的时候,在那段时间并发请求量是相当多的,如果后端没有做好优化网站很容易就崩了。这个时候为了控制这种情况,可以考虑使用消息队列来控制并发请求数量。在消息队列未满之前将请求写入消息队列,当消息队列满了之后不再往里面写,而是将后续的请求直接转到其他页面(如错误页面等),即而控制了这些并发请求,至于后续的操作就从队列中出队后再做处理。 应用解耦 耦合是指两个模块之间的关联程度,解耦就是尽可能地降低模块之间的关联性。还是拿用烂了的例子:订单模块和仓库模块。通常来说,在用户下订单时操作的是订单模块,下了订单之后就需要减少商品库存,这时候需要调仓库模块,从这种关系上看就是订单模块中调用了仓库模块里的接口,从而这两个模块就产生耦合了。现在借助消息队列就可以使得这两个模块解耦,首先让仓库模块订阅消息队列,当订单模块中下了订单并需要修改库存的时候发消息写入消息队列,这时订阅了该消息队列的仓库模块会接收到这条消息,进而解析消息中的内容并去修改对应商品的库存。 RabbitMQ简介消息队列(Message Queue)是一种中间件,位于底层复杂的操作系统和应用软件之间。消息队列从名字就可以看出存入其中的消息是先进先出的(FIFO),我们把存入消息的对象称为生产者(producer),把取出消息的对象称为消费者(consumer),而消息队列只是中间的用来暂存消息的中介罢了。消息队列的实现有很多种,如:RabbitMQ、RocketMQ、ActiveMQ、Kafka等,本文讲的是RabbitMQ的使用。 RabbitMQ官方使用教程:https://www.rabbitmq.com/getstarted.html RabbitMQ下载安装erlang下载地址:https://www.erlang.org/downloads RabbitMQ下载地址:https://www.rabbitmq.com/install-windows.html#installer 说明:RabbitMQ底层是基于erlang这门编程语言写的,因此在安装运行RabbitMQ之前必须先安装erlang,否则RabbitMQ服务将无法正常使用。 RabbitMQ启动windows下配置(Linux版本的后面有需要再进行补充) 在安装完erlang后系统会自动在环境变量里配置ERLANG_HOME,如果没有则需要手动配置,ERLANG_HOME变量对应的值为erlang的安装路径。 命令行启动可视化界面插件(成功图) 下载安装:go get github.com/streadway/amqp 说明:前面的RabbitMQ安装只不过是这个软件的安装而已,现在我们需要在go程序中操作这个中间件,自然就需要调用RabbitMQ官方提供的api来进行一系列的访问操作。 RabbitMQ使用
生产者 package main import ( "github.com/streadway/amqp" "log" ) func main(){ //客户端连接消息队列 conn,err := amqp.Dial("amqp://admin:[email protected]:5672") if err != nil { log.Fatal(err) } defer conn.Close() //获取通道,所有操作基本都是通道控制的 ch,err := conn.Channel() if err != nil { log.Fatal(err) } //队列声明 q,err := ch.QueueDeclare( "name", false, false, false, false, nil, ) if err != nil { log.Fatal(err) } //发送消息 err = ch.Publish( "", q.Name, false, false, amqp.Publishing{ ContentType:"text/plain", Body:[]byte("Jung Ken"), //消息的内容 }, ) if err != nil { log.Fatal(err) } }消费者 package main import ( "fmt" "github.com/streadway/amqp" "log" ) func main(){ conn,err := amqp.Dial("amqp://admin:[email protected]:5672") if err != nil { log.Fatal(err) } defer conn.Close() ch,err := conn.Channel() if err != nil { log.Fatal(err) } //此处队列声明中的参数必须和同名队列参数一致,否则将出错 q,err := ch.QueueDeclare( "name", false, false, false, false, nil, ) if err != nil { log.Fatal(err) } //消费者接收消息,msgs为只读通道 msgs,err := ch.Consume(q.Name,"",true,false,false,false,nil) if err != nil { log.Fatal(err) } for v := range msgs { fmt.Printf("body = %s\n",v.Body) } }上面是最简单的RabbitMQ使用案例,如果消费者窗口多开几个即可模仿同时有多个消费者要消费队列中的消息。 Tip:有一点要特别注意,RabbitMQ生产者在发送消息之前必须确保有消费者在等待读取消息,否则非持久性消息将被丢失,持久性的消息另当别论。 针对当前的消费者、生产者模式可能出现的问题 问: 队列在派遣任务的时候是不知道消费者当前是否有还没处理完的消息的,倘若队列一致给消费者C1分配任务而没有给C2分配任务,会造成C1的任务积压太多而C2一直闲着,这时候应该怎么解决这个问题使得队列的任务可以公平派遣(即所有的消费者都能同等地分配到任务)? 答:需要在消费者代码中添加一段代码,使队列每次给消费者分配任务的时候不要分配1个以上的任务,在消费者处理完任务之前不会再接收其他任务,这时候RabbitMQ就会找一个空闲的消费者进行任务分配。添加的代码如下: err = ch.Qos( 1, 0, false, ) if err != nil { //无法设置Qos log.Fatal(err) }完整的消费者端代码 package main import ( "fmt" "github.com/streadway/amqp" "log" ) func main(){ conn,err := amqp.Dial("amqp://admin:[email protected]:5672") if err != nil { log.Fatal(err) } defer conn.Close() ch,err := conn.Channel() if err != nil { log.Fatal(err) } q,err := ch.QueueDeclare( "name", false, false, false, false, nil, ) if err != nil { log.Fatal(err) } //设置每次从消息队列获取任务的数量 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |