应用RabbitMQ中间件
JMS:
JMS即Java消息服务(JavaMessage Service)应用程序接口,是一个Java平台中关于面向消息中间件的API。JMS是JavaEE规范的一种,RabbitMQ官方没有实现JMS规范,但是开源社区有JMS的实现包。用于操作消息中间件。
5672 是 RabbitMQ 通信端口。
15672 是 Web 管理页面端口。
25672 是集群通信端口。
在应用RabbitMQ工作模式前要准备的工作:
1.启动RabbitMQ(docker的方式)
启动docker:systemctl start docker
查看所有容器(包括未启动的):docker ps -a
进入容器:docker exec -it 容器ID /bin/bash
开启管控台插件:rabbitmq-plugins enable rabbitmq_management
2.创建Maven项目,添加RabbitMQ的依赖
一、RabbitMQ工作模式
RabbitMQ有6种工作模式:
1.简单模式(Simple)
特点:
a.一个生产者对应一个消费者,通过队列进行消息传递
即producer→queue→consumer
b.该模式使用direct交换机,direct交换机是RabbitMQ默认的交换机
即一个队列只为一个消费者服务,采用direct默认交换机。
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 Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.126.3");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
//2.创建连接
Connection connection = connectionFactory.newConnection();
//3.建立信道
Channel channel = connection.createChannel();
//4.创建队列,如果队列已经存在,则使用该队列
/**
* 参数1:队列名
* 参数2:是否持久化,true表示MQ重启后队列还在
* 参数3:是够私有化,false表示所有消费者都可以访问,true表示第一次拥有它的消费者才能访问
* 参数4:是否自动删除,true表示不再使用队列时自动删除
* 参数5:其他额外参数
*/
channel.queueDeclare("simple_queue",false,false,false,null);
//5.发送消息
String message = "hello! rabbitmq";
/**
* 参数1:交换机名,“”表示默认交换机
* 参数2:路由键,简单模式就是队列名
* 参数3:其他额外参数
* 参数4:要传递的消息字节数组
*/
channel.basicPublish("","simple_queue",null,message.getBytes());
//6.关闭信道和连接
channel.close();
connection.close();
System.out.println("success!");
}
}
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//消费者
public class Customer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.126.3");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
//2.创建连接
Connection connection = connectionFactory.newConnection();
//3.建立信道
Channel channel = connection.createChannel();
//4.监听队列
/**
* 参数1:监听的队列名
* 参数2:是否自动签收,如果设置为false,则需要手动确认消息已收到,否则MQ会一直发送消息
* 参数3:Consumer的实现类,重写该类方法表示接收到消息后如何消费。
*/
channel.basicConsume("simple_queue",true,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.工作队列模式(Work Queue)
特点:
a.一个队列对应多个消费者。
b.一条消息只会被一个消费者消费。
c.消息队列默认采用轮询的方式将消息平均发送给消费者
即一个队列为多个消费者服务,队列消息按轮询的方式发送给消费者。
例如 :消息1给消费者1,消息2给消费者2,如果没有消费者3,则消息3给消费者1,消息4给消费者2,如此循环。
工作队列模式用来处理消息较多的情况。
代码实现上跟简单模式相同,只需要增加消费者;至于平均分发的规则,则由direct默认交换机来实现。
即多个消费者绑定一个队列。
3.发布订阅模式(Publish/Subscribe)
特点:
a.生产者将消息发送给交换机,交换机将消息转发到绑定此交换机的每个队列中。
b.工作队列模式(即默认交换机direct)的交换机只能将消息发送给一个队列,发布订阅模式的交换机能将消息发送给多个队列。发布订阅模式使用fanout交换机。
前俩个模式是由生产者绑定一个队列实现路由(即路由为队列名);
发布订阅模式不用指定路由,因为路由改成生产者绑定交换机,然后fanout交换机绑定若干个队列,再由fanout交换机来实现路由。
即前俩者实现路由跟生产者挂钩,后者跟交换机挂钩;
也就是说简单得路由(1对1)由生产者实现,复杂的路由(一对多或多对多)由交换机实现。
发布订阅模式,即生产者绑定一个交换机,交换机绑定多个队列(每个队列都能收到生产者所发送的全部消息)。
当消息过多时,在实现发布订阅模式的基础上,可以采用多个消费者消费一个队列(即工作队列模式)。
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//Publish And Subscribe Mode
//生产者
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.126.3");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
//2.创建连接
Connection connection = connectionFactory.newConnection();
//3.建立信道
Channel channel = connection.createChannel();
//4.创建交换机
/**
* 参数1:交换机名
* 参数2:交换机类型
* 参数3:交换机是否持久化
*/
channel.exchangeDeclare("exchange_fanout", BuiltinExchangeType.FANOUT,true);
//5.创建队列
/**
* 参数1:队列名
* 参数2:是否持久化,true表示MQ重启后队列还在
* 参数3:是否私有化,false表示所有消费者都可以访问,true表示第一次拥有它的消费者才能访问
* 参数4:是否自动删除,true表示不再使用队列时自动删除
* 参数5:其他额外参数
*/
channel.queueDeclare("SEND_MAIL",true,false,false,null);
channel.queueDeclare("SEND_MESSAGE",true,false,false,null);
channel.queueDeclare("SEND_STATION",true,false,false,null);
//6.交换机绑定队列
/**
* 参数1:队列名
* 参数2:交换机名,“”表示默认交换机
* 参数3:路由关键字,发布订阅模式写""即可
*/
channel.queueBind("SEND_MAIL", "exchange_fanout", "");
channel.queueBind("SEND_MESSAGE", "exchange_fanout", "");
channel.queueBind("SEND_STATION", "exchange_fanout", "");
//7.发送消息
for(int i = 1;i<=10;i++)
{
/**
* 参数1:交换机名,“”表示默认交换机
* 参数2:路由键,发布订阅模式为""
* 参数3:表示该消息为持久化消息,即除了保存到内存还会保存到磁盘
* 参数4:要传递的消息字节数组
*/
channel.basicPublish("exchange_fanout","", null,("你好,尊敬的用户,秒杀商品开抢了!"+i).getBytes());
}
System.out.println("success!");
//8.关闭资源
channel.close();
connection.close();
}
}
几个消费者的代码就是绑定的队列不同;
如果要解决消费某一队列的能力不足,可以采用工作队列模式弥补
4.路由模式(Routing)
特点:
a.每个队列绑定路由关键字RoutingKey
b.生产者将带有RoutingKey的消息发送给交换机,交换机根据RoutingKey转发到指定队列。路由模式使用默认交换机direct。
即根据路由键将消息发送到指定队列。
在实现的时候,首先需要队列在绑定交换机时,添加路由键;其次需要在生产者发送消息时,也需要带上相应的路由键,最后由消费者监听绑定队列。
当处理有时发送给单个消费者和有时发送给所有消费者时,需要某些队列用不同的路由键绑定交换机(即一个队列多次用不同的路由键绑定一个交换机,除了路由键之外其他数据相同)。然后交换机就可以根据生产者指定的路由键来匹配队列绑定时的路由键,就直接转发消息给匹配上的队列。
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//Routing Mode
//生产者
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.126.3");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
//2.创建连接
Connection connection = connectionFactory.newConnection();
//3.建立信道
Channel channel = connection.createChannel();
//4.创建交换机
/**
* 参数1:交换机名
* 参数2:交换机类型
* 参数3:交换机是否持久化
*/
channel.exchangeDeclare("exchange_routing", BuiltinExchangeType.DIRECT,true);
//5.创建队列
/**
* 参数1:队列名
* 参数2:是否持久化,true表示MQ重启后队列还在
* 参数3:是否私有化,false表示所有消费者都可以访问,true表示第一次拥有它的消费者才能访问
* 参数4:是否自动删除,true表示不再使用队列时自动删除
* 参数5:其他额外参数
*/
channel.queueDeclare("SEND_MAIL2",true,false,false,null);
channel.queueDeclare("SEND_MESSAGE2",true,false,false,null);
channel.queueDeclare("SEND_STATION2",true,false,false,null);
//6.交换机绑定队列
/**
* 参数1:队列名
* 参数2:交换机名,“”表示默认交换机
* 参数3:路由关键字
*/
channel.queueBind("SEND_MAIL2", "exchange_routing", "import");
channel.queueBind("SEND_MESSAGE2", "exchange_routing", "import");
channel.queueBind("SEND_STATION2", "exchange_routing", "import");
channel.queueBind("SEND_STATION2", "exchange_routing", "normal");
//7.发送消息
/**
* 参数1:交换机名,“”表示默认交换机
* 参数2:路由键
* 参数3:表示该消息为持久化消息,即除了保存到内存还会保存到磁盘
* 参数4:要传递的消息字节数组
*/
channel.basicPublish("exchange_routing","import", null,"双十一大促活动".getBytes());
channel.basicPublish("exchange_routing","normal", null,"小型促销活动".getBytes());
System.out.println("success!");
//8.关闭资源
channel.close();
connection.close();
}
}
此时消费者也是跟前面几个相同,只是监听相应的队列即可。
路由模式就是在发布订阅的基础上,再加上路由键的一个细分。
5.通配符模式(Topics)
特点:
a.消息设置RoutingKey时,RoutingKey由多个单词构成,中间以.分割。
b.队列设置RoutingKey时,#可以匹配任意多个单词,*可以匹配任意一个单词。
就是一个生产者指定一个topic交换机,使用通配符路由键的多个队列绑定该交换机,消费者监听对应的队列;然后由生产者将指定通配符路由键和消息发送到交换机,由交换机根据指定通配符路由键发送到匹配的队列上,最后由消费者接收该队列。
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//Routing Mode
//生产者
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.126.3");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
//2.创建连接
Connection connection = connectionFactory.newConnection();
//3.建立信道
Channel channel = connection.createChannel();
//4.创建交换机
/**
* 参数1:交换机名
* 参数2:交换机类型
* 参数3:交换机是否持久化
*/
channel.exchangeDeclare("exchange_topic", BuiltinExchangeType.TOPIC,true);
//5.创建队列
/**
* 参数1:队列名
* 参数2:是否持久化,true表示MQ重启后队列还在
* 参数3:是否私有化,false表示所有消费者都可以访问,true表示第一次拥有它的消费者才能访问
* 参数4:是否自动删除,true表示不再使用队列时自动删除
* 参数5:其他额外参数
*/
channel.queueDeclare("SEND_MAIL3",true,false,false,null);
channel.queueDeclare("SEND_MESSAGE3",true,false,false,null);
channel.queueDeclare("SEND_STATION3",true,false,false,null);
//6.交换机绑定队列
/**
* 参数1:队列名
* 参数2:交换机名,“”表示默认交换机
* 参数3:路由关键字
*/
channel.queueBind("SEND_MAIL3", "exchange_topic", "#.mail.#");
channel.queueBind("SEND_MESSAGE3", "exchange_topic", "#.message.#");
channel.queueBind("SEND_STATION3", "exchange_topic", "#.station.#");
//7.发送消息
/**
* 参数1:交换机名,“”表示默认交换机
* 参数2:路由键
* 参数3:表示该消息为持久化消息,即除了保存到内存还会保存到磁盘
* 参数4:要传递的消息字节数组
*/
channel.basicPublish("exchange_topic","mail.message.station", null,"双十一大促活动".getBytes());
channel.basicPublish("exchange_topic","station", null,"小型促销活动".getBytes());
System.out.println("success!");
//8.关闭资源
channel.close();
connection.close();
}
}
6.远程调用模式(RPC),不常用
总结:
1.简单模式(Simple)和工作队列模式(Work Queue)采用的都是默认交换机direct;发布订阅模式使用fanout交换机;路由模式使用的是direct交换机;通配符模式使用的是topic交换机;
2.简单模式→工作队列模式→发布订阅模式→路由模式→通配符模式:
从一个生产者指定一个队列,一个消费者监听这个队列,即简单模式;
在简单模式的基础上,再加上几个消费者监听这个队列,即工作队列模式;
将生产者指定一个fanout交换机,几个队列再绑定交换机,消费者去监听这几个队列中的一个和多个,即发布订阅模式;
在发布订阅模式的基础上,增加一个路由键,根据路由键区分,即路由模式;
在路由模式的基础上,将路由键以.分割成多个单词来匹配,即通配符模式。
3.前5种rabbitmq的工作模式,从简单到复杂,从单一到灵活。都是由生产者将路由键(通配符)和消息发送到指定交换机,由交换机根据生产者发送的路由键(通配符)匹配与交换机绑定的队列,最后将消息发送给匹配的队列。消费者监听指定队列,获取队列发送的消息。