RabbitMQ教程

abbitMQ实战教程
1.什么是MQ
消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。
其主要用途:不同进程Process/线程Thread之间通信。
为什么会产生消息队列?有几个原因:

不同进程(process)之间传递消息时,两个进程之间耦合程度过高,改动一个进程,引发必须修改另一个进程,为了隔离这两个进程,在两进程间抽离出一层(一个模块),所有两进程之间传递的消息,都必须通过消息队列来传递,单独修改某一个进程,不会影响另一个;

不同进程(process)之间传递消息时,为了实现标准化,将消息的格式规范化了,并且,某一个进程接受的消息太多,一下子无法处理完,并且也有先后顺序,必须对收到的消息进行排队,因此诞生了事实上的消息队列;

关于消息队列的详细介绍请参阅:
《Java帝国之消息队列》
《一个故事告诉你什么是消息队列》
《到底什么时候该使用MQ》

MQ框架非常之多,比较流行的有RabbitMq、ActiveMq、ZeroMq、kafka,以及阿里开源的RocketMQ。本文主要介绍RabbitMq。

本教程pdf及代码下载地址:
代码:https://download.csdn.net/download/zpcandzhj/10585077
教程:https://download.csdn.net/download/zpcandzhj/10585092

2.RabbitMQ
2.1.RabbitMQ的简介

开发语言:Erlang – 面向并发的编程语言。

2.1.1.AMQP
AMQP是消息队列的一个协议。

2.2.官网

2.3.MQ的其他产品

2.4.学习5种队列

2.5.安装文档

3.搭建RabbitMQ环境
3.1.下载
下载地址:http://www.rabbitmq.com/download.html

3.2.windows下安装
3.2.1.安装Erlang
下载:http://www.erlang.org/download/otp_win64_17.3.exe
安装:

安装完成。

3.2.2.安装RabbitMQ

安装完成。

开始菜单里出现如下选项:

启动、停止、重新安装等。

3.2.3.启用管理工具
1、双击
2、进入C:\Program Files (x86)\RabbitMQ Server\rabbitmq_server-3.4.1\sbin输入命令:
rabbitmq-plugins enable rabbitmq_management

这样就启动了管理工具,可以试一下命令:
停止:net stop RabbitMQ
启动:net start RabbitMQ

3、在浏览器中输入地址查看:http://127.0.0.1:15672/

4、使用默认账号登录:guest/ guest

3.3.Linux下安装
3.3.1.安装Erlang
3.3.2.添加yum支持
cd /usr/local/src/
mkdir rabbitmq
cd rabbitmq

wget http://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
rpm -Uvh erlang-solutions-1.0-1.noarch.rpm

rpm --import http://packages.erlang-solutions.com/rpm/erlang_solutions.asc

使用yum安装:
sudo yum install erlang

3.3.3.安装RabbitMQ
上传rabbitmq-server-3.4.1-1.noarch.rpm文件到/usr/local/src/rabbitmq/
安装:
rpm -ivh rabbitmq-server-3.4.1-1.noarch.rpm

3.3.4.启动、停止
service rabbitmq-server start
service rabbitmq-server stop
service rabbitmq-server restart
3.3.5.设置开机启动
chkconfig rabbitmq-server on
3.3.6.设置配置文件
cd /etc/rabbitmq
cp /usr/share/doc/rabbitmq-server-3.4.1/rabbitmq.config.example /etc/rabbitmq/

mv rabbitmq.config.example rabbitmq.config
3.3.7.开启用户远程访问
vi /etc/rabbitmq/rabbitmq.config

注意要去掉后面的逗号。
3.3.8.开启web界面管理工具
rabbitmq-plugins enable rabbitmq_management
service rabbitmq-server restart
3.3.9.防火墙开放15672端口
/sbin/iptables -I INPUT -p tcp --dport 15672 -j ACCEPT
/etc/rc.d/init.d/iptables save

3.4.安装的注意事项
1、推荐使用默认的安装路径
2、系统用户名必须是英文
Win10改名字非常麻烦,具体方法百度

3、计算机名必须是英文

4、系统的用户必须是管理员

如果安装失败应该如何解决:
1、重装系统 – 不推荐
2、将RabbitMQ安装到linux虚拟机中
a)推荐
3、使用别人安装好的RabbitMQ服务
a)只要给你开通一个账户即可。
b)使用公用的RabbitMQ服务,在192.168.50.22
c)推荐

常见错误:

3.5.安装完成后操作
1、系统服务中有RabbitMQ服务,停止、启动、重启

2、打开命令行工具

如果找不到命令行工具,直接cd到相应目录:

输入命令rabbitmq-plugins enable rabbitmq_management启用管理插件

查看管理页面

通过默认账户 guest/guest 登录
如果能够登录,说明安装成功。

4.添加用户
4.1.添加admin用户

4.2.用户角色
1、超级管理员(administrator)
可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。
2、监控者(monitoring)
可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)
3、策略制定者(policymaker)
可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
4、普通管理者(management)
仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。
5、其他
无法登陆管理控制台,通常就是普通的生产者和消费者。

4.3.创建Virtual Hosts

选中Admin用户,设置权限:

看到权限已加:

4.4.管理界面中的功能

5.学习五种队列

5.1.导入my-rabbitmq项目
项目下载地址:
https://download.csdn.net/download/zpcandzhj/10585077

5.2.简单队列
5.2.1.图示

P:消息的生产者
C:消息的消费者
红色:队列

生产者将消息发送到队列,消费者从队列中获取消息。
5.2.2.导入RabbitMQ的客户端依赖

com.rabbitmq amqp-client 3.4.1 1 2 3 4 5 5.2.3.获取MQ的连接

package com.zpc.rabbitmq.util;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;

