【无标题】rabbitmq的docker安装及使用

1、rabbitmq的介绍

#####什么是AMQP 和 JMS?

AMQP:即Advanced Message Queuing Protocol,是一个应用层标准高级消息队列协议,提供统一消息服务。是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等。

JMS:即Java消息服务(Java Message Service)应用程序接口,由sun公司提出,并且sun公司定义好了接口。包括create、send、recieve。只要想使用它,就得实现它定义的接口。 消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。不好的地方是语言层面的限制,只能为JAVA,这其实稍微有点和微服务的观点相违背。要求语言只能是JAVA,而不能是py等。

常见的MQ产品

ActiveMQ:基于JMS,Apache

RocketMQ:(Rocket,火箭)阿里巴巴的产品,基于JMS,目前由Apache基于会维护

Kafka:分布式消息系统,亮点:吞吐量超级高,没秒中数十万的并发。

RabbitMQ:(Rabbit,兔子)由erlang语言开发,基于AMQP协议,在erlang语言特性的加持下,RabbitMQ稳定性要比其他的MQ产品好一些,而且erlang语言本身是面向高并发的编程的语言,所以RabbitMQ速度也非常快。且它基于AMQP协议,对分布式、微服务更友好。

rabbitmq的几个概念
  • virtual host
  • exchange(交换机)
  • routing key
  • binding key
  • 队列
  • channel
2、docker安装rabbitmq

拉取镜像 docker pull rabbitmq:latest

把rabbitmq镜像跑起来 docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:latest

进入容器 docker exec -it rabbitmq /bin/sh

运行rabbitmq的web管理器 rabbitmq-plugins enable rabbitmq-management

列出rabbitmq的用户 rabbitmqctl list_users

修改用户密码 rabbitmqctl change_password guest '950629'

使超级用户可以远程登陆(默认guest用户不可以远程登陆)echo loopback_users = none >> etc/rabbitmq/conf.d/10-defaults.conf

注意: 解决rabbitmq management API return status code 500 报错问题 echo management_agent.disable_metrics_collector = false > etc/rabbitmq/conf.d/20-management_agent.disable_metrics_collector.conf

退出容器 exit

重启容器 docker restart rabbitmq

连接管理页面 47.92.232.226:15672

3、rabbitmq的五种消息模型
简单模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

P(producer/ publisher):生产者,发送消息的服务

C(consumer):消费者,接收消息的服务

红色区域就是MQ中的Queue,可以把它理解成一个邮箱

  • 首先信件来了不强求必须马上马去拿
  • 其次,它是有最大容量的(受主机和磁盘的限制,是一个缓存区)
  • 允许多个消费者监听同一个队列,争抢消息,此时便是worker模型

示例:

//生产者
package main

import (
	"log"
	"strconv"
	"time"
	"github.com/streadway/amqp"
)

func main() {
	conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226:5672//test")
	if err != nil {
		log.Fatal("连接失败")
		return
	}
	defer conn.Close()
	//创建channel
	chann, err := conn.Channel()
	if err != nil {
		log.Fatal("创建channel失败")
		return
	}
	defer chann.Close()

	//发送消息前必须声明一个queue,往这个队列中发送消息
	queue, err := chann.QueueDeclare(
		"hello",
		false, // 消息持久化
		false,
		false, //独有的,排他的
		false,
		nil)
	if err != nil {
		log.Fatal("创建队列失败!")
		return
	}
	//均衡策略 收到消费者的确认ack后再消费消息
	err = chann.Qos(
		1,
		0,
		false)
	if err != nil {
		return
	}
	message := "hello 潘丽萍!"
	i := 0
	for {
		err = chann.Publish(
			"",
			queue.Name,
			false,
			false,
			amqp.Publishing{
				//DeliveryMode: amqp.Persistent, //将mq中的消息持久化
				ContentType: "text/plain",
				Body:        []byte(message + strconv.Itoa(i)),
			})
		if err != nil {
			log.Fatal("发送消息失败")
			return
		}
		i++
		time.Sleep(time.Millisecond * 1000)
	}
}
//消费者
package main

import (
	"fmt"
	"log"

	"github.com/streadway/amqp"
)

