RabbitMq解析1

一、介绍
号称

RabbitMQ is lightweight and easy to deploy on premises and in the cloud. It supports multiple messaging protocols. RabbitMQ can be deployed in distributed and federated configurations to meet high-scale, high-availability requirements.
RabbitMQ is lightweight and easy to deploy on premises and in the cloud. It supports multiple messaging protocols. RabbitMQ can be deployed in distributed and federated configurations to meet high-scale, high-availability requirements.

RabbitMq是用erlang实现的,对高并发处理有优势,支持的语言也特别多:

  • Python
  • Java
  • Ruby
  • PHP
  • C#
  • JavaScript
  • Go
  • Elixir
  • Objective-C
  • Swift

二、部署
1.window安装
RabbitMq 如同redis一样 他也是采用c/s架构,由服务端 与客户端组成。
我们现在我们计算机上部署他的服务端。
1)安装包
RabbitMQ服务端是由Erlang语言编写所以我们这里先下载Erlang语言的环境。

百度云盘链接erlang,提取码:ui44

再官网下载相应的服务端安装包

百度云盘链接服务端安装包,提取码:fx69

注意:先安装erlang,再安装相应的RabbitMq服务端。否则报错
2)管理工具
RabbitMQ安装会附带一个管理工具,方便我们能直观的查看整个RabbitMQ的运行状态和详细数据等。
安装目录的 RabbitMQ Server\rabbitmq_server-3.7.14\sbin 目录下面 执行一条cmd命令:

    rabbitmq-plugins enable rabbitmq_management

3)访问
开启管理服务。便可以通过本地浏览器访问:http://localhost:15672 ,查看整个RabbitMQ的运行状态和详细数据(默认本地访问权限的用户:guest,密码:guest。生产时,一般删除该用户,需要新增自定义用户)
三、快速开始
1.依赖

<dependency>
	<groupId>com.rabbitmq</groupId>
	<artifactId>amqp-client</artifactId>
	<version>5.1.2</version>
</dependency>

2.连接
这里做一个连接工厂

public class ConnectionUtil {
    public static final String QUEUE_NAME = "testQueue";
    public static final String EXCHANGE_NAME = "exchange";

    public static Connection getConnection() throws Exception {
        //创建一个连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置rabbitmq 服务端所在地址 我这里在本地就是本地
        connectionFactory.setHost("127.0.0.1");
        //设置端口号,连接用户名,虚拟地址等
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("jojo");
        connectionFactory.setPassword("jojo");
        //连接串示例:amqp://jojo@127.0.0.1:5672/testhost
        connectionFactory.setVirtualHost("testhost");
        return connectionFactory.newConnection();
    }

3.生产者
这里采用fanout的分发策略

public class Producer {

    public static void main(String[] args) {
        try {
            sendByExchange("hello");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void sendByExchange(String message) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        // 第二个参数表示队列是否持久化(跟消息持久化不同),
        // 第三个参数表示是否是排他队列,表示这个队列只能绑定在这个链接上。
        // 第四个参数,表示是否队列为空时删除,一般结合第三个参数为ture使用true
        // 第五个参数为队列配置参数集合
        channel.queueDeclare(ConnectionUtil.QUEUE_NAME, true, false, false, null);
        // 声明exchange
        channel.exchangeDeclare(ConnectionUtil.EXCHANGE_NAME, "fanout");
        //交换机和队列绑定
        channel.queueBind(ConnectionUtil.QUEUE_NAME, ConnectionUtil.EXCHANGE_NAME, "");
        //basicPublish参数:1交换机 2路由键 3AMQP基本参数 4消息内容字节数组
        channel.basicPublish(ConnectionUtil.EXCHANGE_NAME, "", null,
                message.getBytes());
        System.out.println("发送的信息为:" + message);
        channel.close();
        connection.close();
    }
}

4.消费者

public class Consumer {

