rabbitmq自动及手动ACK

主要是确认消息被消费者消费完成后通知服务器将队列里面的消息清除,手动自动就是对异常的处理态度不同。

  • 不配置Ack的话,此时的服务是no_ack=true的模式,就是说只要我发现你是消费了这个数据,至于异常不异常的,我不管了

  • 通知Ack机制就是这么来的,更加灵活的,我们需要Ack不自动,而是手动,这样做的好处,就是使得我们开发人员更加人性化或者灵活的来处理我们的业务代码,异常发生不会丢数据,更加方便的处理异常的问题以及数据的返回处理等。下面是通话机制的四条原则:

  • basic.Ack 发回给 RabbitMQ 以说明消费完了,可以将相应 message 从 RabbitMQ 的消息缓存中移除。

  • Basic.Ack 未被 consumer 发回给 RabbitMQ 前出现了异常,消费者过掉了,RabbitMQ 发现与该 consumer 对应的连接被断开,之后将该 message 以轮询方式发送给其他 consumer (假设存在多个 consumer 订阅同一个 queue)。

  • 在 no_ack=true 就是默认的情况下,RabbitMQ 认为 message 一旦被 deliver 出去了,就已被确认了,所以会立即将缓存中的 message 删除。所以在 consumer 异常时会导致消息丢失。

  • 来自 consumer 的 Basic.Ack 与 发送给 Producer 的 Basic.Ack 没有直接关系。

代码操作一波:
生产消费都是一样配置,这是fanpout模式可配置多个队列,就自己加吧
配置文件:

# 配置Rabbit链接信息(集群)
spring.rabbitmq.addresses=
spring.rabbitmq.username=
spring.rabbitmq.password=
server.port=8080
# 开启发送确认
spring.rabbitmq.publisher-confirms=true
# 开启发送失败退回
spring.rabbitmq.publisher-returns=true
# 开启ACK
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual

config:

package com.mbyte.easy.emailProducer.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {
    //配置交换机
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("bootDirectExchange");
    }
    //配置队列
    @Bean
    public Queue directQueue(){
        return new Queue("bootDirectQueue");
    }

    @Bean
    public Queue fanoutQueue(){
        return new Queue("bootFanoutQueue");
    }
    /**
     * 配置交换机队列的绑定
     * @param fanoutQueue 需要绑定队列对象,参数名和方法名一至就自动注入
     * @param fanoutExchange 需要绑定交换机对象,参数名和@Bean方法名一至就自动注入
     * @return
     */
    @Bean
    public Binding fanoutBinding(Queue fanoutQueue, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue).to(fanoutExchange);
    }

   //配置FanoutExchange交换机
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("fanoutExchange");
    }
//    //配置topicExchange交换机
//    @Bean
//    public TopicExchange topicExchange(){
//        return new TopicExchange("topicExchange");
//    }
}

实体模板:

package com.mbyte.easy.emailProducer.entity;

import com.mbyte.easy.mailInfo.entity.MailInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;


@Data
@AllArgsConstructor
@NoArgsConstructor
public class MailTemplate implements Serializable {
    /**
     * 邮件主题
     */
    private String subject;
    /**
     * 邮件类型
     */
    private Integer emailType;

    /**
     * 邮件内容
     */
    private String content;


    private String email;
}

重点的实现业务:

package com.mbyte.easy.emailProducer.service.emailRecevie.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.SerializationUtils;
import com.mbyte.easy.emailProducer.entity.MailTemplate;
import com.mbyte.easy.emailProducer.service.emailRecevie.EmailService;
import com.mbyte.easy.mailInfo.entity.MailInfo;
import com.mbyte.easy.sysUser.entity.User;
import com.mbyte.easy.sysUser.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;


@Service
@Slf4j
public class EmailServiceImpl implements EmailService ,RabbitTemplate.ReturnCallback {

