消息中间件rabbitMQ
一、安装
1、erlang 安装
安装Erlang OTP,由于RabbitMQ是用Erlang编写的,所以在安装RabbitMQ之前要先安装Erlang 下载地址:http://www.erlang.org/downloads 下载最新版本即可,例如下载解压后运行 otp_win64_18.1.exe, 然后一直默认下一步下一步即可,没有什么特别说明的。使用默认设置安装完毕之后,会在看到 erl.exe 文件,它就相当于 jdk里的 java.exe。 这就说明安装好了。
安装完成后我们就要来配置环境变量了:
找到【我的电脑】右键【属性】–》【高级系统设置】–》选择【高级】–》【环境变量】–》【新建】
1、在系统变量中新建变量:ERLANG_HOME,变量值为安装Erlang的路径(不经不要包含bin目录)
2、新建一个系统变量:变量名为ERLANG_HOME,变量值为安装Erlang的路径(路径中不要包含bin目录)
3、将新建的系统变量添加在【系统变量】Path中:格式为%ERLANG_HOME%\bin(这里我用的win7添加path的)
4、然后我们打开cmd输入erl查看是Erlan是否安装好、下图就表示已经安装好了
2、安装RabbitMQ
安装RabbitMQ,在官网上下载最新的RabbitMQ(http://www.rabbitmq.com/)
步骤1:例如下载安装运行里面的 rabbitmq-server-3.6.5.exe ,使用默认设置,下一步下一步即可。安装成功之后,在如图所示位置可以看到这些文件就表示安装OK了。
步骤2、
- 首先使用cmd进入sbin目录(我的sbin目录是:D:\javadevelop\rabbitMq\RabbitMQ Server\rabbitmq_server-3.6.5\sbin)
- 然后输入: rabbitmq-plugins.bat enable rabbitmq_management 开启插件
- 重启 rabbitmq,运行命令: net stop RabbitMQ && net start RabbitMQ
(如下图表示重启成功)
- 访问管理界面输入网址:http://127.0.0.1:15672, 账号:guest,密码:guest
- 管理界面登录成功
二、介绍
1、RABBITMQ - 模式讲解
步骤1:AMQP
与activemq不一样, rabbitmq 使用的是一种叫做 AMQP 的协议来通信。 AMQP 是 dvanced Message Queuing Protocol 的缩写。协议内容我们就没必要深入研究了,简单地说,通过这种协议,可以处理更为复杂的业务需求~以下就是基于 AMQP 这种协议,可以实现的各种模式
步骤 2 : 消息路由过程
与 ActiveMQ 拿到消息就直接放在队列等待消费者拿走不同, Rabbit 拿到消息之后,会先交给 交换机 (Exchange), 然后交换机再根据预先设定的不同绑定( Bindings )策略,来确定要发给哪个队列。如图所示,比起 ActiveMQ 多了 Exchange 和 Bindings。正式由于有了 Exchange 和 Bindings, RabbitMQ 就可以灵活地支撑各种模式。
步骤 3 : 模式
RabbitMQ提供了四种Exchange模式:fanout,direct,topic,header 。 header模式在实际使用中较少,这里只讨论前三种模式.
1、fanout 模式:fanout 模式就是广播模式,消息来了会发给所有的队列
2、Direct 模式:Direct 模式就是指定队列模式, 消息来了,只发给指定的 Queue, 其他Queue 都收不到。
3、Topic 模式:主题模式,注意这里的主题模式,和 ActivityMQ 里的不一样。 ActivityMQ 里的主题,更像是广播模式。
那么这里的主题模式是什么意思呢? 如图所示消息来源有: 美国新闻,美国天气,欧洲新闻,欧洲天气。
如果你想看 美国主题: 那么就会收到 美国新闻,美国天气。
如果你想看 新闻主题: 那么就会收到 美国新闻,欧洲新闻。
如果你想看 天气主题: 那么就会收到 美国天气,欧洲天气。
如果你想看 欧洲主题: 那么就会收到 欧洲新闻,欧洲天气。
这样就可以灵活搭配~
2、RABBITMQ - 模式代码
2.1 FANOUT 模式代码
发送消息
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//生产者
public class TestProducer {
//定义交换模式名称
private static final String EXCHANGE_NAME="fanout_exchange";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//检查服务是否启动
RabbitMQUtil.checkServer();
//创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置RabbitMQ相关信息
connectionFactory.setHost("localhost");
//创建一个新的连接
Connection connection = connectionFactory.newConnection();
///创建一个通道
Channel channel = connection.createChannel();
//交换模式声明
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//消息发送代码
for(int i=0;i<100;i++){
Thread.sleep(1000);
//生产者发送消息
String message="fanout模式下消息发送 "+i;
byte[] bytes = message.getBytes("UTF-8");
channel.basicPublish(EXCHANGE_NAME,"",null,bytes);
System.out.println("发送消息: " + message);
}
//关闭
channel.close();
connection.close();
}
}
接收消息:
package com.example.rabbitmq;
import cn.hutool.core.util.RandomUtil;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//消费者
public class TestDriectCustomer {
private static final String EXCHANGE_NAME="fanout_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
//检查服务是否启动
RabbitMQUtil.checkServer();
//为当前消费者取随机名
String name="consumer-"+RandomUtil.randomString(5);
//创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//这里设置RabbitMq连接设置
connectionFactory.setHost("localhost");
//新建连接
Connection connection = connectionFactory.newConnection();
//创建渠道
Channel channel = connection.createChannel();
//交换机声明(交换名称,交换模式)
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//获取消息队列名称
String queue = channel.queueDeclare().getQueue();
//队列与交换机绑定(参数为:队列名称;交换机名称;routingKey忽略)
channel.queueBind(queue,EXCHANGE_NAME, "");
//等待消息
System.out.println(name+" 等待消息......");
//DefaultConsumer类实现了Consumer接口,通过传入一个频道,
// 告诉服务器我们需要那个频道的消息,如果频道中有消息,就会执行回调函数handleDelivery
DefaultConsumer 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");
System.out.println(name+" "+"接收到来自消息: "+message);
}
};
//自动回复队列应答 -- RabbitMQ中的消息确认机制
channel.basicConsume(queue,true,consumer);
}
}
先运行两次 TestCustomer,启动两个消费者。然后运行一次 TestProducer, 启动生产者,生产100条信息。 此时就可以看到如图所示两个消费者都能收到 这100条信息。
pom.xml中:提供 rabbitmq和hutool的jar
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.3.1</version>
</dependency>
2.2 DIRECT 模式代码
发送消息
package com.example.rabbitmq;
import cn.hutool.core.util.NetUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//exchange的driect模式
public class TestDriectProducer {
public final static String QUEUE_NAME="direct_queue";
public static void main(String[] args) throws IOException, TimeoutException, TimeoutException, InterruptedException {
//检验服务器是否
if(NetUtil.isUsableLocalPort(15672)) {
System.out.println("RabbitMQ 服务器未启动 ");
System.exit(1);
}
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置RabbitMQ相关信息
factory.setHost("localhost");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个通道
Channel channel = connection.createChannel();
//交换模式声明
channel.exchangeDeclare(QUEUE_NAME,"direct");
for (int i = 0; i < 100; i++) {
Thread.sleep(1000);
String message = "direct 消息 " +i;
//发送消息到队列中
//参数说明:string exchange -- 交换机名称,String routingKey -- 路由关键字,BasicProperties props -- 消息的基本属性,例如路由头等,
//byte[] body -- 消息体
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
System.out.println("发送消息: " + message);
}
//关闭通道和连接
channel.close();
connection.close();
}
}
接收消息
package com.example.rabbitmq;
import cn.hutool.core.util.NetUtil;
import cn.hutool.core.util.RandomUtil;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//消息消费者:direct模式下
public class TestDirectCustomer {
private final static String QUEUE_NAME = "direct_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//检验服务器是否
if(NetUtil.isUsableLocalPort(15672)) {
System.out.println("RabbitMQ 服务器未启动 ");
System.exit(1);
}
//为当前消费者取随机名
String name = "consumer-"+ RandomUtil.randomString(5);
//判断服务器是否启动
RabbitMQUtil.checkServer();
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置RabbitMQ地址
factory.setHost("localhost");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个通道
Channel channel = connection.createChannel();
//交换模式声明
channel.exchangeDeclare(QUEUE_NAME,"direct");
//声明要关注的队列,参数:队列名称;是否持久化;是否排外的;是否自动删除
channel.queueDeclare(QUEUE_NAME, false, false, true, null);
System.out.println(name +" 等待接受消息");
//DefaultConsumer类实现了Consumer接口,通过传入一个频道,
// 告诉服务器我们需要那个频道的消息,如果频道中有消息,就会执行回调函数handleDelivery
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");
System.out.println(name + " 接收到消息 '" + message + "'");
}
};
//自动回复队列应答 -- RabbitMQ中的消息确认机制
//参数:队列名称;消息标记;消费者
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
运行一次生产者TestDriectProducer,两次消费者TestDirectCustomer
2.2 TOPIC 模式代码
发送消息
package com.example.rabbitmq;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//消息生成者
public class TestTopicProducer {
public final static String EXCHANGE_NAME="topics_exchange";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//检验服务器是否
if(NetUtil.isUsableLocalPort(15672)) {
System.out.println("RabbitMQ 服务器未启动 ");
System.exit(1);
}
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置RabbitMQ相关信息
factory.setHost("localhost");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个通道
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String[] routing_keys = new String[] { "usa.news", "usa.weather",
"europe.news", "europe.weather" };
String[] messages = new String[] { "美国新闻", "美国天气",
"欧洲新闻", "欧洲天气" };
for (int i = 0; i < routing_keys.length; i++) {
Thread.sleep(5000);
String routingKey = routing_keys[i];
String message = messages[i];
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
System.out.printf("发送消息到路由:%s, 内容是: %s%n ", routingKey,message);
}
//关闭通道和连接
channel.close();
connection.close();
}
}
接收消息:专门用于接受 usa.* 消息
package com.example.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//设置路由,专门用于接受 usa.* 消息
public class TestTopicCustomer1 {
public final static String EXCHANGE_NAME="topics_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
//检验服务器是否
if(NetUtil.isUsableLocalPort(15672)) {
System.out.println("RabbitMQ 服务器未启动 ");
System.exit(1);
}
//为当前消费者取名称
String consumerName = "consumer-usa";
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置RabbitMQ地址
factory.setHost("localhost");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个通道
Channel channel = connection.createChannel();
//交换机声明(参数为:交换机名称;交换机类型)
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//获取一个临时队列
String queue = channel.queueDeclare().getQueue();
//接受USA信息
//参数:String queue, String exchange, String routingKey
channel.queueBind(queue, EXCHANGE_NAME, "usa.*");
System.out.println(consumerName +" 等待接受消息");
//DefaultConsumer类实现了Consumer接口,通过传入一个频道,
// 告诉服务器我们需要那个频道的消息,如果频道中有消息,就会执行回调函数handleDelivery
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");
System.out.println(consumerName + " 接收到消息 '" + message + "'");
}
};
//自动回复队列应答 -- RabbitMQ中的消息确认机制
channel.basicConsume(queue, true, consumer);
}
}
接收消息:专门用于接受 *.news 消息
package com.example.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class TestTopicCustomer2 {
public final static String EXCHANGE_NAME="topics_exchange";
//设置路由,专门用于接受 *.news 消息
public static void main(String[] args) throws IOException, TimeoutException {
//为当前消费者取名称
String consumerName = "consumer-news";
//判断服务器是否启动
RabbitMQUtil.checkServer();
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置RabbitMQ地址
factory.setHost("localhost");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个通道
Channel channel = connection.createChannel();
//交换机声明(参数为:交换机名称;交换机类型)
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//获取一个临时队列
String queueName = channel.queueDeclare().getQueue();
//接受 USA 信息
//String queue, String exchange, String routingKey
channel.queueBind(queueName, EXCHANGE_NAME, "*.news");
System.out.println(consumerName +" 等待接受消息");
//DefaultConsumer类实现了Consumer接口,通过传入一个频道,
// 告诉服务器我们需要那个频道的消息,如果频道中有消息,就会执行回调函数handleDelivery
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");
System.out.println(consumerName + " 接收到消息 '" + message + "'");
}
};
//自动回复队列应答 -- RabbitMQ中的消息确认机制
channel.basicConsume(queueName, true, consumer);
}
}
运行一次生产者TestTopicProducer,两次消费者TestTopicCustomer1,TestTopicCustomer2,运行结果如下图