    public static void main(String[] args) {
        try {
            getMessage();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
   public static void getMessage() throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        // channel.queueDeclare(ConnectionUtil.QUEUE_NAME,true,false,false,null);
        DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body, "UTF-8"));
                //表示消息消费成功
                System.out.println("消息消费成功");
                //手工确认,第一个参数是消息的id,第二个参数是批量标识(ture标识批量)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //消费,消息从ready状态,变成unacked状态
		//channel.basicConsume(ConnectionUtil.QUEUE_NAME, deliverCallback);
        //消费自动确认,一旦选择这种模式,消费发送到消费端,不管消息是否被消费端正常消费,都会将队列中的消息删除掉
		//channel.basicConsume(ConnectionUtil.QUEUE_NAME,true, deliverCallback);
        //手工确认,结合上面的handleDelivery中channel.basicAsc
        channel.basicConsume(ConnectionUtil.QUEUE_NAME,fasle, deliverCallback);
    }
}

四、消息丢失
1.queue中消息,有ready,unacked,total属性.
当生产者生产消息后,队列中的消息状态为reading.一旦消费者消费消息,就会将消息状态更改为unacked,一旦消费端确认后,消息消费成功,这三个属性都会清除。

在这里插入图片描述
如消费者消费消息采用自动确认,name消息会自动从队列中删除。

 channel.basicConsume(ConnectionUtil.QUEUE_NAME, true,deliverCallback);

但是消费端并没有正常消费,比如报错。那么就会出现消息丢失的情况。

所以一般讲手工确认,autoack属性改为false。当消费端消费消息时。消息从ready状态变成unacked状态。断开连接,宕机等情况,消息状态从unacked重回ready.一旦手工确认,就会将消息从队列中删除。
如果一直没有确认,就会出现消息堆积的问题。

五、分发规则
1.fanout
fanout
注:上面队列3连接的是消费者3
生产者

public static void sendByExchange(String message) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明exchange
        channel.exchangeDeclare("exchangeTest", BuiltinExchangeType.FANOUT);
        //声明队列
        // 第二个参数表示队列是否持久化(跟消息持久化不同),
        // 第三个参数表示是否是排他队列,表示这个队列只能绑定在这个链接上。
        // 第四个参数,表示是否队列为空时删除,一般结合第三个参数为ture使用true
        // 第五个参数为队列配置参数集合
        channel.queueDeclare("queue1", true, false, false, null);
        channel.queueDeclare("queue2", true, false, false, null);
        channel.queueDeclare("queue3", true, false, false, null);
        //交换机和队列绑定
        channel.queueBind("queue1", "exchangeTest", "");
        channel.queueBind("queue2", "exchangeTest", "");
        channel.basicPublish("exchangeTest", "", null,
                message.getBytes());
        System.out.println("发送的信息为:" + message);
        channel.close();
        connection.close();
    }

消费者1

  public static void getMessage() throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body, "UTF-8"));
                //表示消息消费成功
                System.out.println("消费者1消费消息成功");
                //手工确认,第一个参数是消息的id,第二个参数是批量标识(ture标识批量)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //消费,消息从ready状态,变成unacked状态
//        channel.basicConsume(ConnectionUtil.QUEUE_NAME, deliverCallback);
        //消费自动确认,一旦选择这种模式,消费发送到消费端,不管消息是否被消费端正常消费,都会将队列中的消息删除掉
//        channel.basicConsume(ConnectionUtil.QUEUE_NAME,true, deliverCallback);
        //手工确认
        channel.basicConsume("queue1",false, deliverCallback);
    }

消费者2

 public static void getMessage() throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        // channel.queueDeclare(ConnectionUtil.QUEUE_NAME,true,false,false,null);
        DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body, "UTF-8"));
                //表示消息消费成功
                System.out.println("消费者2消息消费成功");
                //手工确认,第一个参数是消息的id,第二个参数是批量标识(ture标识批量)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //消费,消息从ready状态,变成unacked状态