    //注入amqp模版,利用这个就收发送
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private IUserService userService;
    @Override
    public void sendEmailMessage(MailInfo mailInfo) {
        List<User> list = userService.list();
        for (User user : list) {
            MailTemplate mailTemplate=new MailTemplate(mailInfo.getSubject(),mailInfo.getEmailType(),mailInfo.getContent(),user.getEmail());
            this.rabbitTemplate.setReturnCallback(this);
            this.rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
                if (!ack) {
                    System.out.println("HelloSender消息发送失败" + cause + correlationData.toString());
                } else {
                    System.out.println("HelloSender 消息发送成功 ");
                }
            });
            this.rabbitTemplate.convertAndSend("bootFanoutQueue", JSON.toJSONString(mailTemplate));
            log.info("邮件: "+JSON.toJSONString(mailTemplate));
        }

    }

    @Override
    public void returnedMessage(Message message, int i, String s, String s1, String s2) {
        System.out.println("sender return success" + message.toString()+"==="+i+"==="+s1+"==="+s2);

    }
}

现在就是把消息数据放对列了,消费者去拿,注意:生产者消费者不能是部署在同一个服务器,学习的话,可以开个别的端口假装是两个而服务器,就象并发和并行一样,天下武功为快不破,快的好像是并行。
生产者:

package com.emailProducer.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.emailProducer.entity.MailTemplate;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

import java.util.Date;


@Slf4j
@Component
@RabbitListener(queues = "bootFanoutQueue")
public class HelloReceiver {

    @RabbitHandler
    public void process(String mailTemplate, Channel channel, Message message) throws IOException {

        try {
            javaSendEmail(mailTemplate);
            //告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
            System.out.println("receiver success");
        } catch (IOException e) {
            e.printStackTrace();
            //丢弃这条消息
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
            System.out.println("receiver fail");
        }

    }
    public void javaSendEmail (String mailTemplateStr){
        log.info("------------------------------------------"+mailTemplateStr);
        MailTemplate mailTemplate= null;
        try {
            JSONObject jsonObject = JSONObject.parseObject(mailTemplateStr);
            mailTemplate = JSON.toJavaObject(jsonObject, MailTemplate.class );
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("------------------------------------------"+mailTemplate);

        Email email = new SimpleEmail();
        try {
            email.setHostName("smtp.qq.com");
            email.setSmtpPort(587);
XXXXXXXXX
            email.setAuthenticator(new DefaultAuthenticator("XXXXXXXXX@qq.com", "授权码"));
            email.setSSLOnConnect(true);
            email.setFrom("XXXXX@qq.com");
            email.setSubject(mailTemplate.getSubject());
            email.setMsg(mailTemplate.getContent());
            email.addTo(mailTemplate.getEmail());
            email.send();

            log.info("发送成功!");
        } catch (EmailException e) {
            e.printStackTrace();
            log.info(e.getMessage());
        }
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现RabbitMQ手动ackacknowledge)代码,您可以按照以下步骤进行操作: 1. 首先,在RabbitMQ配置文件或者使用代码配置的方式中,设置`acknowledge="manual"`,表示使用手动确认模式。 2. 创建一个消费者类(比如MqConsumer),并实现接口ChannelAwareMessageListener,该接口中有一个方法onMessage用于接收消息。 3. 在onMessage方法中,处理接收到的消息,并在处理完成后调用channel.basicAck方法来手动确认消息的消费。 例如,可以在onMessage方法中的代码如下所示: ```java @Override public void onMessage(Message message, Channel channel) throws Exception { try { // 处理接收到的消息 // ... // 手动确认消息的消费 channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } catch (Exception e) { // 发生异常时可以选择进行消息的拒绝或者重新投递 // ... } } ``` 4. 如果在处理消息时发生异常,您可以选择进行消息的拒绝或者重新投递,这取决于您的业务需求。 请注意,在手动确认模式下,如果消费者未调用channel.basicAck方法确认消息消费,消息将会一直保留在RabbitMQ的消息缓存中,直到消费者重新连接或者超时。因此,在实现手动ack代码时,确保正确处理消息的消费以及异常情况的处理是非常重要的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [rabbitmq自动手动ACK](https://blog.csdn.net/qq_18671415/article/details/115349452)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值