func main() {
	//连接到rabbitmq
	conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226:5672//test")
	if err != nil {
		log.Fatal("连接失败")
		return
	}
	defer conn.Close()
	// 创建一个channel
	chann, err := conn.Channel()
	if err != nil {
		log.Fatal("创建channel失败")
		return
	}
	defer chann.Close()

	//声明要消费的channel
	queue, err := chann.QueueDeclare(
		"hello",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		log.Fatal("声明消费的队列失败")
		return
	}

	delieverMsg, err := chann.Consume(
		queue.Name,
		"",
		true, //自动ACK 默认开启(有丢失消息的风险)
		false,
		false,
		false,
		nil,
	)
	if err != nil {
		log.Fatal("消费消息失败")
		return
	}
	err = chann.Qos(
		1,
		0,
		false)
	if err != nil {
		fmt.Println("设置均衡策略")
		return
	}

	for msg := range delieverMsg {
		fmt.Printf("RECV:%s\n", msg.Body)
		//msg.Ack(false) //手动ack,把上方自动ack设置为false
	}
}
worker模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Worker模型中也只有一个工作队列。但它是一种竞争消费模式。可以看到同一个队列我们绑定上了多个消费者

相较于简单模型多增加一个consumer

发布/订阅模型

注意: 以下三种模型,定义一个exchange后,可以绑定多个不同键(key)的队列,可以创建多个消费者向此exchange发送消息,exchange根据routing key向绑定的队列发送消息

发布/订阅之direct

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意交换机的名称为direct 以及添加 特定的routingkey,不支持通配符

示例:

//生产者1
package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/streadway/amqp"
)

func main() {
	conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226:5672//test")
	if err != nil {
		fmt.Println("连接rabbitmq失败")
		return
	}
	defer conn.Close()
	chann, err := conn.Channel()
	if err != nil {
		fmt.Println("创建channel失败")
		fmt.Println(err)
		return
	}
	//声明一个交换机
	err = chann.ExchangeDeclare(
		"direct-ttt",
		"direct",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("创建channel失败")
		fmt.Println(err)
		return
	}
	//声明两个队列并绑定到交换机上
	queue, err := chann.QueueDeclare(
		"direct-log2",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("创建queue失败")
		return
	}
	err = chann.QueueBind(
		queue.Name,
		"error",
		"direct-ttt",
		false,
		nil)
	if err != nil {
		fmt.Println("绑定queue到change logs失败")
		return
	}
	queue1, err := chann.QueueDeclare(
		"direct-log3", //队列名称
		false,         //持久化
		false,         //自动删除
		false,         //唯一的
		false,         //不等待
		nil)           //参数
	if err != nil {
		fmt.Println("创建队列1失败")
		return
	}
	err = chann.QueueBind(
		queue1.Name,
		"info",
		"direct-ttt",
		false,
		nil)
	if err != nil {
		fmt.Println("queue1绑定change失败")
		return
	}
	msg := "你好!小潘"
	i := 0
	for {
		err = chann.Publish(
			"direct-ttt",
			"error",
			false,
			false,
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        []byte(msg + strconv.Itoa(i)),
			})
		if err != nil {
			fmt.Println("生产者生产消息失败")
			return
		}
		i++
		time.Sleep(time.Second)
	}
}
//生产者2
package main

import (
	"fmt"
	"time"

	"github.com/streadway/amqp"
)

func main() {
	conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226//test")
	if err != nil {
		fmt.Println("创建连接失败")
		return
	}
	defer conn.Close()

	chann, err := conn.Channel()
	if err != nil {
		fmt.Println("创建channel失败!")
		return
	}
	err = chann.ExchangeDeclare(
		"direct-ttt",
		"direct",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("声明exchange失败")
		return
	}
	queue1, err := chann.QueueDeclare(
		"direct-log3",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("创建queue1失败")
		return
	}
	err = chann.QueueBind(
		queue1.Name,
		"info",
		"direct-ttt",
		false,
		nil)
	if err != nil {
		fmt.Println("绑定队列失败")
		return
	}
	for {

		err = chann.Publish(
			"direct-ttt",
			"info",
			false,
			false,
			amqp.Publishing{ContentType: "test/plain",
				Body: []byte("小潘小潘")})
		if err != nil {
			fmt.Println("信息生产失败")
			return
		}
		time.Sleep(time.Second)
	}

}

//消费者1
package main

import (
	"fmt"

	"github.com/streadway/amqp"
)