//        channel.basicConsume(ConnectionUtil.QUEUE_NAME, deliverCallback);
        //消费自动确认,一旦选择这种模式,消费发送到消费端,不管消息是否被消费端正常消费,都会将队列中的消息删除掉
//        channel.basicConsume(ConnectionUtil.QUEUE_NAME,true, deliverCallback);
        //手工确认
        channel.basicConsume("queue2",false, deliverCallback);
    }

消费者3

public static void getMessage() throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        // channel.queueDeclare(ConnectionUtil.QUEUE_NAME,true,false,false,null);
        DefaultConsumer deliverCallback = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body, "UTF-8"));
                //表示消息消费成功
                System.out.println("消费者3消息消费成功");
                //手工确认,第一个参数是消息的id,第二个参数是批量标识(ture标识批量)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //消费,消息从ready状态,变成unacked状态
//        channel.basicConsume(ConnectionUtil.QUEUE_NAME, deliverCallback);
        //消费自动确认,一旦选择这种模式,消费发送到消费端,不管消息是否被消费端正常消费,都会将队列中的消息删除掉
//        channel.basicConsume(ConnectionUtil.QUEUE_NAME,true, deliverCallback);
        //手工确认
        channel.basicConsume("queue3",false, deliverCallback);
    }

执行结果:
启动三个消费者监听消息队列,然后启动生产者
发现生产者发送一个消息:hello.
只能在消费者1和消费者2中获取获取到消息,而消费者3没能接受到消息
2.direct
direct
生产者

  public static void sendByExchange(String message) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明exchange
        //因为前面对exchangeTest有定义,直接修改类型会报错,所以要删除交换机重新创建
        channel.exchangeDelete("exchangeTest");
        channel.exchangeDeclare("exchangeTest", BuiltinExchangeType.DIRECT);
        //声明队列
        // 第二个参数表示队列是否持久化(跟消息持久化不同),
        // 第三个参数表示是否是排他队列,表示这个队列只能绑定在这个链接上。
        // 第四个参数,表示是否队列为空时删除,一般结合第三个参数为ture使用true
        // 第五个参数为队列配置参数集合
        channel.queueDeclare("queue1", true, false, false, null);
        channel.queueDeclare("queue2", true, false, false, null);
        channel.queueDeclare("queue3", true, false, false, null);
        //交换机和队列绑定
        channel.queueBind("queue1", "exchangeTest", "info.order");
        channel.queueBind("queue2", "exchangeTest", "debug.order");
        channel.queueBind("queue3", "exchangeTest", "error.order");
        channel.basicPublish("exchangeTest", "info.order", null,
                message.getBytes());
        System.out.println("发送的信息为:" + message);
        channel.close();
        connection.close();
    }

执行情况,只有消费者1能获取到消息
3.topic
topic
生产者

public static void sendByExchange(String message) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明exchange
        channel.exchangeDelete("exchangeTest");
        channel.exchangeDeclare("exchangeTest", BuiltinExchangeType.TOPIC);
        //声明队列
        // 第二个参数表示队列是否持久化(跟消息持久化不同),
        // 第三个参数表示是否是排他队列,表示这个队列只能绑定在这个链接上。
        // 第四个参数,表示是否队列为空时删除,一般结合第三个参数为ture使用true
        // 第五个参数为队列配置参数集合
        channel.queueDeclare("queue1", true, false, false, null);
        channel.queueDeclare("queue2", true, false, false, null);
        channel.queueDeclare("queue3", true, false, false, null);
        //交换机和队列绑定
        channel.queueBind("queue1", "exchangeTest", "#");
        channel.queueBind("queue2", "exchangeTest", "info.order");
        channel.queueBind("queue3", "exchangeTest", "info.order.*");
        channel.basicPublish("exchangeTest", "info.order.a", null,
                message.getBytes());
        System.out.println("发送的信息为:" + message);
        channel.close();
        connection.close();
    }

执行情况:只有消费者1和消费者3能获取到消息

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值