Rabbit的核心组件包含Queue(消息队列)和Exchanges两部分,Exchange的主要部分就是对信息进行路由,经过将消息队列绑定到Exchange上,则能够实现订阅形式的消息发布及Publish/Subscribe在这种模式下消息发布者只须要将信息发布到相应的Exchange中,而Exchange则自动将信息分发到不一样的Queue当中。java
这种模式下Exchange充当的角色服务器
在命令行中可使用dom
sudo rabbitmqctl list_exchanges ide
sudo rabbitmqctl list_bindingsthis
分别查看当前系统种存在的Exchange和Exchange上绑定的Queue信息。spa
消息发布者EmitLog.java命令行
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; public class EmitLog { private static final String EXCHANGE_NAME="logs"; public static void main(String[] args) throws java.io.IOException{ //建立连接工厂 ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); //建立连接 Connection connection = factory.newConnection(); //建立信息管道 Channel channel = connection.createChannel(); //生命Exchange 非持久化 channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); String message = "Message "+Math.random(); //第一个参数是对应的Exchange名称,若是为空则使用默认Exchange channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes()); System.out.println("[x] Sent '"+message+"'"); //关闭连接 channel.close(); connection.close(); } }
消息消费者ReceiveLogs.javacode
import java.io.IOException; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.ConsumerCancelledException; import com.rabbitmq.client.QueueingConsumer; import com.rabbitmq.client.ShutdownSignalException; public class ReceiveLogs { private static final String EXCHANGE_NAME = "logs"; public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException { //建立连接工厂 ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); //建立连接 Connection connection = factory.newConnection(); //建立消息管道 Channel channel = connection.createChannel(); //声明Exchange channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); //利用系统自动声明一个非持久化的消息队列,并返回惟一的队列名称 String queueName = channel.queueDeclare().getQueue(); //将消息队列绑定到Exchange channel.queueBind(queueName, EXCHANGE_NAME, ""); System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); //声明一个消费者 QueueingConsumer consumer = new QueueingConsumer(channel); channel.basicConsume(queueName, true, consumer); while (true) { //循环获取信息 QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String message = new String(delivery.getBody()); System.out.println(" [x] Received '" + message + "'"); } } }
运行时启动一个EmitLog.java多个ReceiveLogs.java则能够看到发布者每次发布信息,只要绑定到了相应Exchange的消费者均可以获取到信息。rabbitmq
上面的例子中咱们实现了Publisher/Subscribe的消息分发方式,可是其中存在一些问题。好比当咱们运行一个ReceiveLog都对应了一个特定的消息队列,能够利用list_queues进行查看,同时这些消息队列是帮到到名为logs的Exchange中,这是发布消息每一个消费者均可以接收到,能够当关闭ReceiveLog程序后这些消息队列就都会自动销毁,由于他们是非持久化的。一样对于EmitLog程序也同样,每次关闭后以前生命的Exchange也将自动销毁。队列
这就产生了一些问题。若是当ReceiveLog为运行时,此时就并无一个消息队列是绑定到Exchange上的,在发布消息后再启动ReceiveLog程序是没法接受到以前发布的信息。这就是为何要进行消息的持久化。
经过持久化技术,咱们能够生命一个持久化的Exchange,以及持久化的Queue这样,在把Queue绑定到Exchange后,即便没有消费者程序运行,信息依然能保存在Queue当中,当下次启动消费者程序时依然能获取到发布的全部信息。就比如当一个消费者程序在执行消息序列中的任务时,若是忽然出现了异常那么从新启动后,依然能从上一次发生错误的位置继续运行,对于某些须要一个有序性和连续性的操做,这点显的尤其重要。
下面仍是给出一个例子,在持久化过程当中,能够借助list_exchanges,list_bindings,list_queues来查看服务器中相关信息来帮组分析过程。
Publisher.java
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.MessageProperties; public class Publisher { private static final String EXCHANGE_NAME="persi";//定义Exchange名称 private static final boolean durable = true;//消息队列持久化 public static void main(String[] args) throws java.io.IOException { ConnectionFactory factory = new ConnectionFactory();//建立连接工厂 factory.setHost("localhost"); Connection connection = factory.newConnection();//建立连接 Channel channel = connection.createChannel();//建立信息通道 channel.exchangeDeclare(EXCHANGE_NAME, "fanout", durable);//建立交换机并生命持久化 String message = "Hello Wrold "+Math.random(); //消息的持久化 channel.basicPublish(EXCHANGE_NAME, "", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes()); System.out.println("[x] Sent '" + message + "'"); channel.close(); connection.close(); } }
Subscriber.java
public class Subscriber { //private static final String[] QUEUE_NAMES= {"que_001","que_002","que_003","que_004","que_005"}; private static final String[] QUEUE_NAMES= {"que_006","que_007","que_008","que_009","que_0010"}; public static void main(String[] args){ for(int i=0;i<QUEUE_NAMES.length;i++){ SubscriberThead sub = new SubscriberThead(QUEUE_NAMES[i]); Thread t = new Thread(sub); t.start(); } } }
SubscriberThead.java
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.QueueingConsumer; import com.rabbitmq.client.AMQP.Queue.DeclareOk; public class SubscriberThead implements Runnable { private String queue_name = null; private static final String EXCHANGE_NAME = "persi";// 定义交换机名称 private static final boolean durable = true;//消息队列持久化 public SubscriberThead(String queue_name) { this.queue_name = queue_name; } @Override public void run() { try{ ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "fanout", durable); DeclareOk ok = channel.queueDeclare(queue_name, durable, false, false, null); String queueName = ok.getQueue(); channel.queueBind(queueName, EXCHANGE_NAME, ""); System.out.println(" ["+queue_name+"] Waiting for messages. To exit press CTRL+C"); channel.basicQos(1);//消息分发处理 QueueingConsumer consumer = new QueueingConsumer(channel); channel.basicConsume(queueName, false, consumer); while (true) { Thread.sleep(2000); QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String message = new String(delivery.getBody()); System.out.println(" ["+queue_name+"] Received '" + message + "'"); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); } }catch(Exception e){ e.printStackTrace(); } } }
经过持久化处理后rabbitMQ将保存Exchange信息以及Queue信息,甚至在rabbitMQ服务器关闭后信息依然能保存,这样就提供了消息传递的可靠性