rabbitMq

 

1,新建exchange  新建queue
   在exchange中绑定queue并设置routing_key
2,消息发送,发送到exchange,routingKey  
   不同的queue会存放自己绑定routingkey的消息
   消息监听,监听queue 
3,各个消费者监听自己的queue, 每个queue可以存放同样的消息,就是广播模式了
4, exchange类型有:
direct直连类型  在exchange中queue需要一一逐个绑定routingKey  test.aa test.bb
topic主题类型  queue只需要模糊匹配绑定就行  test.#

 

1,多个服务器实例,多个消费者,每个消费者又可以多线程,并发消费同一队列
2,多个监听消费者,不同队列,同一条数据每个消费者都会消费到,广播  (消费发送到exchange交换机、routingkey上,不同的queue会存放自己绑定routingkey的消息)

fanout 可由direct替代,它是一种广播式的交换器(散开分列),表示分发,所有的消费者得到同样的队列信息,发布/订阅,会将接收到的消息发送给所有绑定到该交换器的队列,
而不考虑路由键(routing key)。这意味着无论消息发布时指定了什么路由键,Fanout 交换器都会忽略这些信息,并简单地将消息复制并分发给所有的绑定队列。
direct可以建多个queue监听同一个routingkey来实现分发 可来替代fanout

concurrency 多线程消费 

Purge 队列清理

    @Autowired
    private RabbitTemplate rabbitTemplate;
    public String mq(String name) {
        log.info("消息生产:{}", name);
        rabbitTemplate.convertAndSend("queuexmh", name);
        /**
         * one_q one_x 映射到 queue_x1, two_q映射到 queue_x2
         */
        rabbitTemplate.convertAndSend("exchange_direct_xmh","one_q", "queue_x1"+name);
        rabbitTemplate.convertAndSend("exchange_direct_xmh","one_x", "queue_x1"+name);
        rabbitTemplate.convertAndSend("exchange_direct_xmh","two_q", "queue_x2"+name);

        /**
         * one.t1 映射到 queue_x1, two.t1 two.t2 映射到 queue_x2
         * 后台做了关联 two.# 映射到 queue_x2, one.# 映射到 queue_x1
         */
        rabbitTemplate.convertAndSend("exchange_topic_xmh","two.t1", "queue_x2"+name);
        rabbitTemplate.convertAndSend("exchange_topic_xmh","two.t2", "queue_x2"+name);
        rabbitTemplate.convertAndSend("exchange_topic_xmh","one.t1", "queue_x1"+name);
        return "hello"+name;
    }

@Component
public class RabbitConsumer {
    //指定queue消费
    @RabbitListener(queues = "queue_x1",concurrency = "1")
    public void queueX1(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag)  throws IOException {
       try {
           System.out.println("queue_x1收到消息:" + message);
           //设置为手动确认时生效 手动确认消息(第二个参数false表示不批量确认)
           //channel.basicAck(deliveryTag, false);
       } catch (Exception e) {
           // 处理失败时拒绝消息(requeue=false表示不重回队列)
           channel.basicNack(deliveryTag, false, false);
       }
    }
    //指定queue消费
    @RabbitListener(queues = "queue_x2",concurrency = "1")
    public void queueX2(Message message) {
        System.out.println("queue_x2收到消息:" + message);
    }
    //指定queue消费
    @RabbitListener(queues = "${rabbitmq.queue.sendSms}",concurrency = "5",containerFactory =
            "rabbitListenerContainerFactory")
    public void onMessage(Message message) {
}

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.6.8</version>
        </dependency>

spring:
  rabbitmq:
    port: 5672
    host: 192.168.243.128:5672
    username: admin
    password: admin
    virtual-host: xmh
    publisher-confirm-type: correlated
    publisher-returns: true
    listener:
      direct:
        acknowledge-mode: auto
      simple:
        acknowledge-mode: auto
        retry:
          enabled: true

@Configuration
public class RabbitConfig {
    @Value("${spring.rabbitmq.host}")
    private String addresses;
    @Value("${spring.rabbitmq.username}")
    private String username;
    @Value("${spring.rabbitmq.password}")
    private String password;
    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHost;
    // 构建mq实例工厂
    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses(addresses);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        return connectionFactory;
    }
    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        // 若设置为手动确认,必须手动调用ack   AUTO 自动确认 默认值
        factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        return factory;
    }
    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
        return new RabbitAdmin(connectionFactory);
    }
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //每次注入的时候回自动创建一个新的bean实例
    public RabbitTemplate rabbitTemplate(){
        return new RabbitTemplate(connectionFactory());
    }

自动绑定

package com.xmh.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
    // 交换机名称
    public static final String EXCHANGE_NAME = "xmh.exchange";
    // 队列名称
    public static final String QUEUE_NAME = "xmh.queue";
    public static final String QUEUE_NAME2 = "xmh2.queue";
    // 路由键
    public static final String ROUTING_KEY = "xmh.routing.key";
    public static final String ROUTING_KEY2 = "xmh2.routing.key";
    /**
     * 声明交换机
     * 这里使用的是Direct类型交换机,也可以根据需要使用Topic、Fanout等类型
     */
    @Bean
    public DirectExchange exampleExchange() {
        // durable: 是否持久化 autoDelete: 是否自动删除
        return new DirectExchange(EXCHANGE_NAME, true, false);
    }
    /**
     * 声明队列
     */
    @Bean
    public Queue exampleQueue() {
        // durable: 是否持久化 exclusive: 是否排他 autoDelete: 是否自动删除
        return QueueBuilder.durable(QUEUE_NAME).build();
    }
    @Bean
    public Queue exampleQueue2() {
        // durable: 是否持久化 exclusive: 是否排他 autoDelete: 是否自动删除
        return QueueBuilder.durable(QUEUE_NAME2).build();
    }
    /**
     * 绑定交换机和队列
     */
    @Bean
    public Binding binding(Queue exampleQueue, DirectExchange exampleExchange) {
        // 将队列通过路由键绑定到交换机
        return BindingBuilder.bind(exampleQueue).to(exampleExchange).with(ROUTING_KEY);
    }
    @Bean
    public Binding binding2(Queue exampleQueue2, DirectExchange exampleExchange) {
        // 将队列通过路由键绑定到交换机
        return BindingBuilder.bind(exampleQueue2).to(exampleExchange).with(ROUTING_KEY2);
    }
}

    rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING_KEY2, ROUTING_KEY2+name);

    //指定queue消费
    @SneakyThrows
    @RabbitListener(queues = QUEUE_NAME2,concurrency = "1")
    public void queueX2(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println(QUEUE_NAME2+"收到消息:"+msg+":"+ message);
    }

