zoukankan      html  css  js  c++  java
  • rabbitmq消息中间件读后感

    四.RabbitMQ安装与使用
    1.安装(rpm方式)
    下载对应版本的Erlang和RabbitMQ。

    安装erlang:

    安装socat密钥:
    要先安装socat再安装rabbitmq,不然会报错。
    安装rabbitmq:

    2.修改配置文件
    vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app
    默认端口号5672

    修改loopback_users:(保留guest也可)


    3.RabbitMq基本命令
    关于服务的操作:

    服务启动:rabbitmqctl start_app / rabbitmq-server start &
    这里有用到主机名,可以通过vim /etc/hostname来配置。

    可以看到rabbitmq已经启动:
    查看服务是否启动 lsof -i:5672

    服务停止:rabbitmqctl stop_app / rabbitmq-server stop
    服务重启:service rabbitmq-server restart
    节点状态:rabbitmqctl status
    启动管理控制台:

    管理插件:rabbitmq-plugins enable rabbitmq_management
    控制台访问地址:http://192.168.58.129:15672
    如果既修改过配置文件,又开启了插件,但是还访问失败,可以看下是不是没有关闭防火墙。
    firewalld的基本使用
    启动: systemctl start firewalld
    关闭: systemctl stop firewalld
    查看状态: systemctl status firewalld
    开机禁用: systemctl disable firewalld
    开机启用: systemctl enable firewalld

    关于用户的操作:

    添加用户:rabbitmqctl add_user username password
    列出所有用户:rabbitmqctl list_users
    删除用户:rabbitmqctl delete_user username
    清除用户权限:rabbitmqctl clear_permissions -p vhostpath username
    列出用户权限:rabbitmqctl list_user_permissions username
    修改密码:rabbitmqctl change_password username newpassword
    设置用户权限:rabbitmqctl set_permissions -p vhostpath username ".*" ".*" ".*"
    关于虚拟主机的操作:

    创建虚拟主机:rabbitmqctl add_vhost vhostpath
    列出所有虚拟主机:rabbitmqctl list_vhost
    列出虚拟主机上所有权限:rabbitmqctl list_permissions -p vhostpath
    删除虚拟主机:rabbitmqctl delete_vhost vhostpath
    关于消息队列的操作:

    查看所有队列信息:rabbitmqctl list_queues
    清除队列里的消息:rabbitmqctl -p vhostpath purge_queue blue
    等等。

    4.RabbitMq高级命令
    rabbitmqctl reset:移除所有数据,要在rabbitmqctl stop_app之后使用。
    组成集群命令:rabbitmqctl join_cluster <clusternode> [--ram] (ram内存级别存储,disc磁盘)
    查看集群状态:rabbitmqctl cluster_status
    修改集群节点的存储形式:rabbitmqctl change_cluster_node_type disc | ram
    忘记(摘除)节点:rabbitmqctl forget_cluster_node [--offline] (offline服务不启动的情况下)
    修改节点名称:rabbitmqctl rename_cluster_node oldnode1 newnode1 [oldnode2] [newnode2 ...]

    1:RabbitMQ是一个开源的消息代理和队列服务器,可以通过基本协议在完全不同的应用之间共享数据,使用Erlang语言开发的,是基于AMQP(高级消息队列协议)协议,Erlang主要用于交换机的开发,有着与原生socket一样的延迟这也是为什么RabbitMQ高性能的原因

    2:AMQP(高级消息队列协议)协议模型

    3:核心概念

    server:又称Broker
    Connection:连接,应用程序和server的连接
    channel:网络信道,几乎所有的操作都在channel上进行的,channel是进行消息读写的通道,每个客户端可以建立多个channel,
    每个channel代表一个会话任务

    message:有Properties和body组成,Properties可以对消息进行修饰如消息的优先级,消息的延迟,body就是消息的内容

    Virtual host:虚拟地址,用于进行逻辑隔离,最上面的消息路由,一个Virtual host可以有若干个exchange和queue,但是一个Virtual host不能有相同的exchange和queue

     exchange:交换机接受消息,根据路由键转发到绑定的队列

    binding:exchange和queue之间的虚拟连接;binding 可以包含routing key

    routing key:路由规则

    4:依赖项目

    <dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>3.6.5</version>
    </dependency>
    5:生产者代码
    public class Procuder {
    public static void main(String[] args) throws Exception {
    //1 创建一个ConnectionFactory, 并进行配置
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    //2 通过连接工厂创建连接
    Connection connection = connectionFactory.newConnection();
    //3 通过connection创建一个Channel
    Channel channel = connection.createChannel();
    //4 通过Channel发送数据
    for (int i = 0; i < 5; i++) {
    String msg = "Hello RabbitMQ!";
    //1 exchange 2 routingKey
    //props是消息的一些属性设置
    //当exchange为空时候会路由到和routingkey一样名字的消息队列
    channel.basicPublish("", "test001", null, msg.getBytes());
    }
    //5 记得要关闭相关的连接
    channel.close();
    connection.close();
    }
    }

    6:消费者

    public class Consumer {
    public static void main(String[] args) throws Exception {
    //1 创建一个ConnectionFactory, 并进行配置
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    //2 通过连接工厂创建连接
    Connection connection = connectionFactory.newConnection();
    //3 通过connection创建一个Channel
    Channel channel = connection.createChannel();
    //4 声明(创建)一个队列
    String queueName = "test001";
    //durable代表声明的队列持久化到服务器上
    //exclusive是否为当前连接的专用队列,在连接断开后,会自动删除该队列
    //autoDelete 当没有任何消费者使用时,自动删除该队列
    //arguments 其他队列配置
    channel.queueDeclare(queueName, true, false, false, null);
    //5 创建消费者
    QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
    //6 设置Channel
    //autoAck
    //自动确认
    //指定autoAck参数,当autoAck=true时,一旦消费者接收到了消息,就视为自动确认了消息。如果消费者在处理消息的过程中,中间出错就无法重新处理该消息,所以需要经常性的在代码里进行手动确认。
    //
    //手动确认
    //需设置autoAck=false,此时RabbitMQ会等待消费者显式发回ACK信号后才从内存(或磁盘)中移去消息,消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题,因为RabbitMQ会一直持有消息直到消费者显式调用basicAck为止。(消息此处会有两份,投递出去的和broke自留的,如果投递未收到Ack且连接断开,broker将会把消息投递给下一个消费者)
    //一般使用手动确认会将消息的处理放在try/catch语句块中,成功处理了,就给MQ一个确认应答,如果处理异常了,就在catch中,进行消息的拒绝。
    channel.basicConsume(queueName, true, queueingConsumer);
    while (true) {
    //7 获取消息
    Delivery delivery = queueingConsumer.nextDelivery();
    String msg = new String(delivery.getBody());
    System.err.println("消费端: " + msg);
    //Envelope envelope = delivery.getEnvelope();
    }
    }
    }

    7:direct exchange consumer

    public class Consumer4DirectExchange {
    public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory() ;
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    //当出现网络抖动的时候尝试重新连接
    connectionFactory.setAutomaticRecoveryEnabled(true);
    connectionFactory.setNetworkRecoveryInterval(3000);
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    //4 声明
    String exchangeName = "test_direct_exchange";
    String exchangeType = "direct";
    String queueName = "test_direct_queue";
    String routingKey = "test.direct";
    //表示声明了一个交换机
    //internal 标识的是是否是内部使用 通常设置为false即可
    channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    //表示声明了一个队列
    channel.queueDeclare(queueName, false, false, false, null);
    //建立一个绑定关系:
    channel.queueBind(queueName, exchangeName, routingKey);
    QueueingConsumer consumer = new QueueingConsumer(channel);
    //参数:队列名称、是否自动ACK、Consumer
    channel.basicConsume(queueName, true, consumer);
    //循环获取消息
    while(true){
    //获取消息,如果没有消息,这一步将会一直阻塞
    Delivery delivery = consumer.nextDelivery();
    String msg = new String(delivery.getBody());
    System.out.println("收到消息:" + msg);
    }
    }
    }

    8:direct exchange product

    public class Producer4DirectExchange {
    public static void main(String[] args) throws Exception {
    //1 创建ConnectionFactory
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    //2 创建Connection
    Connection connection = connectionFactory.newConnection();
    //3 创建Channel
    Channel channel = connection.createChannel();
    //4 声明
    String exchangeName = "test_direct_exchange";
    String routingKey = "test.direct111";
    //5 发送
    String msg = "Hello World RabbitMQ 4 Direct Exchange Message 111 ... ";
    channel.basicPublish(exchangeName, routingKey , null , msg.getBytes());
    }
    }

    9:所有发送到Direct Exchange的消息被转发到RouteKey中指定的Queue。

      Direct模式,可以使用rabbitMQ自带的Exchange:default Exchange 。所以不需要将Exchange进行任何绑定(binding)操作 。消息传递时,RouteKey必须完全匹配,才会被队列接收,否则该消息会被抛弃。

    10:topic exchange 

    一个exchange可以有两个不同的routekey 绑定同一个队列

    routingkey模糊匹配

     consumer
    public class Consumer4TopicExchange {
    public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory() ;
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    connectionFactory.setAutomaticRecoveryEnabled(true);
    connectionFactory.setNetworkRecoveryInterval(3000);
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    //4 声明
    String exchangeName = "test_topic_exchange";
    String exchangeType = "topic";
    String queueName = "test_topic_queue";
    //String routingKey = "user.*";匹配一个词
    //String routingKey = "user.#";匹配多个词
    String routingKey = "user.*";
    // 1 声明交换机
    channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    // 2 声明队列
    channel.queueDeclare(queueName, false, false, false, null);
    // 3 建立交换机和队列的绑定关系:
    channel.queueBind(queueName, exchangeName, routingKey);
    //durable 是否持久化消息
    QueueingConsumer consumer = new QueueingConsumer(channel);
    //参数:队列名称、是否自动ACK、Consumer
    channel.basicConsume(queueName, true, consumer);
    //循环获取消息
    while(true){
    //获取消息,如果没有消息,这一步将会一直阻塞
    Delivery delivery = consumer.nextDelivery();
    String msg = new String(delivery.getBody());
    System.out.println("收到消息:" + msg);
    }
    }
    }

    product

    public class Producer4TopicExchange {
    public static void main(String[] args) throws Exception {
    //1 创建ConnectionFactory
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    //2 创建Connection
    Connection connection = connectionFactory.newConnection();
    //3 创建Channel
    Channel channel = connection.createChannel();
    //4 声明
    String exchangeName = "test_topic_exchange";
    String routingKey1 = "user.save";
    String routingKey2 = "user.update";
    String routingKey3 = "user.delete.abc";
    //5 发送
    String msg = "Hello World RabbitMQ 4 Topic Exchange Message ...";
    channel.basicPublish(exchangeName, routingKey1 , null , msg.getBytes());
    channel.basicPublish(exchangeName, routingKey2 , null , msg.getBytes());
    channel.basicPublish(exchangeName, routingKey3 , null , msg.getBytes());
    channel.close();
    connection.close();
    }
    }

    11:fanout exchange

    不需要要routingkey 路由 发送到所有exchange绑定的队列上 所以性能最高

    Producer
    public class Producer4FanoutExchange {
    public static void main(String[] args) throws Exception {
    //1 创建ConnectionFactory
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    //2 创建Connection
    Connection connection = connectionFactory.newConnection();
    //3 创建Channel
    Channel channel = connection.createChannel();
    //4 声明
    String exchangeName = "test_fanout_exchange";
    //5 发送
    for(int i = 0; i < 10; i ++) {
    String msg = "Hello World RabbitMQ 4 FANOUT Exchange Message ...";
    channel.basicPublish(exchangeName, "", null , msg.getBytes());
    }
    channel.close();
    connection.close();
    }
    }
    Consumer
    public class Consumer4FanoutExchange {
    public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory() ;
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    connectionFactory.setAutomaticRecoveryEnabled(true);
    connectionFactory.setNetworkRecoveryInterval(3000);
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    //4 声明
    String exchangeName = "test_fanout_exchange";
    String exchangeType = "fanout";
    String queueName = "test_fanout_queue";
    String routingKey = ""; //不设置路由键
    channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    channel.queueDeclare(queueName, false, false, false, null);
    channel.queueBind(queueName, exchangeName, routingKey);
    //durable 是否持久化消息
    QueueingConsumer consumer = new QueueingConsumer(channel);
    //参数:队列名称、是否自动ACK、Consumer
    channel.basicConsume(queueName, true, consumer);
    //循环获取消息
    while(true){
    //获取消息,如果没有消息,这一步将会一直阻塞
    Delivery delivery = consumer.nextDelivery();
    String msg = new String(delivery.getBody());
    System.out.println("收到消息:" + msg);
    }
    }
    }

    12:message 相关属性

    product

    Map<String, Object> headers = new HashMap<>();
    headers.put("my1", "111");
    headers.put("my2", "222");
    AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
    .deliveryMode(2)//是否持久化
    .contentEncoding("UTF-8")
    .expiration("10000")//消息过期时间 当过期了会自动抛弃
    .headers(headers)//自定义消息属性
    .build();

    //4 通过Channel发送数据
    for(int i=0; i < 5; i++){
    String msg = "Hello RabbitMQ!";
    //1 exchange 2 routingKey
    channel.basicPublish("", "test001", properties, msg.getBytes());
    }

    consumer

    while(true){
    //7 获取消息
    Delivery delivery = queueingConsumer.nextDelivery();
    String msg = new String(delivery.getBody());
    System.err.println("消费端: " + msg);
    Map<String, Object> headers = delivery.getProperties().getHeaders();
    System.err.println("headers get my1 value: " + headers.get("my1"));

    //Envelope envelope = delivery.getEnvelope();
    }

     13:生产端消息可靠性投递

       (1)保证消息的成功发出(2)保证mq节点的成功接收(3)发送端收到mq节点的确认应答(4)完善的消息补偿机制

    通常有两种方式

      (1)消息落库,对消息状态进行打标

    (2)消息的延迟投递,做二次确认,回调检查

    消息落库方案如下

    消息的延迟投递方案如下

    14:消息重复投递的幂等性问题

    (1)通过唯一ID利用数据库主键去重(高并发情况下通过分库分表来提高性能)

    (2)利用redis原子性去实现()

     15:confirm确认机制

    生产者投递消息后如果broker收到消息就会给生产端一个应答,生产端接收应答用来确认这个消息是否被正常发送到broker这就是消息可靠性投递的核心需要以下两个步骤

    (1)第一步需要在channel上开启确认模式

    //4 指定我们的消息投递模式: 消息的确认模式 
    channel.confirmSelect();

    (2)在channel上添加监听addConfirmListener监听成功和失败的返回结果

    //6 添加一个确认监听
    channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
    System.err.println("-------no ack!-----------");
    }

    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
    System.err.println("-------ack!-----------");
    }
    });
    完整代码如下
    public class Producer {
    public static void main(String[] args) throws Exception {
    //1 创建ConnectionFactory
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    //2 获取C onnection
    Connection connection = connectionFactory.newConnection();
    //3 通过Connection创建一个新的Channel
    Channel channel = connection.createChannel();
    //4 指定我们的消息投递模式: 消息的确认模式
    channel.confirmSelect();
    String exchangeName = "test_confirm_exchange";
    String routingKey = "confirm.save";
    //5 发送一条消息
    String msg = "Hello RabbitMQ Send confirm message!";
    channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
    //6 添加一个确认监听
    channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
    System.err.println("-------no ack!-----------");
    //可能是最大队列已满,磁盘不足等状况下触发
    }
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
    System.err.println("-------ack!-----------");
    }
    });
    }
    }

     16:Return 消息机制

    Return  Listener 用于处理监听一些不可路由的消息(exchange不存在,或者routing key 路由不到)

    mandatory为true,监听器就会接收路由不可达的消息然后做进一步处理
    mandatory为false 那么broker会自动删除消息
    代码如下:
    public class Producer {
    public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    String exchange = "test_return_exchange";
    String routingKey = "return.save";
    String routingKeyError = "abc.save";
    String msg = "Hello RabbitMQ Return Message";
    channel.addReturnListener(new ReturnListener() {
    @Override
    public void handleReturn(int replyCode, String replyText, String exchange,
    String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {

    System.err.println("---------handle return----------");
    System.err.println("replyCode: " + replyCode);
    System.err.println("replyText: " + replyText);
    System.err.println("exchange: " + exchange);
    System.err.println("routingKey: " + routingKey);
    System.err.println("properties: " + properties);
    System.err.println("body: " + new String(body));
    }
    });
    //mandatory 为true时监听器就会接收路由不可达的消息然后做进一步处理,false 会自动删除
          channel.basicPublish(exchange, routingKeyError, true, null, msg.getBytes());
    //channel.basicPublish(exchange, routingKeyError, true, null, msg.getBytes());
    }
    }

     17:消费端自定义监听

    我们通过while循环进行获取消息进行消费处理,我们也可以自定义consumer更加方便解耦性更加强

     public class Consumer {

       public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    String exchangeName = "test_consumer_exchange";
    String routingKey = "consumer.#";
    String queueName = "test_consumer_queue";
    channel.exchangeDeclare(exchangeName, "topic", true, false, null);
    channel.queueDeclare(queueName, true, false, false, null);
    channel.queueBind(queueName, exchangeName, routingKey);
    channel.basicConsume(queueName, true, new MyConsumer(channel));
    }
    }
    MyConsumer代码如下
    public class MyConsumer extends DefaultConsumer {

    public MyConsumer(Channel channel) {
    super(channel);
    }

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    System.err.println("-----------consume message----------");
    System.err.println("consumerTag: " + consumerTag);
    System.err.println("envelope: " + envelope);
    System.err.println("properties: " + properties);
    System.err.println("body: " + new String(body));
    //同一个会话, consumerTag 是固定的 可以做此会话的名字, deliveryTag 每次接收消息+1,可以做此消息处理通道的名字。

       //因此 deliveryTag 可以用来回传告诉 rabbitmq 这个消息处理成功 清除此消息(basicAck方法)


    }

    }


    19:消费端限流
    使用场景:巨量消息瞬间全部推送过来,单个客户端无法处理这么多消息
    在非自动确认消息的前提下。通过设置consumer或者channel设置的qos的值,如果一定数量的消息未被确认就不进行消息的消息
    //1 限流方式  第一件事就是 autoAck设置为 false
    channel.basicQos(0, 1, false);
    channel.basicConsume(queueName, false, new MyConsumer(channel));
    basicQos(int prefetchSize, int prefetchCount, boolean global)
    //global是否将以上设置应用于channel还是consumer级别。

    prefetchSize 和global这两个配置rabbitmq暂时还没实现
    rabbitmq向第一个消费者投递了prefetchCount条消息后,消费者未对prefetchCount条消息进行ack,rabbitmq不会再向该消费者投递消息


    手动签收
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    System.err.println("-----------consume message----------");
    System.err.println("consumerTag: " + consumerTag);
    System.err.println("envelope: " + envelope);
    System.err.println("properties: " + properties);
    System.err.println("body: " + new String(body));

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

    }

    20:消费端ack和nack
    手工签收与重回队列 (一般实际工作中不会重回队列)
    消息消费者手工签收 必须要关闭 autoAck = false
    // 手工签收 必须要关闭 autoAck = false
    channel.basicConsume(queueName, false, new MyConsumer(channel));
    MyConsumer代码如下
    public class MyConsumer extends DefaultConsumer {
    private Channel channel ;
    public MyConsumer(Channel channel) {
    super(channel);
    this.channel = channel;
    }
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    System.err.println("-----------consume message----------");
    System.err.println("body: " + new String(body));
    try {
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    if((Integer)properties.getHeaders().get("num") == 0) {
    channel.basicNack(envelope.getDeliveryTag(), false, true);//true 重回队列
    } else {
    channel.basicAck(envelope.getDeliveryTag(), false);//
    }
    }

    21:TTL队列与消息
    可以在消息上指定消息过期时间,也可以在队列上指定消息过期时间(从消息进入队列开始到指定的超过队列过期时间,
    那么消息会自动的清除,在声明队列时可以指定x-message-ttl消息过期时间作为Map<String, Object> arguments的参数)
    AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
    .deliveryMode(2)
    .contentEncoding("UTF-8")
    .expiration("10000")
    .headers(headers)
    .build();
     22:死信队列使用
    x-max-length 指定队列最大长度
    x-message-ttl 指定队列的消息过期时间
    Map<String, Object> agruments = new HashMap<String, Object>();
    agruments.put("x-dead-letter-exchange", "dlx.exchange");指定当前队列是对应一个exchange为
    dlx.exchange死信的exchange
    当消息在一个队列中变成死信的时候,它能重新被publish另一个exchange中
    这个exchange就是x-dead-letter-exchange指定的
    public class Consumer {
    public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.11.76");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    // 这就是一个普通的交换机 和 队列 以及路由
    String exchangeName = "test_dlx_exchange";
    String routingKey = "dlx.#";
    String queueName = "test_dlx_queue";
    channel.exchangeDeclare(exchangeName, "topic", true, false, null);
    Map<String, Object> agruments = new HashMap<String, Object>();
    agruments.put("x-dead-letter-exchange", "dlx.exchange");
    //这个agruments属性,要设置到声明队列上
    channel.queueDeclare(queueName, true, false, false, agruments);
    channel.queueBind(queueName, exchangeName, routingKey);
    //要进行死信队列的声明:
    channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
    channel.queueDeclare("dlx.queue", true, false, false, null);
    channel.queueBind("dlx.queue", "dlx.exchange", "#");
    channel.basicConsume(queueName, true, new MyConsumer(channel));
    }
    }
    消息变成死信的以下几种情况
        消息被拒绝,并且requeue= false
        消息ttl过期
        队列达到最大的长度

    23:和springboot 整合需要引入以下包
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId
    </dependency>
    
    
     24:RabbitAdmin类可以很好的操作RabbitMQ,在Spring中直接进行注入即可

    (2)autoStartup必须设置true,否则Spring容器不会加载RabbitAdmin类

    (3)RabbitAdmin底层实现就是从Spring容器中获取Exchagge,Bingding,RoutingKey以及Queue的@Bean声明

    (4)然后使用RabbitTemplate的execute方法执行对应声明,修改,删除等操作

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
    RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
    rabbitAdmin.setAutoStartup(true);
    return rabbitAdmin;
    }
     25:RabbitMQConfig 配置 注入rabbitAdmin
    @Configuration
    @ComponentScan({"com.bfxy.spring.*"})
    public class RabbitMQConfig {

    @Bean
    public ConnectionFactory connectionFactory() {

    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setAddresses("192.168.11.76:5672");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setVirtualHost("/");
    return connectionFactory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
    RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
    rabbitAdmin.setAutoStartup(true);
    return rabbitAdmin;
    }
    }

    26:Test使用rabbitAdmin
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ApplicationTests {

    @Test
    public void contextLoads() {
    }

    @Autowired
    private RabbitAdmin rabbitAdmin;

    @Test
    public void testAdmin() throws Exception {
    rabbitAdmin.declareExchange(new DirectExchange("test.direct", false, false));

    rabbitAdmin.declareExchange(new TopicExchange("test.topic", false, false));

    rabbitAdmin.declareExchange(new FanoutExchange("test.fanout", false, false));

    rabbitAdmin.declareQueue(new Queue("test.direct.queue", false));

    rabbitAdmin.declareQueue(new Queue("test.topic.queue", false));

    rabbitAdmin.declareQueue(new Queue("test.fanout.queue", false));

    rabbitAdmin.declareBinding(new Binding("test.direct.queue",
    Binding.DestinationType.QUEUE,
    "test.direct", "direct", new HashMap<>()));

    rabbitAdmin.declareBinding(
    BindingBuilder
    .bind(new Queue("test.topic.queue", false)) //直接创建队列
    .to(new TopicExchange("test.topic", false, false)) //直接创建交换机 建立关联关系
    .with("user.#")); //指定路由Key


    rabbitAdmin.declareBinding(
    BindingBuilder
    .bind(new Queue("test.fanout.queue", false))
    .to(new FanoutExchange("test.fanout", false, false)));

    //清空队列数据
    rabbitAdmin.purgeQueue("test.topic.queue", false);

    }
    }

    27:直接创建对应交换机 & 队列
    @Configuration
    @ComponentScan({"com.bfxy.spring.*"})
    public class RabbitMQConfig {

    @Bean
    public ConnectionFactory connectionFactory() {

    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setAddresses("192.168.11.76:5672");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setVirtualHost("/");
    return connectionFactory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
    RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
    rabbitAdmin.setAutoStartup(true);
    return rabbitAdmin;
    }

    /**
    * 针对消费者配置
    * 1. 设置交换机类型
    * 2. 将队列绑定到交换机
    * FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念
    * HeadersExchange :通过添加属性key-value匹配
    * DirectExchange:按照routingkey分发到指定队列
    * TopicExchange:多关键字匹配
    */
    @Bean
    public TopicExchange exchange001() {
    return new TopicExchange("topic001", true, false);
    }

    @Bean
    public Queue queue001() {
    return new Queue("queue001", true); //队列持久
    }

    @Bean
    public Binding binding001() {
    return BindingBuilder.bind(queue001()).to(exchange001()).with("spring.*");
    }

    @Bean
    public TopicExchange exchange002() {
    return new TopicExchange("topic002", true, false);
    }

    @Bean
    public Queue queue002() {
    return new Queue("queue002", true); //队列持久
    }

    @Bean
    public Binding binding002() {
    return BindingBuilder.bind(queue002()).to(exchange002()).with("rabbit.*");
    }

    @Bean
    public Queue queue003() {
    return new Queue("queue003", true); //队列持久
    }

    @Bean
    public Binding binding003() {
    return BindingBuilder.bind(queue003()).to(exchange001()).with("mq.*");
    }

    @Bean
    public Queue queue_image() {
    return new Queue("image_queue", true); //队列持久
    }

    @Bean
    public Queue queue_pdf() {
    return new Queue("pdf_queue", true); //队列持久
    }


    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
    RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    return rabbitTemplate;
    }
    }
    28:测试
    直接创建对应交换机 & 队列通过rabbitTemplate发送消息
    它提供了丰富的发送消息方法,包括可靠性投递消息方法、回调监听消息接口 ConfirmCallback、
    返回值确认接口 ReturnCallback 等等。同样我们需要进行注入到 Spring 容器中,然后直接使用。
    RabbitTemplate  Spring 整合时需要实例化,但是 Springboot 整合时,配置文件里添加配置即可
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void testSendMessage() throws Exception {
    //1 创建消息
    MessageProperties messageProperties = new MessageProperties();
    messageProperties.getHeaders().put("desc", "信息描述..");
    messageProperties.getHeaders().put("type", "自定义消息类型..");
    Message message = new Message("Hello RabbitMQ".getBytes(), messageProperties);
    rabbitTemplate.convertAndSend("topic001", "spring.amqp", message, new MessagePostProcessor() {
    @Override
    public Message postProcessMessage(Message message) throws AmqpException {
    System.err.println("------添加额外的设置---------");
    message.getMessageProperties().getHeaders().put("desc", "额外修改的信息描述");
    message.getMessageProperties().getHeaders().put("attr", "额外新加的属性");
    return message;
    }
    });
    }
    @Test
    public void testSendMessage2() throws Exception {
    //1 创建消息
    MessageProperties messageProperties = new MessageProperties();
    messageProperties.setContentType("text/plain");
    Message message = new Message("mq 消息1234".getBytes(), messageProperties);
    rabbitTemplate.send("topic001", "spring.abc", message);
    rabbitTemplate.convertAndSend("topic001", "spring.amqp", "hello object message send!");
    rabbitTemplate.convertAndSend("topic002", "rabbit.abc", "hello object message send!");
    }

    29:
    SimpleMessageListenerContailer

    (1)简单消息监听容器:这个类非常的强大,我们可以对他进行很多设置,对于消费者的配置项,这个类都可以满足

    (2)设置事务特性,事务管理器,事务属性,事务容量,事务开启等

    (3)设置消息确认和自动确认模式,是否重回队列,异常捕获handler函数

    (4)设置消费者标签生成策略,是否独占模式,消费者属性等

    (5)simpleMessageListenerContailer可以进行动态设置,比如在运行中的应用可以动态的修改其消费者数量的大小,接收消息的模式等

    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
    container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
    container.setConcurrentConsumers(1);
    container.setMaxConcurrentConsumers(5);
    container.setDefaultRequeueRejected(false);
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    container.setExposeListenerChannel(true);
    container.setConsumerTagStrategy(new ConsumerTagStrategy() {
    @Override
    public String createConsumerTag(String queue) {
    return queue + "_" + UUID.randomUUID().toString();
    }
    });


    container.setMessageListener(new ChannelAwareMessageListener() {
    @Override public void onMessage(Message message, Channel channel) throws Exception {
    String msg = new String(message.getBody());
    System.err.println("----------消费者: " + msg);
    }
    });
    }

    30:MessageListenerAdapter 适配器

       适配器方式. 默认是有自己的方法名字的:handleMessage
    // 可以自己指定一个方法的名字: consumeMessage
    // 也可以添加一个转换器: 从字节数组转换为String
    MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
    adapter.setDefaultListenerMethod("consumeMessage");
    adapter.setMessageConverter(new TextMessageConverter());
    container.setMessageListener(adapter);

    MessageDelegate 类
    public class MessageDelegate {

    public void handleMessage(byte[] messageBody) {
    System.err.println("默认方法, 消息内容:" + new String(messageBody));
    }

    public void consumeMessage(byte[] messageBody) {
    System.err.println("字节数组方法, 消息内容:" + new String(messageBody));
    }

    public void consumeMessage(String messageBody) {
    System.err.println("字符串方法, 消息内容:" + messageBody);
    }
    }
    TextMessageConverter 类
    public class TextMessageConverter implements MessageConverter {
    @Override
    public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
    return new Message(object.toString().getBytes(), messageProperties);
    }
    @Override
    public Object fromMessage(Message message) throws MessageConversionException {
    String contentType = message.getMessageProperties().getContentType();
    if(null != contentType && contentType.contains("text")) {
    return new String(message.getBody());
    }
    return message.getBody();
    }
    }

    31:配器方式: 我们的队列名称 和 方法名称 也可以进行一一的匹配
    MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
    adapter.setMessageConverter(new TextMessageConverter());
    Map<String, String> queueOrTagToMethodName = new HashMap<>();
    queueOrTagToMethodName.put("queue001", "method1");
    queueOrTagToMethodName.put("queue002", "method2");
    adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
    container.setMessageListener(adapter);
    32:支持json格式的转换器

    MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
    adapter.setDefaultListenerMethod("consumeMessage");

    Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
    adapter.setMessageConverter(jackson2JsonMessageConverter);

    container.setMessageListener(adapter);



    public class MessageDelegate {


    public void consumeMessage(Map messageBody) {
    System.err.println("map方法, 消息内容:" + messageBody);
    }
    }
    @Test
    public void testSendJsonMessage() throws Exception {
    Order order = new Order();
    order.setId("001");
    order.setName("消息订单");
    order.setContent("描述信息");
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(order);
    System.err.println("order 4 json: " + json);

    MessageProperties messageProperties = new MessageProperties();
    //这里注意一定要修改contentType为 application/json
    messageProperties.setContentType("application/json");
    Message message = new Message(json.getBytes(), messageProperties);

    rabbitTemplate.send("topic001", "spring.order", message);
    }

    32:DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter 支持java对象转换

    MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
    adapter.setDefaultListenerMethod("consumeMessage");

    Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();

    DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
    jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);

    adapter.setMessageConverter(jackson2JsonMessageConverter);
    container.setMessageListener(adapter);



    public class MessageDelegate {
       public void consumeMessage(Order order) {
    System.err.println("order对象, 消息内容, id: " + order.getId() +
    ", name: " + order.getName() +
    ", content: "+ order.getContent());
    }

    }

    @Test
    public void testSendJavaMessage() throws Exception {

    Order order = new Order();
    order.setId("001");
    order.setName("订单消息");
    order.setContent("订单描述信息");
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(order);
    System.err.println("order 4 json: " + json);

    MessageProperties messageProperties = new MessageProperties();
    //这里注意一定要修改contentType为 application/json
    messageProperties.setContentType("application/json");
    messageProperties.getHeaders().put("__TypeId__", "com.bfxy.spring.entity.Order");
    Message message = new Message(json.getBytes(), messageProperties);

    rabbitTemplate.send("topic001", "spring.order", message);
    }

    33:DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter 支持java对象多映射转换

    MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
    adapter.setDefaultListenerMethod("consumeMessage");
    Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
    DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();

    Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
    idClassMapping.put("order", com.bfxy.spring.entity.Order.class);
    idClassMapping.put("packaged", com.bfxy.spring.entity.Packaged.class);

    javaTypeMapper.setIdClassMapping(idClassMapping);

    jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
    adapter.setMessageConverter(jackson2JsonMessageConverter);
    container.setMessageListener(adapter);


    public class MessageDelegate {
      
    public void consumeMessage(Order order) {
    System.err.println("order对象, 消息内容, id: " + order.getId() +
    ", name: " + order.getName() +
    ", content: "+ order.getContent());
    }

    public void consumeMessage(Packaged pack) {
    System.err.println("package对象, 消息内容, id: " + pack.getId() +
    ", name: " + pack.getName() +
    ", content: "+ pack.getDescription());
    }
    }

    @Test
    public void testSendMappingMessage() throws Exception {

    ObjectMapper mapper = new ObjectMapper();

    Order order = new Order();
    order.setId("001");
    order.setName("订单消息");
    order.setContent("订单描述信息");

    String json1 = mapper.writeValueAsString(order);
    System.err.println("order 4 json: " + json1);

    MessageProperties messageProperties1 = new MessageProperties();
    //这里注意一定要修改contentType为 application/json
    messageProperties1.setContentType("application/json");
    messageProperties1.getHeaders().put("__TypeId__", "order");
    Message message1 = new Message(json1.getBytes(), messageProperties1);
    rabbitTemplate.send("topic001", "spring.order", message1);

    Packaged pack = new Packaged();
    pack.setId("002");
    pack.setName("包裹消息");
    pack.setDescription("包裹描述信息");

    String json2 = mapper.writeValueAsString(pack);
    System.err.println("pack 4 json: " + json2);

    MessageProperties messageProperties2 = new MessageProperties();
    //这里注意一定要修改contentType为 application/json
    messageProperties2.setContentType("application/json");
    messageProperties2.getHeaders().put("__TypeId__", "packaged");
    Message message2 = new Message(json2.getBytes(), messageProperties2);
    rabbitTemplate.send("topic001", "spring.pack", message2);
    }

    34:配置全局的转换器
    MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
    adapter.setDefaultListenerMethod("consumeMessage");
    //全局的转换器:
    ContentTypeDelegatingMessageConverter convert = new ContentTypeDelegatingMessageConverter();
    TextMessageConverter textConvert = new TextMessageConverter();
    convert.addDelegate("text", textConvert);
    convert.addDelegate("html/text", textConvert);
    convert.addDelegate("xml/text", textConvert);
    convert.addDelegate("text/plain", textConvert);
    Jackson2JsonMessageConverter jsonConvert = new Jackson2JsonMessageConverter();
    convert.addDelegate("json", jsonConvert);
    convert.addDelegate("application/json", jsonConvert);

    ImageMessageConverter imageConverter = new ImageMessageConverter();
    convert.addDelegate("image/png", imageConverter);
    convert.addDelegate("image", imageConverter);

    PDFMessageConverter pdfConverter = new PDFMessageConverter();
    convert.addDelegate("application/pdf", pdfConverter);
    adapter.setMessageConverter(convert);
    container.setMessageListener(adapter);
    return container;
    35:图片转换器
    public class ImageMessageConverter implements MessageConverter {

    @Override
    public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
    throw new MessageConversionException(" convert error ! ");
    }

    @Override
    public Object fromMessage(Message message) throws MessageConversionException {
    System.err.println("-----------Image MessageConverter----------");

    Object _extName = message.getMessageProperties().getHeaders().get("extName");
    String extName = _extName == null ? "png" : _extName.toString();

    byte[] body = message.getBody();
    String fileName = UUID.randomUUID().toString();
    String path = "d:/010_test/" + fileName + "." + extName;
    File f = new File(path);
    try {
    Files.copy(new ByteArrayInputStream(body), f.toPath());
    } catch (IOException e) {
    e.printStackTrace();
    }
    return f;
    }

    }


        @Test
    public void testSendExtConverterMessage() throws Exception {
    // byte[] body = Files.readAllBytes(Paths.get("d:/002_books", "picture.png"));
    // MessageProperties messageProperties = new MessageProperties();
    // messageProperties.setContentType("image/png");
    // messageProperties.getHeaders().put("extName", "png");
    // Message message = new Message(body, messageProperties);
    // rabbitTemplate.send("", "image_queue", message);

    byte[] body = Files.readAllBytes(Paths.get("d:/002_books", "mysql.pdf"));
    MessageProperties messageProperties = new MessageProperties();
    messageProperties.setContentType("application/pdf");
    Message message = new Message(body, messageProperties);
    rabbitTemplate.send("", "pdf_queue", message);
    }


    36:spring boot整合 rabbitmq
    producer端代码
    @Component
    public class RabbitSender {

    //自动注入RabbitTemplate模板类
    @Autowired
    private RabbitTemplate rabbitTemplate;

    //回调函数: confirm确认
    final ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
    System.err.println("correlationData: " + correlationData);
    System.err.println("ack: " + ack);
    if(!ack){
    System.err.println("异常处理....");
    }
    }
    };

    //回调函数: return返回
    final ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
    @Override
    public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText,
    String exchange, String routingKey) {
    System.err.println("return exchange: " + exchange + ", routingKey: "
    + routingKey + ", replyCode: " + replyCode + ", replyText: " + replyText);
    }
    };

    //发送消息方法调用: 构建Message消息
    public void send(Object message, Map<String, Object> properties) throws Exception {
    MessageHeaders mhs = new MessageHeaders(properties);
    Message msg = MessageBuilder.createMessage(message, mhs);
    rabbitTemplate.setConfirmCallback(confirmCallback);
    rabbitTemplate.setReturnCallback(returnCallback);
    //id + 时间戳 全局唯一
    CorrelationData correlationData = new CorrelationData("1234567890");
    rabbitTemplate.convertAndSend("exchange-1", "springboot.abc", msg, correlationData);
    }

    //发送消息方法调用: 构建自定义对象消息
    public void sendOrder(Order order) throws Exception {
    rabbitTemplate.setConfirmCallback(confirmCallback);
    rabbitTemplate.setReturnCallback(returnCallback);
    //id + 时间戳 全局唯一
    CorrelationData correlationData = new CorrelationData("0987654321");
    rabbitTemplate.convertAndSend("exchange-2", "springboot.def", order, correlationData);
    }

    }
     测试代码
    @Autowired
    private RabbitSender rabbitSender;

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    @Test
    public void testSender1() throws Exception {
    Map<String, Object> properties = new HashMap<>();
    properties.put("number", "12345");
    properties.put("send_time", simpleDateFormat.format(new Date()));
    rabbitSender.send("Hello RabbitMQ For Spring Boot!", properties);
    }

    @Test
    public void testSender2() throws Exception {
    Order order = new Order("001", "第一个订单");
    rabbitSender.sendOrder(order);
    }
     
    36:spring boot整合 rabbitmq
    consumer端代码
    @Component
    public class RabbitReceiver {


    @RabbitListener(bindings = @QueueBinding(
    value = @Queue(value = "queue-1",
    durable="true"),
    exchange = @Exchange(value = "exchange-1",
    durable="true",
    type= "topic",
    ignoreDeclarationExceptions = "true"),
    key = "springboot.*"
    )
    )
    @RabbitHandler
    public void onMessage(Message message, Channel channel) throws Exception {
    System.err.println("--------------------------------------");
    System.err.println("消费端Payload: " + message.getPayload());
    Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
    //手工ACK
    channel.basicAck(deliveryTag, false);
    }


    /**
    *
    * spring.rabbitmq.listener.order.queue.name=queue-2
    spring.rabbitmq.listener.order.queue.durable=true
    spring.rabbitmq.listener.order.exchange.name=exchange-1
    spring.rabbitmq.listener.order.exchange.durable=true
    spring.rabbitmq.listener.order.exchange.type=topic
    spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions=true
    spring.rabbitmq.listener.order.key=springboot.*
    * @param order
    * @param channel
    * @param headers
    * @throws Exception
    */
    @RabbitListener(bindings = @QueueBinding(
    value = @Queue(value = "${spring.rabbitmq.listener.order.queue.name}",
    durable="${spring.rabbitmq.listener.order.queue.durable}"),
    exchange = @Exchange(value = "${spring.rabbitmq.listener.order.exchange.name}",
    durable="${spring.rabbitmq.listener.order.exchange.durable}",
    type= "${spring.rabbitmq.listener.order.exchange.type}",
    ignoreDeclarationExceptions = "${spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions}"),
    key = "${spring.rabbitmq.listener.order.key}"
    )
    )
    @RabbitHandler
    public void onOrderMessage(@Payload com.bfxy.springboot.entity.Order order,
    Channel channel,
    @Headers Map<String, Object> headers) throws Exception {
    System.err.println("--------------------------------------");
    System.err.println("消费端order: " + order.getId());
    Long deliveryTag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG);
    //手工ACK
    channel.basicAck(deliveryTag, false);
    }
    }
    37:集群模式 
    主备模式

    实现RabbitMQ的高可用集群,一般在并发和数据量不高的情况下,这种模式非常的好且简单。主备模式也称为Warren模式

    主节点如果挂了,备节点提供服务而已,和activemq利用zookeeper做主/备一样

     
    • (主节点如果挂了,从节点提供服务而已,和activemq利用zookeeper做主/备一样)
     
     
    • HaProxy配置:
      listen rabbitmq_cluster
      bind 0.0.0.0:5672 
      mode tcp  #配置TCP模式
      balance roundrobin #简单的轮询
      server bhz76 192.168.11.12:5672 check inter 5000 rise 2 fall 3 #主节点
      server bhz77 192.168.11.13:5672 backup check inter 5000 rise 2 fall 3 #备用节点
    

    备注:rabbitmq集群节点配置 #inter 每隔5秒对mq集群做健康检查,2次正确证明服务器可用,3次失败证明服务器不可用,并且配置主备机制

    镜像模式(常用)

    • 镜像模式:集群模式非常经典的就是Mirror镜像模式,保证100%数据不丢失,在实际工作中用的最多的。并且实现集群非常的简单,一般互联网大厂都会构建这种镜像集群模式。

    • Mirror镜像队列,目的是为了保证rabbitmq数据的高可靠性解决方案,主要就是实现数据的同步,一般来讲是2-3个实现数据同步(对于100%数据可靠性解决方案一般是3个节点)集群架构如下:

     
     

    多活模式

    • 多活模式:这种模式也是实现异地数据复制的主流模式,因为Shovel模式配置比较复杂,所以一般来说实现异地集群都是使用双活或者多活模式来实现的。这种模式需要依赖rabbitmq的federation插件,可以实现继续的可靠AMQP数据通信,多活模式在实际配置与应用非常的简单。

    • RabbitMQ部署架构采用双中心模式(多中心),那么在两套(或多套)数据中心中各部署一套RabbitMQ集群,各中心之间还需要实现部分队列消息共享。多活集群架构如下:

     
     
    • Federation插件是一个不需要构建Cluster,而在Brokers之间传输消息的高性能插件,Federation插件可以在Brokers或者Cluster之间传输消息,连接双方可以使用不同的users和vistual hosts,双方也可以使用版本不同的RabbitMQ和Erlang。Federation插件使用AMQP协议通信,可以接收不连续的传输。
     
     
    • Federation Exchanges,可以看成Downstream从Upstream主动拉取消息,但并不是拉取所有消息,必须是在Downstream上已经明确定义Bindings关系的Exchange,也就是有实际的物理Queue来接收消息,才会从Upstream拉取消息到Downstream。使用AMQP协议实施代理间通信,Downstream会将绑定关系组合在一起,绑定/解绑命令将会发送到Upstream交换机。因此,FederationExchange只接收具有订阅的消息。

    RabbitMQ集群镜像模式从0到1

    • RabbitMQ集群环境节点说明


       
       

    详细步骤:
    RabbitMQ镜像集群搭建步骤

    • HAProxy是一款提供高可用性、负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,支持虚拟主机,他是免费、快速并且可靠的一种解决方案。HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在时下的硬件上,完全可以支撑数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中,同时可以保护你的web服务器不被暴露到网络上。
    • HAProxy借助于OS上几种常见的技术来实现性能的最大化:
    1. 单进程、时间驱动模型显著降低上下文切换的开销及内存占用
    2. 在任何可用的情况下,单缓冲(single buffering)机制能以不复制任何数据的方式完成读写操作,这会节约大量的CPU时钟周期及内存带宽
    3. 借助于Linux2.6上的splice()系统调用,HAProxy可以实现零复制转发(Zero-copy- forwarding),在linux3.5及以上的OS上还可以实现零复制启动(zero-starting)
    • KeepAlived软件主要是通过VRRP协议实现高可用功能的。VRRP是Virtual Router RedundancyProtocol(虚拟路由器冗余协议)的缩写,VRRP出现的目的就是为了解决静态路由单点故障问题的,它能保证党个别节点宕机时,整个网络可以不间断地运行,所以,KeepAlived一方面具有配置管理LVS的功能,同时还具备对LVS下面节点进行健康检查差的功能,另一方面可实现系统网络服务的高可用功能。

    • KeepAlived服务的三个重要功能:

      1. 管理LVS负载均衡软件
      2. 实现LVS集群节点的健康检查
      3. 作为系统网络服务的高可用性(failover)
    • KeepAlived高可用原理
      KeepAlived高可用服务对之间的故障转移,是通过VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议)来实现的。在KeepAlived服务正常工作是,主Master节点会不断地向备节点发送(多播的方式)心跳消息,用以告诉备Backup节点自己还活着,当主master节点发生故障时,就无法发送心跳消息,备节点也就因此无法继续监测到来自主Master节点的心跳了,于是调用自身的接管程序,接管主Master节点 的IP资源及服务。当主Master节点恢复时,备Backup节点又会释放主节点故障时自身接管的IP资源和服务,恢复到原来的备用角色。



     
     40: 延迟插件

    声明exchange类型为x-delayed-message,
    声明queue的参数中增加x-dead-letter-exchange,
    消息头部增加x-delay参数,
     41:消息发送模式
    迅速消息 不做可靠性投递 不做消息落库
    批量消息发送 把消息放到一个集合里面统一进行提交
    延迟消息发送
    顺序消息发送
    必须保证所有消息投递到一个队列中且这个队列只能有一个消费者独占模式
  • 相关阅读:
    JS定时循环
    JS分组
    中位数 题解
    NOIP2017 D2T3 题解
    CF949E Binary Cards 题解
    友善的树形DP
    300英雄的危机(heroes)
    [北京省选集训2019]图的难题 题解
    洛谷 P1268 树的重量 题解
    洛谷 P2633 Count on a tree 题解
  • 原文地址:https://www.cnblogs.com/zyy1688/p/9627014.html
Copyright © 2011-2022 走看看