生產者將消息發送到消息系統的時候,不是直接發送到隊列,而是發送到交換機,由交換機路由到相應的隊列
交換機有三種路由模式
1.direct
交換機通過一個routing key和隊列綁定,當生產者生產消息的時候指定一個routing key,當綁定隊列的routing key和生產者發送的routing key一致,那麼交換機就會把消息發送到這個隊列
public class ConnectionUtil {
public static String QUEUE_NAME = "testQueue";
public static String EXCHANGE_NAME = "testExchange";
public static Connection getConnection() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
//設置服務端所在地址
factory.setHost("127.0.0.1");
//設置端口號
factory.setPort(5672);
//設置用戶名
factory.setUsername("helloWorld");
//設置密碼
factory.setPassword("helloWorld");
//設置虛擬地址
factory.setVirtualHost("testHost");
return factory.newConnection();
}
}
public class Send {
public static void main(String[] args) throws Exception{
//獲取連接
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//聲明隊列
channel.queueDeclare("testQueue1",false,false,false,null);
channel.queueDeclare("testQueue2",false,false,false,null);
channel.queueDeclare("testQueue3",false,false,false,null);
//聲明交換機
channel.exchangeDelete(ConnectionUtil.EXCHANGE_NAME);
channel.exchangeDeclare(ConnectionUtil.EXCHANGE_NAME,BuiltinExchangeType.DIRECT);
//交換機和隊列綁定
channel.queueBind("testQueue1",ConnectionUtil.EXCHANGE_NAME,"info.user");
channel.queueBind("testQueue2",ConnectionUtil.EXCHANGE_NAME,"warning.user");
channel.queueBind("testQueue3",ConnectionUtil.EXCHANGE_NAME,"error.user");
channel.basicPublish(ConnectionUtil.EXCHANGE_NAME,"info.user",null,"helloWorld".getBytes());
System.out.println("發送的信息爲:helloWorld");
channel.close();
connection.close();
}
}
消息只會發送到與交換機綁定並且routing key一致的隊列
下面三個消費者分別和三個隊列綁定,只有第一個消費者可以拉取到消息進行消費
public class Recv1 {
public static void main(String[] args) throws Exception{
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body, "UTF-8"));
}
};
channel.basicConsume("testQueue1",true, deliverCallback);
}
}
public class Recv2 {
public static void main(String[] args) throws Exception{
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body, "UTF-8"));
}
};
channel.basicConsume("testQueue2",true, deliverCallback);
}
}
public class Recv3 {
public static void main(String[] args) throws Exception{
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body, "UTF-8"));
}
};
channel.basicConsume("testQueue3",true, deliverCallback);
}
}
2.fanout
這種類型的交換機路由規則很簡單,只要跟這種交換機綁定的隊列,交換機就會把消息發送到隊列
public class Send {
public static void main(String[] args) throws Exception{
//獲取連接
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//聲明隊列
channel.queueDeclare("testQueue1",false,false,false,null);
channel.queueDeclare("testQueue2",false,false,false,null);
channel.queueDeclare("testQueue3",false,false,false,null);
//聲明交換機
channel.exchangeDelete(ConnectionUtil.EXCHANGE_NAME);
channel.exchangeDeclare(ConnectionUtil.EXCHANGE_NAME,BuiltinExchangeType.FANOUT);
//交換機和隊列綁定
channel.queueBind("testQueue1",ConnectionUtil.EXCHANGE_NAME,"");
channel.queueBind("testQueue2",ConnectionUtil.EXCHANGE_NAME,"");
channel.queueBind("testQueue3",ConnectionUtil.EXCHANGE_NAME,"");
channel.basicPublish(ConnectionUtil.EXCHANGE_NAME,"",null,"helloWorld".getBytes());
System.out.println("發送的信息爲:helloWorld");
channel.close();
connection.close();
}
}
可以看到和交換機綁定的三個隊列都有一條消息
3.topic
Topic Exchange – 將路由鍵和某模式進行匹配。此時隊列需要綁定要一個模式上。符號“#”匹配一個或多個詞,符號“*”匹配不多不少一個詞。因此“audit.#”能夠匹配到“audit.irs.corporate”,但是“audit.*” 只會匹配到“audit.irs”
任何發送到Topic Exchange的消息都會被轉發到所有關心RouteKey中指定話題的Queue上
1.這種模式較爲複雜,簡單來說,就是每個隊列都有其關心的主題,所有的消息都帶有一個“標題”(RouteKey),Exchange會將消息轉發到所有關注主題能與
RouteKey模糊匹配的隊列。
2.這種模式需要RouteKey,也需要提前綁定Exchange與Queue。
3.在進行綁定時,要提供一個該隊列關心的主題,如“#.log.#”表示該隊列關心所有涉及log的消息(一個RouteKey爲”MQ.log.error”的消息會被轉發到該隊列)。
4.“#”表示0個或若干個關鍵字,“*”表示一個關鍵字。如“log.*”能與“log.warn”匹配,無法與“log.warn.timeout”匹配;但是“log.#”能與上述兩者匹配。
5.同樣,如果Exchange沒有發現能夠與RouteKey匹配的Queue,則會拋棄此消息。
public class Send {
public static void main(String[] args) throws Exception{
//獲取連接
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//聲明隊列
channel.queueDeclare("testQueue1",false,false,false,null);
channel.queueDeclare("testQueue2",false,false,false,null);
channel.queueDeclare("testQueue3",false,false,false,null);
//聲明交換機
channel.exchangeDelete(ConnectionUtil.EXCHANGE_NAME);
channel.exchangeDeclare(ConnectionUtil.EXCHANGE_NAME,BuiltinExchangeType.TOPIC);
//交換機和隊列綁定
channel.queueBind("testQueue1",ConnectionUtil.EXCHANGE_NAME,"info.*");
channel.queueBind("testQueue2",ConnectionUtil.EXCHANGE_NAME,"warning.*");
channel.queueBind("testQueue3",ConnectionUtil.EXCHANGE_NAME,"error.*");
channel.basicPublish(ConnectionUtil.EXCHANGE_NAME,"info.user",null,"helloWorld".getBytes());
System.out.println("發送的信息爲:helloWorld");
channel.close();
connection.close();
}
}