• 1.RabbitMq


    RabbitMq - Work 模式

    一、什么是Work模式

      如果有几个消息都需要处理,且每个消息的处理时间很长,仅有一个消费者,那么当它在处理一个消息的时候,其他消息就只有等待。

    等待有时候是好的,但在程序中并不那么好,当队列中有多个消息待处理,将其分发给多个消费者,当一个消费者在处理的时候,有其他消费者继续消费队列中的消息,便缓解了等待的尴尬。

    那么这篇文章将实现一个生产者,多个消费者的模式,实现任务分发:work模式,如图所示。

    二、消息确认机制

    问题:怎样保证消息不因消费者gg而丢失

    处理一个消息可能会花一定的时间,万一还没处理完消费者就gg了…生产者一发送消息,便会将其标记为已删除,故最终的结果是:这条消息没有得到正确的处理。而且,指派给该消费者且尚未处理的所有消息都会gg。

    解决策略:取消自动回复机制
    为了解决消息的丢失问题,RabbitMQ提供了消息确认机制:message acknowledgments,一个消费者处理完成后,将会回传一个ack给生产者,以表示处理成功,这样生产者才可以将消息删除。

    这样即使一个消费者gg了,没有回传ack,那么发送者便会重发消息到队列,如果这时候有其他的消费者服务该队列,那么便会从队列中取出消息并处理。这就保证了消息的不丢失。

    自动回复机制:不管是否处理成功,还是失败,都会回复ack。

    1 channel.basicConsume(QUEUE_NAME, true, consumer);

    自动恢复机制默认是打开的,在接收端的代码最后:第二个参数为true,表示会自动回复,只要生产发送消息,就会标记删除。所以我们需要将自动回复设置为false。

    1 boolean autoAck = false;
    2 channel.basicConsume(QUEUE_NAME, autoAck, consumer);

    这样来保证消息不会因为消费者的gg而丢失了。

    那么取消自动回复以后,我们需要手动回复一次:

    1 channel.basicAck(envelope.getDeliveryTag(), false);

    注意当前的消息确认机制只适用于同一个channel。
    ---------------------

    application.yml

    #############################################################
    ############## rabbitmq config ##############################
    #############################################################
    spring.rabbitmq.host: 127.0.0.1
    spring.rabbitmq.port: 5672
    spring.rabbitmq.username: admin
    spring.rabbitmq.password: admin
     1 package com.maozw.mq.config;
     2 
     3 import org.slf4j.Logger;
     4 import org.slf4j.LoggerFactory;
     5 import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
     6 import org.springframework.amqp.rabbit.connection.ConnectionFactory;
     7 import org.springframework.amqp.rabbit.core.RabbitTemplate;
     8 import org.springframework.beans.factory.annotation.Value;
     9 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    10 import org.springframework.context.annotation.Bean;
    11 import org.springframework.context.annotation.Configuration;
    12 import org.springframework.context.annotation.Scope;
    13 
    14 /**
    15  * @author MAOZW
    16  * @Description: Broker:它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输,
    17  * Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
    18  * Queue:消息的载体,每个消息都会被投到一个或多个队列。
    19  * Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来.
    20  * Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
    21  * vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。
    22  * Producer:消息生产者,就是投递消息的程序.
    23  * Consumer:消息消费者,就是接受消息的程序.
    24  * Channel:消息通道,在客户端的每个连接里,可建立多个channel.
    25  * @date 2018/11/26 14:33
    26  */
    27 @Configuration
    28 public class RabbitConfig {
    29 
    30     private static final Logger LOGGER = LoggerFactory.getLogger(RabbitConfig.class);
    31 
    32     @Value("${spring.rabbitmq.host}")
    33     private String rabbitmqHost;
    34 
    35     @Value("${spring.rabbitmq.port}")
    36     private int rabbitmqPort;
    37 
    38     @Value("${spring.rabbitmq.username}")
    39     private String userName;
    40 
    41     @Value("${spring.rabbitmq.password}")
    42     private String password;
    43 
    44 
    45     public static final String EXCHANGE_A = "my-mq-exchange_A";
    46     public static final String EXCHANGE_B = "my-mq-exchange_B";
    47     public static final String EXCHANGE_C = "my-mq-exchange_C";
    48 
    49 
    50     public static final String QUEUE_A = "QUEUE_A";
    51     public static final String QUEUE_WORK = "QUEUE_WORK";
    52     public static final String QUEUE_C = "QUEUE_C";
    53 
    54     public static final String ROUTINGKEY_A = "spring-boot-routingKey_A";
    55     public static final String ROUTINGKEY_B = "spring-boot-routingKey_B";
    56     public static final String ROUTINGKEY_C = "spring-boot-routingKey_C";
    57 
    58 
    59     @Bean
    60     public ConnectionFactory connectionFactory() {
    61         CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    62         connectionFactory.setUsername(userName);
    63         connectionFactory.setPassword(password);
    64         connectionFactory.setVirtualHost("/vir_simple");
    65         connectionFactory.setPublisherConfirms(false);
    66         return connectionFactory;
    67     }
    68 
    69     public static ConnectionFactory getConnectionFactory() {
    70         CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    71         connectionFactory.setUsername("admin");
    72         connectionFactory.setPassword("admin");
    73         connectionFactory.setVirtualHost("/vir_simple");
    74         connectionFactory.setPublisherConfirms(false);
    75         return connectionFactory;
    76     }
    77 }

    生产者

     1 package com.maozw.mq.work;
     2 
     3 import com.maozw.mq.config.RabbitConfig;
     4 import com.rabbitmq.client.Channel;
     5 import org.slf4j.Logger;
     6 import org.slf4j.LoggerFactory;
     7 import org.springframework.amqp.rabbit.connection.Connection;
     8 import org.springframework.amqp.rabbit.connection.ConnectionFactory;
     9 import org.springframework.beans.factory.annotation.Autowired;
    10 import org.springframework.web.bind.annotation.RequestMapping;
    11 import org.springframework.web.bind.annotation.RestController;
    12 
    13 import java.io.IOException;
    14 
    15 /**
    16  * work 模式
    17  * 两种分发: 轮询分发 + 公平分发
    18  * 轮询分发:消费端:自动确认消息;boolean autoAck = true;
    19  * 公平分发: 消费端:手动确认消息 boolean autoAck = false; channel.basicAck(envelope.getDeliveryTag(),false);
    20  * @author MAOZW
    21  * @Description: ${todo}
    22  * @date 2018/11/26 15:06
    23  */
    24 @RestController
    25 @RequestMapping("/work")
    26 public class WorkProducer {
    27     private static final Logger LOGGER = LoggerFactory.getLogger(WorkProducer.class);
    28     @Autowired
    29     RabbitConfig rabbitConfig;
    30 
    31     @RequestMapping("/send")
    32     public String send() throws IOException {
    33         ConnectionFactory connectionFactory = rabbitConfig.connectionFactory();
    34         Connection connection = connectionFactory.createConnection();
    35         Channel channel = connection.createChannel(false);
    36         //创建队列申明
    37         channel.queueDeclare(RabbitConfig.QUEUE_WORK, false, false, false, null);
    38 
    39         /**
    40          * 每个消费者 发送确认消息之前,消息队列不会发送下一个消息给消费者,一次只处理一个消息
    41          * 自动模式无需设置下面设置
    42          */
    43         int prefetchCount = 1;
    44         channel.basicQos(prefetchCount);
    45 
    46         String Hello = ">>>> Hello Simple <<<<";
    47         for (int i = 0; i < 50; i++) {
    48             String message = Hello + i;
    49             channel.basicPublish("", RabbitConfig.QUEUE_WORK, null, message.getBytes());
    50             LOGGER.info("生产消息: " + message);
    51         }
    52         return "OK";
    53     }
    54 }

    消费者

     1 package com.maozw.mq.work;
     2 
     3 import com.maozw.mq.config.RabbitConfig;
     4 import com.rabbitmq.client.AMQP;
     5 import com.rabbitmq.client.Channel;
     6 import com.rabbitmq.client.DefaultConsumer;
     7 import com.rabbitmq.client.Envelope;
     8 import org.slf4j.Logger;
     9 import org.slf4j.LoggerFactory;
    10 import org.springframework.amqp.rabbit.connection.Connection;
    11 import org.springframework.amqp.rabbit.connection.ConnectionFactory;
    12 
    13 import java.io.IOException;
    14 
    15 /**
    16  * @author MAOZW
    17  * @Description: ${todo}
    18  * @date 2018/11/26 15:06
    19  */
    20 
    21 public class WorkConsumer2 {
    22     private static final Logger LOGGER = LoggerFactory.getLogger(WorkConsumer2.class);
    23 
    24     public static void main(String[] args) throws IOException {
    25         ConnectionFactory connectionFactory = RabbitConfig.getConnectionFactory();
    26         Connection connection = connectionFactory.createConnection();
    27         Channel channel = connection.createChannel(false);
    28         //创建队列申明
    29         channel.queueDeclare(RabbitConfig.QUEUE_WORK, false, false, false, null);
    30 
    31         /**
    32          * 改变分发规则
    33          */
    34         channel.basicQos(1);
    35 
    36         DefaultConsumer consumer = new DefaultConsumer(channel) {
    37             @Override
    38             public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    39                 super.handleDelivery(consumerTag, envelope, properties, body);
    40                 System.out.println("[2] 接口数据 : " + new String(body, "utf-8"));
    41 
    42                 try {
    43                     Thread.sleep(200);
    44                 } catch (InterruptedException e) {
    45                     e.printStackTrace();
    46                 } finally {
    47                     System.out.println("[2] done!");
    48                     //消息应答:手动回执,手动确认消息
    49                     channel.basicAck(envelope.getDeliveryTag(),false);
    50                 }
    51             }
    52         };
    53         //监听队列
    54         /**
    55          * autoAck 消息应答
    56          *  默认轮询分发打开:true :这种模式一旦rabbitmq将消息发送给消费者,就会从内存中删除该消息,不关心客户端是否消费正常。
    57          *  使用公平分发需要关闭autoAck:false  需要手动发送回执
    58          */
    59         boolean autoAck = false;
    60         channel.basicConsume(RabbitConfig.QUEUE_WORK,autoAck, consumer);
    61     }
    62     
    63 }
  • 相关阅读:
    java中的变量
    小程序获取设备信息
    小游戏学习1
    live-server
    Babel安装在本地并用webstrom由ES6转Es5
    测试目录
    使用Java模拟一个简单的Dos学生成绩管理系统:
    RabbitMQ——开源稳定的消息队列
    wpf读写app.config中的数据
    C# Arc Gis实例教程——网络链接
  • 原文地址:https://www.cnblogs.com/Mao-admin/p/10032938.html
Copyright © 2020-2023  润新知