RabbitMQ基础教程之基本使用篇

RabbitMQ基础教程之基本使用篇

最近因为工作原因使用到RabbitMQ,之前也接触过其他的mq消息中间件,从实际使用感觉来看,却不太一样,正好趁着周末,可以好好看一下RabbitMQ的相关知识点;希望可以通过一些学习,可以搞清楚以下几点

  • 基础环境搭建
  • 可以怎么使用
  • 实现原理是怎样的
  • 实际工程中的使用(比如结合SpringBoot可以怎么玩)

相关博文,欢迎查看:

I. 前提准备

在开始之前,先得搭建基本的环境,因为个人主要是mac进行的开发,所有写了一篇mac上如何安装rabbitmq的教程,可以通过 《mac下安装和测试rabbitmq》 查看

1. Centos安装过程

下面简单说一下Linux系统下,可以如何安装

Centos 系统:

# 安装erlang
rpm -Uvh http://download.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm
yum install erlang

# 安装RabbitMQ
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-3.6.6-1.el7.noarch.rpm
yum install rabbitmq-server-3.6.6-1.el7.noarch.rpm

启动和查看的命令

# 完成后启动服务:
service rabbitmq-server start
# 可以查看服务状态:
service rabbitmq-server status

2. 注意

  • 安装完毕之后,可以开启控制台,主要就是 rabbitmq-plugins enable rabbitmq_management, 默认的端口号为15672
  • 默认分配的用户/密码为: guest/guest, 只允许本地访问;如果跨应用读写数据时,请添加账号和设置对应的权限(推荐参考上面mac安装的博文,里面有介绍)

II. 基本使用篇

直接使用amqp-client客户端做基本的数据读写,先不考虑Spring容器的场景,我们可以怎样进行塞数据,然后又怎样可以从里面获取数据;

在实际使用之前,有必要了解一下RabbitMQ的几个基本概念,即什么是Queue,Exchange,Binding,关于这些基本概念,可以参考博文:

1. 基本使用姿势

首先是建立连接,一般需要设置服务器的IP,端口号,用户名密码之类的,公共代码如下

public class RabbitUtil {

    public static ConnectionFactory getConnectionFactory() {
        //创建连接工程,下面给出的是默认的case
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setVirtualHost("/");
        return factory;
    }
}

a. 生产者

要使用,基本的就需要一个消息投递和一个消息消费两方,线看消息生产者的一般写法

public class MsgProducer {
    public static void publishMsg(String exchange, BuiltinExchangeType exchangeType, String toutingKey, String message)
            throws IOException, TimeoutException {
        ConnectionFactory factory = RabbitUtil.getConnectionFactory();

        //创建连接
        Connection connection = factory.newConnection();

        //创建消息通道
        Channel channel = connection.createChannel();

        // 声明exchange中的消息为可持久化,不自动删除
        channel.exchangeDeclare(exchange, exchangeType, true, false, null);

        // 发布消息
        channel.basicPublish(exchange, toutingKey, null, message.getBytes());

        channel.close();
        connection.close();
    }
}

针对上面的代码,结合RabbitMQ的基本概念进行分析

基本结构

  • 不管是干啥,第一步都是获取连接,也就是上面的Connection
  • 《RabbitMq基础教程之基本概念》直到,生产者消费者都是借助Channel与Exchange或者Queue打交道,接下来就是通过Connection创建数据流通信道Channel
  • Channel准备完毕之后,生产者就可以向其中投递数据
  • 投递完毕之后,回收现场资源

疑问:

  • 在声明Exchange时,是否就需要选择消息绑定策略?
  • 不声明时,默认是什么策略?

b. 消费者

结合上面的代码和分析,大胆的预测下消费者的流程

  • 获取连接Connection
  • 创建Channel
  • 将Channel与Queue进行绑定
  • 创建一个Consumer,从Queue中获取数据
  • 消息消费之后,ack

下面给出一个mq推数据的消费过程

