RabbitMQ我在上一篇讲了怎么配置https://blog.csdn.net/sirenloazhang/article/details/80501900
现在开始正式入门RabbitMQ,首先先不和spring一起使用,单独使用RabbitMQ
这里我们需要用到rabbit.client的jar包
如果是用的Maven则
Maven Pom.xml
com.rabbitmq
amqp-client
4.1.0
要其他版本请去http://mvnrepository.com/artifact/com.rabbitmq/amqp-client自己选择
关于消息队列我们需要知道生产者(Producer)和消费者(Consumer),简单的理解就是消息的发送源和接收方
RabbitMQ有几种队列模式,分别为
Simple(简单模式):一个消息只能被一个消费者获取
Work(工作模式):一个消息只能被一个消费者获取,在有多个消费者时,可以用轮询和公平分发
轮询:轮流发给消费者,不管应答数目,傻瓜式分发
公平:根据应答的数目轮流发,比如basicQos( prefetchCount = 1)即应答数目不超过1条,必须关闭自动应答进行手动,而默认是打开自动应答
boolean ack = false ; //打开应答机制
channel.basicConsume(QUEUE_NAME, ack, consumer);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),ack);
ack为true 时则关闭自动应答
Subscribe(订阅模式):一个消息可以被多个消费者获取(主要由Exchange交换机实现)
Routing(路由模式):一个消息可以被多个消费者获取(主要由Exchange交换机实现),可以定义ExchangeKey
Topic(通配符模式):一个消息可以被多个消费者获取,发送的目的队列(queue)可以由通配符#、*指定
基础的认识了RabbitMQ,现在我们开始测试实例
各个模式的实例,我整合在一个文件里进行测试,以上的几个模式我都写在方法里了(要用去掉注释就行),需要直接调用测试
需要直接Run这几个线程,就可以看到结果,方便入门理解
RabbitMQ连接
package com.test.rabbitmq;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @author Sirenes
* @version 创建时间:2018年5月30日 下午3:04:55
* 类说明:
*/
public class ConnectionUtil {
public static Connection getConnection() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
// 设置MabbitMQ所在主机ip或者主机名
factory.setHost("localhost");
// 创建一个连接
Connection connection;
connection = factory.newConnection();
return connection;
}
}
生产者
package com.test.rabbitmq;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @author Sirenes
* @version 创建时间:2018年5月29日 下午11:25:17 类说明:
*/
public class Send {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
//new Send().simple();
//new Send().work();
//new Send().subscribe();
//new Send().routing();
new Send().topic();
}
public void simple(){ /*Simplemoshi*/
ConnectionFactory factory = new ConnectionFactory();
// 设置MabbitMQ所在主机ip或者主机名
factory.setHost("localhost");
// 创建一个连接
Connection connection;
try {
connection = factory.newConnection();
Channel channel = connection.createChannel();
// 指定一个队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 发送的消息
String message = "hello world!";
// 往队列中发出一条消息
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
// 关闭频道和连接
channel.close();
connection.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 创建一个频道
}
public void work() throws Exception{ /*Work模式*/
Connection connection = ConnectionUtil.getConnection();
Channel channel;
try {
channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
for (int i = 0; i < 50; i++) {
String message = "" + i;
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
Thread.sleep(i * 10);
}
channel.close();
connection.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 声明队列
}
private final static String EXCHANGE_NAME= "exchange_fanout";
public void subscribe() throws Exception{ /*Subscribe模式*/
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明exchange
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
// 消息内容
String message = "Hello world";
// 与前面不同, 生产者将消息发送给exchange, 而非队列. 若发消息时还没消费者绑定queue与该exchange, 消息将丢失
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
channel.close();
connection.close();
}
public void routing() throws Exception{ /*routing模式*/
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明exchange
channel.exchangeDeclare("exchange_direct", "direct");
String message = "Hello world";
// 发送消息, RoutingKey为 insert
channel.basicPublish("exchange_direct", "insert", null, message.getBytes());
channel.close();
connection.close();
}
public void topic() throws Exception{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明exchange
channel.exchangeDeclare("exchange_topic", "topic");
String message = "Hello world";
// 发送消息, 指定RoutingKey
channel.basicPublish("exchange_topic", "item.delete", null, message.getBytes());
channel.close();
connection.close();
}
}
消费者
package com.test.rabbitmq;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.ShutdownSignalException;
/**
* @author Sirenes
* @version 创建时间:2018年5月29日 下午11:30:09 类说明:
*/
public class Rec {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv)
throws Exception {
Rec rec1=new Rec();
//rec1.simple();
//rec1.work("1",100);
//rec1.subscribe("1");
//rec1.routing1("1");
rec1.topic1("1");
}
public void simple() throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{
// 打开连接和创建频道,与发送端一样
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明队列,主要为了防止消息接收者先运行此程序,队列还不存在时创建队列。
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
// 创建队列消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 指定消费队列
channel.basicConsume(QUEUE_NAME, true, consumer);
while (true) {
// nextDelivery是一个阻塞方法(内部实现其实是阻塞队列的take方法)
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
}
}
public void work(String name,int time) throws Exception{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 开启Qos, 同一时刻服务器只发送一条消息. 可以尝试注释该行, 会发现消息会被平均分配给两个消费者
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(QUEUE_NAME, false, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(name+"获取:" + message);
// 模拟handling
Thread.sleep(time);
// 手动确认消息接收. 在basicConsume方法中, true为自动, false为手动
/* 消息确认方式:
* 1. 自动确认. 只要消息从队列中移除, 服务端认为消息被成功消费
* 2. 手动确认. 消费者获取消息后, 服务器将该消息标记为不可用, 并等待反馈. 如果消费者一直不反馈, 则该消息将一直处于不可用状态
*/
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
private final static String EXCHANGE_NAME= "exchange_fanout";
public void subscribe(String name) throws Exception {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(name, false, false, false, null);
// 绑定队列到交换机. 绑定也可在rabbitMQ的管理界面进行
channel.queueBind(name, EXCHANGE_NAME, "");
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(name, false, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(name+"获取:" + message);
Thread.sleep(100);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
private final static String EXCHANGE_NAME1= "exchange_direct";
public void routing1(String name) throws Exception {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(name, false, false, false, null);
// 绑定队列到交换机, BindingKey为 delete update
channel.queueBind(name, EXCHANGE_NAME1, "update");
channel.queueBind(name, EXCHANGE_NAME1, "delete");
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(name, false, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(name+"获取:" + message);
Thread.sleep(100);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
public void routing2(String name) throws Exception {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(name, false, false, false, null);
// 绑定队列到交换机, BindingKey为 delete update
channel.queueBind(name, EXCHANGE_NAME1, "insert");
channel.queueBind(name, EXCHANGE_NAME1, "update");
channel.queueBind(name, EXCHANGE_NAME1, "delete");
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(name, false, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(name+"获取:" + message);
Thread.sleep(100);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
private final static String EXCHANGE_NAME2 = "exchange_topic";
public void topic1(String name) throws Exception{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(name, false, false, false, null);
// 绑定队列到交换机
channel.queueBind(name, EXCHANGE_NAME2, "item.update");
channel.queueBind(name, EXCHANGE_NAME2, "item.delete");
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(name, false, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("获取:" + message);
Thread.sleep(100);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
public void topic2(String name) throws Exception{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(name, false, false, false, null);
// 绑定队列到交换机
channel.queueBind(name, EXCHANGE_NAME2, "item.#"); /*用通配符匹配*/
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(name, false, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("获取:" + message);
Thread.sleep(100);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
这里在增加一个消费者线程方便测试
package com.test.rabbitmq;
/**
* @author Sirenes
* @version 创建时间:2018年5月30日 下午3:18:15
* 类说明:
*/
public class testTread {
public static void main(String[] args) throws Exception {
//new Rec().simple();
//new Rec().work("2",200);
//new Rec().subscribe("2");
//new Rec().routing2("2");
new Rec().topic2("2");
}
}
到这里了解了关于Rabbit消息队列的基本模式使用,具体细节解释以及RabbitMQ整合Spring,我会在之后的文章更新。