1. fanout模式

1.1 Publish/Subscribe(发布/订阅)结构图

  

  上图表示一个消费者消费消息之后,不讲消息直接存储到队列,而是使用两个消费者各自声明一个队列,将各自的对应的队列与交换机绑定。这样每个消费者都读取的是自身所对应的队列的所有消息,大达到了一个生产者生产消息,所有消费者都能消费的目的。

  将交换机类型设置为fanout即可实现Publish/Subscribe

1.2 生产者代码

package com.wangx.rabbitmq.sp;

import com.rabbitmq.client.BuiltinExchangeType;
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 Producer { private static final String EXCHANGE_NAME = "exchange";
public static void main(String[] args) throws IOException, TimeoutException { //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("127.0.0.1");
//设置用户名
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel(); //声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
String message = "Hello World!";
//发送消息
for (int i = 0; i < 10; i++) {
//发送消息
channel.basicPublish(EXCHANGE_NAME, "", null, (message + i).getBytes());
System.out.println(" [x] Sent '" + message + i + "'");
}
}catch (Exception e) { }finally {
channel.close();
connection.close();
}
}
}

  与普通消息生产者不同的地方在于,发布订阅时必须要显示的声明一个交换机,并且在发送消息的时候,没有队列,必须设置交换机名称。

1.3 消费者实现代码

package com.wangx.rabbitmq.sp;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException; public class Consumer1 {
/**
* 队列名字
*/
private static String QUEUE_NAME = "queue1";
private static final String EXCHANGE_NAME = "exchange";
public static void main(String[] args){ //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("127.0.0.1");
//设置用户名
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
try {
//创建连接
connection = factory.newConnection();
//创建消息通道
final Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
//声明队列
channel.queueDeclare(QUEUE_NAME,false, false, false, null);
//绑定队列与交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//消息服务器每次只向消费者发送一条消息
// channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
//重写DefaultConsumer中handleDelivery方法,在方法中获取消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException{
try {
//消息沉睡一秒
Thread.sleep(1000);
String message = new String(body, "UTF-8");
System.out.println("consumer1 收到消息 '" + message + "'");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("consumer1 消息消费完成....");
} }
};
//监听消息
channel.basicConsume(QUEUE_NAME, true,consumer);
}catch (Exception e) {
e.printStackTrace();
}finally {
}
}
}

  这里仍然使用的是两个不同的消费者,并且将两个不同的消费者分别声明不同的消息队列,然后将声明的队列与交换机进行绑定,使用channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "fanout");方法。启动两个消费者,将两个不同的消息队列注册并绑定上去。启动消息生产者发送消息,将会看到,两个不同的消费者均能接收到生产者发送的所有消息。

2. Routing 模式

  结构图:

  

  发送消息时指定不同的key,交换机分发消息是根据key分发消息到不同的消费者队列中。

  将交换机模式改为DIRECT,在消费端设置了不同的key,相当于为消息分个类,以便于在交换机分发消息时,将消息分发给持有该key的消费者,生产者代码如下:

package com.wangx.rabbitmq.routing;

import com.rabbitmq.client.BuiltinExchangeType;
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 Producer { private static final String EXCHANGE_NAME = "exchange-routing";
public static void main(String[] args) throws IOException, TimeoutException { //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("127.0.0.1");
//设置用户名
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel(); //声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
String message = "Hello World!";
//发送消息
for (int i = 0; i < 10; i++) {
if ( i % 2 == 0) {
//发送消息,指定key
channel.basicPublish(EXCHANGE_NAME, "key2", null, (message + i).getBytes());
System.out.println(" 偶数消息 '" + message + i + "'");
} else {
//发送消息
channel.basicPublish(EXCHANGE_NAME, "key1", null, (message + i).getBytes());
System.out.println(" 奇数消息 '" + message + i + "'");
}
}
}catch (Exception e) { }finally {
channel.close();
connection.close();
}
}
}

消费者代码如下:

package com.wangx.rabbitmq.routing;

import com.rabbitmq.client.*;

import java.io.IOException;

