1.简单队列模式
1.1模型
P:消息的生产者。
红色的:队列
C:洧费者。
1.2连接上rabbitmq
创建一个工具类来连接
引入jar包或依赖
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
public class ConnectionUtil {
public static Connection getConnection() throws IOException, TimeoutException {
-----定义一个连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
-----设置服务地址
connectionFactory.setHost("127.0.1");
-----设置端口号地址
connectionFactory.setPort(5672);
-----设置数据库
connectionFactory.setVirtualHost("/rabbitmq");
-----设置用户名和密码
connectionFactory.setUsername("MXH");
connectionFactory.setPassword("123456");
return connectionFactory.newConnection();
}
}
1.3 创建生产者(消息发送方)
public class send {
private static final String QUEUE_NAME="MXH";
public static void main(String[] args) throws IOException, TimeoutException {
------创建一个连接
Connection connection = ConnectionUtil.getConnection();
------从连接中获取一个通道
Channel channel = connection.createChannel();
------创建一个消息队列 QUEUE_NAME:队列名
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
String msg = "hello,rqbbitmq你好1";
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
channel.close();
connection.close();
}
}
1.4 创建消息接收方
public class received {
private static final String QUEUE_NAME="MXH";
public static void main(String[] args) throws IOException, TimeoutException {
------创建一个连接
Connection connection = ConnectionUtil.getConnection();
------创建通道
Channel channel = connection.createChannel();
------创建一个消息队列 QUEUE_NAME:队列名
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
------定义队列消费者
DefaultConsumer consumer = new DefaultConsumer(channel)
{
------handleDelivery方法可以只要消息队列中有消息,配合监听就能一直读取
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException
{
String msg = new String(body);
System.out.println("接收到消息:"+msg);
}
};
------监听队列
channel.basicConsume(QUEUE_NAME,true, consumer);
}
}
1.5简单队列的不足点
耦合性高,生产者- - -对应消费者(如果我想有多个消费者消费队列中消息,这时候就不行了)。队列名变更这时候得同时变更.
2.Work Queues模式
2.1轮询分发模式
为什么会出现工作队列
Simple队列是一一对应的,而且我们实际开发,生产者发送消息是毫不费力的,而消费者一般是要跟业务相结合的,消费者接收到消息之后就需要处理可能需要花费时间,这时候队列就会积压了很多消息。
创建一个生产者
public class send {
private static final String QUEUE_NAME="MXH";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接
Connection connection = ConnectionUtil.getConnection();
//创建一个通道
Channel channel = connection.createChannel();
//创建一个队列,用户名
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
for(int i=0;i<50;i++)
{
String msg = "hello"+i;
System.out.println("MQ"+msg);
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
}
channel.close();
connection.close();
}
}
创建第一个消费者
public class received {
private static final String QUEUE_NAME="MXH";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接
Connection connection = ConnectionUtil.getConnection();
//创建一个通道
Channel channel = connection.createChannel();
//创建一个队列,用户名
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
DefaultConsumer consumer = new DefaultConsumer(channel)
{
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException
{
String msg = new String(body);
System.out.println("RE"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}finally
{
System.out.println("done");
}
}
};
channel.basicConsume(QUEUE_NAME,true, consumer);
}
}
创建第二个消费者
public class receiver2
{
private static final String QUEUE_NAME="MXH";
public static void main(String[] args) throws IOException, TimeoutException
{
------创建一个连接
Connection connection = ConnectionUtil.getConnection();
------创建一个通道
Channel channel = connection.createChannel();
------创建一个队列,用户名
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
DefaultConsumer consumer = new DefaultConsumer(channel)
{
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException
{
String msg = new String(body);
System.out.println("RE"+msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e)
{
e.printStackTrace();
}finally
{
System.out.println("done");
}
}
};
channel.basicConsume(QUEUE_NAME,true, consumer);
}
}
现象
消费者1和消费者2处理的消息是一样的。
消费者1:偶数。
洧费者2:奇数。
这种方式叫做轮询分发(round-robin)结果就是不管谁忙活着谁清闲都不会多给一个消息
任务消息总是你-一个我-一个。
2.2 公平分发模式
使用公平分发必须
关闭自动应答 ack 改成手动
创建一个生产者
需要加channel.basicQos(1):
注意:channel.basicQos(1):限制发送给同一个消费者不得超过一条消息,每个消费者发送确认消息之前,消息队列不发送下一个消息到消费者
public class send {
private static final String QUEUE_NAME="MXH";
public static void main(String[] args) throws IOException, TimeoutException {
创建一个连接
Connection connection = ConnectionUtil.getConnection();
创建一个通道
Channel channel = connection.createChannel();
创建一个队列,用户名
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
每个消费者发送确认消息之前,消息队列不发送下一个消息到消费者, 一 次只处理一个消息
限制发送给同一个消费者不得超过一条消息
int prefetch=1;
channel.basicQos(prefetch);
for(int i=0;i<50;i++)
{
String msg = "hello"+i;
System.out.println("MQ"+msg);
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
}
channel.close();
connection.close();
}
}
创建第一个消费者
注意:
//一次应答前只发送一次
channel.basicQos(1);
//手动回执
channel.basicAck(envelope.getDeliveryTag(),false);
//自动应答 true为开启 false为关闭
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
public class received {
private static final String QUEUE_NAME="MXH";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接
Connection connection = ConnectionUtil.getConnection();
//创建一个通道
Channel channel = connection.createChannel();
//一次应答前只发送一次
channel.basicQos(1);
//创建一个队列,用户名
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
DefaultConsumer consumer = new DefaultConsumer(channel)
{
int i =0;
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException
{
int p = i++;
String msg = new String(body);
System.out.println("RE"+msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e)
{
e.printStackTrace();
}finally
{
//手动回执
channel.basicAck(envelope.getDeliveryTag(),false);
}
System.out.println(i);
}
};
boolean autoAck = false;//自动应答 true为开启 false为关闭
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
创建第二个消费者
public class receiver2
{
private static final String QUEUE_NAME="MXH";
public static void main(String[] args) throws IOException, TimeoutException
{
//创建一个连接
Connection connection = ConnectionUtil.getConnection();
//创建一个通道
Channel channel = connection.createChannel();
//一次应答前只发送一次
channel.basicQos(1);
//创建一个队列,用户名
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
DefaultConsumer consumer = new DefaultConsumer(channel)
{
int i=0;
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException
{
i++;
String msg = new String(body);
System.out.println("RE"+msg);
try {
Thread.sleep(500);
} catch (InterruptedException e)
{
e.printStackTrace();
}finally
{
System.out.println(i);
//手动回执
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;//自动应答 true为开启 false为关闭
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
现象:
现象:消费者2处理的消息比消费者1多,能者多劳。
消息应答
boolean autoack= falsez
channel.basicConsume (QUEUENANE, autoAck,consumer)
boolean autonck=true; (自动确认模式)一rabbitnq将消息分发给消费者 ,就会从内存中珊除;。
这种情况下,如果杀死正在执行的消费者,就会丢失正在处理的消息。
boolean autcAck=false; (手动模式),如果有一个消费者挂掉,rabbitmq不会将内存的信息删除,直到下个消费者处理后返回应答信息才会删除
洧息应答默认是打开的,falser;
大家想想如果我rabbitmq挂了我们的消息任然会丢失!!!
消息持久化
可以在rabbitmq挂了下而不丢失数据
//声明队列。
boolean durable= false;fasle为不持久化,true则是允许持久化
channel.queueDeclare (QUEUE_ NAME, durable, false, false, nu11)
我们将程序中的boolean durable= false;改成true;是不可以的 ,尽管代码是正确的,rabbitmq不许重新定义(不同参数)一个已存在的队列。