RabbitMQ学习之工作队列(java)

   参考:http://blog.csdn.net/lmj623565791/article/details/37620057

1.生产任务Task.java

package cn.slimsmart.rabbitmq.demo.workqueue;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

public class Task {
	
	//队列名称  
    private final static String QUEUE_NAME = "workqueue-durable";  

	public static void main(String[] args) throws Exception {
		 //创建连接和频道  
        ConnectionFactory factory = new ConnectionFactory();  
        factory.setHost("192.168.101.174");  
        //指定用户 密码
        factory.setUsername("admin");
        factory.setPassword("admin");
        //指定端口
        factory.setPort(AMQP.PROTOCOL.PORT);
        Connection connection = factory.newConnection();  
        Channel channel = connection.createChannel();  
        boolean durable = true; //设置消息持久化  RabbitMQ不允许使用不同的参数重新定义一个队列,所以已经存在的队列,我们无法修改其属性。
        //声明队列 
        channel.queueDeclare(QUEUE_NAME, durable, false, false, null);  
       
        //发送10条消息,依次在消息后面附加1-10个点  
        for (int i = 5; i > 0; i--)  
        {  
            String dots = "";  
            for (int j = 0; j <= i; j++)  
            {  
                dots += ".";  
            }  
            String message = "helloworld" + dots+dots.length();  
            //MessageProperties.PERSISTENT_TEXT_PLAIN 标识我们的信息为持久化的
            channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());  
            System.out.println("Sent Message:'" + message + "'");  
        }  
        //关闭频道和资源  
        channel.close();  
        connection.close();  
	}

}

2.消费工作队列

package cn.slimsmart.rabbitmq.demo.workqueue;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

public class Work {
	//队列名称  
    private final static String QUEUE_NAME = "workqueue-durable";  
    
	public static void main(String[] args) throws Exception {
		 //区分不同工作进程的输出  
        int hashCode = Work.class.hashCode();  
        //创建连接和频道  
        ConnectionFactory factory = new ConnectionFactory();  
        factory.setHost("192.168.101.174");  
        //指定用户 密码
        factory.setUsername("admin");
        factory.setPassword("admin");
        //指定端口
        factory.setPort(AMQP.PROTOCOL.PORT);
        Connection connection = factory.newConnection();  
        Channel channel = connection.createChannel();  
        boolean durable = true; //设置消息持久化  RabbitMQ不允许使用不同的参数重新定义一个队列,所以已经存在的队列,我们无法修改其属性。
        //声明队列  
        channel.queueDeclare(QUEUE_NAME, durable, false, false, null);  
      
        QueueingConsumer consumer = new QueueingConsumer(channel);  
        
        /**
         * ack= true: Round-robin 转发   消费者被杀死,消息会丢失
         * ack=false:消息应答 ,为了保证消息永远不会丢失,RabbitMQ支持消息应答(message acknowledgments)。
         * 消费者发送应答给RabbitMQ,告诉它信息已经被接收和处理,然后RabbitMQ可以自由的进行信息删除。
         * 如果消费者被杀死而没有发送应答,RabbitMQ会认为该信息没有被完全的处理,然后将会重新转发给别的消费者。
         * 通过这种方式,你可以确认信息不会被丢失,即使消者偶尔被杀死。
         * 消费者需要耗费特别特别长的时间是允许的。
         * 
         */
        
        boolean ack = false ; //打开应答机制 
        // 指定消费队列  
        channel.basicConsume(QUEUE_NAME, ack, consumer);  
        
        
        //公平转发  设置最大服务转发消息数量    只有在消费者空闲的时候会发送下一条信息。
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);
        
        while (true)  
        {  
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();  
            String message = new String(delivery.getBody());  
  
            System.out.println(hashCode + " Received Message:'" + message + "'");  
            doWork(message);  
            System.out.println(hashCode + " Received Done");  
            //发送应答  
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);  
  
        }  
	}
	
	/** 
     * 每个点耗时1s 
     * @param task 
     * @throws InterruptedException 
     */  
    private static void doWork(String task) throws InterruptedException  
    {  
        for (char ch : task.toCharArray())  
        {  
            if (ch == '.')  
                Thread.sleep(1000);  
        }  
    }  

}

多启动几个消费者工作进程,使用生产者发送消息,可以观察消费情况。

