关于AMQP协议
AMQP(高级消息队列协议)是一个网络协议,它支持符合要求的客户端应用(application)和消息中间件代理(messaging middleware broker)之间进行通信
消息代理和他们所扮演的角色
消息代理(message brokers)从发布者(publishers)亦称生产者(producers)那儿接收消息,并根据既定的路由规则把接收到的消息发送给处理消息的消费者(consumers)。
由于AMQP是一个网络协议,所以这个过程中的发布者,消费者,消息代理 可以存在于不同的设备上。
RabbitMQ内部结构
重要概念
1.Broker:队列服务器实体。
2.Virtual host:虚拟主机,表示一批交换器,消息队列和相关对象
3.Connection:网络连接
4.Channel:信道,对于操作系统,建立销毁TCP都是很贵的开销,所以引入信道,AMQP都通过信道发出去(发消息、订阅队列、接消息等)
5.Exchange:交换机,接收消息,将消息发给队列
6.Queue:队列,消息容器
7.Binding:队列和交换机绑定
8.Message:消息
9.Publisher:消息生产者
10.Consumer:消费者,获取消息
1.简单队列
一个生产者对应一个消费者,不适用Exchange(在多消费者的时候不适用),消息message在通道Channel中进行传递
消息队列queue,图中红色部分,类似于邮箱,生产者向其中投递消息,消费者从中取出消息
//创建mq的连接工厂对象
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接rabbitmq主机
connectionFactory.setHost("");
//设置端口号
connectionFactory.setPort(5672);
//设置连接哪个虚拟主机
connectionFactory.setVirtualHost("/");
//设置访问虚拟主机的用户名密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//获取连接对象
Connection connection = connectionFactory.newConnection();
//获取连接中的通道
Channel channel = connection.createChannel();
//通道绑定对应的消息队列
//参数1:队列名称,如果队列不存在自动创建
//参数2: durable用来定义队列的特性是否要持久化 true:持久化队列 false:不持久化
//参数3:exclusive 是否独占队列 true:独占队列 false:不独占
//参数4:autodelete 是否在消费完成后删除队列 true:自动删除 false:不自动删除
//参数5:额外参数
channel.queueDeclare("hello",false,false,false,null);
//发布消息
//参数1:交换机名称
//参数2:队列名称
//参数3:传递消息的额外设置 可以设置队列中消息的持久化(MessageProperties.PERSISTENT_TEXT_PLAIN)
//参数4:消息的具体内容
channel.basicPublish("","hello", MessageProperties.PERSISTENT_TEXT_PLAIN,"hello_rabbitmq".getBytes());
channel.close();
connection.close();
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//创建连接对象
Connection connection = connectionFactory.newConnection();
//创建通道
Channel channel = connection.createChannel();
//通道绑定队列
channel.queueDeclare("hello",false,false,false,null);
//消费消息
//参数1:消费哪个队列的消息 队列名称
//参数2:开启消息的自动确认机制
//参数3:消费时的回调接口
channel.basicConsume("hello",true,new DefaultConsumer(channel){
@Override//最后一个参数:消息队列中取出的消息
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消息1:"+new String(body));
}
});
2.work(Round-Robin轮询分发)
一个生产者对应多个消费者,但是只有一个生产者获得消息
这种模型消费者消费消息默认是均衡的,那么如何让消费快的多消费,实现能者多劳呢?
1.可以设置channel.basicQos(perfetch=1);同一时刻只发给消费者一条消息,直到它已经处理上一条消息并且做出响应,MQ才会把下一条消息分发消费者。
2.消息自动确认设置成false,改成手动确认
channel.basicQos(1);//每一次消费一个消息
//参数2:消息自动确认:true代表消费者自动向rabbitMQ确认消息消费了 推荐使用false
//参数2:消息自动确认:true代表消费者自动向rabbitMQ确认消息消费了 推荐使用false
channel.basicConsume("work",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者-1:"+new String(body));
//手动确认: 参数1:手动确认消息标识
// 参数2:是否开启多个消息同事确认 false 每次确认一个
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
3.发布/订阅
1、1个生产者,多个消费者
2、每一个消费者都有自己的一个队列
3、生产者没有将消息直接发送到队列,而是发送到了交换机
4、每个队列都要绑定到交换机(这里的X交换机为fanout)
5、生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的
4.路由direct
生产者发送消息到direct交换器,交换器和队列绑定时有路由key,生产者会指定路由key,消息只会发到对应的队列(routingkey完全相等)
topic(通配符)
*:匹配一个单词
#:匹配多个单词
routingkey:order.*匹配一个
order.#匹配多个(比如生产者发送的routingkey是order.add或者order.update,消费者设置order.#都可以接收到)
provider:
channel.exchangeDeclare("topics","topic");
String routeKey = "user.save.all";
channel.basicPublish("topics",routeKey,null,("这里是topic动态路由模型,routekey:["+routeKey+"]").getBytes());
consumer:
channel.exchangeDeclare("topics","topic");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定交换机 routekey使用通配符表示
channel.queueBind(queue,"topics","user.*");
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:"+new String(body));
}
});
consumer2:
channel.exchangeDeclare("topics","topic");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定交换机 routekey使用通配符表示
channel.queueBind(queue,"topics","user.#");
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:"+new String(body));
}
});
这里只有消费者2能收到消息