前言:
RabbitMQ服务器安装完成后,通过管理界面可以访问服务器,接下来我们将在java语言中应用rabbitMQ,下面主要给大家介绍一下生产者如何生产消息,消费者又如何获取消息进行消费。
RabbitMQ-客户端对象简介
ConnectionFactory、Connection、Channel都是RabbitMQ对外提供的API中最基本的对象。Connection是RabbitMQ的socket链接,它封装了socket协议相关部分逻辑。ConnectionFactory为Connection的制造工厂。 Channel是我们与RabbitMQ打交道的最重要的一个接口,我们大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息、消费消息等。
下载最新的java客户端包,要求JDK版本JDK8
下面我们用简单的例子给MQ发送一个消息。
public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); // 设置RabbitMQ相关信息 factory.setHost("127.0.0.1"); factory.setUsername("root"); factory.setPassword("1234"); // factory.setVirtualHost("/"); //VirtualHost默认为/ // factory.setPort(5672);//端口默认为5672 // 创建一个新的连接 Connection connection = factory.newConnection(); // 创建一个通道 Channel channel = connection.createChannel(); channel.queueDeclare("queueName", true, false, false, null); System.out.println("Producer Send begin"); channel.basicPublish("", "queueName", null, "hello World".getBytes("UTF-8")); System.out.println("Producer Send end"); channel.close(); connection.close(); } |
运行后,我们可以通过rabbitMQ提代的管理页面看到该消息
接下来我们了解一下,消息端是如何消费的。消费分两个模式,拉/推 模式。
首先我们讲一下拉模式:客户端主动去取数据,一次只能取一条数据,如果队列中无数据,返回空。
public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); // 设置RabbitMQ相关信息 factory.setHost("127.0.0.1"); factory.setUsername("root"); factory.setPassword("1234"); // factory.setVirtualHost("/"); //VirtualHost默认为/ // factory.setPort(5672);//端口默认为5672 // 创建一个新的连接 Connection connection = factory.newConnection(); // 创建一个通道 Channel channel = connection.createChannel(); //拉模式-消费数据,主动去取数据 GetResponse response = channel.basicGet("queueName", true); if(response!=null){ System.out.println(new String(response.getBody())); } // channel.basicAck(response.getEnvelope().getDeliveryTag(),false); //应答。 } |
推模板: 由rabbitMQ推消息。实现Consumer监听消息推送。
public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); // 设置RabbitMQ相关信息 factory.setHost("127.0.0.1"); factory.setUsername("root"); factory.setPassword("1234"); // factory.setVirtualHost("/"); //VirtualHost默认为/ // factory.setPort(5672);//端口默认为5672 // 创建一个新的连接 Connection connection = factory.newConnection(); // 创建一个通道 Channel channel = connection.createChannel(); System.out.println("Customer Waiting Received messages"); // DefaultConsumer类实现了Consumer接口,通过传入一个频道, // 告诉服务器我们需要那个频道的消息,如果频道中有消息,就会执行回调函数handleDelivery channel.basicQos(1); // 一次可以接收多少条消息。 Consumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String message = new String(body, "UTF-8"); System.out.println("Customer Received :" + message); } }; // RabbitMQ中的消息确认机制,其中下面的false代表手动应答。为true表示自动应答,收到消息到就可以从队列中删除消息 channel.basicConsume("queueName", true, consumer); // 监听,rabbitMQ的队列的消息 } |
了解完简单的生产与消费之后,我们可以继续了解一下rabbitMQ的一些特性,RabbitMQ支持的一些特性支持它在我们业务使用中解决更多问题。
-
RabbitMQ的事务:当生产者发送消息到RabbitMQ,如果RabbitMQ在接收的过程宕机或者重启,消息就会丢失。针对这种场景,RabbitMQ提供消息发布确认机制,配合定义消息与队列的持久化,可以确保消息不会丢失。支持同步确认或者异步确认。
- RabbitMQ消费应答机制:当消费者获取消息进行消费的时候,可以设置为自动应答或者手功应答。
- 自动应答的情况下, RabbitMQ投递消息给消费者之后就会把消息从队列进行删除。
- 手功应答,RabbitMQ投递消息给消费者之后,消息在当前队列不会马上删除,需要消费者进行应答之后再处理,此时消费者可以长时间处理,处理完成后应答成功或者失败。RabbitMQ会根据应答情况把消息进行相应的处理,支持投放回原队列、投到该队列的死信队列、或者消费成功。如果未应答且连接断开的情况下,消息会放回原队列,支持下一次重新消费。
- 备份交换器(Alternate Exchange):用来处理投递到交换器之后,根据路由规则无法投递到具体的队列时消息将转投到该备份交换器。定义交换器的时添加 alternate-exchange 参数来实现,交换器的类型建议声明成 fanout 类型,因为消息被重新发送到备份交换器时的路由键和从生产者出发的路由键是一致的。当你发送一条消息到 fanout 交换器时,它会把消息投递给所有绑定到此交换器上的队列,而且会忽略路由键。
channel.exchangeDeclare( "alexchange " , "fanout" , true , false , null) ; Map<String, Object> args = new HashMap<String, Object>(); args.put("alternate-exchange" , "alexchange"); //设置备份交换器 channel.exchangeDeclare("exchange", "topic",true,false, args);
- 过期机制:
- 支持为队列每一条消息统一设置超时时间RabbitMQ保证死消息(在队列中的时间超过设定的TTL时间)不会被消费者获得。消息不会在消费者的缓冲区中过期,也就是说,只要队列在消息过期前将消息推送给消费者,消费者就一定能处理到这条消息。重新入队(例如被取消确认或者信道关闭或拒绝并重新入队)的消息的过期时间保留初始值,即不刷新过期时间
Map<String, Object> args = new HashMap<String, Object>(); args.put("x-message-ttl" , 20000);//设置超时时间 毫秒 channel.queueDeclare("queueName2", true, false, false, args);
- 也支持为单独的消息设置超时时间,在生产者发布消息的时候设置超时时间。
AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder(); builder.expiration("10000"); AMQP.BasicProperties build = builder.build(); channel.basicPublish("", "queue", build, "helloWorld".getBytes("UTF-8"));
- 如果同时设置队列统一超时时间、消息超时时间,时间短的生效。RabbitMQ只对处于队头的消息判断是否过期(即不会扫描队列),所以,很可能队列中已存在死消息(消息单独设置的过期时间),但是队列并不知情。这会影响队列统计数据的正确性,妨碍队列及时释放资源
- 支持为队列每一条消息统一设置超时时间RabbitMQ保证死消息(在队列中的时间超过设定的TTL时间)不会被消费者获得。消息不会在消费者的缓冲区中过期,也就是说,只要队列在消息过期前将消息推送给消费者,消费者就一定能处理到这条消息。重新入队(例如被取消确认或者信道关闭或拒绝并重新入队)的消息的过期时间保留初始值,即不刷新过期时间
- 死信队列
- 消息消费应答为被拒绝,且requeue参数为false
- 消息过期
- 队列达最大长度,队伍前的消息会进死信队列
//通过channel.queueDeclare上设置x-dead-letter-exchange添加死信交换器。 //死信消息会投递到该死信交换器,由死信交换器最终路由到死信队列。 //通过x-dead-letter-routing-key可以重新设置路由到死信队列的routingkey. Map<String, Object> argsMap = new HashMap<String, Object>(); argsMap.put("x-dead-letter-exchange","dl_exchange"); argsMap.put("x-dead-letter-routing-key","normal"); channel.queueDeclare("queue", false, false, true, argsMap);
-
- 延迟队列:通过过期机制与死信队列实现。
- 隋性队列:消息直接保存到磁盘,占用内存小。
Map<String, Object> args1 = new HashMap<String, Object>(); args1.put("x-queue-mode", "lazy"); channel.queueDeclare("myqueue ", false, false, false, args1);
- 镜像队列:针对一些重要的队列,我们可以采取镜像队列,消息缓存备份到集群下的多个broker.需要配置集群,添加策略