本文将从三个方便了解的RabbitMQ
1,RabbitMQ的是什么
2,为什么要有的RabbitMQ
3,RabbitMQ的五种队列的实现
一,RabbitMQ的是什么
首先在了解RabbitMQ之间我们要知道什么是MQ,MQ的全称为Message Queue也就是消息队列,是一种应用程序之间的通信方法 ;应用程序通过读写消息队列的消息(针对应用程序的数据)来通信,而无需专门用连接来链接它们。
二,为什么要有的RabbitMQ
由于高并发环境下数据来不及同步处理,请求往往会发生阻塞;比如大量的插入,更新等语句同时到达db,直接导致行锁,表锁,甚至最后请求会堆积过多,触发太多连接的错误;而RabbitMQ的是异步的消息,可以很好的解决高并发环境下的种种问题。
使用场景:一些无需及时报道查看御姐耗时的操作,使用MQ来处理,这样的话可以大大节省服务器的请求响应时间,从而提高系统的吞吐量。
三,RabbitMQ的五种队列的实现
1,Simple Queue(简单队列)
消息发送者
package com.vcredit.jdr.rabbitmq.project.send;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.vcredit.jdr.rabbitmq.project.config.RabbitMQConfig;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class SendMessage {
private static final String QUEUE_NAME = "queue_simple";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = null;
Channel channel = null;
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare(
QUEUE_NAME, // 队列的名称
false, // 是否为持久化队列,若为true,服务器关闭后次队列依然处在
false, // 是否为独占队列(仅限于此连接),true为是
false, // 是否自动删除(服务器不适用其时删除次队列),true为是
null // 队列的其他属性(构造参数)
);
String message = "[1] Hello World";
channel.basicPublish(
"", // 消息发布到的交换器名
QUEUE_NAME, // 路由秘钥
null, // 消息的其他属性 - 路由头等
message.getBytes() // 消息信息(消息主体)
);
System.err.println("[1] success, send message: " + message);
} catch (Exception e) {
e.printStackTrace();
} finally {
channel.close();
connection.close();
}
}
}
消息接收者
package com.vcredit.jdr.rabbitmq.project.receive;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceiveMessage {
private static final String QUEUE_NAME = "queue_simple";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.err.println("[1] waiting for message...");
Consumer consumer = 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");
System.err.println("[1] success,received message: " + message);
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
运行ReceivedMessage显示消费者正在等待消息
运行SendMessage函数显示成功发送了一条消息
让我们回ReceivedMessage发现,成功接收了一条消息
这就是简单队列,一个发送者对应一个消费者,当然多个发送者与多个消费者肯定是可以的; 这里建议读者多写几个发送者与消费者来测试效果,我这里就不一一列举了
因为一下的几个队列都需要创建连接,频道对象,所以我们稍作封装
RabbitMQUtil
package com.vcredit.jdr.rabbitmq.project.utils;
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 RabbitMQUtil {
public static final String HOST = "127.0.0.1";
public static Connection getConnection(String host) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(host);
return factory.newConnection();
}
public static Channel getChannel(String host) throws IOException, TimeoutException {
Connection connection = getConnection(host);
return connection.createChannel();
}
public static void closeAll(Channel channel, Connection connection) throws IOException, TimeoutException {
if (channel != null) channel.close();
if (connection != null) connection.close();
}
}
2,Work Queue(工作队列)
上面的简单队列只能简单的发送与接收消息,而不能很好的分发消息;例如有一个消息发送者发送了50条消息,接收者1的处理能力差些,接收者2的处理能力好些,这时你会发现一会后接收者2处理完了消息后,接收者1还在工作;这样的话一个很忙一个很闲,服务器的资源不能很好的分配;这样该怎么办这时候我们可以使用工作队列来处理类似问题
队列工作的主要思想的英文避免某一个消费者执行资源密集型任务,并且必须等待它完成,而其他消费者少任务或无任务执行(这里有一个机制,RabbitMQ的情况默认下的英文轮询配给物任务,也就是说如果有2个消费者,50条消息,每个消费者会执行25条消息,而不会管消息执行的繁琐程度)
发送者
package com.vcredit.jdr.rabbtmq.project.task;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import com.vcredit.jdr.rabbitmq.project.config.RabbitMQConfig;
import com.vcredit.jdr.rabbitmq.project.utils.RabbitMQUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class NewTask {
private static final String QUEUE_NAME = "task";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = RabbitMQUtil.getConnection(RabbitMQConfig.MQ_HOST);
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
// 发送多条消息
for (int i = 0; i < 50; i++) {
String message = "send message: " + i;
channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
System.out.println("success, send message: " + message);
}
RabbitMQUtil.closeAll(channel, connection);
}
}
接收者1
package com.vcredit.jdr.rabbtmq.project.task;
import com.rabbitmq.client.*;
import com.vcredit.jdr.rabbitmq.project.utils.RabbitMQUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceiveTask {
private static final String QUEUE_NAME = "task";
public static void main(String[] args) throws TimeoutException, IOException {
Connection connection = RabbitMQUtil.getConnection(RabbitMQUtil.HOST);
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
// channel.basicQos(1); // 设置basicQos=1,每次发完消息后告诉队列自己已经发完了,队列就会给自己在发送一条消息
System.err.println("[1] wait message ...");
final Consumer consumer = 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");
System.out.println("[1] Received1" + message);
try {
Thread.sleep(1000); // 使用线程休眠模拟任务的复杂程度
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("[1] Done");
// channel.basicAck(
// envelope.getDeliveryTag(), // 收到的标签{@link com.rabbitmq.client.AMQP.Basic.GetOk}或{@link com.rabbitmq.client.AMQP.Basic.Deliver}
// false // 自动回执,true为是;收到消息后告诉消息发送者已经收到消息
// );
}
}
};
channel.basicConsume(
QUEUE_NAME, // 队列名称
// 是否自动应答
// true为是,一旦RabbitMQ将消息发送给了消费者就会从内存中删除;如果杀死正在执行消息的消费者那么数据就会丢失
// 若为false,消费者挂了后会将此消息交给另外一个消费者执行,收到消息后告诉消息发送者已经收到消息,并删除内存中的消息
true,
consumer
);
}
}
接收者2
package com.vcredit.jdr.rabbtmq.project.task;
import com.rabbitmq.client.*;
import com.vcredit.jdr.rabbitmq.project.utils.RabbitMQUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceiveTask2 {
private static final String QUEUE_NAME = "task";
public static void main(String[] args) throws TimeoutException, IOException {
Connection connection = RabbitMQUtil.getConnection(RabbitMQUtil.HOST);
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
// channel.basicQos(1);
System.err.println("[2] wait message ...");
final Consumer consumer = 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");
System.out.println("[2] Received" + message);
try {
Thread.sleep(3000); // 使用线程休眠模拟任务的复杂程度
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Done2");
// channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
分别执行接收者1、接收者2、发送者,你会发现每个接收者处理了25条消息,且接收者1处理速度要比接收者2快;接下来我们改造上面代码,使队列公平的遣派任务。
其实很简单,我们只需要将两个消费者的注释去掉,将最后一行的channel.basicConsume(QUEUE_NAME,true,consume);的第二个参数给为假即可;然后我们会惊奇的发现接收者1与接收者2基本是同一时间完成处理,而他们处理消息的数量也不一样了,这样我就解决了Simple Queue解决不了的问题了