public class Consumer1 {
/**
* 队列名字
*/
private static String QUEUE_NAME = "queue1";
private static final String EXCHANGE_NAME = "exchange-routing";
public static void main(String[] args){ //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("127.0.0.1");
//设置用户名
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
try {
//创建连接
connection = factory.newConnection();
//创建消息通道
final Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//声明队列
channel.queueDeclare(QUEUE_NAME,false, false, false, null);
//绑定队列与交换机,指定接收的消息的key
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key2");
//消息服务器每次只向消费者发送一条消息
// channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
//重写DefaultConsumer中handleDelivery方法,在方法中获取消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException{
try {
//消息沉睡一秒
Thread.sleep(1000);
String message = new String(body, "UTF-8");
System.out.println("consumer1 收到消息 '" + message + "'");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("consumer1 消息消费完成....");
} }
};
//监听消息
channel.basicConsume(QUEUE_NAME, true,consumer);
}catch (Exception e) {
e.printStackTrace();
}finally {
}
}
}

  两个消费者一个使用接收奇数,一个接收偶数消息(根据发送消息时判断奇偶设置不同的key),接收时接收相应的key即可。

  这样就可以指定接收想要接收的类型的消息了,相当于前面学习的mq的消息的过滤。

3. Topic模式

  将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。

   生产者实现:

package com.wangx.rabbitmq.topic;

import com.rabbitmq.client.BuiltinExchangeType;
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 Producer { private static final String EXCHANGE_NAME = "exchange-topic";
public static void main(String[] args) throws IOException, TimeoutException { //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("127.0.0.1");
//设置用户名
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel(); //声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
String message = "Hello World!";
//发送消息,绑定模式
channel.basicPublish(EXCHANGE_NAME, "key.one", null, ("one -" + message).getBytes());
channel.basicPublish(EXCHANGE_NAME, "key.two.msg", null, ("two -" + message).getBytes());
}catch (Exception e) { }finally {
channel.close();
connection.close();
}
}
}

  这里发送消息时,绑定了key.one和key.two.msg两种模式。接下来使用*号和#号两种通配符得消费者,如下:

Consumer1

package com.wangx.rabbitmq.topic;

import com.rabbitmq.client.*;

import java.io.IOException;

public class Consumer1 {
/**
* 队列名字
*/
private static String QUEUE_NAME = "queue-topic";
private static final String EXCHANGE_NAME = "exchange-topic";
public static void main(String[] args){ //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("127.0.0.1");
//设置用户名
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
try {
//创建连接
connection = factory.newConnection();
//创建消息通道
final Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
//声明队列
channel.queueDeclare(QUEUE_NAME,false, false, false, null);
//绑定队列与交换机,使用通配符key.* 表示只能匹配key下的一个路径,
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.*");
//消息服务器每次只向消费者发送一条消息
// channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
//重写DefaultConsumer中handleDelivery方法,在方法中获取消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException{
try {
//消息沉睡一秒
Thread.sleep(1000);
String message = new String(body, "UTF-8");
System.out.println("consumer1 收到消息 '" + message + "'");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("consumer1 消息消费完成....");
} }
};
//监听消息
channel.basicConsume(QUEUE_NAME, true,consumer);
}catch (Exception e) {
e.printStackTrace();
}finally {
}
}
}

  所以在本例中只能匹配到key.one的消息,控制台打印如下:

consumer1 收到消息 'one -Hello World!'
consumer1 消息消费完成....

  Consumer2实现:

package com.wangx.rabbitmq.topic;

import com.rabbitmq.client.*;

import java.io.IOException;

public class Consumer2 {
/**
* 队列名字
*/
private static String QUEUE_NAME = "queue-topic-2";
private static final String EXCHANGE_NAME = "exchange-topic";
public static void main(String[] args){
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("127.0.0.1");
//设置用户名
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
try {
//创建连接
connection = factory.newConnection();
//创建消息通道
final Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
//声明队列
channel.queueDeclare(QUEUE_NAME,false, false, false, null);
//这里使用#号通配符,表示能够匹配到key下的任意路径
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.#");
Consumer consumer = new DefaultConsumer(channel){
//重写DefaultConsumer中handleDelivery方法,在方法中获取消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException{
try {
//消息沉睡100ms
Thread.sleep(100);
String message = new String(body, "UTF-8");
System.out.println("consumer2 收到消息 '" + message + "'");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("consumer2 消息消费完成....");
} }
};
//监听消息,第二个参数为true时表示自动确认
channel.basicConsume(QUEUE_NAME, true,consumer);
}catch (Exception e) {
e.printStackTrace();
}finally {
}
}
}

  这里使用了key.#表示匹配所有key.下的所有路径,所以能够接收到所有以key开头的消息,控制台打印如下:

