一、RabbitMQ
MQ中文意思为消息队列。
- 不同进程之间需要进行数据交互时,如果直接调用的话耦合性太高,一个进程的修改会影响到其它进程,所以把需要传递的信息放进消息队列中,当其它进程需要使用时直接从消息队列中获取,解除系统耦合性。
- 当一个系统需要接收多个请求的时候,无法同时进行处理,这时可以将请求放进消息队列中,由系统自己从队列中拿取合适数量的请求。
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。
二、RabbitMQ安装
下载地址:http://www.rabbitmq.com/download.html
下载完成之后按照默认的配置安装即可。
安装完成之后启动服务进入管理界面
地址:http://127.0.0.1:15672/
net stop RabbitMQ //停止服务
net start RabbitMQ //启动服务
三、测试
编写一个简单的生产消费者测试
首先创建一个 virtual host
选中用户,设置权限
之后便是编写代码
引入依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.8.0</version>
</dependency>
连接MQ的工具类
package rabbitmq;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
public class ConnectionUtil {
public static Connection getConnection() throws Exception {
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("localhost");
//端口
factory.setPort(5672);
//设置账号信息,用户名、密码、vhost
factory.setVirtualHost("test");
factory.setUsername("admin");
factory.setPassword("123456");
// 通过工程获取连接
Connection connection = factory.newConnection();
return connection;
}
}
生产者
package rabbitmq;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = null;
Channel channel = null;
try {
// 获取到连接以及mq通道
connection = ConnectionUtil.getConnection();
// 从连接中创建通道
channel = connection.createChannel();
// 声明(创建)队列
channel.queueDeclare("rabbit_test_1", false, false, false, null);
// 消息内容
for(int i=0;i<10;i++){
channel.basicPublish("", "rabbit_test_1", null, ("hello rabbit"+i).getBytes("UTF-8"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
channel.close();
connection.close();
}
}
}
可以在管理工具中查看消息
消费者
package rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.*;
public class Consumer {
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
try {
// 获取到连接以及mq通道
connection = ConnectionUtil.getConnection();
// 从连接中创建通道
channel = connection.createChannel();
// 声明队列
channel.queueDeclare("rabbit_test_1", false, false, false, null);
// 定义队列的消费者
final Channel finalChannel = channel;
DefaultConsumer consumer = new DefaultConsumer(finalChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(envelope.getDeliveryTag() + "接收消息" + new String(body));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
finalChannel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 监听队列
channel.basicConsume("rabbit_test_1", consumer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
创建多个消费者查看运行情况
package rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.*;
public class Consumers {
public static class MyThread implements Callable{
public Object call() throws Exception {
Connection connection = null;
Channel channel = null;
try {
// 获取到连接以及mq通道
connection = ConnectionUtil.getConnection();
// 从连接中创建通道
channel = connection.createChannel();
// 声明队列
channel.queueDeclare("rabbit_test_1", false, false, false, null);
// 定义队列的消费者
final Channel finalChannel = channel;
DefaultConsumer consumer = new DefaultConsumer(finalChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(Thread.currentThread().getName() + envelope.getDeliveryTag() + "接收消息" + new String(body));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
finalChannel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 监听队列
channel.basicConsume("rabbit_test_1", false, consumer);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) throws IOException, TimeoutException {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for(int i=0; i<3;i++){
fixedThreadPool.submit(new MyThread());
}
}
}
创建一个有3个线程的线程池,运行时会同时处理3条消息
四、订阅发布
一个消息发送给多个消费者(工作者),这种模式一般被称为“发布/订阅”模式。
生产者将消息发送给Exchange(路由器/交换机),然后路由器转送到队列,消费者各自到自己的队列里面获取消息进行消费。有了exchange,生产者不需要知道有哪些队列,因此队列名字可以不用指定了,而是通过RabbitMQ 接口自己去生成临时队列,队列名字也由RabbitMQ自动生成。
生产者代码:
package rabbitmq;
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 {
//exchange名字
public static String EXCHANGE_NAME = "exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = null;
Channel channel = null;
try {
// 获取到连接以及mq通道
connection = ConnectionUtil.getConnection();
// 从连接中创建通道
channel = connection.createChannel();
// 为通道声明exchange和exchange的类型
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
// 消息内容
for(int i=0;i<1;i++){
channel.basicPublish(EXCHANGE_NAME, "", null, ("hello rabbit"+i).getBytes("UTF-8"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
channel.close();
connection.close();
}
}
}
消费者代码:
package rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.*;
public class Consumers {
public static class MyThread implements Callable{
public Object call() throws Exception {
Connection connection = null;
Channel channel = null;
try {
// 获取到连接以及mq通道
connection = ConnectionUtil.getConnection();
// 从连接中创建通道
channel = connection.createChannel();
// 2.为通道声明exchange以及exchange类型
channel.exchangeDeclare(Producer.EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
// 声明一个随机队列
String queueName = channel.queueDeclare().getQueue();
// 4.建立exchange和队列的绑定关系
channel.queueBind(queueName, Producer.EXCHANGE_NAME, "");
// 定义队列的消费者
final Channel finalChannel = channel;
DefaultConsumer consumer = new DefaultConsumer(finalChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(Thread.currentThread().getName() + envelope.getDeliveryTag() + "接收消息" + new String(body));
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
finalChannel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 监听队列
channel.basicConsume(queueName,false, consumer);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) throws IOException, TimeoutException {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for(int i=0; i<3;i++){
fixedThreadPool.submit(new MyThread());
}
}
}
三个消费者可以收到同一个消息
总结
- 一个消息只会被一个消费者获取。
- 消费者在确认处理完消息后都可以自动获取队列中的下一条消息。