处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “abc”,则只有被标记为“abc”的消息才被转发,不会转发abc.def,也不会转发dog.ghi,只会转发abc。
不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。
将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“”匹配不多不少一个词。因此“abc.#”能够匹配到“abc.def.ghi”,但是“abc.” 只会匹配到“abc.def”。
不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。在绑定Queue与Exchange时指定一组键值对;当消息发送到RabbitMQ时会取到该消息的headers与Exchange绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers属性是一个键值对,可以是Hashtable,键值对的值可以是任何类型。而fanout,direct,topic 的路由键都需要要字符串形式的。
匹配规则x-match有下列两种类型:
x-match = all :表示所有的键值对都匹配才能接受到消息
x-match = any :表示只要有键值对匹配就能接受到消息
1:如果maven项目,在pom.xml文件里配置java操作RabbitMQ的jar包。
com.rabbitmq
amqp-client
5.2.0
2:创建连接RabbitMQ的工具类
package com.example.rabbitmq.util;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConnectionUtils {
//1:用命令行创建rabbitMQ用户
//rabbitmqctl add_user luoxu 123456
//2:赋权 set_permissions [-p ]
//rabbitmqctl set_permissions -p / luoxu ".*" ".*" ".*"
//3:配置角色 rabbitmqctl set_user_tags [user] [role] 关于有几种角色,分别能做什么自己百度
//rabbitmqctl set_user_tags luoxu administrator
/**
* 获取MQ的连接
* @return MQ连接资源
*/
public static Connection getConnection() throws IOException,TimeoutException {
//定义一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器地址
factory.setHost("127.0.0.1");
// AMQP 端口
factory.setPort(5672);
//vhost
factory.setVirtualHost("zhuji");
//用户名
factory.setUsername("luoxu");
//密码
factory.setPassword("123456");
return factory.newConnection();
}
}
3:建立提供者类
package com.example.rabbitmq.util;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class producer {
/**
* 测试rabbitmq客户端,制造者端测试
*
* 生产者发送信息的过程:
* 1.创建连接
* 2.设置虚拟机
* 3.创建会话通道
* 4.声明队列
* 5.声明交换机
* 6.绑定交换机和队列,创建路由关键字
* 7.发送消息
* 8.关闭资源
* 注明:4-6这三步不是必须的,但是如果直接发送消息而没有队列的话程序会出错,所以,在发送之前先声明,
* 同理,消费者端也是这样,需要先声明,没有的话就会创建,有的话就不发生什么;
*
* 对于工作模式:
* 1.work queues:不使用交换机,只有一个队列,可以有多个消费端,队列通知采用轮询的方法给监听的多个消费端
* 发送消息;
* 2.publish/subscribe:通过交换机进行消息转发,有多个队列,每个队列均可有一个或者多个消费者进行监听,
* 每次生产者发送消息,则均由交换机转发至各个队列,由各个队列自行通知监听的消费者;
* 3.Routing:模式同2,区别,为每个队列配备一个,或多个路由关键字,发送消息时指定路由关键字,由交换机根据路由
* 关键字匹配进行转发;
* 4.Topics:模式同3,区别在于,配备的路由关键字可以为通配符的形式,通配符有:#和*,区别:#匹配任意个单词,而*
* 只能匹配单个单词,其中路由关键字指定规则:多个单词使用.隔开;
* 5.Header:模式同3,区别,匹配的是键值对;
* 6.RPC:远程异步调用,mq的一个应用,有客户端和服务端,客户端向mq发送一个调用服务端的信息,服务端获得信息,调用
* 相应服务,将返回结果作为消息发送到另一个队列,客户端监听该队列获取返回信息;
*
* 对于消费者,每个消费者监听队列时指定的参数中,有队列名,是否自动回复,以及一个的map属性和一个回调方法;
* 消费者,单独的消费者无法实现同时监听两个队列的操作;
* 针对队列:可以给它增加路由key,也可以给一系列的键值对,作为头信息,这些东西都是对队列的标识,当生产者发送消息时,
* 使用这些标识来决定将消息发送到那个队列,有路由key的话,优先匹配路由key;
* 消费者的行为很单纯,就是监听一个队列,然后发现消息就回收;
*/
public static void testSendMessage() throws IOException, TimeoutException {
try {
String QUEUE_INFORM_EMAIL = "queue_inform_email";
String QUEUE_INFORM_SMS = "queue_inform_sms";
String EXCHANGE_TOPICS_INFORM="exchange_topics_inform";
String ROUTINGKEY_EMAIL="inform.#.email.#";
String ROUTINGKEY_SMS="inform.#.sms.#";
//获取一个链接
Connection connection = ConnectionUtils.getConnection();
//从连接中获取一个通道
Channel channel = connection.createChannel();
//声明队列,如果队列在mq 中没有则要创建
//参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments
/**
* 参数明细
* 1、queue 队列名称
* 2、durable 是否持久化,如果持久化,mq重启后队列还在
* 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
* 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)
* 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
//声明一个交换机
//参数:String exchange, String type
/**
* 参数明细:
* 1、交换机的名称
* 2、交换机的类型
* fanout:对应的rabbitmq的工作模式是 publish/subscribe
* direct:对应的Routing 工作模式
* topic:对应的Topics工作模式
* headers: 对应的headers工作模式
*/
channel.exchangeDeclare(EXCHANGE_TOPICS_INFORM, BuiltinExchangeType.TOPIC);
// 绑定交换机和队列
//参数:String queue, String exchange, String routingKey
/**
* 参数明细:
* 1、queue 队列名称
* 2、exchange 交换机名称
* 3、routingKey 路由key,作用是交换机根据路由key的值将消息转发到指定的队列中,在发布订阅模式中调协为空字符串
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_TOPICS_INFORM,ROUTINGKEY_EMAIL);
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_TOPICS_INFORM,ROUTINGKEY_SMS);
for(int i=0;i<5;i++){
//发送消息的时候指定routingKey
String message = "send email inform message to user";
channel.basicPublish(EXCHANGE_TOPICS_INFORM,"inform.email",null,message.getBytes());
System.out.println("send to mq "+message);
}
for(int i=0;i<5;i++){
//发送消息的时候指定routingKey
String message = "send sms inform message to user";
channel.basicPublish(EXCHANGE_TOPICS_INFORM,"inform.sms",null,message.getBytes());
System.out.println("send to mq "+message);
}
for(int i=0;i<5;i++){
//发送消息的时候指定routingKey
String message = "send sms and email inform message to user";
channel.basicPublish(EXCHANGE_TOPICS_INFORM,"inform.sms",null,message.getBytes());
System.out.println("send to mq "+message);
}
/**
*可以不关闭通道和连接、会一直保持心跳
* */
//关闭通道
channel.close();
//关闭连接
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException, TimeoutException {
testSendMessage();
}
}
4:创建消费者端类
package com.example.rabbitmq.util;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeoutException;
public class Comsumer {
private static final String QUEUE_NAME = "test_simple_queue";
public static void main(String[] args) throws TimeoutException {
try {
String QUEUE_INFORM_EMAIL = "queue_inform_email";
String QUEUE_INFORM_SMS = "queue_inform_sms";
//获取连接
Connection connection = ConnectionUtils.getConnection();
//创建频道
Channel channel = connection.createChannel();
//定义队列的消费者
//声明队列,如果队列在mq 中没有则要创建
//参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments
/**
* 参数明细
* 1、queue 队列名称
* 2、durable 是否持久化,如果持久化,mq重启后队列还在
* 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
* 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)
* 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
//实现消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
/**
* 当接收到消息后此方法将被调用
* @param consumerTag 消费者标签,用来标识消费者的,在监听队列时设置channel.basicConsume
* @param envelope 信封,通过envelope
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
System.out.println("receive message:" + message);
}
};
//监听队列
//参数:String queue, boolean autoAck, Consumer callback
/**
* 参数明细:
* 1、queue 队列名称
* 2、autoAck 自动回复,当消费者接收到消息后要告诉mq消息已接收,如果将此参数设置为tru表示会自动回复mq,如果设置为false要通过编程实现回复
* 3、callback,消费方法,当消费者接收到消息要执行的方法
*/
channel.basicConsume(QUEUE_INFORM_EMAIL, true, defaultConsumer);
channel.basicConsume(QUEUE_INFORM_SMS, true, defaultConsumer);
/**
*可以不关闭通道和连接、会一直保持心跳
* */
//关闭通道
channel.close();
//关闭连接
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}