要了解RabbitMQ的路由机制,exchange是一个关键。exchange可以叫做交换机,也似乎可以叫做路由器,反正它是用来选择路由的。RabbitMQ的核心思想就是消息的发布者不是直接把消息发送到目标队列中的,事实上,通常它并不知道消息要发到哪个队列中,它只知道把消息队列发送到exchange中。exchange一边接收发送者发过来的消息,而另一边则把消息发送到目标队列中去。exchange一定知道哪些队列需要接收这个消息,是加到一个队列里还是加到好几个队列里,还是直接扔掉。

如果用空字符串去申明一个exchange,那么系统就会使用"amq.direct"这个exchange。前面我们使用的都是amq.direct类型。
channel.BasicPublish("", "TaskQueue", properties, bytes);

direct exchange 发送消息是要看routingKey的。举个例子,定义了一个direct exchange 名字是X1,然后一个queue名字为Q1 用routingKey=K1 绑定到exchange X1上,当一个routeKey为 K2 的消息到达X1上,那么只有K1=K2的时候,这个消息才能到达Q1上。

fanout类型的exchange就比较好理解。就是简单的广播,而且是忽略routingKey的。所以只要是有queue绑定到fanout exchange上,通过这个exchange发送的消息都会被发送到那些绑定的queue中,不管你有没有输入routingKey。

Topic类型的exchange给与我们更大的灵活性。通过定义routingKey可以有选择的订阅某些消息,此时routingKey就会是一个表达式。exchange会通过匹配绑定的routingKey来决定是否要把消息放入对应的队列中。有两种表达式符号可以让我们选择:#和*。

*(星号):代表任意的一个词。 例:*.a会匹配a.a,b.a,c.a等

#(井号):代码任意的0个或多个词。 例:#.a会匹配a.a,aa.a,aaa.a等

topic exchange 有时候的行为会像其他类型的exchange,比如说:

当routingKey只是有#号的时候,它的行为和fanout的行为是一样的。

当routingKey什么的没有,空字符串的时候,它的行为是和direct是一样的。

要注意的是,符号代表的是词不是字符。RabbitMQ中在表达式中词的定义是以.(点号)分隔的。

Headers类型的exchange使用的比较少。以后再说。

下面主要用代码,实现一下direct、fanout、topic的效果。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
要设置一个死信队列,在 RabbitMQ 中,需要进行以下步骤: 1. 首先创建两个队列,分别是正常队列和死信队列,以及一个交换机。 2. 正常队列中需要设置死信队列地址。 3. 将交换机和正常队列通过一个 routing key 绑定起来。 4. 当正常队列中的消息过期或者未能被消费时,就会被转发到死信队列中。 以下是一个使用 Java 实现的示例代码: ``` // 创建连接工厂对象 ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); factory.setUsername("guest"); factory.setPassword("guest"); // 创建连接对象 Connection connection = factory.newConnection(); // 创建信道对象 Channel channel = connection.createChannel(); // 定义交换机名称和类型 String exchangeName = "demo.exchange"; String exchangeType = "direct"; // 定义正常队列名称、死信队列名称和 routing key String queueName = "demo.queue"; String deadLetterQueueName = "demo.dead.letter.queue"; String routingKey = "demo.routing.key"; // 定义正常队列属性 Map<String, Object> queueArgs = new HashMap<>(); queueArgs.put("x-message-ttl", 10000); queueArgs.put("x-dead-letter-exchange", exchangeName); queueArgs.put("x-dead-letter-routing-key", deadLetterRoutingKey); // 声明交换机 channel.exchangeDeclare(exchangeName, exchangeType, true, false, null); // 声明正常队列 channel.queueDeclare(queueName, true, false, false, queueArgs); // 声明死信队列 channel.queueDeclare(deadLetterQueueName, true, false, false, null); // 绑定正常队列和 routing key 到交换机 channel.queueBind(queueName, exchangeName, routingKey); // 发送消息到正常队列 channel.basicPublish(exchangeName, routingKey, null, "Hello World!".getBytes()); // 关闭连接 channel.close(); connection.close(); ``` 在以上代码中,我们创建了一个连接工厂对象,使用默认的主机、用户名和密码创建连接对象。然后我们创建了一个信道对象,定义了交换机名称和类型,以及正常队列名称、死信队列名称和 routing key。接着,我们定义了正常队列的属性,其中包括了消息过期时间和死信队列的地址。然后我们声明了交换机、正常队列和死信队列,并将正常队列和 routing key 绑定到交换机上。最后,我们发送了一个消息到正常队列,此时如果消息未被消费或者过期了,就会被转发到死信队列中。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值