RabbitMQ是一个受欢迎的消息代理,通常用于应用程序之间或者程序的不同组件之间通过消息来进行集成。本文简单介绍了如何使用 RabbitMQ,假定你已经配置好了rabbitmq服务器。
RabbitMQ是用Erlang,对于主要的编程语言都有驱动或者客户端。我们这里要用的是Java,所以先要获得Java客户端。。下面是Java客户端的maven依赖的配置。
<dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>3.0.4</version> </dependency>
gradle
compile 'com.rabbitmq:amqp-client:4.1.0'
像RabbitMQ这样的消息代理可用来模拟不同的场景,例如点对点的消息分发或者订阅/推送。我们的程序足够简单,有两个基本的组件,一个生产者用于产生消息,还有一个消费者用来使用产生的消息。
在这个例子里,生产者会产生大量的消息,每个消息带有一个序列号,另一个线程中的消费者会使用这些消息。
抽象类EndPoint:
我们首先写一个类,将产生产者和消费者统一为 EndPoint类型的队列。不管是生产者还是消费者, 连接队列的代码都是一样的,这样可以通用一些。
package com.gl365.payment.util; import java.io.IOException; import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; /** * date: 2017年4月26日 下午12:07:22 <br/> * * @author lenovo */ public abstract class EndPoint { protected Channel channel; protected Connection connection; protected String endPointName; public EndPoint(String endpointName) throws IOException, TimeoutException { this.endPointName = endpointName; // Create a connection factory ConnectionFactory factory = new ConnectionFactory(); // hostname of your rabbitmq server factory.setHost("localhost"); //factory.setHost("192.168.163.33"); factory.setPort(5672); factory.setUsername("guest"); factory.setPassword("guest"); // getting a connection connection = factory.newConnection(); // creating a channel channel = connection.createChannel(); // declaring a queue for this channel. If queue does not exist, // it will be created on the server. channel.queueDeclare(endpointName, false, false, false, null); } /** * 关闭channel和connection。并非必须,因为隐含是自动调用的。 * * @throws IOException * @throws TimeoutException */ public void close() throws IOException, TimeoutException { this.channel.close(); this.connection.close(); } }
生产者:
生产者类的任务是向队列里写一条消息。我们使用Apache Commons Lang把可序列化的Java对象转换成 byte 数组。commons lang的maven依赖如下:
<dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency>
package com.gl365.payment.util; import java.io.IOException; import java.io.Serializable; import java.util.concurrent.TimeoutException; import org.springframework.util.SerializationUtils; public class Producer extends EndPoint{ public Producer(String endPointName) throws IOException, TimeoutException{ super(endPointName); } public void sendMessage(Serializable object) throws IOException { channel.basicPublish("",endPointName, null, SerializationUtils.serialize(object)); } }
/** * Project Name:payment * File Name:Test.java * Package Name:com.gl365.payment.util * Date:2017年4月26日下午5:49:16 * Copyright (c) 2017, chenzhou1025@126.com All Rights Reserved. * */ package com.gl365.payment.util; import java.io.IOException; import java.util.HashMap; import java.util.concurrent.TimeoutException; /** * ClassName:Test <br/> * Date: 2017年4月26日 下午5:49:16 <br/> * @author lenovo * @see */ public class Test { public static void main(String[] args) throws IOException, TimeoutException { Producer producer = new Producer("duan"); for (int i = 0; i < 100000; i++) { HashMap message = new HashMap(); message.put("message number", i); producer.sendMessage(message); System.out.println("Message Number "+ i +" sent."); } System.out.println("finish"); } }
消费者:
消费者可以以线程方式运行,对于不同的事件有不同的回调函数,其中最主要的是处理新消息到来的事件。
package com.gl365.payment.util; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeoutException; import org.apache.commons.lang.SerializationUtils; import com.rabbitmq.client.AMQP.BasicProperties; import com.rabbitmq.client.Consumer; import com.rabbitmq.client.Envelope; import com.rabbitmq.client.ShutdownSignalException; /** * 读取队列的程序端,实现了Runnable接口。 */ public class QueueConsumer extends EndPoint implements Runnable, Consumer { public QueueConsumer(String endPointName) throws IOException, TimeoutException { super(endPointName); } public void run() { try { // start consuming messages. Auto acknowledge messages. channel.basicConsume(endPointName, true, this); } catch (IOException e) { e.printStackTrace(); } } /** * Called when consumer is registered. */ public void handleConsumeOk(String consumerTag) { System.out.println("Consumer " + consumerTag + " registered"); } /** * Called when new message is available. */ public void handleDelivery(String consumerTag, Envelope env, BasicProperties props, byte[] body) throws IOException { Map map = (HashMap) SerializationUtils.deserialize(body); System.out.println("Message Number " + map.get("message number") + " received."); } public void handleCancel(String consumerTag) { } public void handleCancelOk(String consumerTag) { } public void handleRecoverOk(String consumerTag) { } public void handleShutdownSignal(String consumerTag, ShutdownSignalException arg1) { } }
Putting it together:
在下面的测试类中,先运行一个消费者线程,然后开始产生大量的消息,这些消息会被消费者取走。
package com.gl365.payment.util; import java.io.IOException; import java.sql.SQLException; import java.util.HashMap; public class ConsumeTest { public ConsumeTest() throws Exception{ QueueConsumer consumer = new QueueConsumer("duan"); Thread consumerThread = new Thread(consumer); consumerThread.start(); } /** * @param args * @throws SQLException * @throws IOException */ public static void main(String[] args) throws Exception{ new ConsumeTest(); } }