consumer2 收到消息 'one -Hello World!'
consumer2 消息消费完成....
consumer2 收到消息 'two -Hello World!'
consumer2 消息消费完成....

使用topic模式既可以轻易的实现fanout模式,也可以实现routing模式,同时提供了通配符的情况下,使得匹配更加灵活,使用方式更加简洁。

RabbitMQ学习笔记(4)----RabbitMQ Exchange(交换机)的使用的更多相关文章

  1. [RabbitMQ学习笔记] - 初识RabbitMQ

    RabbitMQ是一个由erlang开发的AMQP的开源实现. 核心概念 Message 消息,消息是不具名的,它由消息头和消息体组成,消息体是不透明的,而消息头则由 一系列的可选属性组成,这些属性包 ...

  2. 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群

    在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...

  3. RabbitMQ学习笔记(五) Topic

    更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...

  4. RabbitMQ学习笔记1-hello world

    安装过程略过,一搜一大把. rabbitmq管理控制台:http://localhost:15672/   默认账户:guest/guest RabbitMQ默认监听端口:5672 JAVA API地 ...

  5. 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ

    鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...

  6. (转) Rabbitmq学习笔记

    详见原文: http://blog.csdn.net/shatty/article/details/9529463 Rabbitmq学习笔记

  7. RabbitMQ学习笔记五:RabbitMQ之优先级消息队列

    RabbitMQ优先级队列注意点: 1.只有当消费者不足,不能及时进行消费的情况下,优先级队列才会生效 2.RabbitMQ3.5以后才支持优先级队列 代码在博客:RabbitMQ学习笔记三:Java ...

  8. 消息队列——RabbitMQ学习笔记

    消息队列--RabbitMQ学习笔记 1. 写在前面 昨天简单学习了一个消息队列项目--RabbitMQ,今天趁热打铁,将学到的东西记录下来. 学习的资料主要是官网给出的6个基本的消息发送/接收模型, ...

  9. RabbitMQ学习笔记(六) RPC

    什么RPC? 这一段是从度娘摘抄的. RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的 ...

随机推荐

  1. PAT 1072. Gas Station

    A gas station has to be built at such a location that the minimum distance between the station and a ...

  2. Hibernate事务管理-HibernateTransactionManager-对hibernate session的管理

    由于对SSH还停留在比较初级的应用水平上,今天在遇到一个疑惑时折腾了很久,具体问题是这样的, 有这么一个测试方法, public static void test1() { ApplicationCo ...

  3. Java设计模式之 — 组合(Composite)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153753 听说你们公司最近新推出了一款电子书阅读应用,市场反应很不错,应用里还有 ...

  4. GOF23设计模式之适配器模式

    GOF23设计模式之适配器模式 结构型模式: 核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题. 分类:适配器模式.代理模式.桥接模式.装饰模式.组合模式.外观模式. ...

  5. (14)Spring Boot定时任务的使用【从零开始学Spring Boot】

    本文介绍在 Spring Boot 中如何使用定时任务,使用非常简单,就不做过多说明了. com.kfit.base.scheduling.SchedulingConfig: package com. ...

  6. javascript--闭包与this

    理解javascript中的闭包与this http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html http: ...

  7. JavaFX本地应用自己主动更新功能的实现FXLauncher

    JavaFX本地应用自己主动更新功能的实现--FXLauncher 作者:chszs,未经博主同意不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 一. ...

  8. ORM进阶:Hibernate框架搭建及开发

    本节将開始.使用hibernate搭建持久层.当然在决定用不用之前,还请斟酌一下是否使用.了解一下Hibernate的优缺点. Hibernate优劣对照 Hibernate是一个持久的ORM框架.首 ...

  9. python TypeError: &#39;builtin_function_or_method&#39; object is not iterable keys

    statinfo = os.stat( OneFilePath ) if AllFiles.has_key( statinfo.st_size ): OneKey = AllFiles[ statin ...

  10. Linux - 理不清的权限chmod与chown区别

    chmod是修改第一列内容的 ,chown是修改第3,4列内容的. [root@local ~]# chmod 777 -R add.sh [root@local ~]# chown jiqing:j ...