一 、Spring整合RabbitMQ
1、添加maven依赖
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
2、配置ConnectionFactory
@Configuration
public class RabbitmqConfig {
/**
* todo 创建连接工厂
* @return
*/
@Bean
public ConnectionFactory connectionFactory () {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
cachingConnectionFactory.setHost("47.93.60.129");
cachingConnectionFactory.setPort(5672);
cachingConnectionFactory.setVirtualHost("cyan");
cachingConnectionFactory.setUsername("cyan");
cachingConnectionFactory.setPassword("cyan");
cachingConnectionFactory.setConnectionTimeout(10000);
cachingConnectionFactory.setCloseTimeout(10000);
return cachingConnectionFactory;
}
/**
* todo RabbitAdmin用于创建、绑定、管理队列与交换机
* @param connectionFactory
* @return
*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
//todo spring容器启动加载该类
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
//todo =====================================声明三个交换机===========================================================
@Bean
public TopicExchange topicExchange() {
TopicExchange topicExchange = new TopicExchange("cyan.topic.exchange",true,false);
return topicExchange;
}
@Bean
public DirectExchange directExchange() {
DirectExchange directExchange = new DirectExchange("cyan.direct.exchange",true,false);
return directExchange;
}
@Bean
public FanoutExchange fanoutExchange() {
FanoutExchange fanoutExchange = new FanoutExchange("cyan.faout.exchange",true,false);
return fanoutExchange;
}
//todo ===========================================声明队列===========================================================
@Bean
public Queue testTopicQueue1() {
Queue queue = new Queue("testTopicQueue1",true,false,false,null);
return queue;
}
@Bean
public Queue testTopicQueue2() {
Queue queue = new Queue("testTopicQueue2",true,false,false,null);
return queue;
}
@Bean
public Queue testDirectQueue() {
Queue queue = new Queue("testDirectQueue",true,false,false,null);
return queue;
}
@Bean
public Queue testFaoutQueue() {
Queue queue = new Queue("testfaoutQueue",true,false,false,null);
return queue;
}
//... ...省略部分队列声明
//todo ========================================声明绑定关系==========================================================
@Bean
public Binding topicBingding1() {
return BindingBuilder.bind(testTopicQueue1()).to(topicExchange()).with("topic.#");
}
@Bean
public Binding topicBingding2() {
return BindingBuilder.bind(testTopicQueue2()).to(topicExchange()).with("topic.key.#");
}
@Bean
public Binding directBinding() {
return BindingBuilder.bind(testDirectQueue()).to(directExchange()).with("direct.key");
}
@Bean
public Binding orderQueueBinding() {
return BindingBuilder.bind(orderQueue()).to(directExchange()).with("rabbitmq.order");
}
//... ...省略部分绑定关系
/**
* todo rabbitTemplate模板
* @return
*/
@Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
rabbitTemplate.setReceiveTimeout(50000);
return rabbitTemplate;
}
}
3、自定义消息委托类
public class CyanMsgDelegate {
public void handleMessage(String msgBody) {
System.out.println("CyanMsgDelegate...... handleMessage"+msgBody);
}
public void consumerMsg(String msg){
System.out.println("CyanMsgDelegate。。。。。。consumerMsg"+msg);
}
public void consumerTopicQueue1(String msgBody) {
System.out.println("CyanMsgDelegate。。。。。。consumerTopicQueue1"+msgBody);
}
public void consumerTopicQueue2(String msgBody) {
System.out.println("CyanMsgDelegate。。。。。。consumerTopicQueue2"+msgBody);
}
/**
* todo 处理json
* @param jsonMap
*/
public void consumerJsonMessage(Map jsonMap) {
System.out.println("CyanMsgDelegate ============================处理json"+jsonMap);
}
/**
* todo 处理order对象
* @param order
*/
public void consumerJavaObjMessage(Order order) {
System.out.println("CyanMsgDelegate ============================处理java对象"+order.toString());
}
/**
* todo 处理文件(和图片)对象
* @param file
*/
public void consumerFileMessage(File file) {
System.out.println("CyanMsgDelegate========================处理文件"+file.getName());
}
}
4、使用默认的监听方法监听消息
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer() {
//todo 创建简单消息监听容器
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer(connectionFactory());
//todo 监听我们的队列
simpleMessageListenerContainer.setQueues(testTopicQueue1(),testDirectQueue(),testTopicQueue2(),orderQueue(),addressQueue(),fileQueue());
//todo 消费者的数量
simpleMessageListenerContainer.setConcurrentConsumers(5);
//todo 最大消费者数量
simpleMessageListenerContainer.setMaxConcurrentConsumers(10);
//todo 签收模式
simpleMessageListenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
//todo 设置拒绝重回队列
simpleMessageListenerContainer.setDefaultRequeueRejected(false);
//设置使用默认的监听方法
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(new CyanMsgDelegate());
simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
return simpleMessageListenerContainer;
}
发送消息测试:
@Test
public void simpleMessageListenerContainerTest() {
rabbitTemplate.convertAndSend("cyan.topic.exchange","topic.xixi","你好 青子");
}
5、指定监听方法监听消息
//... ...省略部分代码
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(new CyanMsgDelegate());
messageListenerAdapter.setDefaultListenerMethod("consumerMsg");
simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
发送消息测试:
@Test
public void simpleMessageListenerContainerTest() {
rabbitTemplate.convertAndSend("cyan.topic.exchange","topic.xixi","你好 青子");
}
6、给指定队列绑定指定监听方法
//... ...省略部分代码
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(new CyanMsgDelegate());
Map<String,String> queueMaps = new HashMap<>();
queueMaps.put("testTopicQueue1","consumerTopicQueue1");
queueMaps.put("testTopicQueue2","consumerTopicQueue2");
messageListenerAdapter.setQueueOrTagToMethodName(queueMaps);
simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
发送消息测试:
@Test
public void messageListenerAdaperQueueOrTagToMethodName(){
rabbitTemplate.convertAndSend("cyan.topic.exchange","topic.xixi","你好 青子");
rabbitTemplate.convertAndSend("cyan.topic.exchange","topic.key.xixi","你好 青子");
}
7、指定方法监听消息处理json数据
//... ...省略部分代码
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(new CyanMsgDelegate());
messageListenerAdapter.setDefaultListenerMethod("consumerJsonMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
messageListenerAdapter.setMessageConverter(jackson2JsonMessageConverter);
simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
发送消息测试:
@Test
public void sendJson() throws JsonProcessingException {
Order order = new Order();
order.setOrderNo(UUID.randomUUID().toString());
order.setCreateDt(new Date());
order.setPayMoney(10000.00);
order.setUserName("青子");
ObjectMapper objectMapper = new ObjectMapper();
String orderJson = objectMapper.writeValueAsString(order);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/json");
Message orderMsg = new Message(orderJson.getBytes(),messageProperties);
rabbitTemplate.convertAndSend("cyan.direct.exchange","rabbitmq.order",orderMsg);
}
7、指定方法监听消息处理java对象
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(new CyanMsgDelegate());
messageListenerAdapter.setDefaultListenerMethod("consumerJavaObjMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
//todo 设置json转java对象
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
javaTypeMapper.setTrustedPackages("com.cyan.pojo");
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
messageListenerAdapter.setMessageConverter(jackson2JsonMessageConverter);
simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
发送消息测试:
@Test
public void sendJavaObj() throws JsonProcessingException {
Order order = new Order();
order.setOrderNo(UUID.randomUUID().toString());
order.setCreateDt(new Date());
order.setPayMoney(10000.00);
order.setUserName("青子");
ObjectMapper objectMapper = new ObjectMapper();
String orderJson = objectMapper.writeValueAsString(order);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/json");
messageProperties.getHeaders().put("__TypeId__","com.cyan.pojo.Order");
Message orderMsg = new Message(orderJson.getBytes(),messageProperties);
rabbitTemplate.convertAndSend("cyan.direct.exchange","rabbitmq.order",orderMsg);
}
8、指定方法监听消息处理文件和图片
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(new CyanMsgDelegate());
messageListenerAdapter.setDefaultListenerMethod("consumerFileMessage");
//todo 设置转换器
ContentTypeDelegatingMessageConverter messageConverter = new ContentTypeDelegatingMessageConverter();
messageConverter.addDelegate("img/png",new CyanImageConverter());
messageConverter.addDelegate("img/jpg",new CyanImageConverter());
messageConverter.addDelegate("application/word",new CyanWordConverter());
messageConverter.addDelegate("word",new CyanWordConverter());
messageListenerAdapter.setMessageConverter(messageConverter);
simpleMessageListenerContainer.setMessageListener(messageListenerAdapter);
自定义图片转换器:
public class CyanImageConverter implements MessageConverter {
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
return null;
}
@Override
public Object fromMessage(Message message) throws MessageConversionException {
System.out.println("自定义的图片转换器................");
String msgContentType = message.getMessageProperties().getContentType();
String fileSuffix = null;
if(msgContentType !=null &&(msgContentType.contains("png")||msgContentType.contains("jpg"))) {
fileSuffix = msgContentType.split("/")[1];
}else {
fileSuffix="jpg";
}
byte[] msgBody = message.getBody();
String filePrefixName = UUID.randomUUID().toString();
String filePath = "d:/cyan/file02/"+filePrefixName+"."+fileSuffix;
System.out.println("文件路径:"+filePath);
File file = new File(filePath);
try {
Files.copy(new ByteArrayInputStream(msgBody), file.toPath());
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
}
自定义word转换器:
public class CyanWordConverter implements MessageConverter {
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
return null;
}
@Override
public Object fromMessage(Message message) throws MessageConversionException {
System.out.println("自定义的文档转换器................");
String msgContentType = message.getMessageProperties().getContentType();
String fileSuffix = null;
if(msgContentType !=null && msgContentType.contains("word")) {
fileSuffix = "docx";
}else {
fileSuffix="doc";
}
byte[] msgBody = message.getBody();
String filePrefixName = UUID.randomUUID().toString();
String filePath = "d:/cyan/file02/"+filePrefixName+"."+fileSuffix;
System.out.println("文件路径:"+filePath);
File file = new File(filePath);
try {
Files.copy(new ByteArrayInputStream(msgBody), file.toPath());
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
}
发送图片测试:
@Test
public void sendImage() throws IOException {
byte[] imgBody = Files.readAllBytes(Paths.get("D:/cyan/file01","cyan.jpg"));
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("img/png");
Message message = new Message(imgBody, messageProperties);
rabbitTemplate.send("cyan.direct.exchange","rabbitmq.file",message);
}
发送word文件测试:
@Test
public void sendWord() throws IOException {
byte[] imgBody = Files.readAllBytes(Paths.get("D:/cyan/file01","cyan.docx"));
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/word");
Message message = new Message(imgBody, messageProperties);
rabbitTemplate.send("cyan.direct.exchange","rabbitmq.file",message);
}
二、RabbitMQ如何保障消息的可靠性投递
1、什么是生产端的可靠性投递
- 保障消息成功发送出去
- 保障mq节点成功接收消息
- 消息发送端需要收到mq服务的确认应答
- 完善的消息补偿机制(百分百成功需要该步骤)
2、解决保障可靠性投递的方案(消息落库+定时任务)
1)正常链路流程
- 在发送消息的时候,把消息插入到消息表中,初始状态为0
- 把消息投递到消息队列中
- broker来确认消息发送到队列
- 根据收到的broker确认消息来更新数据库中的消息记录的状态
2)异常链路流程
- 在发送消息的时候,把消息插入到消息表中,初始状态为0
- 把消息投递到消息队列中
- 由于网络闪断,生产者无法收到broker的消息确认,那么该条状态在数据库中永远是0,这个时候,就需要对这种情况做出补偿
3)补偿机制
启动一个分布式的定时任务,不定时的去扫描消息表状态为0的消息记录,然后根据业务来设置扫描重发规则
- 规则一:扫描消息表中状态为0的消息,每隔五分钟重试发送一次
- 规则二:若重试次数超过5次状态还是0,则更改消息状态为2,此时需要人工去确认状态为2的消息是什么原因导致没有发送成功
消息入库缺点: 在高并发的环境下,会有性能瓶颈
3、延时投递,做二次确认检测,回调检测
三、RabbitMQ如何保障消息的幂等性
1、什么是接口的幂等性
对同一接口发起一次调用和多次调用,所产生的结果都是一样的。
若接口没有保障幂等性,那么就有可能会出现问题
2、幂等性保障应用场景
比如订单提交过程中,用户点击了一次提交,但是由于网络等原因,导致后端处理延时,客户就连续点了多次,在没有幂等性的条件下,那么就会造成订单的重复提交。
解决方案: 在保存订单的时候,根据生成的系统全局唯一ID(可以是订单号+业务类型),并且把该唯一ID调用redis的setnx命令保存起来,在第一次保存的时候,由于redis中没有该key,那么就会把全局唯一ID保存到redis中,此时订单就会保存成功,这个时候若出现前端重复点击按钮,由于第一步已经setnx保存上了,所有重复提交不会保存到redis中
3、RabbitMQ是如何解决幂等性的
1)消息重复发送的原因
为了保障消息的百分之百的投递,我们使用了消息重发,确认机制,使得消息可能被重复发送,由于网络原因,无论是上半场(MQ服务端到生产端)ack丢失还是下半场(MQ服务端到消费端)ack丢失,都会导致消息重复发送
2)消息重复发送导致的后果
比如上半场消息生产者是用户支付模块,专门是用来给用户扣费的,而下半场的消息消费者服务是会员卡服务,是通过接受扣费服务发送的消息来进行发卡的,由于ack丢失,那么就会导致上游服务重复发送消息就会导致扣多次款,发多次卡
3)MQ是如何保障幂等性的
消息队列的服务中,对每一条消息都会生成一个全局唯一的与业务无关的ID(inner_msg_id),当mq_server接受到消息的时候,先根据inner_msg_id是否需要重复发送,再决定消息是否落库,这样保证每条消息都只会落库一次
4)消费端如何做到幂等性
把对每条消息做生成一个唯一性的ID,通过redis的来setnx命令来保证幂等性
四、RabbitMQ保障消息的可靠性与幂等性代码示例(以消息落库+定时任务为例)
1、生产端可靠性保障
1)创建消息表
CREATE TABLE `message_content` (
`msg_id` varchar(50) NOT NULL COMMENT '唯一标识',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`msg_status` int(10) DEFAULT NULL COMMENT '消息状态 0=发送中 1=mq的broker确认接受到消息 3=没有对应交换机 4=没有对应的路由 5=消费端成功消费消息',
`exchange` varchar(50) DEFAULT NULL COMMENT '交换器',
`routing_key` varchar(50) DEFAULT NULL COMMENT '路由键',
`err_cause` varchar(1000) DEFAULT NULL COMMENT '错误信息',
`order_no` bigint(32) DEFAULT NULL COMMENT '订单ID',
`max_retry` int(10) DEFAULT NULL COMMENT '最大重试次数',
`current_retry` int(10) DEFAULT NULL COMMENT '当前重试次数',
`product_no` int(10) DEFAULT NULL COMMENT '产品ID',
PRIMARY KEY (`msg_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='消息表';
2)保存订单
public void saveOrderInfoWithMessage(OrderInfo orderInfo) throws JsonProcessingException {
//todo 构建消息对象
MessageContent messageContent = new MessageContent();
String msgId = UUID.randomUUID().toString();
messageContent.setMsgId(msgId);
messageContent.setCreateTime(new Date());
messageContent.setUpdateTime(new Date());
messageContent.setExchange(MqConst.ORDER_TO_PRODUCT_EXCHANGE_NAME);
messageContent.setRoutingKey(MqConst.ORDER_TO_PRODUCT_QUEUE_NAME);
messageContent.setMsgStatus(MsgStatusEnum.SENDING.getCode());
messageContent.setOrderNo(orderNo);
messageContent.setProductNo(productNo);
messageContent.setMaxRetry(MqConst.MSG_RETRY_COUNT);
//todo 保存数据库(业务失败,直接抛出异常,不会发送消息到mq)
saveOrderInfo(orderInfo,messageContent);
//todo 构建消息发送对象
MsgTxtBo msgTxtBo = new MsgTxtBo();
msgTxtBo.setMsgId(messageContent.getMsgId());
msgTxtBo.setOrderNo(orderInfo.getOrderNo());
msgTxtBo.setProductNo(orderInfo.getProductNo());
//todo 发送消息(消息发送失败,调用ConfirmListener)
msgSender.senderMsg(msgTxtBo);
}
//如果业务出现异常,消息不会入库,也不会发送到mq中
@Transactional
@Override
public void saveOrderInfo(OrderInfo orderInfo, MessageContent messageContent) {
try {
orderInfoMapper.saveOrderInfo(orderInfo);
//插入消息表
msgContentMapper.saveMsgContent(messageContent);
}catch (Exception e) {
LOGGER.error("操作数据库失败:{}",e);
throw new RuntimeException("操作数据库失败");
}
}
public void senderMsg(MsgTxtBo msgTxtBo){
LOGGER.info("发送的消息ID:{}",msgTxtBo.getMsgId());
//todo 唯一可靠性保障
CorrelationData correlationData = new CorrelationData(msgTxtBo.getMsgId());
rabbitTemplate.convertAndSend(MqConst.ORDER_TO_PRODUCT_EXCHANGE_NAME,MqConst.ORDER_TO_PRODUCT_ROUTING_KEY,msgTxtBo,correlationData);
}
3)消息确认机制
@Component
public class CyanMsgComfirm implements RabbitTemplate.ConfirmCallback{
private static final Logger LOGGER = LoggerFactory.getLogger(TulingMsgComfirm.class);
@Autowired
private MsgContentMapper msgContentMapper;
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String msgId = correlationData.getId();
if(ack) {
LOGGER.info("消息Id:{}对应的消息被broker签收成功",msgId);
updateMsgStatusWithAck(msgId);
}else{
//todo 消息发送失败,更改消息状态
LOGGER.warn("消息Id:{}对应的消息被broker签收失败:{}",msgId,cause);
updateMsgStatusWithNack(msgId,cause);
}
}
}
4)消息不可达监听
@Component
public class TulingMsgRetrunListener implements RabbitTemplate.ReturnCallback {
private static final Logger LOGGER = LoggerFactory.getLogger(TulingMsgRetrunListener.class);
@Autowired
private MsgContentMapper msgContentMapper;
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
try{
ObjectMapper objectMapper = new ObjectMapper();
MsgTxtBo msgTxtBo = objectMapper.readValue(message.getBody(),MsgTxtBo.class);
LOGGER.info("无法路由消息内容:{},cause:{}",msgTxtBo,replyText);
//构建消息对象
MessageContent messageContent = new MessageContent();
messageContent.setErrCause(replyText);
messageContent.setUpdateTime(new Date());
messageContent.setMsgStatus(MsgStatusEnum.SENDING_FAIL.getCode());
messageContent.setMsgId(msgTxtBo.getMsgId());
//更新消息表
msgContentMapper.updateMsgStatus(messageContent);
}catch (Exception e) {
LOGGER.error("更新消息表异常:{}",e);
}
}
}
5)定时任务
public void retrySend() {
System.out.println("-----------------------------");
//todo 定时查询消息状态还没有完结的消息
List<MessageContent> messageContentList = msgContentMapper.qryNeedRetryMsg(MsgStatusEnum.CONSUMER_SUCCESS.getCode(), MqConst.TIME_DIFF);
for(MessageContent messageContent:messageContentList) {
if(messageContent.getMaxRetry()>messageContent.getCurrentRetry()) {
MsgTxtBo msgTxtBo = new MsgTxtBo();
msgTxtBo.setMsgId(messageContent.getMsgId());
msgTxtBo.setProductNo(messageContent.getProductNo());
msgTxtBo.setOrderNo(messageContent.getOrderNo());
//更新消息重试次数
msgContentMapper.updateMsgRetryCount(msgTxtBo.getMsgId());
msgSender.senderMsg(msgTxtBo);
}else {
LOGGER.warn("消息:{}以及达到最大重试次数",messageContent);
}
}
}
2、消费端可靠性保障
@RabbitListener(queues = {ORDER_TO_PRODUCT_QUEUE_NAME})
@RabbitHandler
public void consumerMsgWithLock(Message message, Channel channel) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
MsgTxtBo msgTxtBo = objectMapper.readValue(message.getBody(), MsgTxtBo.class);
Long deliveryTag = message.getMessageProperties().getDeliveryTag();
//todo 利用redis分布式锁解决消息幂等性问题
if (redisTemplate.opsForValue().setIfAbsent(LOCK_KEY + msgTxtBo.getMsgId(), msgTxtBo.getMsgId())) {
LOGGER.info("消费消息:{}", msgTxtBo);
try {
//todo 更新消息表也业务表(出异常catch更新消息状态)
productService.updateProductStore(msgTxtBo);
//todo 消息签收(这里出现异常,业务事务已提交,会存在幂等性问题)
System.out.println(1/0);
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
/**
* 更新数据库异常说明业务没有操作成功需要删除分布式锁
*/
if (e instanceof BizExp) {
BizExp bizExp = (BizExp) e;
LOGGER.info("数据业务异常:{},即将删除分布式锁", bizExp.getErrMsg());
//todo 删除分布式锁
redisTemplate.delete(LOCK_KEY);
}
//todo 更新消息表状态
MessageContent messageContent = new MessageContent();
messageContent.setMsgStatus(MsgStatusEnum.CONSUMER_FAIL.getCode());
messageContent.setUpdateTime(new Date());
messageContent.setErrCause(e.getMessage());
messageContent.setMsgId(msgTxtBo.getMsgId());
msgContentMapper.updateMsgStatus(messageContent);
channel.basicReject(deliveryTag,false);
}
} else {
LOGGER.warn("请不要重复消费消息{}", msgTxtBo);
channel.basicReject(deliveryTag,false);
}
}