func main() {
	conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226//test")
	if err != nil {
		fmt.Println("创建连接失败")
		return
	}
	defer conn.Close()
	chann, err := conn.Channel()
	if err != nil {
		fmt.Println("创建channel失败")
		return
	}
	err = chann.ExchangeDeclare(
		"direct-ttt",
		"direct",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("exchange声明失败!")
		return
	}
	queue, err := chann.QueueDeclare(
		"direct-log2",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("queue声明失败")
		return
	}
	err = chann.QueueBind(
		queue.Name,
		"error",
		"direct-ttt",
		false,
		nil)
	if err != nil {
		fmt.Println("queue绑定exchange失败")
		return
	}
	msg, err := chann.Consume(
		queue.Name,
		"",
		true,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("消费者消费消息失败")
		return
	}
	for d := range msg {
		fmt.Printf("consumer2 RECV:%s\n", d.Body)
	}
}
//消费者2
package main

import (
	"fmt"

	"github.com/streadway/amqp"
)

func main() {
	conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226:5672//test")
	if err != nil {
		fmt.Println("创建连接失败")
		return
	}
	defer conn.Close()
	chann, err := conn.Channel()
	if err != nil {
		fmt.Println("创建channel失败")
		return
	}
	err = chann.ExchangeDeclare(
		"direct-ttt",
		"direct",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("exchange声明失败!")
		return
	}
	queue, err := chann.QueueDeclare(
		"direct-log3",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("创建队列失败")
		return
	}
	err = chann.QueueBind(
		queue.Name,
		"info",
		"direct-ttt",
		false,
		nil)
	if err != nil {
		fmt.Println("queue绑定exchange失败")
		return
	}
	msg, err := chann.Consume(
		queue.Name,
		"",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("消费者消费消息失败")
		return
	}
	for d := range msg {
		fmt.Printf("consumer1 RECV:%s\n", d.Body)
		_ = d.Ack(false)
	}
}

以下两种模式和此模式类似,以下两种模式的示例省略部分代码。

发布/订阅之Topic

xxxxxxxxxx1 1{“msg”:{“date”:“date必须要晚于当前日期”}}go

和Direct模型相似,不同点:type为topic、并别routingkey支持正则表达式。#匹配一个至多个单词,*匹配一个单词。

示例:

//生产者  
package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/streadway/amqp"
)

func main() {
	conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226:5672//test")
	if err != nil {
		fmt.Println("连接rabbitmq失败")
		return
	}
	defer conn.Close()
	chann, err := conn.Channel()
	if err != nil {
		fmt.Println("创建channel失败")
		fmt.Println(err)
		return
	}
	//声明一个交换机
	err = chann.ExchangeDeclare(
		"lllf",
		"topic",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("创建channel失败")
		fmt.Println(err)
		return
	}
	//声明两个队列并绑定到交换机上
	queue, err := chann.QueueDeclare(
		"topic-log",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("创建queue失败")
		return
	}
	err = chann.QueueBind(
		queue.Name,
		"c.*",
		"lllf",
		false,
		nil)
	if err != nil {
		fmt.Println("绑定queue到change logs失败")
		return
	}
	queue1, err := chann.QueueDeclare(
		"topic-log1", //队列名称
		false,        //持久化
		false,        //自动删除
		false,        //唯一的
		false,        //不等待
		nil)          //参数
	if err != nil {
		fmt.Println("创建队列1失败")
		return
	}
	err = chann.QueueBind(
		queue1.Name,
		"c.g",
		"lllf",
		false,
		nil)
	if err != nil {
		fmt.Println("queue1绑定change失败")
		return
	}
	msg := "你好!小潘"
	i := 0
	for {
		err = chann.Publish(
			"lllf",
			"c.k",
			false,
			false,
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        []byte(msg + strconv.Itoa(i)),
			})
		if err != nil {
			fmt.Println("生产者生产消息失败")
			return
		}
		i++
		time.Sleep(time.Second)
       
	}
}
//消费者
package main

import (
	"fmt"

	"github.com/streadway/amqp"
)

