在默认路由下,当有多个工作线程时,可以共同完成这个工作队列,他们的模式有两种:轮训分发和不公平分发。
1.轮训分发:依次按照顺序一条一条分发到每个工作线程处理,一个工作线处理完,下一个工作线程才能进行。
案例:一个生产者发消息,两个消费者消费消息,采用轮训分发模式
(1)开启linux上rabbitmq的服务和关闭linux防火墙
/sbin/service rabbitmq-server start
systemctl stop firewalld
(2)写一个连接工厂,创建信道的工具类(减少代码重复)
①获取连接工厂
②设置连接rabbitmq的ip地址(即linux上的本地地址)
③设置用户名和密码
④获取连接,并创建信道
//连接工厂,创建信道工具类
public class RabbitUtils {
// 得到一个连接的 channel
public static Channel getChannel() throws Exception {
// 创建一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.23.111");
factory.setUsername("user");
factory.setPassword("123");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
return channel;
}
}
(3)写生产者发送消息
①调用工具类获取信道
②调用queueDeclare(队列名称,消息是否持久化,是否只供一个消费者消费,是否自动删除消息,其他参数)声明队列
③调用scanner从控制台输入信息发送
④调用basicPublic(路由,队列名称,其他参数,发送消息)方法发送消息
public class Task01 {
//队列
public static final String QUEUE_NAME="hello";
//发送消息
public static void main(String[] args) throws Exception {
//获取信道
Channel channel = RabbitUtils.getChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//从控制台发送消息
Scanner sc=new Scanner(System.in);
while (sc.hasNext()){
String message = sc.nextLine();
channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
System.out.println("发送消息完成:"+message);
}
}
}
(4)写两个消费者消费消息
①调用工具类获取信道
②调用接收消息回调函数deliverCallback(消息标记,消息),打印收到消息到控制台
③调用消息中断回调函数
④调用basicConsume(队列名称,是否自动应答,接收消息回调函数,消息中断回调函数)方法接收消息
//工作线程,消费者
public class Worker01 {
//队列
public static final String QUEUE_NAME="hello";
//接受消息
public static void main(String[] args) throws Exception {
//获取信道
Channel channel = RabbitUtils.getChannel();
//声明,接受消息
DeliverCallback deliverCallback=(consumerTag, message)->{
System.out.println("接收到的消息:"+new String(message.getBody()));
};
//消息被中断声明
CancelCallback cancelCallback=(consumerTag)->{
System.out.println(consumerTag+"消息消费被中断");
};
System.out.println("C1等待接受消息......");
//消息接收
channel.basicConsume(QUEUE_NAME,true,deliverCallback,cancelCallback);
}
}
//工作线程,消费者
public class Worker02 {
//队列
public static final String QUEUE_NAME="hello";
//接受消息
public static void main(String[] args) throws Exception {
//获取信道
Channel channel = RabbitUtils.getChannel();
//声明,接受消息
DeliverCallback deliverCallback=(consumerTag, message)->{
System.out.println("接收到的消息:"+new String(message.getBody()));
};
//消息被中断声明
CancelCallback cancelCallback=(consumerTag)->{
System.out.println(consumerTag+"消息消费被中断");
};
System.out.println("C2等待接受消息......");
//消息接收
channel.basicConsume(QUEUE_NAME,true,deliverCallback,cancelCallback);
}
}
(5)测试,启动生产者和两个消费者
2、不公平分发:多个线程工作下,可以为每个线程分配固定数量的消息接收,两个线程可以同时处理接收消息
案例:一个生产者发送5条消息,一个处理快的(睡眠1秒)消费者分发3条,一个处理慢的消费者(睡眠30秒)分发两条
(1)启动服务,关闭防火墙,创建工具类同上一样
(2)创建线程睡眠工具类,用于模拟消费者处理速度
//线程等待工具类
public class SleepUtils {
public static void sleep(int second){
try {
Thread.sleep(1000*second);
} catch (InterruptedException _ignored) {
Thread.currentThread().interrupt();
}
}
}
(3)创建生产者发送消息
①获取信道,开启信道发布确认(手动应答消息不会丢失)
②声明队列,开启消息持久化(防止服务器宕机消息的丢失)
③控制台输入发送消息
④发送消息,消息持久化保存到磁盘上
public class Task02 {
//队列
public static final String TASK_NAME="ack_queue";
public static void main(String[] args) throws Exception {
Channel channel = RabbitUtils.getChannel();
//开启信道发布确认
channel.confirmSelect();
//声明队列
boolean durable=true;//需要队列持久化
channel.queueDeclare(TASK_NAME,durable,false,false,null);
//控制台输入信息
Scanner sc=new Scanner(System.in);
while (sc.hasNext()){
String message = sc.nextLine();
//第三个参数设置消息持久化,保存到磁盘上
channel.basicPublish("",TASK_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN
,message.getBytes("UTF-8"));
System.out.println("生产者发出消息:"+message);
}
}
}
(4)创建处理快的消费者接收消息
①获取信道连接
②消息接收回调函数,调用睡眠工具类,睡眠1秒,调用basicAck(消息标记,是否批量应答)方法自动应答
③消息中断回调函数调用
④调用basicQos(分发数量)方法设置不公平分发,设置数量为3
⑤接收消息
public class Worker03 {
//队列
public static final String TASK_NAME="ack_queue";
public static void main(String[] args) throws Exception {
Channel channel = RabbitUtils.getChannel();
System.out.println("C1等待接收处理,时间较短......");
//声明,接受消息
DeliverCallback deliverCallback=(consumerTag, message)->{
//接受消息前进行沉睡,1秒
SleepUtils.sleep(1);
System.out.println("接收到的消息:"+new String(message.getBody(),"UTF-8"));
//应答消息,手动应答
channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
};
//消息被中断声明
CancelCallback cancelCallback=(consumerTag)->{
System.out.println(consumerTag+"消息消费被中断");
};
//设置不公平分发
//prefetchCont为1,表示开启不公平分发,大于1,则表示预取值
//预取值为2
int prefetchCont=5;
channel.basicQos(prefetchCont);
//采用手动应答
boolean autoAck=false;
channel.basicConsume(TASK_NAME,autoAck,deliverCallback,cancelCallback);
}
}
(5).创建处理慢的消费者
①获取信道连接
②消息接收回调函数,调用睡眠工具类,睡眠30秒,调用basicAck(消息标记,是否批量应答)方法自动应答
③消息中断回调函数调用
④调用basicQos(分发数量)方法设置不公平分发,设置数量为2
⑤接收消息
public class Worker04 {
//队列
public static final String TASK_NAME="ack_queue";
public static void main(String[] args) throws Exception {
Channel channel = RabbitUtils.getChannel();
System.out.println("C2等待接收处理,时间较长......");
//声明,接受消息
DeliverCallback deliverCallback=(consumerTag, message)->{
//接受消息前进行沉睡,30秒
SleepUtils.sleep(30);
System.out.println("接收到的消息:"+new String(message.getBody(),"UTF-8"));
//应答消息,手动应答
channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
};
//消息被中断声明
CancelCallback cancelCallback=(consumerTag)->{
System.out.println(consumerTag+"消息消费被中断");
};
//设置不公平分发
//预取值为5
int prefetchCont=2;
channel.basicQos(prefetchCont);
//采用手动应答
boolean autoAck=false;
channel.basicConsume(TASK_NAME,autoAck,deliverCallback,cancelCallback);
}
}
(6)测试,启动生产者和消费者
C1处理3秒后完成三个消息接受,C2一共处理60秒完成两个消息接收