RabbitMq 消费失败,重试机制

自定义异常业务处理
步骤1 设置自动确认,自定义表记录失败数据(包含业务数据、失败次数)触发定时任务重新投放该消息,执行3次,每次间隔30分钟
执行成功,删除重发任务
执行失败,第一次失败执行步骤1,否则记录失败次数,达到一定次数删除定时任务

channel.basicReject(deliveryTag, true);// 拒绝消息并重回队列

 

  


image

 

 安装rabbitmq

https://www.rabbitmq.com/release-information
rabbitmq erlang CentOS

tar -zxf otp_src_28.0.2.tar.gz
修改 Centos 镜像地址 参考 https://www.cnblogs.com/xingminghui111/articles/17514510.html
sudo yum install -y ncurses-devel
sudo yum install -y gcc gcc-c++ openssl-devel libssh-devel unixODBC-devel
./configure && make && make install
erl -version

rpm -i --nodeps rabbitmq-server-4.0.9-1.el8.noarch.rpm

service rabbitmq-server start
service rabbitmq-server status 查看服务状态

配置管理员账号:
rabbitmqctl add_user admin admin
rabbitmqctl set_user_tags admin administrator # 设置用户为管理员
# 授予所有权限
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

# 启用管理插件
rabbitmq-plugins enable rabbitmq_management
# 重启服务使配置生效
systemctl restart rabbitmq-server
关闭防火墙  
sudo systemctl stop firewalld
sudo systemctl disable firewalld

后台管理 http://192.168.243.128:15672/

# 停止服务   自启动
systemctl stop rabbitmq-server     sudo systemctl enable rabbitmq-server

 

  

 其他

@Service
public class OrderItemServiceImpl  {
    /**
     * https://blog.csdn.net/aetawt/article/details/128957417
     * 监听方式获取消息 消息只消费一次,其他消费者消费后,本消费者不再消费
     * @RabbitHandler:标注在方法上
     * @RabbitListener: 标注在类、方法上
     * 使用 @RabbitHandler +  @RabbitListener 接受不同类型的消息
     */
    @RabbitListener(queues = {MqConstant.SEARCH_DATA_QUEUE})
    public void recieveOrderMessage(Message message,  Channel channel) throws IOException {
        System.out.println("收到了消息了--->" + message + " ====》内容:" + new String(message.getBody()));
        System.out.println("渠道数量:" + channel.getChannelNumber());

        MessageProperties messageProperties = message.getMessageProperties();
        System.out.println("消息处理完成---------------------------------");
        //消息顺序,自增
        long deliveryTag = messageProperties.getDeliveryTag();
        System.out.println(deliveryTag);
        //回复,签收消息, fasle表示只签收当前消息,true签收所有
        channel.basicAck(deliveryTag, false);
    }


/**
 * RabbitTemplate 拉取的方式获取消息
 */
@Component
public class RabbitListenerConfig {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @PostConstruct
    public void receive(){
        Thread thread = new Thread(){
          public void run(){
              while (true){
                  System.out.println("*************ready");
                  String message = (String)rabbitTemplate.receiveAndConvert(MqConstant.SEARCH_DATA_QUEUE,1000*2);
                  System.out.println("*************receive:"+message);
              }
          }
        };
        thread.start();
    }


自定义多线程处理
@RabbitListener(queues = {MqConstant.DEAL_TOBE_HANDLE})//,containerFactory = "batchQueueRabbitListenerContainerFactory"
    @Bean("batchQueueRabbitListenerContainerFactory")
    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        //确认方式,manual为手动ack.
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        //每次处理数据数量,提高并发量
        //factory.setPrefetchCount(250);
        //设置线程数
        //factory.setConcurrentConsumers(30);
        //最大线程数
        //factory.setMaxConcurrentConsumers(50);
        /* setConnectionFactory:设置spring-amqp的ConnectionFactory。 */
        factory.setConnectionFactory(connectionFactory);
        factory.setConcurrentConsumers(1);
        factory.setPrefetchCount(1);
        //factory.setDefaultRequeueRejected(true);
        //使用自定义线程池来启动消费者。
        factory.setTaskExecutor(taskExecutor());
        return factory;
    }

    @Bean("correctTaskExecutor")
    @Primary
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(100);
        // 设置最大线程数
        executor.setMaxPoolSize(100);
        // 设置队列容量
        executor.setQueueCapacity(0);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(300);
        // 设置默认线程名称
        executor.setThreadNamePrefix("thread-rabbitmq");
        // 设置拒绝策略rejection-policy:当pool已经达到max size的时候, 调用者执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }

  

posted @ 2023-08-24 17:29  XUMT111  阅读(26)  评论(0)    收藏  举报