func main() {
    conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226:5672//test")
	if err != nil {
		fmt.Println("创建连接失败")
		return
	}
	defer conn.Close()
	chann, err := conn.Channel()
	if err != nil {
		fmt.Println("创建channel失败")
		return
	}
	err = chann.ExchangeDeclare(
		"lllf",
		"topic",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("exchange声明失败!")
		return
	}
	queue, err := chann.QueueDeclare(
		"topic-log",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("queue声明失败")
		return
	}
	err = chann.QueueBind(
		queue.Name,
		"c.*",
		"lllf",
		false,
		nil)
	if err != nil {
		fmt.Println("queue绑定exchange失败")
		return
	}
	msg, err := chann.Consume(
		queue.Name,
		"",
		true,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("消费者消费消息失败")
		return
	}
	for d := range msg {
		fmt.Printf("consumer2 RECV:%s\n", d.Body)
	}
}
发布/订阅之fanout

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个模型的特点就是它在发送消息的时候,并没有指明Rounting Key , 或者说他指定了Routing Key,但是所有的消费者都知道,大家都能接收到消息,就像听广播。

生产者:在获取channel之后紧接着创建一个交换机,交换机的类型为 fanout 扇出。

注意,fanout模式会忽略路由规则和queue名称,所有绑定到该exchange的队列都会收到消息

示例:

//生产者
package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/streadway/amqp"
)

func main() {
	conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226:5672//test")
	if err != nil {
		fmt.Println("连接rabbitmq失败")
		return
	}
	defer conn.Close()
	chann, err := conn.Channel()
	if err != nil {
		fmt.Println("创建channel失败")
		return
	}
	err = chann.ExchangeDeclare(
		"logs",//交换机名称
		"fanout",//交换机类型
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("创建channel失败")
		return
	}
	queue, err := chann.QueueDeclare(
		"",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("创建queue失败")
		return
	}
	err = chann.QueueBind(
		"",
		"",
		"logs",
		false,
		nil)
	if err != nil {
		fmt.Println("绑定queue到change logs失败")
		return
	}
	msg := "你好!小潘"
	i := 0
	for {
		err = chann.Publish(
			"logs",
			"",
			false,
			false,
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        []byte(msg + strconv.Itoa(i)),
			})
		if err != nil {
			fmt.Println("生产者生产消息失败")
			return
		}
		i++
		time.Sleep(time.Second)
	}
}
//消费者  
package main

import (
	"fmt"

	"github.com/streadway/amqp"
)

func main() {
    conn, err := amqp.Dial("amqp://guest:950629@47.92.232.226:5672//test")
	if err != nil {
		fmt.Println("创建连接失败")
		return
	}
	defer conn.Close()
	chann, err := conn.Channel()
	if err != nil {
		fmt.Println("创建channel失败")
		return
	}
	err = chann.ExchangeDeclare(
		"logs",
		"fanout",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("exchange声明失败!")
		return
	}
	queue, err := chann.QueueDeclare(
		"fanout-log",
		false,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("queue声明失败")
		return
	}
	err = chann.QueueBind(
		"",
		"rrrrr",
		"logs",
		false,
		nil)
	if err != nil {
		fmt.Println("queue绑定exchange失败")
		return
	}
	msg, err := chann.Consume(
		"",
		"",
		true,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("消费者消费消息失败")
		return
	}
	for d := range msg {
		fmt.Printf("consumer2 RECV:%s\n", d.Body)
	}
}

注意: fanout、topic和direct模式的区别在于键(key)的不同

4、rabbitmq的消息持久化
交换机持久化
	err = chann.ExchangeDeclare(
		"direct-ttt",
		"direct",
		true,   //durable 设置为true
		false,
		false,
		false,
		nil)
队列持久化
queue, err := chann.QueueDeclare(
   	"direct-log2",
   	true,  //durable设置为true
   	false,
   	false,
   	false,
   	nil
持久化消息
err = chann.Publish(
			"direct-ttt",
			"error",
			false,
			false,
			amqp.Publishing{
				DeliveryMode: amqp.Persistent,//持久化消息
				ContentType:  "text/plain",
				Body:         []byte(msg + strconv.Itoa(i)),
			})
5、rabbitmq的消息确认机制
自动确认

消费端自动确认的部分代码

msg, err := chann.Consume(
		queue.Name,
		"",
		true,
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("消费者消费消息失败")
		return
	}
	for d := range msg {
		fmt.Printf("consumer2 RECV:%s\n", d.Body)
	}
手动确认

贴入消费者cli的部分代码

msg, err := chann.Consume(
		queue.Name,
		"",
		false,  //自动确认设置为false
		false,
		false,
		false,
		nil)
	if err != nil {
		fmt.Println("消费者消费消息失败")
		return
	}
	for d := range msg {
		fmt.Printf("consumer1 RECV:%s\n", d.Body)
		_ = d.Ack(false) // 设置了手动确认
	}
  • 29
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值