public class ConnectionUtil {

public static Connection getConnection() throws Exception {
    //定义连接工厂
    ConnectionFactory factory = new ConnectionFactory();
    //设置服务地址
    factory.setHost("localhost");
    //端口
    factory.setPort(5672);
    //设置账号信息,用户名、密码、vhost
    factory.setVirtualHost("testhost");
    factory.setUsername("admin");
    factory.setPassword("admin");
    // 通过工程获取连接
    Connection connection = factory.newConnection();
    return connection;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
5.2.4.生产者发送消息到队列

package com.zpc.rabbitmq.simple;

import com.zpc.rabbitmq.util.ConnectionUtil;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Send {

private final static String QUEUE_NAME = "q_test_01";

public static void main(String[] argv) throws Exception {
    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    // 从连接中创建通道
    Channel channel = connection.createChannel();

    // 声明(创建)队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    // 消息内容
    String message = "Hello World!";
    channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
    System.out.println(" [x] Sent '" + message + "'");
    //关闭通道和连接
    channel.close();
    connection.close();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
5.2.5.管理工具中查看消息

点击上面的队列名称,查询具体的队列中的信息:

5.2.6.消费者从队列中获取消息

package com.zpc.rabbitmq.simple;

import com.zpc.rabbitmq.util.ConnectionUtil;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

public class Recv {

private final static String QUEUE_NAME = "q_test_01";

public static void main(String[] argv) throws Exception {

    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    // 从连接中创建通道
    Channel channel = connection.createChannel();
    // 声明队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    // 定义队列的消费者
    QueueingConsumer consumer = new QueueingConsumer(channel);

    // 监听队列
    channel.basicConsume(QUEUE_NAME, true, consumer);

    // 获取消息
    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [x] Received '" + message + "'");
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
5.3.Work模式

5.3.1.图示

一个生产者、2个消费者。

一个消息只能被一个消费者获取。
5.3.2.消费者1

package com.zpc.rabbitmq.work;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import com.zpc.rabbitmq.util.ConnectionUtil;

public class Recv {

private final static String QUEUE_NAME = "test_queue_work";

public static void main(String[] argv) throws Exception {

    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    // 同一时刻服务器只会发一条消息给消费者
    //channel.basicQos(1);

    // 定义队列的消费者
    QueueingConsumer consumer = new QueueingConsumer(channel);
    // 监听队列,false表示手动返回完成状态,true表示自动
    channel.basicConsume(QUEUE_NAME, true, consumer);

    // 获取消息
    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [y] Received '" + message + "'");
        //休眠
        Thread.sleep(10);
        // 返回确认状态,注释掉表示使用自动确认模式
        //channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
5.3.3.消费者2

package com.zpc.rabbitmq.work;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import com.zpc.rabbitmq.util.ConnectionUtil;

public class Recv2 {

private final static String QUEUE_NAME = "test_queue_work";

public static void main(String[] argv) throws Exception {

    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    // 同一时刻服务器只会发一条消息给消费者
    //channel.basicQos(1);

    // 定义队列的消费者
    QueueingConsumer consumer = new QueueingConsumer(channel);
    // 监听队列,false表示手动返回完成状态,true表示自动
    channel.basicConsume(QUEUE_NAME, true, consumer);

    // 获取消息
    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [x] Received '" + message + "'");
        // 休眠1秒
        Thread.sleep(1000);
        //下面这行注释掉表示使用自动确认模式
        //channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
5.3.4.生产者
向队列中发送100条消息。

package com.zpc.rabbitmq.work;

import com.zpc.rabbitmq.util.ConnectionUtil;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Send {

private final static String QUEUE_NAME = "test_queue_work";

public static void main(String[] argv) throws Exception {
    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    for (int i = 0; i < 100; i++) {
        // 消息内容
        String message = "" + i;
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

        Thread.sleep(i * 10);
    }

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
5.3.5.测试
测试结果:
1、消费者1和消费者2获取到的消息内容是不同的,同一个消息只能被一个消费者获取。
2、消费者1和消费者2获取到的消息的数量是相同的,一个是消费奇数号消息,一个是偶数。

其实,这样是不合理的,因为消费者1线程停顿的时间短。应该是消费者1要比消费者2获取到的消息多才对。
RabbitMQ 默认将消息顺序发送给下一个消费者,这样,每个消费者会得到相同数量的消息。即轮询(round-robin)分发消息。

怎样才能做到按照每个消费者的能力分配消息呢?联合使用 Qos 和 Acknowledge 就可以做到。
basicQos 方法设置了当前信道最大预获取(prefetch)消息数量为1。消息从队列异步推送给消费者,消费者的 ack 也是异步发送给队列,从队列的视角去看,总是会有一批消息已推送但尚未获得 ack 确认,Qos 的 prefetchCount 参数就是用来限制这批未确认消息数量的。设为1时,队列只有在收到消费者发回的上一条消息 ack 确认后,才会向该消费者发送下一条消息。prefetchCount 的默认值为0,即没有限制,队列会将所有消息尽快发给消费者。

2个概念

轮询分发 :使用任务队列的优点之一就是可以轻易的并行工作。如果我们积压了好多工作,我们可以通过增加工作者(消费者)来解决这一问题,使得系统的伸缩性更加容易。在默认情况下,RabbitMQ将逐个发送消息到在序列中的下一个消费者(而不考虑每个任务的时长等等,且是提前一次性分配,并非一个一个分配)。平均每个消费者获得相同数量的消息。这种方式分发消息机制称为Round-Robin(轮询)。

公平分发 :虽然上面的分配法方式也还行,但是有个问题就是:比如:现在有2个消费者,所有的奇数的消息都是繁忙的,而偶数则是轻松的。按照轮询的方式,奇数的任务交给了第一个消费者,所以一直在忙个不停。偶数的任务交给另一个消费者,则立即完成任务,然后闲得不行。而RabbitMQ则是不了解这些的。这是因为当消息进入队列,RabbitMQ就会分派消息。它不看消费者为应答的数目,只是盲目的将消息发给轮询指定的消费者。

为了解决这个问题,我们使用basicQos( prefetchCount = 1)方法,来限制RabbitMQ只发不超过1条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发送。
还有一点需要注意,使用公平分发,必须关闭自动应答,改为手动应答。

5.4.Work模式的“能者多劳”
打开上述代码的注释:

// 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1);
1
2
//开启这行 表示使用手动确认模式
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
1
2
同时改为手动确认:

// 监听队列,false表示手动返回完成状态,true表示自动
channel.basicConsume(QUEUE_NAME, false, consumer);
1
2
测试:
消费者1比消费者2获取的消息更多。

5.5.消息的确认模式
消费者从队列中获取消息,服务端如何知道消息已经被消费呢?

模式1:自动确认
只要消息从队列中获取,无论消费者获取到消息后是否成功消息,都认为是消息已经成功消费。
模式2:手动确认
消费者从队列中获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态。

手动模式:

自动模式:

5.6.订阅模式

5.6.1.图示

解读:
1、1个生产者,多个消费者
2、每一个消费者都有自己的一个队列
3、生产者没有将消息直接发送到队列,而是发送到了交换机
4、每个队列都要绑定到交换机
5、生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的
注意:一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费

5.6.2.消息的生产者(看作是后台系统)
向交换机中发送消息。

package com.zpc.rabbitmq.subscribe;

import com.zpc.rabbitmq.util.ConnectionUtil;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Send {

private final static String EXCHANGE_NAME = "test_exchange_fanout";

public static void main(String[] argv) throws Exception {
    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明exchange
    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

    // 消息内容
    String message = "Hello World!";
    channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
    System.out.println(" [x] Sent '" + message + "'");

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
注意:消息发送到没有队列绑定的交换机时,消息将丢失,因为,交换机没有存储消息的能力,消息只能存在在队列中。
5.6.3.消费者1(看作是前台系统)

package com.zpc.rabbitmq.subscribe;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

import com.zpc.rabbitmq.util.ConnectionUtil;

public class Recv {

private final static String QUEUE_NAME = "test_queue_work1";

private final static String EXCHANGE_NAME = "test_exchange_fanout";

public static void main(String[] argv) throws Exception {

    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    // 绑定队列到交换机
    channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

    // 同一时刻服务器只会发一条消息给消费者
    channel.basicQos(1);

    // 定义队列的消费者
    QueueingConsumer consumer = new QueueingConsumer(channel);
    // 监听队列,手动返回完成
    channel.basicConsume(QUEUE_NAME, false, consumer);

    // 获取消息
    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [Recv] Received '" + message + "'");
        Thread.sleep(10);

        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
5.6.4.消费者2(看作是搜索系统)

package com.zpc.rabbitmq.subscribe;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

import com.zpc.rabbitmq.util.ConnectionUtil;

public class Recv2 {

private final static String QUEUE_NAME = "test_queue_work2";

private final static String EXCHANGE_NAME = "test_exchange_fanout";

public static void main(String[] argv) throws Exception {

    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    // 绑定队列到交换机
    channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

    // 同一时刻服务器只会发一条消息给消费者
    channel.basicQos(1);

    // 定义队列的消费者
    QueueingConsumer consumer = new QueueingConsumer(channel);
    // 监听队列,手动返回完成
    channel.basicConsume(QUEUE_NAME, false, consumer);

    // 获取消息
    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [Recv2] Received '" + message + "'");
        Thread.sleep(10);

        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
5.6.5.测试
测试结果:
同一个消息被多个消费者获取。一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费到消息。

在管理工具中查看队列和交换机的绑定关系:

5.7.路由模式

5.7.1.图示

5.7.2.生产者

5.7.3.消费者1(假设是前台系统)

5.7.4.消费2(假设是搜索系统)

5.8.主题模式(通配符模式)

5.8.1.图示

同一个消息被多个消费者获取。一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费到消息。

5.8.2.生产者

package com.zpc.rabbitmq.topic;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import com.zpc.rabbitmq.util.ConnectionUtil;

public class Send {

private final static String EXCHANGE_NAME = "test_exchange_topic";

public static void main(String[] argv) throws Exception {
    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明exchange
    channel.exchangeDeclare(EXCHANGE_NAME, "topic");

    // 消息内容
    String message = "Hello World!!";
    channel.basicPublish(EXCHANGE_NAME, "routekey.1", null, message.getBytes());
    System.out.println(" [x] Sent '" + message + "'");

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
5.8.3.消费者1(前台系统)

package com.zpc.rabbitmq.topic;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

import com.zpc.rabbitmq.util.ConnectionUtil;

public class Recv {

private final static String QUEUE_NAME = "test_queue_topic_work_1";

private final static String EXCHANGE_NAME = "test_exchange_topic";

public static void main(String[] argv) throws Exception {

    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    // 绑定队列到交换机
    channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "routekey.*");

    // 同一时刻服务器只会发一条消息给消费者
    channel.basicQos(1);

    // 定义队列的消费者
    QueueingConsumer consumer = new QueueingConsumer(channel);
    // 监听队列,手动返回完成
    channel.basicConsume(QUEUE_NAME, false, consumer);

    // 获取消息
    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [Recv_x] Received '" + message + "'");
        Thread.sleep(10);

        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
5.8.4.消费者2(搜索系统)

package com.zpc.rabbitmq.topic;

import com.zpc.rabbitmq.util.ConnectionUtil;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

public class Recv2 {

private final static String QUEUE_NAME = "test_queue_topic_work_2";

private final static String EXCHANGE_NAME = "test_exchange_topic";

public static void main(String[] argv) throws Exception {

    // 获取到连接以及mq通道
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();

    // 声明队列
    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    // 绑定队列到交换机
    channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "*.*");

    // 同一时刻服务器只会发一条消息给消费者
    channel.basicQos(1);

    // 定义队列的消费者
    QueueingConsumer consumer = new QueueingConsumer(channel);
    // 监听队列,手动返回完成
    channel.basicConsume(QUEUE_NAME, false, consumer);

    // 获取消息
    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [Recv2_x] Received '" + message + "'");
        Thread.sleep(10);

        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
6.Spring-Rabbit
6.1.Spring项目
http://spring.io/projects

6.2.简介

6.3.使用
6.3.1.消费者

package com.zpc.rabbitmq.spring;

/**

  • 消费者

  • @author Evan
    */
    public class Foo {

    //具体执行业务的方法
    public void listen(String foo) {
    System.out.println("\n消费者: " + foo + “\n”);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    6.3.2.生产者

package com.zpc.rabbitmq.spring;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringMain {
public static void main(final String… args) throws Exception {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
“classpath:spring/rabbitmq-context.xml”);
//RabbitMQ模板
RabbitTemplate template = ctx.getBean(RabbitTemplate.class);
//发送消息
template.convertAndSend(“Hello, 鸟鹏!”);
Thread.sleep(1000);// 休眠1秒
ctx.destroy(); //容器销毁
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
6.3.3.配置文件
1、定义连接工厂

<rabbit:connection-factory id=“connectionFactory”
host=“127.0.0.1” port=“5672” username=“admin” password=“admin”
virtual-host=“testhost” />
1
2
3
4
2、定义模板(可以指定交换机或队列)

<rabbit:template id=“amqpTemplate” connection-factory=“connectionFactory” exchange=“fanoutExchange” />
1
3、定义队列、交换机、以及完成队列和交换机的绑定

<rabbit:queue name=“zpcQueue” auto-declare=“true”/>

<rabbit:fanout-exchange name=“fanoutExchange” auto-declare=“true”>
rabbit:bindings
<rabbit:binding queue=“zpcQueue”/>
</rabbit:bindings>
</rabbit:fanout-exchange>
1
2
3
4
5
6
7
8
9
4、定义监听

<rabbit:listener-container connection-factory=“connectionFactory”>
<rabbit:listener ref=“foo” method=“listen” queue-names=“zpcQueue” />
</rabbit:listener-container>

1 2 3 4 5 5、定义管理,用于管理队列、交换机等:

<rabbit:admin connection-factory=“connectionFactory” />
1
2
完整配置文件rabbitmq-context.xml

<rabbit:connection-factory id=“connectionFactory”
host=“127.0.0.1” port=“5672” username=“admin” password=“admin”
virtual-host=“testhost” />

<rabbit:template id=“amqpTemplate” connection-factory=“connectionFactory” exchange=“fanoutExchange” />

<rabbit:admin connection-factory=“connectionFactory” />

<rabbit:queue name=“zpcQueue” auto-declare=“true”/>

<rabbit:fanout-exchange name=“fanoutExchange” auto-declare=“true”>
rabbit:bindings
<rabbit:binding queue=“zpcQueue”/>
</rabbit:bindings>
</rabbit:fanout-exchange>

<rabbit:listener-container connection-factory=“connectionFactory”>
<rabbit:listener ref=“foo” method=“listen” queue-names=“zpcQueue” />
</rabbit:listener-container>

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 6.4.持久化交换机和队列

持久化:将交换机或队列的数据保存到磁盘,服务器宕机或重启之后依然存在。
非持久化:将交换机或队列的数据保存到内存,服务器宕机或重启之后将不存在。

非持久化的性能高于持久化。

如何选择持久化?非持久化? – 看需求。

欢迎关注公众号「程猿薇茑」

7.Spring集成RabbitMQ一个完整案例
创建三个系统A,B,C
A作为生产者,B、C作为消费者(B,C作为web项目启动)
项目下载地址:https://download.csdn.net/download/zpcandzhj/10585077

7.1.在A系统中发送消息到交换机
7.1.1.导入依赖

<?xml version="1.0" encoding="UTF-8"?>


4.0.0

com.zpc
myrabbitA
0.0.1-SNAPSHOT
jar
myrabbit

org.springframework.amqp spring-rabbit 1.4.0.RELEASE
  <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>1.2.47</version>
  </dependency>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 7.1.2.队列和交换机的绑定关系 实现: 1、在配置文件中将队列和交换机完成绑定 2、可以在管理界面中完成绑定 a)绑定关系如果发生变化,需要修改配置文件,并且服务需要重启 b)管理更加灵活 c)更容易对绑定关系的权限管理,流程管理 本例选择第2种方式 7.1.3.配置 rabbitmq-context.xml

<rabbit:connection-factory id=“connectionFactory”
host=“127.0.0.1” port=“5672” username=“admin” password=“admin”
virtual-host=“testhost” />

<rabbit:admin connection-factory=“connectionFactory” />

<rabbit:direct-exchange name=“directExchange” auto-declare=“true” ></rabbit:direct-exchange>

<rabbit:template id=“amqpTemplate” connection-factory=“connectionFactory” exchange=“directExchange” />

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 7.1.4.消息内容 方案: 1、消息内容使用对象做json序列化发送 a)数据大 b)有些数据其他人是可能用不到的 2、发送特定的业务字段,如id、操作类型

7.1.5.实现
生产者MsgSender.java:

package com.zpc.myrabbit;

import com.alibaba.fastjson.JSON;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**

  • 消息生产者
    */
    public class MsgSender {
    public static void main(String[] args) throws Exception {
    AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
    “classpath:spring/rabbitmq-context.xml”);
    //RabbitMQ模板
    RabbitTemplate template = ctx.getBean(RabbitTemplate.class);

     String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//24小时制
     //发送消息
     Map<String, Object> msg = new HashMap<String, Object>();
     msg.put("type", "1");
     msg.put("date", date);
     template.convertAndSend("type2", JSON.toJSONString(msg));
     Thread.sleep(1000);// 休眠1秒
     ctx.destroy(); //容器销毁
    

    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    7.2.在B系统接收消息
    7.2.1.导入依赖

<?xml version="1.0" encoding="UTF-8"?>


4.0.0

<groupId>com.zpc</groupId>
<artifactId>myrabbitB</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>

<name>myrabbit</name>
<properties>
    <spring.version>4.1.3.RELEASE</spring.version>
    <fastjson.version>1.2.46</fastjson.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>3.4.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>1.4.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.47</version>
    </dependency>
</dependencies>

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <!-- web层需要配置Tomcat插件 -->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <configuration>
                <path>/testRabbit</path>
                <uriEncoding>UTF-8</uriEncoding>
                <port>8081</port>
            </configuration>
        </plugin>
    </plugins>
</build>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 7.2.2.配置

<rabbit:connection-factory id=“connectionFactory”
host=“127.0.0.1” port=“5672” username=“admin” password=“admin”
virtual-host=“testhost” />

<rabbit:admin connection-factory=“connectionFactory” />

<rabbit:queue name=“q_topic_testB” auto-declare=“true”/>

<rabbit:listener-container connection-factory=“connectionFactory”>
<rabbit:listener ref=“myMQlistener” method=“listen” queue-names=“q_topic_testB” />
</rabbit:listener-container>

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 7.2.3.具体处理逻辑

public class Listener {
//具体执行业务的方法
public void listen(String msg) {
System.out.println("\n消费者B开始处理消息: " + msg + “\n”);
}
}
1
2
3
4
5
6
7.2.4.在界面管理工具中完成绑定关系
选中定义好的交换机(exchange)

1)direct

2)fanout

3)topic

7.3.在C系统中接收消息
(和B系统配置差不多,无非是Q名和Q对应的处理逻辑变了)

7.3.1.配置

<rabbit:connection-factory id=“connectionFactory”
host=“127.0.0.1” port=“5672” username=“admin” password=“admin”
virtual-host=“testhost” />

<rabbit:admin connection-factory=“connectionFactory” />

<rabbit:queue name=“q_topic_testC” auto-declare=“true”/>

<rabbit:listener-container connection-factory=“connectionFactory”>
<rabbit:listener ref=“myMQlistener” method=“listen” queue-names=“q_topic_testC” />
</rabbit:listener-container>

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 7.3.2.处理业务逻辑

public class Listener {

//具体执行业务的方法
public void listen(String msg) {
    System.out.println("\n消费者C开始处理消息: " + msg + "\n");
}

}
1
2
3
4
5
6
7
7.3.3.在管理工具中绑定队列和交换机
见7.2.4

7.3.4.测试
分别启动B,C两个web应用,然后运行A的MsgSender主方法发送消息,分别测试fanout、direct、topic三种类型

8.Springboot集成RabbitMQ
springboot集成RabbitMQ非常简单,如果只是简单的使用配置非常少,springboot提供了spring-boot-starter-amqp对消息各种支持。
代码下载地址:https://download.csdn.net/download/zpcandzhj/10585077
8.1.简单队列
1、配置pom文件,主要是添加spring-boot-starter-amqp的支持

org.springframework.boot spring-boot-starter-amqp 1 2 3 4 2、配置application.properties文件 配置rabbitmq的安装地址、端口以及账户信息

spring.application.name=spirng-boot-rabbitmq
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
1
2
3
4
5
3、配置队列

package com.zpc.rabbitmq;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {
@Bean
public Queue queue() {
return new Queue(“q_hello”);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
4、发送者

package com.zpc.rabbitmq;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class HelloSender {
@Autowired
private AmqpTemplate rabbitTemplate;

public void send() {
    String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//24小时制
    String context = "hello " + date;
    System.out.println("Sender : " + context);
    //简单对列的情况下routingKey即为Q名
    this.rabbitTemplate.convertAndSend("q_hello", context);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
5、接收者

package com.zpc.rabbitmq;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = “q_hello”)
public class HelloReceiver {

@RabbitHandler
public void process(String hello) {
    System.out.println("Receiver  : " + hello);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
6、测试

package com.zpc.rabbitmq;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqHelloTest {

@Autowired
private HelloSender helloSender;

@Test
public void hello() throws Exception {
    helloSender.send();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
8.2.多对多使用(Work模式)
注册两个Receiver:

package com.zpc.rabbitmq;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = “q_hello”)
public class HelloReceiver2 {

@RabbitHandler
public void process(String hello) {
    System.out.println("Receiver2  : " + hello);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void oneToMany() throws Exception {
for (int i=0;i<100;i++){
helloSender.send(i);
Thread.sleep(300);
}
}
1
2
3
4
5
6
7
public void send(int i) {
String date = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(new Date());//24小时制
String context = "hello " + i + " " + date;
System.out.println("Sender : " + context);
//简单对列的情况下routingKey即为Q名
this.rabbitTemplate.convertAndSend(“q_hello”, context);
}
1
2
3
4
5
6
7
8.3.Topic Exchange(主题模式)
topic 是RabbitMQ中最灵活的一种方式,可以根据routing_key自由的绑定不同的队列
首先对topic规则配置,这里使用两个队列(消费者)来演示。
1)配置队列,绑定交换机

package com.zpc.rabbitmq.topic;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TopicRabbitConfig {

final static String message = "q_topic_message";
final static String messages = "q_topic_messages";

@Bean
public Queue queueMessage() {
    return new Queue(TopicRabbitConfig.message);
}

@Bean
public Queue queueMessages() {
    return new Queue(TopicRabbitConfig.messages);
}

/**
 * 声明一个Topic类型的交换机
 * @return
 */
@Bean
TopicExchange exchange() {
    return new TopicExchange("mybootexchange");
}

/**
 * 绑定Q到交换机,并且指定routingKey
 * @param queueMessage
 * @param exchange
 * @return
 */
@Bean
Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
    return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
}

@Bean
Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
    return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2)创建2个消费者
q_topic_message 和q_topic_messages

package com.zpc.rabbitmq.topic;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = “q_topic_message”)
public class Receiver1 {

@RabbitHandler
public void process(String hello) {
    System.out.println("Receiver1  : " + hello);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.zpc.rabbitmq.topic;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = “q_topic_messages”)
public class Receiver2 {

@RabbitHandler
public void process(String hello) {
    System.out.println("Receiver2 : " + hello);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3)消息发送者(生产者)

package com.zpc.rabbitmq.topic;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MsgSender {

@Autowired
private AmqpTemplate rabbitTemplate;

public void send1() {
    String context = "hi, i am message 1";
    System.out.println("Sender : " + context);
    this.rabbitTemplate.convertAndSend("mybootexchange", "topic.message", context);
}


public void send2() {
    String context = "hi, i am messages 2";
    System.out.println("Sender : " + context);
    this.rabbitTemplate.convertAndSend("mybootexchange", "topic.messages", context);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
send1方法会匹配到topic.#和topic.message,两个Receiver都可以收到消息,发送send2只有topic.#可以匹配所有只有Receiver2监听到消息。
4)测试

package com.zpc.rabbitmq.topic;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitTopicTest {

@Autowired
private MsgSender msgSender;

@Test
public void send1() throws Exception {
    msgSender.send1();
}

@Test
public void send2() throws Exception {
    msgSender.send2();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
8.4.Fanout Exchange(订阅模式)
Fanout 就是我们熟悉的广播模式或者订阅模式,给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。
1)配置队列,绑定交换机
package com.zpc.rabbitmq.fanout;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FanoutRabbitConfig {

@Bean
public Queue aMessage() {
    return new Queue("q_fanout_A");
}

@Bean
public Queue bMessage() {
    return new Queue("q_fanout_B");
}

@Bean
public Queue cMessage() {
    return new Queue("q_fanout_C");
}

@Bean
FanoutExchange fanoutExchange() {
    return new FanoutExchange("mybootfanoutExchange");
}

@Bean
Binding bindingExchangeA(Queue aMessage, FanoutExchange fanoutExchange) {
    return BindingBuilder.bind(aMessage).to(fanoutExchange);
}

@Bean
Binding bindingExchangeB(Queue bMessage, FanoutExchange fanoutExchange) {
    return BindingBuilder.bind(bMessage).to(fanoutExchange);
}

@Bean
Binding bindingExchangeC(Queue cMessage, FanoutExchange fanoutExchange) {
    return BindingBuilder.bind(cMessage).to(fanoutExchange);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2)创建3个消费者

package com.zpc.rabbitmq.fanout;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = “q_fanout_A”)
public class ReceiverA {

@RabbitHandler
public void process(String hello) {
    System.out.println("AReceiver  : " + hello + "/n");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.zpc.rabbitmq.fanout;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = “q_fanout_B”)
public class ReceiverB {

@RabbitHandler
public void process(String hello) {
    System.out.println("BReceiver  : " + hello + "/n");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.zpc.rabbitmq.fanout;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = “q_fanout_C”)
public class ReceiverC {

@RabbitHandler
public void process(String hello) {
    System.out.println("CReceiver  : " + hello + "/n");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3)生产者

package com.zpc.rabbitmq.fanout;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MsgSenderFanout {

@Autowired
private AmqpTemplate rabbitTemplate;

public void send() {
    String context = "hi, fanout msg ";
    System.out.println("Sender : " + context);
    this.rabbitTemplate.convertAndSend("mybootfanoutExchange","", context);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
4)测试

package com.zpc.rabbitmq.fanout;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitFanoutTest {

@Autowired
private MsgSenderFanout msgSender;

@Test
public void send1() throws Exception {
    msgSender.send();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
结果如下,三个消费者都收到消息:
AReceiver : hi, fanout msg
CReceiver : hi, fanout msg
BReceiver : hi, fanout msg

9.总结
使用MQ实现商品数据的同步优势:
1、降低系统间耦合度
2、便于管理数据的同步(数据一致性)

推荐阅读
《RabbitMQ详解》
《大型网站技术架构:核心原理与案例分析》

推荐springCloud教程:
https://blog.csdn.net/hellozpc/article/details/83692496

推荐Springboot2.0教程:
https://blog.csdn.net/hellozpc/article/details/82531834

文章摘自可爱的小军老师。

欢迎关注公众号【程猿薇茑】

微信扫一扫
点赞 308
收藏
分享
有2人打赏

niaobirdfly
发布了136 篇原创文章 · 获赞 903 · 访问量 76万+
他的留言板
关注
RabbitMQ入门 用途说明和深入理解
阅读数 6万+

RabbitMQ在上一家公司已经接触过了,但是懵懵懂懂的.不是很清楚.具体怎么个逻辑.这次公司打算搭建新的系统.领导要求研究一下MQ.经过研究得出的结论是.MSMQ的设计理念不适合做系统的底层框架.他…
博文
来自: 走错路的程序员

mason_roy5个月前
不想说其他的,就想对作者说一句,你真的很给力~~~ 金钱支持有点难,精神支持绝对满分
查看回复(2)
80

fukua20174个月前但凡我有一点钱,一定捐了
3

Top丶环5个月前
QueueingConsumer会有内存溢出的问题,在4.x版本就被Deprecated了,你这不更新一下嘛
3

你敲代码的样子真像cxk2个月前下回一定
2

Coder_Maker3个月前
现在consumer 的定义使用 这个样子了,没在使用QueueingConsumer ,感谢楼主
//定义消费者
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
//获取并转成String
String message = new String(body, “UTF-8”);
System.out.println("–>消费者2号,收到消息,msg :"+message);
//休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);//手动确认
}

        }
    };

2

zhangshengqiang1684个月前
楼主为啥我的订阅模式,每个消费者都拿到了实例.而且还都消费了
查看回复(2)
2

cubmonk2个月前太棒了。已发红包支持查看回复(1)
1

xyuan182个月前
请问楼主订阅模式说的是只有一个消费者实例会消费消息,但是订阅模式不是每个订阅了该生产者的消费者都能获取到生产者产生的数据吗?
查看回复(3)
1

码上就好3个月前赞赞赞赞赞赞
1

时间所至4个月前
请问 您能改一下吗 你给的erlang是6.2版本的
查看回复(1)
1
上一页123…8下一页
RabbitMQ的应用场景以及基本原理介绍
阅读数 14万+

1.背景RabbitMQ是一个由erlang开发的AMQP(AdvanvedMessageQueue)的开源实现。2.应用场景2.1异步处理场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有…
博文
来自: 杨龙飞的博客
简书最完整的RabbitMQ入门教程
阅读数 8322

https://www.jianshu.com/p/79ca08116d57感谢大佬的分享
博文
来自: 编程战五渣
rabbitmq官方的六种工作模式
阅读数 2万+

1.RabbitMq1.1介绍RabbitMQ是一个消息代理:它接受并转发消息。你可以把它当成一个邮局:当你想邮寄信件的时候,你会把信件放在投递箱中,并确信邮递员最终会将信件送到收件人的手里。在这个例…
博文
来自: qq_33040219的博客

1、RabbitMQ的简单使用
阅读数 2万+

1、AMQPAMQP,即AdvancedMessageQueuingProtocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的…
博文
来自: 天之冰的博客
RabbitMQ基础概念详细介绍
阅读数 535

RabbitMQ简介AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解…
博文
来自: woailuohui的专栏
字节跳动视频编解码面经
阅读数 3万+

三四月份投了字节跳动的实习(图形图像岗位),然后hr打电话过来问了一下会不会opengl,c++,shador,当时只会一点c++,其他两个都不会,也就直接被拒了。七月初内推了字节跳动的提前批,因为内…
博文
来自: ljh_shuai的博客
Java学习的正确打开方式
阅读数 7万+

在博主认为,对于入门级学习java的最佳学习方法莫过于视频+博客+书籍+总结,前三者博主将淋漓尽致地挥毫于这篇博客文章中,至于总结在于个人,实际上越到后面你会发现学习的最好方式就是阅读参考官方文档其次…
博文
来自: 程序员宜春的博客
Linux系统常见命令
阅读数 7486

###Linux基本命令Linux系统经典语录:命令操作完没有任何消息信息, 就是最好的消息系统一切从根开始系统中数据一切皆文件一 .Linux系统命令结构命令 +空格+条件/参数+空格+对象/目录/…
博文
来自: gaoshiyuba的博客

RabbitMq基础教程之基本概念
阅读数 36

2019独角兽企业重金招聘Python工程师标准>>> …
博文
来自: weixin_33922672的博客
RabbitMQ实例教程:发布/订阅者消息队列
阅读数 15

消息交换机(Exchange)  RabbitMQ消息模型的核心理念是生产者永远不会直接发送任何消息给队列,一般的情况生产者甚至不知道消息应该发送到哪些队列。  相反的,生产者只能发送消息给交换机…
博文
来自: weixin_34275734的博客
关注
走错路的程序员
321篇文章

排名:2000+

关注
杨龙飞的博客
109篇文章

排名:千里之外

关注
敬一个遥远的她
42篇文章

排名:千里之外

关注
爱真白真是太好了
24篇文章

排名:千里之外

硬核干货,史上最强获取GitHub所有仓库内容数据分析教程
阅读数 1万+

四种获取GitHub上仓库历史数据的方法,用于分析Github的历史和趋势,你将彻底掌握。…
博文
来自: 村中少年的专栏
消息队列Rabbitmq基本操作
阅读数 220

转自:http://www.cnblogs.com/kaituorensheng/p/4985767.html消息队列Rabbitmq阅读目录1. 启动2. 队列重置(清空队列、用户等)3. 关闭4…
博文
来自: 一只小棉花的博客
RabbitMQ入门学习
阅读数 1万+

一、消息队列学习MQ是一个互联网架构中常见的解耦利器。什么时候不使用MQ?上游实时关注执行结果什么时候使用MQ?1)数据驱动的任务依赖,多个任务需要轮流执行,轮流订阅上一个任务。2)上游不关心多下游执…
博文
来自: cairuojin的博客

Python——画一棵漂亮的樱花树(不同种樱花+玫瑰+圣诞树喔)
阅读数 14万+

最近翻到一篇知乎,上面有不少用Python(大多是turtle库)绘制的树图,感觉很漂亮,我整理了一下,挑了一些我觉得不错的代码分享给大家(这些我都测试过,确实可以生成)one 樱花树 动态生成樱花效…
博文
来自: 碎片
nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件
阅读数 4万+

文章目录前言一、nginx简介1. 什么是 nginx 和可以做什么事情2.Nginx 作为 web 服务器3. 正向代理4. 反向代理5. 动静分离6.动静分离二、Nginx 的安装三、 Ngin…
博文
来自: 冯安晨
前后端角度看接口(什么是json)?
阅读数 2万+

什么是JSON?
博文
来自: Bean冷的心的博客
教你3分钟搭建一个RabbitMQ服务
阅读数 930

华为云的分布式消息服务DMS提供RabbitMQ类型的队列服务,RabbitMQ实例采用物理隔离的方式部署,租户独占RabbitMQ实例。支持用户自定义规格和自定义特性,您可以根据业务需要定制相应计算…
博文
来自: wangwenyuan_2006的专栏
Spring Boot2 系列教程(十七)SpringBoot 整合 Swagger2
阅读数 4962

前后端分离后,维护接口文档基本上是必不可少的工作。一个理想的状态是设计好后,接口文档发给前端和后端,大伙按照既定的规则各自开发,开发好了对接上了就可以上线了。当然这是一种非常理想的状态,实际开发中却很…
博文
来自: 江南一点雨的专栏

从入门到精通,Java学习路线导航(附学习资源)
阅读数 6万+

引言最近也有很多人来向我"请教",他们大都是一些刚入门的新手,还不了解这个行业,也不知道从何学起,开始的时候非常迷茫,实在是每天回复很多人也很麻烦,所以在这里统一作个回复吧。Java学习路线当然,这里…
博文
来自: java_sha的博客
RabbitMQ基础教程之基于配置的消费者实现
阅读数 11

2019独角兽企业重金招聘Python工程师标准>>> …
博文
来自: weixin_33911824的博客
rabbitmq启用日志功能记录消息队列收发情况
阅读数 1万+

原文:http://blog.csdn.net/u013256816/article/details/760392011、启用日志插件命令rabbitmq-pluginsenablerabbitmq_…
博文
来自: 不屑哥的专栏
Spring Cloud入门操作手册(Hoxton)
阅读数 2万+

文章目录@[toc]spring cloud 介绍spring cloud 技术组成Spring Cloud 对比 Dubbo一、service - 服务二、commons 通用项目新建 maven …
博文
来自: weixin_38305440的博客
RabbitMq简单实例教程
阅读数 893

mq原理不再赘述,百度一下大把介绍。但很难找到简单实用易理解的实例,故写此博客。说明:本实例使用的是两个最基本的Java项目,开始吧第一步:RabbitMq实例所用到的jar包请先下载这三个jar包h…
博文
来自: DonviYang的博客
终于明白阿里百度这样的大公司,为什么面试经常拿ThreadLocal考验求职者了
阅读数 7万+

点击上面↑「爱开发」关注我们每晚10点,捕获技术思考和创业资源洞察什么是ThreadLocalThreadLocal是一个本地线程副本变量工具类,各个线程都拥有一份线程私…
博文
来自: 爱开发
重磅发布-RabbitMQ实战系列完整视频教程
阅读数 1万+

概要介绍:历经一个多月的时间,我录制的RabbitMQ实战系列完整视频教程终于出世了!在本课程中,我将带领大家一窥消息中间件RabbitMQ的容貌,并将学到的知识要点实战到实际的应用场景中。本课程将分…
博文
来自: steadyjack博客
RabbitMQ五种消息队列学习(三)–Work模式
阅读数 300

RabbitMQ五种消息队列学习(三)–Work模式标签(空格分隔): RabbitMQ由于在实际应用中,简单队列模型无法解决很多实际问题,而且生产者和消费者是一对一的关系。模型较为单一。故引入Wor…
博文
来自: lbr2008的专栏
RabbitMQ消息队列
阅读数 27

RabbitMQ简介AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解…
博文
来自: 刻苦的樊同学
天天学JAVA-JAVA基础(6)
阅读数 9697

如果觉得我写的还行,请关注我的博客并且点个赞哟。本文主要介绍JAVA 中最常使用字符串常量String相关知识。1.String简介2.创建字符串对象两种方式的区别3.String常用的方法4.Str…
博文
来自: 穿越清华
【深入理解RabbitMQ原理】RabbitMQ 相关问题总结–RabbitMQ 如何确保消息发送和消费?
阅读数 7007

RabbitMQ 相关问题总结HA 的RabbitMQ 集群架构:一、RabbitMQ 如何高可用部署,如何确保集群不宕机?RabbitMQ可用采用三种方式来部署集群:1. cluster a. 不…
博文
来自: 王小明的专栏
我花了一夜用数据结构给女朋友写个H5走迷宫游戏
阅读数 31万+

起因又到深夜了,我按照以往在csdn和公众号写着数据结构!这占用了我大量的时间!我的超越妹妹严重缺乏陪伴而 怨气满满!而女朋友时常埋怨,认为数据结构这么抽象难懂的东西没啥作用,常会问道:天天写这玩意,…
博文
来自: bigsai
数据库优化 - SQL优化
阅读数 10万+

以实际SQL入手,带你一步一步走上SQL优化之路!
博文
来自: 飘渺Jam的博客
14位享誉全球的程序员
阅读数 2万+

本文转载至:http://www.cricode.com/2922.html
博文
来自: 闲云孤鹤
2019年10月中国编程语言排行榜
阅读数 6010

2019年10月2日,我统计了某招聘网站,获得有效程序员招聘数据9万条。针对招聘信息,提取编程语言关键字,并统计如下:编程语言比例rankpl_percentage1java33.54%2cpp16…
博文
来自: 毛毛虫
RabbitMQ入门教程(一):安装和常用命令
阅读数 7515

一:Mac安装Mac安装比Windows安装更加方便,也不需要再额外配置Web插件,因为在安装的时候默认已经配置好了// 在Updating Homebrew…时可能会卡一会,只需要等就行了// …
博文
来自: vbirdbest的博客
RabbitMQ 实战教程(一)
阅读数 8344

MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。RabbitMQ是信息传输的中间者。本质上,他从生产者接收消息,转发这些消息给消费者。换句…
博文
来自: 菜鸟路上的小白
网页实现一个简单的音乐播放器(大佬别看。(⊙﹏⊙))
阅读数 1万+

今天闲着无事,就想写点东西。然后听了下歌,就打算写个播放器。于是乎用h5 audio的加上js简单的播放器完工了。演示地点演示html代码如下` music 这个年纪 七月的风…
博文
来自: qq_44210563的博客
字节跳动面经
阅读数 2192

操作系统1.进程间的通信方式?匿名管道 有名管道 共享内存 socke通信 信号 信号量2.管道间如何具体通信?管道之间 一端负责写 一段负责读 之间进行字节流进行数据流动 如果需要两端进行需要相互通…
博文
来自: weixin_43846428的博客
爬虫福利二 之 妹子图网MM批量下载
阅读数 13万+

爬虫福利一:27报网MM批量下载    点击 看了本文,相信大家对爬虫一定会产生强烈的兴趣,激励自己去学习爬虫,在这里提前祝:大家学有所成! 目标网站:妹子图网 环境:Python3.x …
博文

程序员必须掌握的核心算法有哪些?
阅读数 16万+

由于我之前一直强调数据结构以及算法学习的重要性,所以就有一些读者经常问我,数据结构与算法应该要学习到哪个程度呢?,说实话,这个问题我不知道要怎么回答你,主要取决于你想学习到哪些程度,不过针对这个问题,…
博文
大学四年自学走来,这些私藏的实用工具/学习网站我贡献出来了
阅读数 21万+

大学四年,看课本是不可能一直看课本的了,对于学习,特别是自学,善于搜索网上的一些资源来辅助,还是非常有必要的,下面我就把这几年私藏的各种资源,网站贡献出来给你们。主要有:电子书搜索、实用工具、在线视频…
博文
linux系列之常用运维命令整理笔录
阅读数 11万+

本博客记录工作中需要的linux运维命令,大学时候开始接触linux,会一些基本操作,可是都没有整理起来,加上是做开发,不做运维,有些命令忘记了,所以现在整理成博客,当然vi,文件操作等就不介绍了,慢…
博文
Vue + Spring Boot 项目实战(十四):用户认证方案与完善的访问拦截
阅读数 9187

本篇文章主要讲解 token、session 等用户认证方案的区别并分析常见误区,以及如何通过前后端的配合实现完善的访问拦截,为下一步权限控制的实现打下基础。…
博文
比特币原理详解
阅读数 11万+

一、什么是比特币 比特币是一种电子货币,是一种基于密码学的货币,在2008年11月1日由中本聪发表比特币白皮书,文中提出了一种去中心化的电子记账系统,我们平时的电子现金是银行来记账,因为银行的背后是…
博文

程序员接私活怎样防止做完了不给钱?
阅读数 9万+

首先跟大家说明一点,我们做 IT 类的外包开发,是非标品开发,所以很有可能在开发过程中会有这样那样的需求修改,而这种需求修改很容易造成扯皮,进而影响到费用支付,甚至出现做完了项目收不到钱的情况。 那…
博文
Python十大装B语法
阅读数 19万+

Python 是一种代表简单思想的语言,其语法相对简单,很容易上手。不过,如果就此小视 Python 语法的精妙和深邃,那就大错特错了。本文精心筛选了最能展现 Python 语法之精妙的十个知识点,并…
博文
2019年11月中国大陆编程语言排行榜
阅读数 3万+

2019年11月2日,我统计了某招聘网站,获得有效程序员招聘数据9万条。针对招聘信息,提取编程语言关键字,并统计如下: 编程语言比例 rank pl_ percentage 1 jav…
博文
通俗易懂地给女朋友讲:线程池的内部原理
阅读数 5万+

餐厅的约会 餐盘在灯光的照耀下格外晶莹洁白,女朋友拿起红酒杯轻轻地抿了一小口,对我说:“经常听你说线程池,到底线程池到底是个什么原理?”我楞了一下,心里想女朋友今天是怎么了,怎么突然问出这么专业的问…
博文
经典算法(5)杨辉三角
阅读数 3万+

写在前面: 我是 扬帆向海,这个昵称来源于我的名字以及女朋友的名字。我热爱技术、热爱开源、热爱编程。技术是开源的、知识是共享的。 这博客是对自己学习的一点点总结及记录,如果您对 Java、算法 感…
博文

腾讯算法面试题:64匹马8个跑道需要多少轮才能选出最快的四匹?
阅读数 4万+

昨天,有网友私信我,说去阿里面试,彻底的被打击到了。问了为什么网上大量使用ThreadLocal的源码都会加上private static?他被难住了,因为他从来都没有考虑过这个问题。无独有偶,今天笔…
博文
面试官:你连RESTful都不知道我怎么敢要你?
阅读数 7万+

面试官:了解RESTful吗? 我:听说过。 面试官:那什么是RESTful? 我:就是用起来很规范,挺好的 面试官:是RESTful挺好的,还是自我感觉挺好的 我:都挺好的。 面试官:… 把门关上。…
博文
为啥国人偏爱Mybatis,而老外喜欢Hibernate/JPA呢?
阅读数 3万+

关于SQL和ORM的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行了一番讨论,感触还是有一些,于是就有了今天这篇文。 声明:本文不会下关于Mybatis和JPA两个持久层框架…
博文
SQL-小白最佳入门sql查询一
阅读数 2万+

一 说明 如果是初学者,建议去网上寻找安装Mysql的文章安装,以及使用navicat连接数据库,以后的示例基本是使用mysql数据库管理系统; 二 准备前提 需要建立一张学生表,列分别是id,名称,…
博文
项目中的if else太多了,该怎么重构?
阅读数 9万+

介绍 最近跟着公司的大佬开发了一款IM系统,类似QQ和微信哈,就是聊天软件。我们有一部分业务逻辑是这样的 if (msgType = “文本”) { // dosomething } else i…
博文
“狗屁不通文章生成器”登顶GitHub热榜,分分钟写出万字形式主义大作
阅读数 11万+

一、垃圾文字生成器介绍 最近在浏览GitHub的时候,发现了这样一个骨骼清奇的雷人项目,而且热度还特别高。 项目中文名:狗屁不通文章生成器 项目英文名:BullshitGenerator 根据作…
博文
程序员:我终于知道post和get的区别
阅读数 16万+

IT界知名的程序员曾说:对于那些月薪三万以下,自称IT工程师的码农们,其实我们从来没有把他们归为我们IT工程师的队伍。他们虽然总是以IT工程师自居,但只是他们一厢情愿罢了。 此话一出,不知激起了多少(…
博文
《程序人生》系列-这个程序员只用了20行代码就拿了冠军
阅读数 4万+

你知道的越多,你不知道的越多 点赞再看,养成习惯GitHub上已经开源https://github.com/JavaFamily,有一线大厂面试点脑图,欢迎Star和完善 前言 这一期不算…
博文
加快推动区块链技术和产业创新发展,2019可信区块链峰会在京召开
阅读数 3万+

11月8日,由中国信息通信研究院、中国通信标准化协会、中国互联网协会、可信区块链推进计划联合主办,科技行者协办的2019可信区块链峰会将在北京悠唐皇冠假日酒店开幕。   区块链技术被认为…
博文
程序员把地府后台管理系统做出来了,还有3.0版本!12月7号最新消息:已在开发中有github地址
阅读数 14万+

第一幕:缘起 听说阎王爷要做个生死簿后台管理系统,我们派去了一个程序员…… 996程序员做的梦: 第一场:团队招募 为了应对地府管理危机,阎王打算找“人”开发一套地府后台管理系统,于是…
博文
Android 9.0系统新特性,对刘海屏设备进行适配
阅读数 4377

其实Android 9.0系统已经是去年推出的“老”系统了,这个系统中新增了一个比较重要的特性,就是对刘海屏设备进行了支持。一直以来我也都有打算针对这个新特性好好地写一篇文章,但是为什么直到拖到了An…
博文
网易云6亿用户音乐推荐算法
阅读数 4万+

网易云音乐是音乐爱好者的集聚地,云音乐推荐系统致力于通过 AI 算法的落地,实现用户千人千面的个性化推荐,为用户带来不一样的听歌体验。 本次分享重点介绍 AI 算法在音乐推荐中的应用实践,以及在算法…
博文
【技巧总结】位运算装逼指南
阅读数 1万+

位算法的效率有多快我就不说,不信你可以去用 10 亿个数据模拟一下,今天给大家讲一讲位运算的一些经典例子。不过,最重要的不是看懂了这些例子就好,而是要在以后多去运用位运算这些技巧,当然,采用位运算,也…
博文
日均350000亿接入量,腾讯TubeMQ性能超过Kafka
阅读数 8030


博文
8年经验面试官详解 Java 面试秘诀
阅读数 6万+

作者 | 胡书敏 责编 | 刘静 出品 | CSDN(ID:CSDNnews) 本人目前在一家知名外企担任架构师,而且最近八年来,在多家外企和互联网公司担任Java技术面试官…
博文
面试官如何考察你的思维方式?
阅读数 3万+


博文
碎片化的时代,如何学习
阅读数 1万+

今天周末,和大家聊聊学习这件事情。 在如今这个社会,我们的时间被各类 APP 撕的粉碎。 刷知乎、刷微博、刷朋友圈; 看论坛、看博客、看公号; 等等形形色色的信息和知识获取方式一个都不错过。 貌似学了…
博文
so easy! 10行代码写个"狗屁不通"文章生成器
阅读数 6万+


博文
知乎高赞:中国有什么拿得出手的开源软件产品?(整理自本人原创回答)
阅读数 4万+

知乎高赞:中国有什么拿得出手的开源软件产品? 在知乎上,有个问题问“中国有什么拿得出手的开源软件产品(在 GitHub 等社区受欢迎度较好的)?” 事实上,还不少呢~ 本人于2019.7.6进行了较为…
博文
MySQL数据库总结
阅读数 5万+

文章目录一、数据库简介二、MySQL数据类型(5.5版本)三、Sql语句(1)Sql语句简介(2)数据定义语言DDLcreate,alter,drop(3)数据操纵语言DMLupdate,insert…
博文
记一次腾讯面试:进程之间究竟有哪些通信方式?如何通信? ---- 告别死记硬背
阅读数 3万+

有一次面试的时候,被问到进程之间有哪些通信方式,不过由于之前没深入思考且整理过,说的并不好。想必大家也都知道进程有哪些通信方式,可是我猜很多人都是靠着”背“来记忆的,所以今天的这篇文章,讲给大家详细着…
博文
20行Python代码爬取王者荣耀全英雄皮肤
阅读数 9万+

引言 王者荣耀大家都玩过吧,没玩过的也应该听说过,作为时下最火的手机MOBA游戏,咳咳,好像跑题了。我们今天的重点是爬取王者荣耀所有英雄的所有皮肤,而且仅仅使用20行Python代码即可完成。 准备工…
博文
程序设计的5个底层逻辑,决定你能走多快
阅读数 3万+

阿里妹导读:肉眼看计算机是由CPU、内存、显示器这些硬件设备组成,但大部分人从事的是软件开发工作。计算机底层原理就是连通硬件和软件的桥梁,理解计算机底层原理才能在程序设计这条路上越走越快,越走越轻松。…
博文
张小龙-年薪近3亿的微信之父,他是如何做到的?
阅读数 8万+

张小龙生于湖南邵东魏家桥镇, 家庭主要特点:穷。 不仅自己穷,亲戚也都很穷,可以说穷以类聚。爷爷做过铜匠,总的来说,标准的劳动阶级出身。 家有兄弟两人, 一个小龙,一个小虎。 小虎好动,与邻…
博文
阿里靠什么武功秘籍渡过“双十一“的天量冲击
阅读数 3万+

双十一大概会产生多大的数据量呢,可能大家没概念,举个例子央视拍了这么多年电视新闻节目,几十年下来他存了大概80P的数据。而今年双11一天,阿里要处理970P的数据,做为一个IT人,笔者认为今年”双十一…
博文
西游记团队中如果需要裁掉一个人,会先裁掉谁?
阅读数 5万+

2019年互联网寒冬,大批企业开始裁员,下图是网上流传的一张截图: 裁员不可避免,那如何才能做到不管大环境如何变化,自身不受影响呢? 我们先来看一个有意思的故事,如果西游记取经团队需要裁员一名,会裁…
博文
4G LTE/EPC 协议栈
阅读数 1746

目录 文章目录目录前文列表LTE/EPC 协议栈概览LTE 层 1LTE 层 2MACRLCPDCP参考文档 前文列表 《LTE/EPC 第四代移动通信系统》 LTE/EPC 协议栈概览 LTE/E…
博文
iOS Bug 太多,苹果终于坐不住了!
阅读数 4万+

开源的 Android 和闭源的 iOS,作为用户的你,更偏向哪一个呢? 整理 | 屠敏 出品 | CSDN(ID:CSDNnews) 毋庸置疑,当前移动设备操作系统市场中,Android …
博文
究竟你适不适合买Mac?
阅读数 2万+

我清晰的记得,刚买的macbook pro回到家,开机后第一件事情,就是上了淘宝网,花了500元钱,找了一个上门维修电脑的师傅,上门给我装了一个windows系统。。。。。。 表砍我。。。 当时买ma…
博文
python json java mysql pycharm android linux json格式 c#处理浮点数 c# 生成字母数字随机数 c# 动态曲线 控件 c# oracle 开发 c#选择字体大小的控件 c# usb 批量传输 c#10进制转8进制 c#转base64 c# 科学计算 c#下拉列表获取串口
没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

niaobirdfly
TA的个人主页 >
原创
136
粉丝
1716
获赞
903
评论
367
访问
76万+
等级:
周排名:
1618
积分:
6642
总排名:
5711
勋章:

关注
私信
最新文章
一个权限管理系统设计案例
分库分表实战
一文读懂IO通信模型和Reactor线程模型
HttpClient连接池小记
SpringCloud教程
分类专栏

Java
76篇

随笔
11篇

Web
24篇

algorithm
11篇

database
23篇

linux/系统运维
22篇

design pattern

开源框架/hadoop/hbase/spark
13篇

data mining
2篇

python
9篇

支付
4篇
展开

最新评论
SpringCloud教程
m0_37888675:你这个6.1里面,注册中心换成zookeeper后,order可以成功注册到注册中心里,但是item只是注释掉eureka好像不行吧?我试了好久,我最后把整个bootstrap.yml都注释掉(只注释里面eureka的配置好像没用),然后把与之相关的rabbit和jdbc的那几个类以及方法都注释掉,然后再启动,这个时候是可以成功注册进去的,并且order也可以成功通过注册中心调用。请问,这个正常应该是如何配置的?应该不需要注释这么多吧?

Mybatis教程-实战看这一篇就…
weixin_39040527:牛逼啊

SpringCloud教程
GxGQAQ:后来的兄弟记着把启动类放到包的最外层,这样不配置componentscan也可以扫描, 放到里面的,得配置上componentscan,这个问题很重要,哈哈, 尤其是加了安全验证的,配置没问题还是注册不上的,检查一下自己的配置文件那个类扫描了没有

SpringCloud教程
GxGQAQ:[reply]qq_37739233[/reply]兄弟,我也是这个问题,一直没有解决,看到你的回复豁然开朗。哈哈

Mybatis教程-实战看这一篇就…
xiaoyong21:写的很好

归档
2020年1月1篇
2019年12月9篇
2019年11月1篇
2019年10月1篇
2019年8月1篇
2019年6月1篇
2019年5月1篇
2019年4月1篇
2019年1月1篇
2018年12月1篇
2018年10月3篇
2018年8月2篇
2018年7月1篇
2018年6月2篇
2018年5月5篇
2017年11月1篇
2017年9月3篇
2017年8月1篇
2017年7月9篇
2017年6月4篇
2016年6月2篇
2016年4月2篇
2016年1月2篇
2015年11月4篇
2015年10月1篇
2015年9月8篇
2015年8月5篇
2015年7月8篇
2015年6月1篇
2015年5月6篇
2015年4月7篇
2015年3月4篇
2015年1月1篇
2014年12月5篇
2014年11月1篇
2014年8月4篇
2014年7月10篇
2014年6月3篇
2014年5月9篇
2014年4月9篇
2014年3月5篇
2013年10月1篇
2013年6月1篇
2013年5月2篇
2013年4月3篇
2013年3月5篇
2013年1月3篇
2012年9月1篇
2012年2月1篇
展开

热门文章
RabbitMQ教程
阅读数 220577

Mybatis教程-实战看这一篇就够了
阅读数 101193

SpringCloud教程
阅读数 34040

dubbo教程
阅读数 29936

常用Linux日志查看命令
阅读数 21538

程序人生

CSDN资讯

kefu@csdn.netQQ客服

客服论坛400-660-0108

工作时间 8:30-22:00

关于我们招聘广告服务 网站地图

京ICP备19004658号 经营性网站备案信息

公安备案号 11010502030143

©1999-2020 北京创新乐知网络技术有限公司

网络110报警服务

北京互联网违法和不良信息举报中心

中国互联网举报中心家长监护版权申诉
————————————————
版权声明:本文为CSDN博主「niaobirdfly」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hellozpc/article/details/81436980

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值