发送端:
package cn.rabbitmq.project.started;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.ConfirmType;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Application {
public static void main(String[] args) {
String hostName = "home.cn";
int port = 5672;
String virtualHost = "host";
String userName = "user";
String password = "password";
String exchange = "ex.sit";
String routingKey = "k.sit";
String queueName = "q.sit";
String msg = "{\"id\":193375,\"authorization\":\"test_123\",\"accountNo\":\"62270088\",\"code\":\"bf\"}";
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(hostName, port);
connectionFactory.setUsername(userName);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
// 消息的确认机制(confirm);
connectionFactory.setPublisherConfirmType(ConfirmType.CORRELATED);
connectionFactory.setPublisherReturns(true);
// setCacheMode:设置缓存模式,共有两种,CHANNEL和CONNECTION模式。
// 1、CONNECTION模式,这个模式下允许创建多个Connection,会缓存一定数量的Connection,每个Connection中同样会缓存一些Channel,
// 除了可以有多个Connection,其它都跟CHANNEL模式一样。
// 2、CHANNEL模式,程序运行期间ConnectionFactory会维护着一个Connection,
// 所有的操作都会使用这个Connection,但一个Connection中可以有多个Channel,
// 操作rabbitmq之前都必须先获取到一个Channel,
// 否则就会阻塞(可以通过setChannelCheckoutTimeout()设置等待时间),
// 这些Channel会被缓存(缓存的数量可以通过setChannelCacheSize()设置);
connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CHANNEL); // 设置CONNECTION模式,可创建多个Connection连接
// 设置每个Connection中缓存Channel的数量,不是最大的。操作rabbitmq之前(send/receive message等)
// 要先获取到一个Channel.获取Channel时会先从缓存中找闲置的Channel,如果没有则创建新的Channel,
// 当Channel数量大于缓存数量时,多出来没法放进缓存的会被关闭。
connectionFactory.setChannelCacheSize(100);
// 单位:毫秒;配合channelCacheSize不仅是缓存数量,而且是最大的数量。
// 从缓存获取不到可用的Channel时,不会创建新的Channel,会等待这个值设置的毫秒数
// 同时,在CONNECTION模式,这个值也会影响获取Connection的等待时间,
// 超时获取不到Connection也会抛出AmqpTimeoutException异常。
connectionFactory.setChannelCheckoutTimeout(600);
// 仅在CONNECTION模式使用,设置Connection的缓存数量。
connectionFactory.setConnectionCacheSize(3);
// setConnectionLimit:仅在CONNECTION模式使用,设置Connection的数量上限。
connectionFactory.setConnectionLimit(10);
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 当mandatory 参数设为true 时,交换器无法根据自身的类型和路由键找到一个符合条件的队列,那么RabbitM Q 会调用Basic.Return
// 命令将消息返回给生产者。
// 当mandatory 参数设置为false 时,出现上述情形,则消息直接被丢弃。
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String id = correlationData != null ? correlationData.getId() : "";
if (ack) {
System.out.println("交换机已经收到消息 id:" + id);
} else {
System.out.println("交换机还未收到消息 id:" + id);
System.out.println("原因:" + cause);
}
}
});
// 1。方式一发送
// rabbitTemplate.convertAndSend(exchange, routingKey, "rabbitmq方式一",new
// org.springframework.amqp.rabbit.connection.CorrelationData(UUID.randomUUID().toString()));
// 2.方式二发送(延迟发送至死信队列,消费端去消费)
//DLX ,全称为Dead-Letter-Exchange,可以称之为死信交换器,也有人称之为死信邮箱。当
//消息在一个队列中变成死信( dead message )之后,它能被重新被发送到另一个交换器中,这个
//交换器就是DLX ,绑定DLX 的队列就称之为死信队列。
//消息变成死信一般是由于以下几种情况:
//〈〉消息被拒绝( Basic.Reject/Basic.Nack ),井且设置requeue 参数为false;
//〈〉消息过期;
//令队列达到最大长度。
//DLX 也是一个正常的交换器,和一般的交换器没有区别,它能在任何的队列上被指定,实
//际上就是设置某个队列的属性。当这个队列中存在死信时, RabbitMQ 就会自动地将这个消息重
//新发布到设置的DLX 上去,进而被路由到另一个队列,即死信队列。可以监听这个队列中的消
//息、以进行相应的处理,这个特性与将消息的TTL 设置为0 配合使用可以弥补immediate 参数的功能。
Message message = MessageBuilder.withBody(msg.getBytes()).setDeliveryMode(MessageDeliveryMode.PERSISTENT)
.build();
//message.getMessageProperties().setExpiration(String.valueOf(10 * 1000)); // 设置消息延迟时间:单位毫秒;如果没有再过期时间内消费的话,就会进入到死信队列进行消费
message.getMessageProperties().setHeader("Message-Type", "account-auth");
message.getMessageProperties().setHeader("Version", "1.0.0");
message.getMessageProperties().setContentType(MessageProperties.CONTENT_TYPE_JSON);
message.getMessageProperties().setCorrelationId(UUID.randomUUID().toString());
rabbitTemplate.send(exchange, routingKey, message,
new org.springframework.amqp.rabbit.connection.CorrelationData(UUID.randomUUID().toString()));
System.out.println("sent msg: " + msg);
// -------------------------------------------------------------------------------------------------------------------------------------------
// 3.方式三发送
/*
* ConnectionFactory factory = null; Connection connection = null; Channel
* channel = null; try { factory = new ConnectionFactory();
* factory.setUri("amqp://userName:password@ipAddress:portNumber/virtualHost");
* connection = factory.newConnection(); channel = connection.createChannel();
* channel.exchangeDeclare(exchange, "direct", true, false, null); //
* channel.exchangeBind(exchange, exchange, routingKey); //可以交换器绑定交换器
* channel.queueDeclare(queueName, true, false, false, null);
* channel.queueBind(queueName, exchange, routingKey);// 绑定交换器下面指定的队列 //
* 当mandatory 参数设为true 时,交换器无法根据自身的类型和路由键找到一个符合条件的队列,那么RabbitM Q 会调用Basic.Return
* // 命令将消息返回给生产者。 // 当mandatory 参数设置为false 时,出现上述情形,则消息直接被丢弃。 //
* 那么生产者如何获取到没有被正确路由到合适队列的消息呢?这时候可以通过调用channel.
* addReturnListener来添加ReturnListener监昕器实现。 channel.basicPublish(exchange,
* routingKey, true, null, "rabbitmq方式三".getBytes(Charset.forName("UTF-8"))); }
* catch (KeyManagementException e) { // TODO Auto-generated catch block
* e.printStackTrace(); } catch (NoSuchAlgorithmException e) { // TODO
* Auto-generated catch block e.printStackTrace(); } catch (URISyntaxException
* e) { // TODO Auto-generated catch block e.printStackTrace(); } catch
* (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }
* finally { try { channel.close(); connection.close(); } catch (IOException |
* TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace();
* } }
*/
}
}
消费端:
package cn.rabbitmq.project.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
@Component
public class RabbitMqListener {
/*
* @RabbitListener(bindings = @QueueBinding(value = @Queue(value =
* "${rabbit_mq_queue_name}", durable = "true"), exchange = @Exchange(value =
* "${rabbit_mq_exchange}", durable = "true"), key =
* "${rabbit_mq_routing_key}")) public void consumerMqMessage(String message) {
* System.out.println("消费rabbitmq:" + message); }
*/
@RabbitListener(queues = "${proxy.generating.queue.name}")
public void consumer(Message message, Channel channel) throws IOException {
String data = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println(LocalDateTime.now()+"--消费rabbitmq被拒:" + data);
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);//basicNack拒绝响应,设置requeue 参数为false,不回到原队列
}
//监控死信队列消息
@RabbitListener(queues = "${proxy.dl.generating.queue.name}")
public void process(Message message, Channel channel) throws IOException {
String data = new String(message.getBody(), StandardCharsets.UTF_8);
System.out.println(LocalDateTime.now()+"--死信消费rabbitmq:" + data);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);// manual确认消费然后删除,可以设置配置spring.rabbitmq.listener.simple.acknowledge-mode=manual
}
}
消费端启动应用的时候,会自动加载配置文件里面的MQ参数配置。
配置文件:
spring.rabbitmq.addresses=dd.cn:5672
spring.rabbitmq.host=dd
spring.rabbitmq.virtual-host=host
spring.rabbitmq.port=5672
spring.rabbitmq.username=sd
spring.rabbitmq.password=pd
# 60
spring.rabbitmq.requested-heartbeat=60
spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-returns=true
# 60
spring.rabbitmq.connection-timeout=300000
# cache
spring.rabbitmq.cache.channel.size=10
spring.rabbitmq.cache.channel.checkout-timeout=60
# customer's parameter
proxy.generating.queue.name=q.sit
proxy.generating.exchange.name=ex.sit
proxy.generating.routerkey.name=k.sit
# DLX(Dead-Letter-Exchange)
proxy.dl.generating.queue.name=q.sit.dl
proxy.dl.generating.exchange.name=ex.sit
proxy.dl.generating.routerkey.name=k.sit.dl
# customer's parameter 延迟10s
proxy.generating.timeout.batch.small.tts=10000
# consumer
# 100
spring.rabbitmq.listener.simple.prefetch=10
# 1
spring.rabbitmq.listener.simple.transaction-size=1
# 1
spring.rabbitmq.listener.simple.concurrency=1
# 3
spring.rabbitmq.listener.simple.max-concurrency=3
#manual ack mode
spring.rabbitmq.listener.simple.acknowledge-mode=manual