rabbitmq介绍
AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。 AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。 下面将重点介绍RabbitMQ中的一些基础概念,了解了这些概念,是使用好RabbitMQ的基础。
ConnectionFactory、Connection、Channel都是RabbitMQ对外提供的API中最基本的对象。Connection是RabbitMQ的socket链接,它封装了socket协议相关部分逻辑。ConnectionFactory为Connection的制造工厂。 Channel是我们与RabbitMQ打交道的最重要的一个接口,我们大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。
rabbitmq原理图解
1.安装rabbitmq
rabbitmq适合安装在linux系统上
安装过程 参考 (http://www.rabbitmq.com/install-rpm.html)
安装命令:yum -y install epel-release.noarch
直接无法安装需要安装类似maven的私服叫epel-release.noarch
查看是否存在rabbitmq 然后安装
yum search rabbitmq-server
yum -y install rabbitmq-server
搜索包 rpm -ql rabbitmq-server
控制的命令在 /sbin/rabbitmqct
默认没有配置文件
查看rabbitmq-server被安装的所有文件的位置
/etc/logrotate.d/rabbitmq-server
/etc/rabbitmq
/etc/rabbitmq/rabbitmq.config
/usr/lib/ocf/resource.d/rabbitmq/rabbitmq-server
/usr/lib/rabbitmq/bin
/usr/lib/rabbitmq/bin/rabbitmq-defaults
/usr/lib/rabbitmq/bin/rabbitmq-env
/usr/lib/rabbitmq/bin/rabbitmq-plugins
/usr/lib/rabbitmq/bin/rabbitmq-server
/usr/lib/rabbitmq/bin/rabbitmqctl
其中比较重要的两个参考文件(这两个只是参考)
运行配置文件:rabbitmq.config.example
环境配置文件:/usr/share/man/man5/rabbitmq-env.conf.5.gz
默认该两文件需要配置在 /etc/rabbitmq目录下 默认只有一个
配置文件在 rabbitmq.config-example中需要将改目录考到 cd /etc/rabbitmq目录下
默认安装过程中会自动到操作系统创建一个账号rabbitmq改账号默认在 /etc/passwd 目录下
rabbitmq默认安装后 会添加账号rabbitmq 默认以该账号运行
2.启动rabbitmq
启动后 查看默认的端口 5672
启动 命令
service rabbitmq-server start
Starting rabbitmq-server: SUCCESS启动成功
查看端口是否启动命令
windows中查看端口命令将grep换成find即可
[root@hyp]# netstat -aon | grep 5672
tcp 0 0 0.0.0.0:25672 0.0.0.0:* LISTEN off (0.00/0/0)
tcp6 0 0 :::5672 :::* LISTEN off (0.00/0/0)
默认会在 cd/etc/init.d目录下会创建一个文件叫rabbitmq-server
cd/etc/init.d服务的路径
查看运行状态
[root@hyp]# service rabbitmq-server status
Redirecting to /bin/systemctl status rabbitmq-server.service
● rabbitmq-server.service - RabbitMQ broker
Loaded: loaded (/usr/lib/systemd/system/rabbitmq-server.service; disabled; vendor preset: disabled)
Active: active (running) since Tue 2017-11-07 05:52:27 PST; 6min ago
Main PID: 8507 (beam)
CGroup: /system.slice/rabbitmq-server.service
├─8507 /usr/lib64/erlang/erts-5.10.4/bin/beam -W w -K true -A30 -P 1048576 -- -root /usr/lib64/erlang -progname...
├─8522 /usr/lib64/erlang/erts-5.10.4/bin/epmd -daemon
├─8579 inet_gethost 4
└─8580 inet_gethost 4
Nov 07 05:52:24 node3 systemd[1]: Starting RabbitMQ broker...
Nov 07 05:52:24 node3 systemd[1]: rabbitmq-server.service: Got notification message from PID 8522, but reception on...ID 8507
Nov 07 05:52:26 node3 rabbitmq-server[8507]: RabbitMQ 3.3.5. Copyright (C) 2007-2014 GoPivotal, Inc.
Nov 07 05:52:26 node3 rabbitmq-server[8507]: ## ## Licensed under the MPL. See http://www.rabbitmq.com/
Nov 07 05:52:26 node3 rabbitmq-server[8507]: ## ##
Nov 07 05:52:26 node3 rabbitmq-server[8507]: ########## Logs: /var/log/rabbitmq/rabbit@node3.log
Nov 07 05:52:26 node3 rabbitmq-server[8507]: ###### ## /var/log/rabbitmq/rabbit@node3-sasl.log
Nov 07 05:52:26 node3 rabbitmq-server[8507]: ##########
Nov 07 05:52:27 node3 systemd[1]: Started RabbitMQ broker.
Nov 07 05:52:27 node3 rabbitmq-server[8507]: Starting broker... completed with 0 plugins.
Hint: Some lines were ellipsized, use -l to show in full.
查看已经安装的插件
rabbitmq-plugins list
默认在rabbitmq-plugins
rabbitmq默认提供了一个web管理工具(rabbitmq_management)参考官方http://www.rabbitmq.com/management.html 默认已经安装 是一个插件
查看所有插件
启动rabbitmq的界面插件
./rabbitmq-plugins enable rabbitmq_management
重启rabbitmq-server
service rabbitmq-server restart
加入插件后会有一个默认端口15672 浏览器访问(当前机器ip )http://192.168.73.130:15672/
输入用户名和密码 guest和guest
登录后效果
3.rabbitmq api调用
参考官方文档 http://www.rabbitmq.com/getstarted.html
rabbitmq 官方操作api提供了n多种语言 前面几种都会 java是本职 所以使用java
rabbitmq支持6种消息接受和转发机制
1.简单模式(http://www.rabbitmq.com/tutorials/tutorial-one-java.html)
Queue(队列)是RabbitMQ的内部对象,用于存储消息,用下图表示。
RabbitMQ中的消息都只能存储在Queue中,生产者(下图中的P)生产消息并最终投递到Queue中,消费者(下图中的C)可以从Queue中获取消息并消费。
eclipse添加maven的jar项目添加依赖
<!-- rabbitmq的配置 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.2.0</version>
</dependency>
生产者代码
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* 简单模式
* @author Administrator
*
*/
public class Pub_Email {
/**
* 序列化
* 将对象序列化转为字节数组
*/
public static byte[] ser(Object obj)throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(obj);
return bos.toByteArray();
}
/**
* 反序列化
* 将字节数组反序列化转为字节数组
*/
public static Object dser(byte[] src)throws Exception{
ByteArrayInputStream bis=new ByteArrayInputStream(src);
ObjectInputStream ois=new ObjectInputStream(bis);
ois.readObject();
return ois.readObject();
}
/**
* 任务被发送的队列名称
*/
static String QUEUE_NAME="MAIL_QUEUE";
public static void main(String[] args) throws Exception, TimeoutException {
// 模拟一个任务消息
Map<String, String> map=new HashMap<String, String>();
map.put("sendTo","hyp2549882772@126.com");
map.put("subject", "测试邮件");
map.put("content", "注册成功您的验证马是123456");
//推送到队列服务器
//连接远程rabbit-server服务器
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.73.130");
factory.setPort(5672);
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//定义创建一个队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发送的消息
String message="Hello World";
//生产者向消费这发送消息(pulsh) 注意发送和接收段相同字符集则出现乱码
channel.basicPublish("", QUEUE_NAME, null, ser(map));
System.out.println(" [x] Sent 5 message");
//关闭频道
channel.close();
//关闭连接
connection.close();
}
}
消费者代码
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* 简单模式
* @author Administrator
*
*/
public class Mail_Cons {
/**
* 序列化
* 将对象序列化转为字节数组
*/
public static byte[] ser(Object obj)throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(obj);
return bos.toByteArray();
}
/**
* 反序列化
* 将字节数组反序列化转为字节数组
*/
public static Object dser(byte[] src)throws Exception{
ByteArrayInputStream bis=new ByteArrayInputStream(src);
ObjectInputStream ois=new ObjectInputStream(bis);
return ois.readObject();
}
/**
*异步接收
*/
static String QUEUE_NAME="MAIL_QUEUE";
public static void main(String[] args) throws Exception, TimeoutException {
//创建一个ConnectionFactory连接工厂
ConnectionFactory factory = new ConnectionFactory();
//通过ConnectionFactory设置Rabbitmq所在ip等信息
factory.setHost("192.168.73.130");
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//消费者也需要定义队列 有可能消费者先于生产者启动
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//定义回调抓取消息
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
try {
Map map=(Map)Mail_Cons.dser(body);
System.out.println(map.get("content"));
} catch (Exception e) {
e.printStackTrace();
}
}
};
//为channel指定消费者
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
消费这发送邮件
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
@RestController
public class Mail_Cons {
@Autowired
private JavaMailSender jms;
/**
* 序列化
* 将对象序列化转为字节数组
*/
public static byte[] ser(Object obj)throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(obj);
return bos.toByteArray();
}
/**
* 反序列化
* 将字节数组反序列化转为字节数组
*/
public static Object dser(byte[] src)throws Exception{
ByteArrayInputStream bis=new ByteArrayInputStream(src);
ObjectInputStream ois=new ObjectInputStream(bis);
return ois.readObject();
}
/**
*异步接收
*/
static String QUEUE_NAME="MAIL_QUEUE";
@GetMapping("/send")
public String send() throws Exception{
//创建一个ConnectionFactory连接工厂
ConnectionFactory factory = new ConnectionFactory();
//通过ConnectionFactory设置Rabbitmq所在ip等信息
factory.setHost("192.168.73.130");
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//消费者也需要定义队列 有可能消费者先于生产者启动
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//定义回调抓取消息
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
try {
Map map=(Map)Mail_Cons.dser(body);
System.out.println(map.get("content"));
//真实发送邮件
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom("hyp2549882772@126.com");
mailMessage.setTo(map.get("sendTo").toString());
mailMessage.setSubject(map.get("subject").toString());
mailMessage.setText(map.get("content").toString());
jms.send(mailMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
};
//为channel指定消费者
channel.basicConsume(QUEUE_NAME, true, consumer);
return "1";
}
}
application.properties
spring.mail.host=smtp.126.com
spring.mail.username= hyp2549882772
spring.mail.password= yyz123456
spring.mail.port= 25
eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka/
spring.application.name=SENDMAIL
server.port=8080
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.et</groupId>
<artifactId>Rabbitmq-Cons</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- rabbitmq的配置 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.2.0</version>
</dependency>
<!-- 发送邮件配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- 将发送邮件的为服务注册到eureka注册中心的配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
</project>
生产者启动后
消费者启动后
2. 工作队列模式(http://www.rabbitmq.com/tutorials/tutorial-two-java.html)
对于资源密集型任务,我们等待其处理完成在很多情况下是不现实的,比如无法在http的短暂请求窗口中处理大量耗时任务,
为了达到主线程无需等待,任务异步执行的要求,我们可以将任务加入任务队列,如图,多个workers可以共享
工作队列一般用于任务分配 发布者发布任务到队列 多个消息接受者 接受消息 谁接受到某个消息 其他接受者就只能消费其他消息 队列中的
工作队列一般用于任务分配 发布者发布任务到队列 多个消息接受者 接受消息 谁接受到某个消息 其他接受者就只能消费其他消息 队列中的
一个消息只能被一个接受者消费
生产者代码
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* 工作队列模式
* @author Administrator
*
*/
public class Pub_Work {
/**
* 任务被发送的队列名称
*/
static String QUEUE_NAME="WORK_QUEUE";
public static void main(String[] args) throws Exception, TimeoutException {
//推送到队列服务器
//连接远程rabbit-server服务器
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.73.130");
factory.setPort(5672);
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//定义创建一个队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发送的消息
String message="Hello World";
for(int i=0;i<10;i++){
//生产者向消费这发送消息(pulsh) 注意发送和接收段相同字符集则出现乱码
channel.basicPublish("", QUEUE_NAME, null,("这是"+i).getBytes("UTF-8"));
}
System.out.println(" [x] Sent 5 message");
//关闭频道
channel.close();
//关闭连接
connection.close();
}
}
消费者代码
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* 工作队列模式
* @author Administrator
*
*/
public class Mail_Work {
/**
*异步接收
*/
static String QUEUE_NAME="WORK_QUEUE";
public static void main(String[] args) throws Exception, TimeoutException {
//创建一个ConnectionFactory连接工厂
ConnectionFactory factory = new ConnectionFactory();
//通过ConnectionFactory设置Rabbitmq所在ip等信息
factory.setHost("192.168.73.130");
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//消费者也需要定义队列 有可能消费者先于生产者启动
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//定义回调抓取消息
Consumer consumer = 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"));
}
};
//为channel指定消费者
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
测试先同时启动两个任务接受者
可以通过 webgui 查看 是否出现了两个连接 一个队列(监听同一个队列mywork)
运行 发布者 查看两个接受者的控制台 发现一个接收到三条消息
第一个接受者
这是0
这是2
这是4
这是6
这是8
第二个接受者
这是1
这是3
这是5
这是7
这是9
3.发布订阅模式(http://www.rabbitmq.com/tutorials/tutorial-three-java.html)
RabbitMQ消息模型的核心理念是生产者永远不会直接发送任何消息给队列,一般的情况生产者甚至不知道消息应该发送到哪些队列。
相反的,生产者只能发送消息给转发器(Exchange)。转发器是非常简单的,一边接收从生产者发来的消息,另一边把消息推送到队列中。转发器必须清楚的知道消息如何处理它收到的每一条消息。是否应该追加到一个指定的队列?是否应该追加到多个队列?或者是否应该丢弃?这些规则通过转发器的类型进行定义。
发布订阅模式 引入了交换器的概念 消息发布者发布消息到交换器 订阅者定义一个队列(每个订阅者定义一个)用于接受消息 交换器 起到了
交换器 有以下几种类型 direct, topic, headers and fanout
发布订阅模式 应该使用fanout(广播)
通过命令 rabbitmqctl list_exchanges 或者查看webgui 查看exchange
列表中有很多amq.*开头的交换器 第一个是默认 之前的两种简单模式和工作队列模式未定义交换器 使用的是第一个defalt
发布者代码
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
/**
*发布订阅模式
* @author Administrator
*
*/
public class Pub_Log {
/**
*交换器的名字 交换器有很多类型
*/
private static String EXCHANGE_NAME="amp_log";
public static void main(String[] args) throws Exception, TimeoutException {
//推送到队列服务器
//连接远程rabbit-server服务器
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.73.130");
factory.setPort(5672);
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//发布订阅模式 没有key 为空 类型(fanout)
channel.exchangeDeclare(EXCHANGE_NAME, "fanout",true);
String message = null;
//同时发送5条消息
for(int i=0;i<10;i++){
message="发送第"+i+"消息";
//第二个参数就是routingkey 不填 默认会转发给所有的订阅者队列
channel.basicPublish(EXCHANGE_NAME, "", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes("UTF-8"));
}
System.out.println(" [x] Sent 6 message");
}
}
订阅者代码
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* 发布订阅模式
* @author Administrator
*
*/
public class Log_Cons {
/**
*交换器的名字 交换器有很多类型
*/
private static String EXCHANGE_NAME="amp_log";
public static void main(String[] args) throws Exception, TimeoutException {
//创建一个ConnectionFactory连接工厂
ConnectionFactory factory = new ConnectionFactory();
//通过ConnectionFactory设置Rabbitmq所在ip等信息
factory.setHost("192.168.73.130");
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//消费者也需要定义队列 有可能消费者先于生产者启动
channel.exchangeDeclare(EXCHANGE_NAME, "fanout",true);
//channel.basicQos(1);
//产生一个随机的队列 该队列用于从交换器获取消息
String queueName = channel.queueDeclare().getQueue();
//将队列和某个交换机丙丁 就可以正式获取消息了 routingkey和交换器的一样都设置成空
channel.queueBind(queueName, EXCHANGE_NAME, "");
//定义回调抓取消息
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(message);
//参数2 true表示确认该队列所有消息 false只确认当前消息 每个消息都有一个消息标记
}
};
//参数2 表示手动确认
channel.basicConsume(queueName, true, consumer);
}
}
先启动两个订阅者 Rec
发送者Pub执行
发现两个订阅者都获取到了消息
发送第0消息
发送第1消息
发送第2消息
发送第3消息
发送第4消息
发送第5消息
发送第6消息
发送第7消息
发送第8消息
发送第9消息
4.路由模式(http://www.rabbitmq.com/tutorials/tutorial-three-java.html)
不同机器用来做不同的事情 类型 direct
路由模式其实和订阅模式差不多,只不过交换机的类型不同而已
前面第三种发布订阅模式 每个订阅者都会收到交换器发出的消息 因为发布者发布消息的routingkey是空 订阅者接受消息队列的routingkey也是空
发布者发布的消息 所有的订阅者都能接收到 路由模式表示 发布者发布的消息的routingkey和订阅者接受消息队列的routingkey都不为空 相同的则可以
发布者代码
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
/**
*路由模式
* @author Administrator
*
*/
public class Pub_Log_Route {
/**
*交换器的名字 交换器有很多类型
*/
private static String EXCHANGE_NAME="amp_log_route";
public static void main(String[] args) throws Exception, TimeoutException {
//推送到队列服务器
//连接远程rabbit-server服务器
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.73.130");
factory.setPort(5672);
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//路由模式 类型(direct)
channel.exchangeDeclare(EXCHANGE_NAME, "direct",true);
String message = null;
//第二个参数就是routingkey 不填 默认会转发给所有的订阅者队列
channel.basicPublish(EXCHANGE_NAME, "error", MessageProperties.PERSISTENT_TEXT_PLAIN, "系统奔溃,内存溢出".getBytes("UTF-8"));
channel.basicPublish(EXCHANGE_NAME, "info", MessageProperties.PERSISTENT_TEXT_PLAIN, "张三于2018-1-11号访问该页面".getBytes("UTF-8"));
channel.basicPublish(EXCHANGE_NAME, "info", MessageProperties.PERSISTENT_TEXT_PLAIN, "李四于2018-1-11号登录该网页".getBytes("UTF-8"));
System.out.println(" [x] Sent 6 message");
}
}
订阅者代码
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* 路由模式 类型 direct
* @author Administrator
*
*/
public class Log_Cons_Route {
/**
*交换器的名字 交换器有很多类型
*/
private static String EXCHANGE_NAME="amp_log_route";
public static void main(String[] args) throws Exception, TimeoutException {
//创建一个ConnectionFactory连接工厂
ConnectionFactory factory = new ConnectionFactory();
//通过ConnectionFactory设置Rabbitmq所在ip等信息
factory.setHost("192.168.73.130");
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//消费者也需要定义队列 有可能消费者先于生产者启动
channel.exchangeDeclare(EXCHANGE_NAME, "direct",true);
//channel.basicQos(1);
//产生一个随机的队列 该队列用于从交换器获取消息
String queueName = channel.queueDeclare().getQueue();
//将队列和某个交换机丙丁 就可以正式获取消息了 routingkey和交换器的一样都设置成空
channel.queueBind(queueName, EXCHANGE_NAME, "info");
// channel.queueBind(queueName, EXCHANGE_NAME, "error");
//定义回调抓取消息
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(message);
//参数2 true表示确认该队列所有消息 false只确认当前消息 每个消息都有一个消息标记
}
};
//参数2 表示手动确认
channel.basicConsume(queueName, true, consumer);
}
}
先启动两个订阅者 Rec
发送者Pub执行
第一个订阅者获取到消息
张三于2018-1-11号访问该页面
李四于2018-1-11号登录该网页
第二个订阅者获取到消息
系统奔溃,内存溢出
5.Topics路由模式(http://www.rabbitmq.com/tutorials/tutorial-three-java.html)
该种模式和路由模式类似 只是消息的routingkey 是通过.隔开的多个字符组成 订阅者的消息队列绑定的routingkey可以使用通配符通配所有满足
条件的交换机消息 匹配上则接受消息 这种类型的消息 使用的交换器类型是 topic
接受者接受消息可以设置routingkey为表达式模糊匹配
*可以匹配一个标识符。
#可以匹配0个或多个标识符。
l比如 1610.# 表示 1610班所有学生 获取到 两条
比如 1607.*.girl 表示1701的所有女生 获取到一条
发布者代码块
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
/**
*topic模式 特殊的路由模式
* @author Administrator
*
*/
public class Pub_Log_TopicRoute {
/**
*交换器的名字 交换器有很多类型
*/
private static String EXCHANGE_NAME="amp_log_topic";
public static void main(String[] args) throws Exception, TimeoutException {
//推送到队列服务器
//连接远程rabbit-server服务器
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.73.130");
factory.setPort(5672);
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//topic模式 类型(topic) 系统名.error
channel.exchangeDeclare(EXCHANGE_NAME, "topic",true);
String message = null;
//第二个参数就是routingkey 不填 默认会转发给所有的订阅者队列
//如果a系统下有子系统用c.c1.info表示
channel.basicPublish(EXCHANGE_NAME, "a.error", MessageProperties.PERSISTENT_TEXT_PLAIN, "系统奔溃,内存溢出".getBytes("UTF-8"));
channel.basicPublish(EXCHANGE_NAME, "a.info", MessageProperties.PERSISTENT_TEXT_PLAIN, "张三于2018-1-11号访问该页面".getBytes("UTF-8"));
channel.basicPublish(EXCHANGE_NAME, "a.info", MessageProperties.PERSISTENT_TEXT_PLAIN, "李四于2018-1-11号登录该网页".getBytes("UTF-8"));
channel.basicPublish(EXCHANGE_NAME, "b.error", MessageProperties.PERSISTENT_TEXT_PLAIN, "系统奔溃,内存溢出".getBytes("UTF-8"));
channel.basicPublish(EXCHANGE_NAME, "b.info", MessageProperties.PERSISTENT_TEXT_PLAIN, "张三于2018-1-11号访问该页面".getBytes("UTF-8"));
channel.basicPublish(EXCHANGE_NAME, "b.info", MessageProperties.PERSISTENT_TEXT_PLAIN, "李四于2018-1-11号登录该网页".getBytes("UTF-8"));
channel.basicPublish(EXCHANGE_NAME, "c.c1.info", MessageProperties.PERSISTENT_TEXT_PLAIN, "李四于2018-1-11号登录该网页".getBytes("UTF-8"));
System.out.println(" [x] Sent 6 message");
}
}
消费者代码
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* 路由模式 类型 direct
* @author Administrator
*
*/
public class Log_Cons_TopicRoute {
/**
*交换器的名字 交换器有很多类型
*/
private static String EXCHANGE_NAME="amp_log_topic";
public static void main(String[] args) throws Exception, TimeoutException {
//创建一个ConnectionFactory连接工厂
ConnectionFactory factory = new ConnectionFactory();
//通过ConnectionFactory设置Rabbitmq所在ip等信息
factory.setHost("192.168.73.130");
//通过ConnectionFactory创建一个连接connection
Connection connection = factory.newConnection();
//通过connection创建一个频道channel
Channel channel = connection.createChannel();
//消费者也需要定义队列 有可能消费者先于生产者启动
channel.exchangeDeclare(EXCHANGE_NAME, "topic",true);
//channel.basicQos(1);
//产生一个随机的队列 该队列用于从交换器获取消息
String queueName = channel.queueDeclare().getQueue();
//将队列和某个交换机丙丁 就可以正式获取消息了 routingkey和交换器的一样都设置成空
//消费者定义的key 可以使用通配符 通配交换器的消息 a.*代表两个中的一段 c.#代表多段
channel.queueBind(queueName, EXCHANGE_NAME, "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");
System.out.println(message);
//参数2 true表示确认该队列所有消息 false只确认当前消息 每个消息都有一个消息标记
}
};
//参数2 表示手动确认
channel.basicConsume(queueName, true, consumer);
}
}
先启动两个订阅者 Rec
发送者Pub执行
a.*的Rec输出
系统奔溃,内存溢出
张三于2018-1-11号访问该页面
李四于2018-1-11号登录该网页
c.#l的Rec输出
李四于2018-1-11号登录该网页
6.RPC模式
该模式是rpc 远程方法调用 这里不介绍 http协议调用更经典