1. RabbitMQ的组成
//生成者客户端
public class RabbitMQProducer {
private static final String IP="192.168.10.128";
private static final String USER="root";
private static final String PASSWORD="123456";
private static final int PORT=5672;
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection=null;
Channel channel=null;
try {
String exchange_demo="exchange_demo";//声明一个交换器名称
String queue_demo="queue_demo";//声明一个队列名称
String route_demo="route_demo";//声明一个路由键,用于绑定交换器和队列
ConnectionFactory fac=new ConnectionFactory();//获取一个rabbitMQ连接池,并设置相关参数
fac.setHost(IP);
fac.setPassword(PASSWORD);
fac.setUsername(USER);
fac.setPort(PORT);
//从连接池中获取一个rabbitMQ连接(或者说是一个RabbitMQ客户端)
connection=fac.newConnection();
channel=connection.createChannel();//创建一个频道
channel.exchangeDeclare(exchange_demo, "direct",false,false,null);//创建一个type为direct,持久化的、非自动删除的交换器
channel.queueDeclare(queue_demo, true, false, false, null);//创建一个持久化、非排他的、非自动删除的交换器
channel.queueBind(queue_demo, exchange_demo, route_demo);//将交换器和队列通过路由键绑定
//发送一条消息
String message="Hello World";
channel.basicPublish(exchange_demo, route_demo, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
} finally {
//关闭资源
if(connection!=null){
if(channel!=null){
channel.close();//可以不用关闭,当connection关闭后,channel也会自动关闭
}
connection.close();
}
}
}
}
//消费者客户端
public class RabbitMQConsumer {
private static final String IP="192.168.10.128";
private static final String USER="root";
private static final String PASSWORD="123456";
private static final int PORT=5672;
private static final String QUEUE_NAME="queue_demo";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection=null;
Address[] address={new Address(IP,PORT)};
try {
String queue_demo="queue_demo";//声明一个队列名称
ConnectionFactory fac=new ConnectionFactory();//获取一个rabbitMQ连接池,并设置相关参数
fac.setPassword(PASSWORD);
fac.setUsername(USER);
//从连接池中获取一个rabbitMQ连接
connection=fac.newConnection(address);
final Channel channel=connection.createChannel();//创建一个频道
channel.basicQos(64);
Consumer con=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
System.out.println("get message:"+new String(body,"utf-8"));
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(queue_demo, con);
if(channel!=null){
channel.close();
}
} finally {
//关闭资源
if(connection!=null){
connection.close();
}
}
}
}
1. 生产者就是产生消息的客户端,消息产生后会发送到RabbitMQ中。
2. 消息:消息包含两个部分,分别是消息体(payload)和标签(Label)。消息体是一个带有业务逻辑结构的数据,比如JSON字符串,甚至是序列化之后的Java对象。消息的标签用来描述消息,比如该消息所属的交换器名称以及路由键,RabbitMQ会根据标签将消息发送到对应的消费者客户端。
3. 消费者:即接收处理消息的客户端,消费者连接到RabbitMQ后,需要订阅(或者说绑定)某个队列,消费者消费一个消息只会消费消息体,在消息路由的过程中消息的标签会被丢弃,存入到队列中的消息只有消息体,所以消费者也只会消费到消息体。
4. Broker:消息中间件的服务节点,一个RabbitMQ Broker就是一个服务节点,或者说就是一个RabbitMQ服务器。一个Broker中主要包括多个交换器和消息队列。
5. Queue:即队列,是RabbitMQ的内部对象,用于存储消息。多个消费者可以订阅同一个队列,那么此时队列中的消息就会被平均分摊(即轮询),多个消费者处理,但并不会收到队列中的每一条消息进行处理。也就是说,RabbitMQ不支持队列层面的广播消费,如果需要广播消费,则需要进行二次开发,一般不建议这么做。
6. Exchange:交换器,是RabbitMQ的内部对象,生产者客户端通过交换器将消息发送到队列中存储,如果找不到目标队列,要么返回给生产者要么直接丢弃。交换器有多种类型,包括fanout、direct、topic、hearders四种。
7. RoutingKey:即路由键,生产者将消息发送给交换器时,一般会指定一个RoutingKey,用来指定这个消息的路由规则
8. Binding:绑定,RabbitMQ中通过BindingKey(绑定键)将交换器和队列关联起来,这样RabbitMQ就知道如何将消息路由到队列了
9. RabbitMQ结构模型总结:RabbitMQ是基于生产者消费者模型的组织结构,和生活中的快递公司非常类似。首先生产者就相当于发件人,产生的消息就是需要邮寄的快递包裹,消费者就是收件人,RabbitMQ就相当于快递公司,RabbitMQ中的Exchange就相当于通往收件地的交通工具,而BindingKey就相当于收件人的实际地址,RoutingKey就相当于写在包裹上的收件人地址,通过RoutingKey和Exchange就可以运送到队列中存放,队列就相当于收件地的快递公司分部,在这里等待收件人主动收取快递(消费消息的拉模式),或者是由快递员主动送至收件人手中(消费消息的推模式)。
10. 对于RoutingKey和BindingKey,实际上可以看做一个东西,在direct类型的交换器中,RoutingKey和BindingKey必须完全匹配才能使用
2. 交换器类型
1. fanout:该类型的交换器会把发送到该交换器的所有消息路由到所有与该交换器绑定的队列中
2. direct:该类型的交换器会把消息路由到RoutingKey和BindingKey完全匹配的队列中。比如,声明将队列queue1和交换器Exchange1通过路由键"warning"绑定,但在发送时,发送的路由键写为"debug",此时如果有队列queue2通过"debug"路由键绑定到当前交换器,那么则会发送到队列queue2中,如果没有队列通过"debug"路由键绑定到当前交换器,那么该消息就会发送失败
3. topic:该类型的交换器会将消息发送到RoutingKey与BindingKey模糊匹配成功的所有队列中。匹配规则为用 * 或 # 做占位符,* 相当于多个字符,可以是0个,而 # 代表一个字符。比如,BindingKey为"*.rabbitmq.*",则可以匹配RoutingKey有"com.rabbitmq.client"等等,也就是说在RoutingKey和BindingKey进行匹配时,除了占位符之外的所有字符要完全匹配。
4. hearders:不依赖于路由键的匹配规则来路由消息,而是根据发送消息中的hearders属性进行匹配,但该类型不基本使用
3. 客户端中的Connection和Channel
1. 无论是生产者客户端还是消费者客户端,都需要与RabbitMQ服务器建立连接,也就是Connection,这个连接是一个TCP协议的连接,一旦连接建立,客户端就可以通过该连接创建一个AMQP(高级消息队列协议)信道(Channel),每个信道都会指派一个唯一的id,信道是建立在Connection之上的虚拟连接,RabbitMQ处理的每条AMQP指令都是通过信道完成。
2. AMQP:高级消息队列协议,它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。是一个通信协议,和HTTP协议一样,属于应用层协议,AMQP协议相当于一系列结构化命令操作的结合,类似于HTTP协议中的GET、POST、PUT等操作。