public class MsgConsumer {

    public static void consumerMsg(String exchange, String queue, String routingKey)
            throws IOException, TimeoutException {
        ConnectionFactory factory = RabbitUtil.getConnectionFactory();
        //创建连接
        Connection connection = factory.newConnection();

        //创建消息信道
        final Channel channel = connection.createChannel();

        //消息队列
        channel.queueDeclare(queue, true, false, false, null);
        //绑定队列到交换机
        channel.queueBind(queue, exchange, routingKey);
        System.out.println("[*] Waiting for message. To exist press CTRL+C");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                    byte[] body) throws IOException {
                String message = new String(body, "UTF-8");

                try {
                    System.out.println(" [x] Received ‘" + message);
                } finally {
                    System.out.println(" [x] Done");
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        // 取消自动ack
        channel.basicConsume(queue, false, consumer);
    }
}

2. Direct方式

a. Producer

直接在前面的基础上进行测试,我们定义一个新的exchange名为direct.exchange,并且制定ExchangeType为直接路由方式 (先不管这种写法的合理性)

public class DirectProducer {
    private static final String EXCHANGE_NAME = "direct.exchange";

    public void publishMsg(String routingKey, String msg) {
        try {
            MsgProducer.publishMsg(EXCHANGE_NAME, BuiltinExchangeType.DIRECT, routingKey, msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        DirectProducer directProducer = new DirectProducer();
        String[] routingKey = new String[]{"aaa", "bbb"};
        String msg = "hello >>> ";

        for (int i = 0; i < 30; i++) {
            directProducer.publishMsg(routingKey[i % 2], msg + i);
        }
        System.out.println("----over-------");
    }
}

上面的代码执行一遍之后,看控制台会发现新增了一个Exchange

exchange

b. consumer

同样的我们写一下对应的消费者,一个用来消费aaa,一个消费bbb

public class DirectConsumer {

    private static final String exchangeName = "direct.exchange";

    public void msgConsumer(String queueName, String routingKey) {
        try {
            MsgConsumer.consumerMsg(exchangeName, queueName, routingKey);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        DirectConsumer consumer = new DirectConsumer();
        String[] routingKey = new String[]{"aaa", "bbb"};
        String[] queueNames = new String[]{"qa", "qb"};

        for (int i = 0; i < 2; i++) {
            consumer.msgConsumer(queueNames[i], routingKey[i]);
        }

        Thread.sleep(1000 * 60 * 10);
    }
}

执行上面的代码之后,就会多两个Queue,且增加了Exchange到Queue的绑定

binding

queue

当上面两个代码配合起来使用时,就可以看到对于消费者而言,qa一直消费的是偶数,qb一直消费的是奇数,一次输出如下:

[qa] Waiting for message. To exist press CTRL+C
[qb] Waiting for message. To exist press CTRL+C
 [qa] Received ‘hello >>> 0
 [qb] Received ‘hello >>> 1
 [qa] Received ‘hello >>> 2
 [qb] Received ‘hello >>> 3
 [qa] Received ‘hello >>> 4
...

3. Fanout方式

有了上面的case之后,这个的实现和测试就比较简单了

a. Producer

public class FanoutProducer {
    private static final String EXCHANGE_NAME = "fanout.exchange";

    public void publishMsg(String routingKey, String msg) {
        try {
            MsgProducer.publishMsg(EXCHANGE_NAME, BuiltinExchangeType.FANOUT, routingKey, msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        FanoutProducer directProducer = new FanoutProducer();
        String[] routingKey = new String[]{"aaa", "bbb"};
        String msg = "hello >>> ";

        for (int i = 0; i < 30; i++) {
            directProducer.publishMsg(routingKey[i % 2], msg + i);
        }
        System.out.println("----over-------");
    }
}

b. consumer

public class FanoutProducer {
    private static final String EXCHANGE_NAME = "fanout.exchange";

    public void publishMsg(String routingKey, String msg) {
        try {
            MsgProducer.publishMsg(EXCHANGE_NAME, BuiltinExchangeType.FANOUT, routingKey, msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        FanoutProducer directProducer = new FanoutProducer();
        String[] routingKey = new String[]{"aaa", "bbb"};
        String msg = "hello >>> ";

        for (int i = 0; i < 30; i++) {
            directProducer.publishMsg(routingKey[i % 2], msg + i);
        }
        System.out.println("----over-------");
    }
}

这个的输出就比较有意思了,fa,fb两个队列都可以接收到发布的消息,而且单独的执行一次上面的投递数据之后,发现fa/fb两个队列的数据都是30条

30

然后消费的结果如下

[qa] Waiting for message. To exist press CTRL+C
[qb] Waiting for message. To exist press CTRL+C
 [qa] Received ‘hello >>> 0
 [qb] Received ‘hello >>> 0
 [qa] Received ‘hello >>> 1
 [qb] Received ‘hello >>> 1
 [qb] Received ‘hello >>> 2
 [qa] Received ‘hello >>> 2
 [qa] Received ‘hello >>> 3
 [qb] Received ‘hello >>> 3
 [qb] Received ‘hello >>> 4
 [qa] Received ‘hello >>> 4
 ...

4. Topic方式

代码和上面差不多,就不重复拷贝了,接下来卡另外几个问题

III. 基础进阶

在上面的基础使用中,会有几个疑问如下:

  • Exchange声明的问题(是否必须声明,如果不声明会怎样)
  • Exchange声明的几个参数(durable, autoDelete)有啥区别
  • 当没有队列和Exchange绑定时,直接往队列中塞数据,好像不会有数据增加(即先塞数据,然后创建queue,建立绑定,从控制台上看这个queue里面也不会有数据)
  • 消息消费的两种姿势(一个主动去拿数据,一个是rabbit推数据)对比
  • ack/nack怎么用,nack之后消息可以怎么处理

以上内容,留待下一篇进行讲解

IV. 其他

1. 相关博文

2. 一灰灰Blog: https://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

3. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

4. 扫描关注

QrCode

原文地址:https://www.cnblogs.com/yihuihui/p/9127300.html

时间: 2024-11-12 07:28:19

RabbitMQ基础教程之基本使用篇的相关文章

RabbitMq基础教程之基本概念

RabbitMq基础教程之基本概念 RabbitMQ是一个消息队列,和Kafka以及阿里的ActiveMQ从属性来讲,干的都是一回事.消息队列的主要目的实现消息的生产者和消费者之间的解耦,支持多应用之间的异步协调工作 由于工作原因,接触和使用rabbitmq作为生产环境下的消息队列,因此准备写一些博文,记录下这个过程中的收货:而开篇除了环境搭建之外,就是对于其内部的基本概念进行熟悉和了解了. 基础环境搭建可以参考: <RabbitMq基础教程之安装与测试> 本文则主要集中在以下几点: 几个基本

MongoDB基础教程系列--第七篇 MongoDB 聚合管道

在讲解聚合管道(Aggregation Pipeline)之前,我们先介绍一下 MongoDB 的聚合功能,聚合操作主要用于对数据的批量处理,往往将记录按条件分组以后,然后再进行一系列操作,例如,求最大值.最小值.平均值,求和等操作.聚合操作还能够对记录进行复杂的操作,主要用于数理统计和数据挖掘.在 MongoDB 中,聚合操作的输入是集合中的文档,输出可以是一个文档,也可以是多条文档. MongoDB 提供了非常强大的聚合操作,有三种方式: 聚合管道(Aggregation Pipeline)

MongoDB基础教程系列--第五篇 MongoDB 映射与限制记录

上一篇提到的 find() 的方法,细心的伙伴会发现查询的结果都是显示了集合中全部的字段,实际应用中,显然是不够用的.那么有没有办法指定特定的字段显示出文档呢?答案是肯定的,MongoDB 中用映射实现这种功能. 1.映射 MongoDB 中限制字段的显示,可以利用 0 或 1 来设置字段列表.1 用于显示字段,0 用于隐藏字段. 格式 db.COLLECTION_NAME.find({},{KEY:1}) 范例 查询文档时,只显示文档中的 name.首先查询出 user 集合中的所有文档,然后

MongoDB基础教程系列--第三篇 MongoDB基本操作(二)

1.集合操作 1.1.创建集合 MongoDB 用 db.createCollection(name, options) 方法创建集合. 格式 db.createCollection(name, options) 其中,name 是集合名称,是一个字符串:options 是可选项,是一个文档,指定内存大小和索引等选项,具体参数说明如下表: 字段 类型 说明 capped 布尔         (可选)如果为 true,表示为固定集合.固定集合是指具有固定存储空间的集合.当该值为 true 时,必

Django 基础教程

Django 基础教程 这是第一篇 Django 简介 ?  Django 是由 Python 开发的一个免费的开源网站框架,可以用于快速搭建高性能,优雅的网站! 你一定可以学会,Django 很简单!本教程一直在更新,从开始写到现在大概写了一年多了,现在也一直在坚持写,每一篇教程都可能随时更新,可以在网站首页看到最近更新的情况. 我阅读学习了全部的 Django英文的官方文档,觉得国内比较好的Django学习资源不多,所以决定写自己的教程.本教程开始写的时候是 Django 的版本是 1.6,

MongoDB基础教程系列--未完待续

最近对 MongoDB 产生兴趣,在网上找的大部分都是 2.X 版本,由于 2.X 与 3.X 差别还是很大的,所以自己参考官网,写了本系列.MongoDB 的知识还是很多的,本系列会持续更新,本文作为目录篇,方便阅读,有问题大家一块交流哈.多谢大家关注,转载请注明出处,谢谢! MongoDB基础教程系列--第一篇 进入MongoDB世界 MongoDB基础教程系列--第二篇 MongoDB基本操作(一) MongoDB基础教程系列--第三篇 MongoDB基本操作(二) MongoDB基础教程

NSIS安装制作基础教程[初级篇], 献给对NSIS有兴趣的初学者

NSIS安装制作基础教程[初级篇], 献给对NSIS有兴趣的初学者 作者: raindy 来源:http://bbs.hanzify.org/index.php?showtopic=30029 时间:2005-02-15 点击:70791 raindy NSIS简介: NSIS 是“Nullsoft 脚本安装系统”(Nullsoft Scriptable Installation System)的缩写,它是一个免费的 Win32 安装.卸载系统,它的特点:脚本简洁高效:系统开销小:当然进行安装.

Thrift 基础教程(二)编码篇

上接Thrift 基础教程(一)安装篇,今天来介绍下Thrift的编码过程,首先通过命令行生成框架代码. 命令格式如下: thrift -gen language xxx.thrift 1.首先介绍下那个xxx.thrift文件,我们需要先创建myserver.thrift文件,这个接口主要定义服务接口和数据格式. 介绍一下thrift文件的语法格式,以我写的myserver.thrift说明. /** * thrift中的数据类型 * bool 布尔类型 * byte 同java里的byte

基础教程系列之装系统篇

[科普]基础教程系列之装系统篇 鉴于每天有很多新人询问安装系统问题,我决定花一些时间来系统性的整理一下安装系统的步骤,带领新人一步步来安装好系统. 目录 0.前言 1.物质准备 2.制作启动盘 3.选择自己需要的系统 4.其他准备工作 5.进入win pe 6.举例进入win pe 7-1.确保正确安装系统的准备工作 7-2.确保正确安装系统的准备工作 8-1.使用原版镜像安装系统 8-2使用ghost安装系统 8-3使用nt6 hdd installer安装系统 9.重启进入操作系统 10.安