1、工作队列模式
特点:一个生产者,一个消息队列,多个消费者。
应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。 采用轮询的方式
2、 发布订阅模式
特点:
1、每个消费者监听自己的队列。
2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息 。
应用场景:一个消息需要被多个消费者接悉数收到
流程:
在生产者与消费者都声明通道和交换机,一个交换机绑定多个通道,因此在发送消息时只需要将消息发送给交换机,在消费者那端只需要接收到自己所独享的通道的消息(如果一个通道被多个消费者享有,那么通道会采取轮询的方式发送给多个消费者)。
代码:
生产者:
package com.test.rabbitmq;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class produce_publish {
public static final String QUEUE_INFORM_EMAIL="queue_inform_email";
public static final String QUEUE_INFORM_MESSAGE="queue_inform_message";
public static final String EXCHANGE_FANOUT_INFORM="exchange_fanout_inform";
public static void main(String[] args) throws IOException, TimeoutException {
//生产者通过连接工厂与mq建立连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);//设置的端口号加主机号
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq可以安装多个虚拟机,一个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection=null;
Channel channel=null;
try {
connection= connectionFactory.newConnection();
//创建会话通道,生产者与mq之间的消息通信都是在通道中完成的
channel=connection.createChannel();
//声明队列(队列名称,是否持久化持久化之后mq重启队列依然在,是否独占连接独占的只允许队列在该链接中访问连接一旦关闭队列自动删除,自动删除)
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_MESSAGE,true,false,false,null);
/**
*1、交换机名称
* 2、交换机类型 fanout对应模式为发布订阅
* TOPIC对应模式为通配符模式
* DIRECT routeing模式
* HEADERS heads模式
* */
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
/**交换机与队列进行绑定
*1、队列名
* 2、交换机名
* 路由key
* */
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
channel.queueBind(QUEUE_INFORM_MESSAGE,EXCHANGE_FANOUT_INFORM,"");
//发送消息
/**参数明细
* 1、exchange,交换机,如果不指定使用默认交换机
* 2、routekey,交换路由key,交换机根据路由key转发到指定的队列,如果使用默认交换机routekey为队列名
* 3、消息属性
* 4、消息内容
*/
for (int i=0;i<10;i++){
String message="发通知";
channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,message.getBytes());
System.out.println("send successful");
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
channel.close();
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
一号消费者:
package com.test;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_publish1 {
public static final String EXCHANGE_FANOUT_INFORM="exchange_fanout_inform";
public static final String QUEUE_INFORM_EMAIL="queue_inform_email";
public static final String QUEUE_INFORM_MESSAGE="queue_inform_message";
public static void main(String[] args) throws IOException, TimeoutException {
//生产者通过连接工厂与mq建立连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);//设置的端口号加主机号
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq可以安装多个虚拟机,一个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection= connectionFactory.newConnection();
//创建会话通道,生产者与mq之间的消息通信都是在通道中完成的
Channel channel=connection.createChannel();
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_MESSAGE,true,false,false,null);
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
channel.queueBind(QUEUE_INFORM_MESSAGE,EXCHANGE_FANOUT_INFORM,"");
//实现方法
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){
//当接收到消息之后此方法将会被调用
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message=new String(body,"utf-8");
Long id=envelope.getDeliveryTag();//获取通信id
System.out.println("receive"+message+" id="+id
);
}
};
/**参数说明
* 1、队列名称
* 2、自动回复,当消费者接收到了消息之后,会告诉mq自己收到消息,如果设置为true的话会自动回复,false需要手动回复,如果不回复的话消息会一直存在
* 3、消费回调方法,当消费者接收到消息之后需要执行的方法
* */
channel.basicConsume(QUEUE_INFORM_EMAIL,true,defaultConsumer);
//不用关闭连接 因为需要监听
}
}
二号消费者:
package com.test;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_publish2 {
public static final String EXCHANGE_FANOUT_INFORM="exchange_fanout_inform";
public static final String QUEUE_INFORM_EMAIL="queue_inform_email";
public static final String QUEUE_INFORM_MESSAGE="queue_inform_message";
public static void main(String[] args) throws IOException, TimeoutException {
//生产者通过连接工厂与mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);//设置的端口号加主机号
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq可以安装多个虚拟机,一个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
//创建会话通道,生产者与mq之间的消息通信都是在通道中完成的
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
channel.queueDeclare(QUEUE_INFORM_MESSAGE, true, false, false, null);
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_FANOUT_INFORM, "");
channel.queueBind(QUEUE_INFORM_MESSAGE, EXCHANGE_FANOUT_INFORM, "");
//实现方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//当接收到消息之后此方法将会被调用
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
Long id = envelope.getDeliveryTag();//获取通信id
System.out.println("receive" + message + " id=" + id
);
}
};
/**参数说明
* 1、队列名称
* 2、自动回复,当消费者接收到了消息之后,会告诉mq自己收到消息,如果设置为true的话会自动回复,false需要手动回复,如果不回复的话消息会一直存在
* 3、消费回调方法,当消费者接收到消息之后需要执行的方法
* */
channel.basicConsume(QUEUE_INFORM_MESSAGE, true, defaultConsumer);
//不用关闭连接 因为需要监听
}
}
3、Routing模式(路由模式)
特点:灵活配置路由key
应用场景:按照分组,或按个数发。
1、每个消费者监听自己的队列,并且设置routingkey。
2、生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。
关键在于routingkey,交换机根据routingkey将通道内的信息转发到指定的队列,并且,routingkey可以绑定一个或者多个队列,在发送消息指定routingkey时,也就可以同时发送给多个对列,从而更加灵活的同时发消息给一个或多个消费者。对于消费者而言,只需要监听自己的绑定的队列就行了
代码:
生产者:
package com.test.rabbitmq;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Produce_routing {
public static final String QUEUE_INFORM_EMAIL="queue_inform_email";
public static final String QUEUE_INFORM_MESSAGE="queue_inform_message";
public static final String EXCHANGE_ROUTING_INFORM="exchange_routing_inform";
public static final String ROUTINGKEY_EMAIL="routingkey_email";
public static final String ROUTINGKEY_MESSAGE="routingkey_message";
public static void main(String[] args) throws IOException, TimeoutException {
//生产者通过连接工厂与mq建立连接
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
Connection connection=null;
Channel channel=null;
try {
connection= connectionFactory.newConnection();
channel=connection.createChannel(); channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null); channel.queueDeclare(QUEUE_INFORM_MESSAGE,true,false,false,null);
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);//交换机类型为direct
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL);
//同一个routingkey可以绑定多个队列 只需要添加一行代码,交换机不变,路由key不变,更改队列名就好了
channel.queueBind(QUEUE_INFORM_MESSAGE,EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL);
for (int i=0;i<10;i++){
String message="发短信";
channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL,null,message.getBytes());
System.out.println("send message successful");
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
channel.close();
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
消费者1绑定QUEUE_INFORM_EMAIL:
消费者只需要说明自己监听的队列就可以了,通过回调方法就可以办事了
package com.test;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_routing1 {
public static final String QUEUE_INFORM_EMAIL="queue_inform_email";
// public static final String QUEUE_INFORM_MESSAGE="queue_inform_message";
public static final String EXCHANGE_ROUTING_INFORM="exchange_routing_inform";
public static final String ROUTINGKEY_EMAIL="routingkey_email";
// public static final String ROUTINGKEY_MESSAGE="routingkey_message";
public static void main(String[] args) throws IOException, TimeoutException {
//生产者通过连接工厂与mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);//设置的端口号加主机号
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq可以安装多个虚拟机,一个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
//创建会话通道,生产者与mq之间的消息通信都是在通道中完成的
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
// channel.queueDeclare(QUEUE_INFORM_MESSAGE, true, false, false, null);
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_ROUTING_INFORM, ROUTINGKEY_EMAIL);
// channel.queueBind(QUEUE_INFORM_MESSAGE, EXCHANGE_ROUTING_INFORM, ROUTINGKEY_MESSAGE);
//实现方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//当接收到消息之后此方法将会被调用
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
Long id = envelope.getDeliveryTag();//获取通信id
System.out.println("receive" + message + " id=" + id
);
}
};
/**参数说明
* 1、队列名称
* 2、自动回复,当消费者接收到了消息之后,会告诉mq自己收到消息,如果设置为true的话会自动回复,false需要手动回复,如果不回复的话消息会一直存在
* 3、消费回调方法,当消费者接收到消息之后需要执行的方法
* */
channel.basicConsume(QUEUE_INFORM_EMAIL, true, defaultConsumer);
//不用关闭连接 因为需要监听
}
}
2、消费者2监听QUEUE_INFORM_MESSAGE队列
package com.test;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class COnsumer_routing2 {
// public static final String QUEUE_INFORM_EMAIL="queue_inform_email";
public static final String QUEUE_INFORM_MESSAGE="queue_inform_message";
public static final String EXCHANGE_ROUTING_INFORM="exchange_routing_inform";
// public static final String ROUTINGKEY_EMAIL="routingkey_email";
public static final String ROUTINGKEY_MESSAGE="routingkey_message";
public static void main(String[] args) throws IOException, TimeoutException {
//生产者通过连接工厂与mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);//设置的端口号加主机号
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq可以安装多个虚拟机,一个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
//创建会话通道,生产者与mq之间的消息通信都是在通道中完成的
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_INFORM_MESSAGE, true, false, false, null);
// channel.queueDeclare(QUEUE_INFORM_MESSAGE, true, false, false, null);
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
channel.queueBind(QUEUE_INFORM_MESSAGE, EXCHANGE_ROUTING_INFORM, ROUTINGKEY_MESSAGE);
// channel.queueBind(QUEUE_INFORM_MESSAGE, EXCHANGE_ROUTING_INFORM, ROUTINGKEY_MESSAGE);
//实现方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//当接收到消息之后此方法将会被调用
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
Long id = envelope.getDeliveryTag();//获取通信id
System.out.println("receive" + message + " id=" + id
);
}
};
/**参数说明
* 1、队列名称
* 2、自动回复,当消费者接收到了消息之后,会告诉mq自己收到消息,如果设置为true的话会自动回复,false需要手动回复,如果不回复的话消息会一直存在
* 3、消费回调方法,当消费者接收到消息之后需要执行的方法
* */
channel.basicConsume(QUEUE_INFORM_MESSAGE, true, defaultConsumer);
//不用关闭连接 因为需要监听
}
}
4、topics模式(通配符模式)
特点:
1、每个消费者监听自己的队列,并且设置带通配符的routingkey。
2、生产者将消息发给broker,在消息队列与路由key的绑定的时候按照通配符的方式,方便在发消息的时候,让自定义路由key匹配上通配符路由key。
应用场景:使用通配符匹配路由,按照匹配结果发送到队列。
与路由模式的区别:
路由匹配是通过发送时的routingkey与绑定队列的routingkey相同。
通配符匹配是通过发送时的routingkey与绑定队列的routingkey匹配。
场景:根据用户的通知设置去通知用户,设置接收Email的用户只接收Email,设置接收sms的用户只接收sms,设置两种 通知类型都接收的则两种通知都有效。
代码思路:
生产者:
routingkey字符串常量采用统配符inform.#.email.#,这样可以匹配上inform.email、inform.message.email和inform.email.message的路由key。
routingkey字符串常量采用统配符inform.#.messagel.#,这样可以匹配上inform.messagel、inform.message.email和inform.email.message的路由key。
inform.#.email.#、inform.#.messagel.#分别绑定不同的队列。在发送消息时可以自由选择routingkey实现转发到一个队列上或者多个队列上。
消费者:
只需要监听自己的队列就行了。
生产者:
package com.test.rabbitmq;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Produce_topics {
public static final String QUEUE_INFORM_EMAIL="queue_inform_email";
public static final String QUEUE_INFORM_MESSAGE="queue_inform_message";
public static final String EXCHANGE_TOPICS_INFORM="exchange_topics_inform";
//定义通配路由key
public static final String ROUTINGKEY_EMAIL="inform.#.email.#";
//定义通配路由key
public static final String ROUTINGKEY_MESSAGE="inform.#.message.#";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
Connection connection=null;
Channel channel=null;
try {
connection= connectionFactory.newConnection();
channel=connection.createChannel(); channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null); channel.queueDeclare(QUEUE_INFORM_MESSAGE,true,false,false,null); channel.exchangeDeclare(EXCHANGE_TOPICS_INFORM, BuiltinExchangeType.TOPIC);
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_TOPICS_INFORM,ROUTINGKEY_EMAIL);
hannel.queueBind(QUEUE_INFORM_MESSAGE,EXCHANGE_TOPICS_INFORM,ROUTINGKEY_MESSAGE);
for (int i=0;i<10;i++){
String message="发邮件";
//自定义路由
channel.basicPublish(EXCHANGE_TOPICS_INFORM,"inform.email",null,message.getBytes());
System.out.println("send email successful");
}
for (int i=0;i<10;i++){
String message="发短信";
channel.basicPublish(EXCHANGE_TOPICS_INFORM,"inform.message",null,message.getBytes());
System.out.println("send message successful");
}
for (int i=0;i<10;i++){
String message="发短信邮件";
channel.basicPublish(EXCHANGE_TOPICS_INFORM,"inform.message.email",null,message.getBytes());
System.out.println("send email message successful");
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
channel.close();
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
消费者代码不需要改变
5、header
header模式与routing不同的地方在于,header模式取消routingkey,使用header中的 key/value(键值对)匹配 队列。
7、RPC
RPC即客户端远程调用服务端的方法 ,使用MQ可以实现RPC的异步调用,基于Direct交换机实现,流程如下:
1、客户端即是生产者就是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列。
2、服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果
3、服务端将RPC方法 的结果发送到RPC响应队列
4、客户端(RPC调用方)监听RPC响应队列,接收到RPC调用结果。