官網介紹:https://www.rabbitmq.com/getstarted.html
(1) 簡單模式:一個生產者,一個消費者
(2) work模式:一個生產者,多個消費者,每個消費者獲取到的消息唯一。
(3) 訂閱模式:一個生產者發送的消息會被多個消費者獲取。
(4) 路由模式:發送消息到交換機並且要指定路由key ,消費者將隊列綁定到交換機時需要指定路由key
(5) topic模式:將路由鍵和某模式進行匹配,此時隊列需要綁定在一個模式上,“#”匹配一個詞或多個詞,“*”只匹配一個詞
(6) RPC模式:使用RabbitMQ構建RPC系統:客戶端和可伸縮RPC服務器。
(7)發佈確認:與發佈者進行可靠的發佈確認
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=rabbitadmin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/vhost_rabbitmq
public class ConnectionUitls {
public static Connection getConnection() throws IOException, TimeoutException{
//定義連接工廠
ConnectionFactory factory = new ConnectionFactory();
//設置連接地址
factory.setHost("127.0.0.1");
//AMQP 5672
factory.setPort(5672);
//vhost
factory.setVirtualHost("/vhost_jetsen");
//username
factory.setUsername("rabbitadmin");
//password
factory.setPassword("123456");
Connection newConnection = factory.newConnection();
return newConnection;
}
}
(1) 簡單模式:一個生產者,一個消費者
package com.jetsen.mq.q1simplequeue;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
//Java操作simple簡單隊列
//1.耦合性高,生產者一一對應,(如果想有多個消費者 ,不可行)
//2.隊列名稱變更,生產者、消費者需同時變更。
public class Sender {
private static final String QUEUE_NAME="test_simple_queueu";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
//獲取通道
Channel channel = connection.createChannel();
//隊列聲明
//String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "send message !";
//String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println("===>send msg:"+message);
channel.close();
connection.close();
}
}
隊列的申明
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
queue:隊列名稱
durable:是否持久化, 隊列的聲明默認是存放到內存中的,如果rabbitmq重啓會丟失,如果想重啓之後還存在就要使隊列持久化,保存到Erlang自帶的Mnesia數據庫中,當rabbitmq重啓之後會讀取該數據庫。
exclusive:是否排外的,有兩個作用,一:當連接關閉時connection.close()該隊列是否會自動刪除;二:該隊列是否是私有的private,如果不是排外的,可以使用兩個消費者都訪問同一個隊列,沒有任何問題,如果是排外的,會對當前隊列加鎖,其他通道channel是不能訪問的,如果強制訪問會報異常
autoDelete:是否自動刪除,當最後一個消費者斷開連接之後隊列是否自動被刪除,可以通過RabbitMQ Management,查看某個隊列的消費者數量,當consumers = 0時隊列就會自動刪除
arguments: 隊列中的消息什麼時候會自動被刪除
void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
package com.jetsen.mq.q1simplequeue;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
//Java操作simple簡單隊列
public class Recv {
private static final String QUEUE_NAME="test_simple_queueu";
public static void main(String[] args) throws IOException, TimeoutException {
//獲取連接
Connection connection = ConnectionUitls.getConnection();
//創建頻道
Channel channel = connection.createChannel();
//QUEUE_NAME, durable, exclusive, autoDelete, arguments 隊列聲明
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//事件模型
DefaultConsumer consumerCallback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer receive:=>"+messageStr);
}
};
//String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME,true, consumerCallback);
}
}
(2) work模式:一個生產者,多個消費者,每個消費者獲取到的消息唯一。
RabbitMq消費分發機制及主題消費分發(公平分發 + 輪訓分發)
公平分發 :現在有2個消費者,我們使用basicQos( prefetchCount = 1)方法,來限制RabbitMQ只發不超過1條的消息給同一個消費者。當消息處理完畢後,有了反饋,纔會進行第二次發送。
生產者每次發送間隔爲20ms,消費者1消費間隔爲2s,消費者2消費間隔爲1s,代表消費者2消費能力高。
公平分發
//每個消費者,在發送【確認消息】之前,消息隊列不發送下一個消息到消費者,一次只處理一個消息
//限制發送給同一個消費者不得超過1條消息
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
//公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
package com.jetsen.mq.q2workqueue.fairdispatch;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* 工作隊列
* |----cl 2s
* p-----------queue------|
* |----c2 1s
*/
public class Sender {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//獲取連接
Connection connection = ConnectionUitls.getConnection();
//獲取channel
Channel channel = connection.createChannel();
//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//每個消費者,在發送【確認消息】之前,消息隊列不發送下一個消息到消費者,一次只處理一個消息
//限制發送給同一個消費者不得超過1條消息
int prefetchCount = 1;
channel.basicQos(prefetchCount);
for (int i = 0; i < 50; i++) {
String sendMsg = "Sender==>send message"+i;
System.out.println("Sender==>send message"+i);
channel.basicPublish("", QUEUE_NAME, null, sendMsg.getBytes());
Thread.sleep(i*20);
}
channel.close();
connection.close();
}
}
package com.jetsen.mq.q2workqueue.fairdispatch;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作隊列之Round-robin:消費者1、消費者2處理的消息一樣多,進行的是輪詢分發
public class Recv1 {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//獲取連接
Connection connection = ConnectionUitls.getConnection();
//創建頻道
Channel channel = connection.createChannel();
//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//保證一次只分發一個
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[1] receive:=>"+messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[1] done!");
//手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//String queue, boolean autoAck, Consumer callback
//公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
package com.jetsen.mq.q2workqueue.fairdispatch;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作隊列之Round-robin:消費者1、消費者2處理的消息一樣多,進行的是輪詢分發
public class Recv2 {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//獲取連接
Connection connection = ConnectionUitls.getConnection();
//創建頻道
Channel channel = connection.createChannel();
//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//保證一次只分發一個
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[2] receive:=>"+messageStr);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[2] done!");
//手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//String queue, boolean autoAck, Consumer callback
//公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
輪訓分發
手動改爲自動回執消息 channel.basicAck(envelope.getDeliveryTag(), true)
自動應答修改爲autoAck true channel.basicConsume(QUEUE_NAME,true, consumer);
去掉流浪控制channel.basicQos(prefetchCount);
package com.jetsen.mq.q2workqueue.roundrobin;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* 工作隊列
* |----cl 2s
* p-----------queue------|
* |----c2 1s
*/
public class Sender {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//獲取連接
Connection connection = ConnectionUitls.getConnection();
//獲取channel
Channel channel = connection.createChannel();
//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
for (int i = 0; i < 50; i++) {
String sendMsg = "Sender==>send message"+i;
System.out.println("Sender==>send message"+i);
channel.basicPublish("", QUEUE_NAME, null, sendMsg.getBytes());
Thread.sleep(i*20);
}
channel.close();
connection.close();
}
}
package com.jetsen.mq.q2workqueue.roundrobin;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作隊列之Round-robin:消費者1、消費者2處理的消息一樣多,進行的是輪詢分發
public class Recv1 {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//獲取連接
Connection connection = ConnectionUitls.getConnection();
//創建頻道
Channel channel = connection.createChannel();
//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[1] receive:=>"+messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[1] done!");
}
}
};
//String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME,true, consumer);
}
}
package com.jetsen.mq.q2workqueue.roundrobin;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作隊列之Round-robin:消費者1、消費者2處理的消息一樣多,進行的是輪詢分發
public class Recv2 {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//獲取連接
Connection connection = ConnectionUitls.getConnection();
//創建頻道
Channel channel = connection.createChannel();
//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[2] receive:=>"+messageStr);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[2] done!");
}
}
};
//String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME,true, consumer);
}
}
(3) 訂閱模式:一個生產者發送的消息會被多個消費者獲取。
解讀:
1、1個生產者,多個消費者
2、每一個消費者都有自己的一個隊列
3、生產者沒有將消息直接發送到隊列,而是發送到了交換機
4、每個隊列都要綁定到交換機
5、生產者發送的消息,經過交換機,到達隊列,實現,一個消息被多個消費者獲取的目的
注意:一個消費者隊列可以有多個消費者實例,只有其中一個消費者實例會消費
package com.jetsen.mq.q3pubsub;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* publish_subscribe訂閱模式
*
*
* ""匿名轉發: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
*
* Exchange(一方面是接收生產者的消息,另一方面將消息推送至各自的隊列)
*
*
* (1)fanout:(不處理路由鍵)
* |---queue1----c1
* p----exchange-Bindings-|
* |---queue2----c2
*/
public class Sender {
private static final String EXCHANGE_NAME="test_exchage_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//聲明交換機
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分發類型
//發送消息
String message = "send pub sub message ....";
//exchange, routingKey, props, body
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println("Send message:"+message);
channel.close();
connection.close();
}
}
package com.jetsen.mq.q3pubsub;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv1 {
private static final String EXCHANGE_NAME="test_exchage_fanout";
private static final String QUEUE_NAME="test_queue_fanout_email";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//保證一次只分發一個
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[1] receive:=>"+messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[1] done!");
//手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//String queue, boolean autoAck, Consumer callback
//公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
package com.jetsen.mq.q3pubsub;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv2 {
private static final String EXCHANGE_NAME="test_exchage_fanout";
private static final String QUEUE_NAME="test_queue_fanout_msm";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//保證一次只分發一個
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[2] receive:=>"+messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[2] done!");
//手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//String queue, boolean autoAck, Consumer callback
//公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
//聲明交換機
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分發類型
(4) 路由模式:發送消息到交換機並且要指定路由key ,消費者將隊列綁定到交換機時需要指定路由key
package com.jetsen.mq.q4routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
*
* ""匿名轉發: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
*
* Exchange(一方面是接收生產者的消息,另一方面將消息推送至各自的隊列)'
*
*
* (2)Direct:(處理路由鍵)
* type=direct |---queue1(routing key)----c1
* p(routing key)----exchange-Bindings-|
* |---queue2----c2
* |
* |---queue3----c3
*/
public class Sender {
private static final String EXCHANGE_NAME="test_exchange_direct";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//聲明交換機
channel.exchangeDeclare(EXCHANGE_NAME, "direct");//分發類型
//發送消息
String message = "send direct message ....";
String routingKey="error";
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
System.out.println("Send message:"+message);
channel.close();
connection.close();
}
}
package com.jetsen.mq.q4routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv1 {
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct_1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
// queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKey = "error";
// 綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
// 保證一次只分發一個
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body, "UTF-8");
System.out.println("Consumer[1] receive:=>" + messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Consumer[1] done!");
// 手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.jetsen.mq.q4routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv2 {
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct_2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
// queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKeyInfo = "info";
String routingKeyWarning = "warning";
String routingKeyError = "error";
// 綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyInfo);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyWarning);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyError);
// 保證一次只分發一個
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body, "UTF-8");
System.out.println("Consumer[2] receive:=>" + messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Consumer[2] done!");
// 手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
由於Exchange綁定的兩個隊列中, Bingdings四個鍾,有兩個是訂閱了errro的消息,所以兩個消費者都可以消費到消息
(5) topic模式:將路由鍵和某模式進行匹配,此時隊列需要綁定在一個模式上,“#”匹配一個詞或多個詞,“*”只匹配一個詞
package com.jetsen.mq.q5topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
*
* ""匿名轉發: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
*
* Exchange(一方面是接收生產者的消息,另一方面將消息推送至各自的隊列)'
*
*
* (3)topic:(topic主題模式)
* type=topic |---queue1----c1
* p----exchange-Bindings--------------|
* |---queue2----c2
* |
* |---queue3----c3
* 將路由和某個模式匹配(符號) *匹配一個 #匹配多個
*/
public class Sender {
private static final String EXCHANGE_NAME="test_exchange_topic";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String msgString="商品....";
channel.basicPublish(EXCHANGE_NAME, "goods.add", null, msgString.getBytes());
System.out.println("----send" +msgString);
channel.close();
connection.close();
}
}
package com.jetsen.mq.q5topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv1 {
private static final String EXCHANGE_NAME = "test_exchange_topic";
private static final String QUEUE_NAME = "test_queue_topic_1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
// queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKey = "goods.add";
// 綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
// 保證一次只分發一個
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body, "UTF-8");
System.out.println("Consumer[2] receive:=>" + messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Consumer[2] done!");
// 手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.jetsen.mq.q5topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv2 {
private static final String EXCHANGE_NAME = "test_exchange_topic";
private static final String QUEUE_NAME = "test_queue_topic_2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
// queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKey = "goods.#";
// 綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
// 保證一次只分發一個
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body, "UTF-8");
System.out.println("Consumer[1] receive:=>" + messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Consumer[1] done!");
// 手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 公平隊列將自動應答修改爲false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
String routingKey = "goods.#";
// 綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
(6) RPC模式:使用RabbitMQ構建RPC系統:客戶端和可伸縮RPC服務器。
RPC的處理流程
- 當客戶端啓動時,創建一個匿名的回調隊列。
- 客戶端爲RPC請求設置2個屬性:replyTo,設置回調隊列名字;correlationId,標記request。
- 請求被髮送到請求隊列中。
- RPC服務器端監聽請求隊列中的請求,當請求到來時,服務器端會處理並且把帶有結果的消息發送給客戶端。接收的隊列就是replyTo設定的回調隊列。
- 客戶端監聽回調隊列,當有消息時,檢查correlationId屬性,如果與request中匹配,則返回。
package com.jetsen.mq.q11.rpc;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class RpcClient {
private static final String RPC_QUEUE_NAME = "test_rpc_queueu";
public static void main(String[] args) {
Connection connection = null;
// 獲取通道
Channel channel = null;
try {
connection = ConnectionUitls.getConnection();
// 獲取通道
channel = connection.createChannel();
// 創建回調隊列
String callBackQueue = channel.queueDeclare().getQueue();
// 創建帶有correlationId的消息屬性
String correlationId = UUID.randomUUID().toString();
AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder().correlationId(correlationId)
.replyTo(callBackQueue).build();
// 消費者從回調隊列中接受服務端傳遞的消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
String recvcorrelationId = properties.getCorrelationId();
if (correlationId.equals(recvcorrelationId)) {
System.out.println("客戶單接收消息:" + new String(body) + ",correlationId=" + correlationId);
}
}
};
// 監聽隊列
channel.basicConsume(callBackQueue, true, consumer);
String message = "rpc rabbitmq";
channel.basicPublish("", RPC_QUEUE_NAME, basicProperties, message.getBytes());
System.out.println("客戶端發送消息:" + message + ",correlationId=" + correlationId);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
}
}
}
package com.jetsen.mq.q11.rpc;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class RpcServer {
private static final String RPC_QUEUE_NAME="test_rpc_queueu";
public static void main(String[] args) {
try {
Connection connection = ConnectionUitls.getConnection();
//獲取通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(RPC_QUEUE_NAME,false,false,false,null);
//申明消費者預取數量
channel.basicQos(1);
//創建消費者
// 消費者從隊列中接受服務端傳遞的消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
System.out.println("服務端接收消息:" + new String(body));
channel.basicAck(envelope.getDeliveryTag(), false);
String correlationId = properties.getCorrelationId();
AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
.correlationId(correlationId)
.build();
channel.basicPublish("", properties.getReplyTo(), basicProperties, "RPC SERVER:11111111".getBytes());
}
};
// 監聽隊列
channel.basicConsume(RPC_QUEUE_NAME, false, consumer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(7)發佈確認:與發佈者進行可靠的發佈確認
Confirm發送方確認模式使用和事務類似,也是通過設置Channel進行發送方確認的。
package com.jetsen.mq.q7confirm;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME,true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[test_queue_confirm1] receive:=>"+messageStr);
}
});
}
}
Confirm的三種實現方式:
方式一:channel.waitForConfirms()普通發送方確認模式;
package com.jetsen.mq.q7confirm;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* rabbitmq消息確認機制之confirm
*/
public class ConfirmSender {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msgString =" send confirm message";
//生產者調用將channel設爲confirm模式,注意
channel.confirmSelect();
channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
System.out.println("----send" +msgString);
if(!channel.waitForConfirms()){
System.out.println("send confirm failed");
}else{
System.out.println("send confirm success");
}
channel.close();
connection.close();
}
}
我們只需要在推送消息之前,channel.confirmSelect()聲明開啓發送方確認模式,再使用channel.waitForConfirms()等待消息被服務器確認即可。
方式二:channel.waitForConfirmsOrDie()批量確認模式;
package com.jetsen.mq.q7confirm;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* rabbitmq消息確認機制之confirm
*/
public class ConfirmBatchSender {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msgString =" send confirm message";
//生產者調用將channel設爲confirm模式,注意
channel.confirmSelect();
for (int i = 0; i < 10; i++) {
channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
}
// if(!channel.waitForConfirms()){
// System.out.println("send confirm failed");
// }else{
// System.out.println("send confirm success");
// }
channel.waitForConfirmsOrDie(); //直到所有信息都發布,只要有一個未確認就會IOException
channel.close();
connection.close();
}
}
以上代碼可以看出來channel.waitForConfirmsOrDie(),使用同步方式等所有的消息發送之後纔會執行後面代碼,只要有一個消息未被確認就會拋出IOException異常。
方式三:channel.addConfirmListener()異步監聽發送方確認模式;
package com.jetsen.mq.q7confirm;
import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
/**
* 異步模式
* Channel 對象提供的ConfirmListener()回調方法之包含deliveryTag(當前Channel發出
* 的消息序號),我們需要自己爲每一個Channel維護一個unconfirm的消息序號集合,每publish
* 一條數據,集合中元素就+1,每回調一次handlerAck方法,unconfirm結合刪掉相應的一條(multiple=false)
* 或者多條(multiple=true)記錄,從程序運行效率上看,這個unconfirm集合最好採用有序集合
* SortedSet存儲結構
* @author jetsen
*
*/
public class ConfirmAysnSender {
private static final String QUEUE_NAME = "test_queue_confirm3";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//生產者調用confirmSelect將channel設置爲Confirm模式
channel.confirmSelect();
//未確認消息的標識
final SortedSet<Long> confirmSet=Collections.synchronizedSortedSet(new TreeSet<Long>());
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
if(multiple){
System.out.println("----handlerNack-----multiple");
confirmSet.headSet(deliveryTag+1).clear();
}else{
System.out.println("----handlerNack-----multiple----false");
confirmSet.remove(deliveryTag);
}
}
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
if(multiple){
System.out.println("----handleAck-----multiple");
confirmSet.headSet(deliveryTag+1).clear();
}else{
System.out.println("----handleAck-----multiple----false");
confirmSet.remove(deliveryTag);
}
}
});
String msgString =" send confirm Aysn message";
while(true){
long segNo = channel.getNextPublishSeqNo();
channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
confirmSet.add(segNo);
}
}
}
異步模式的優點,就是執行效率高,不需要等待消息執行完,只需要監聽消息即可。