Golang之消息队列——RabbitMQ的使用

消息队列的使用场景

异步处理
      异步是指不用一次性执行完,可以先去执行别的,等这边回应了再做处理。这里我就拿一个网上用烂的例子:用户注册。用户在正确填写信息后点击注册,这时会发起网络请求到后端去做数据合法性等的验证,一旦验证通过则会将数据写入数据库并返回注册成功类的信息。但现在很多的应用都会有短信、邮箱等其他媒介的额外通知,这些又是在什么时候实现的呢?我们先说说同步的处理方式,数据写入数据库之后随即调用发短信、邮箱的接口,发完这些信息之后再给前端返回注册成功,这个就是同步的方式。但这里面有一个问题,那就是效率的问题,可能当前计算机暂时腾不出资源发短信和邮件,即暂时阻塞了,那么前端就必须等到后端成功发送短信邮件了才能收到注册成功的信息,从用户体验来说是非常差的。如果采用异步的处理方式就不一样了,在数据写入数据库之后我们可以将这个请求或者相关标志信息写入队列,然后返回注册成功的字样。这样一来用户很就能得知自己注册成功了,至于短信和邮件其实是次要的,晚一点收到也无所谓,所以在后端程序有空闲的时候可以从消息队列中取出这些信息并发送短信或邮件。这个就是异步,即不必把响应信息和发送短信邮件一次性做完,可以先在消息队列中做记录,然后先返回响应信息,发短信邮件的后续再做。

流量削峰
      这个流量削峰说得通俗一点就是并发请求的控制,像大型电商网站搞什么限时秒杀、大型团购等活动的时候,在那段时间并发请求量是相当多的,如果后端没有做好优化网站很容易就崩了。这个时候为了控制这种情况,可以考虑使用消息队列来控制并发请求数量。在消息队列未满之前将请求写入消息队列,当消息队列满了之后不再往里面写,而是将后续的请求直接转到其他页面(如错误页面等),即而控制了这些并发请求,至于后续的操作就从队列中出队后再做处理。

应用解耦
      耦合是指两个模块之间的关联程度,解耦就是尽可能地降低模块之间的关联性。还是拿用烂了的例子:订单模块和仓库模块。通常来说,在用户下订单时操作的是订单模块,下了订单之后就需要减少商品库存,这时候需要调仓库模块,从这种关系上看就是订单模块中调用了仓库模块里的接口,从而这两个模块就产生耦合了。现在借助消息队列就可以使得这两个模块解耦,首先让仓库模块订阅消息队列,当订单模块中下了订单并需要修改库存的时候发消息写入消息队列,这时订阅了该消息队列的仓库模块会接收到这条消息,进而解析消息中的内容并去修改对应商品的库存。

RabbitMQ简介

      消息队列(Message Queue)是一种中间件,位于底层复杂的操作系统和应用软件之间。消息队列从名字就可以看出存入其中的消息是先进先出的(FIFO),我们把存入消息的对象称为生产者(producer),把取出消息的对象称为消费者(consumer),而消息队列只是中间的用来暂存消息的中介罢了。消息队列的实现有很多种,如:RabbitMQRocketMQActiveMQKafka等,本文讲的是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的安装路径。
在这里插入图片描述
      在安装完RabbitMQ之后系统会自动注册服务并自动启动服务,可以通过命令行输入services.msc打开服务列表,然后找RabbitMQ服务看是否为正在运行,如果不是则右键启动服务。
在这里插入图片描述
      还有方便的办法,打开开始菜单从最近添加或者找R开头的软件列表,从里面可以看到RabbitMQ Service -startRabbitMQ Service -stop这两个命令行快捷方式,只需要双击打开即可开启服务和停止服务。
在这里插入图片描述
      接下来看RabbitMQ服务是否启动成功,直接进入RabbitMQ目录下找sbin目录,路径类似于:E:\RabbitMQ\rabbitmq_server-3.8.2\sbin里面有一个名为rabbitmqctl.bat的脚本文件,在sbin目录中按住shift + 鼠标右键在当前目录下打开命令行窗口,并输入rabbitmqctl status查看RabbitMQ服务的启动状态。如有出现类似如下的详细信息则表示服务已成功启动,否则就需要去看看服务的运行状态了。如果服务启动但是这里却显示启动失败可以尝试下停止服务后再启动服务。
在这里插入图片描述
      RabbitMQ是有一个可视化界面可以进行数据的统计和管理的,这个可视化界面是一个网页的形式,其对应的地址默认是127.0.0.1:15672。但直接在浏览器地址栏输入该地址肯定是404的,因为虽然你启动了RabbitMQ的服务,现在RabbitMQ是可以正常使用了,但是这个可视化界面和RabbitMQ服务本身是两个不同的东西,你需要把可视化界面挂到15672端口(作为进程),因此你还需要去运行命令来启动可视化界面(这个可视化界面对RabbitMQ来说是一个插件,从命令构成上可以看出)。首先还是进入到RabbitMQ下的sbin目录下,在命令行输入:rabbitmq-plugins enable rabbitmq_management,然后浏览器输入:127.0.0.1:15672,如果打开登陆界面则表示可视化界面插件启动成功。

命令行启动可视化界面插件(成功图)
在这里插入图片描述
浏览器打开127.0.0.1:15672页面(成功图)
在这里插入图片描述
      默认账号的账号和密码都是guest,初次登陆可以用guest账号,进入后可以手动创建其他的账号。

扩展包

      下载安装go get github.com/streadway/amqp
      说明:前面的RabbitMQ安装只不过是这个软件的安装而已,现在我们需要在go程序中操作这个中间件,自然就需要调用RabbitMQ官方提供的api来进行一系列的访问操作。

RabbitMQ使用

在这里插入图片描述
      其中P为Producer(生产者),即消息的发送方;Cx为Consumer(消费者),即消息的接收方。红色区域为消息队列。

工作队列

生产者

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)
	}

	//设置每次从消息队列获取任务的数量
  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值