不同MQ的特点
1.ActiveMQ
activemq是能力强劲的开源消息总线,它是一个完全支持JMS规范的消息中间件,丰富的api,多种集群架构模式让activemq在业界成为牌的消息中间间,中小企业用的多
缺点:性能不高
命令行的方式操作linux上的rabiitmq
systemctl start abbitmq-server #启动命令
systemctl restart rabbitmq-server #重启命令
systemctl stop rabbitmq-server #停止命令
systemctl status rabbitmq-server #查看状态命令
systemctl #系统控制命令的简写(contos 的命令)
rabbitmq 提供了一套管理命令
rabbitmqctl help #查看帮助命令
rabbitmq 插件管理命令
rabbitmq-plugins
rabbitmq-plugins list #查所有插件
docker 安装 rabbitmq
1.docker pull rabbitmq:management #拉取rabbitmq镜像,在dockerhub上搜索rabbitmq,要带management,是带管理界面的。
docker pull rabbitmq:3.8.7-management
启动docker
docker run -d --hostname localhost --name rabbitmq3.8.7 -p 5672:5672 -p 15672:15672 rabbitmq:3.8.7-management
rabbitmq管理界面初识
1.overview:整合个rabbitmq的详细信息有一个概览。
2.connection:连接,当前客户端和rabbitmq的连接,可搜索等
3.channels:通道,通过通道去传递消息。可查询,正则表达式查询,分类。
4.exchanges:交换机(路由),安装完成之后默认7种路由
direct:为直连的意思
features为D:表示数据存储在磁盘中,数据不会随着rabbitmq重庆而丢失。
fanout:为广播
headers:已头的形式
match:匹配的模式
trace:
topic:发布订阅模式
还可以通过 add添加自定义交换机
5.queues:队列,可以新增删除等。
6.admin:用户相关,添加用户,分配权限等。
rabbitmq的端口:
amqp:5672:这个端口是我们程序通信的端口
clustering:25672:这个是集群通信的端口
http:15672:这个是访问管理界面的端口
amqp协议
rabbitmq中的虚拟主机(相当于数据库中的库)起到隔离的作用,让数据边界更清晰。还有权限控制等
所以在使用的时候,要先创建用户,在创建虚拟主机,再绑定虚拟主机和用户,最后才是使用。
rabbitmq的集中工作模式
1.直连(hello world)模式
封装连接工具类
public class RabbitMqUtils {
private static ConnectionFactory connectionFactory;
static {
//重量级的资源,类加载的时候只执行一次
connectionFactory=new ConnectionFactory();
connectionFactory.setHost("192.168.31.71");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
}
/**
* 创建工具类
* @return
*/
public static Connection getConnection(){
try{
//创建链接对理想
return connectionFactory.newConnection();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* 关闭连接
* @param channel
* @param conn
*/
public static void closeConnetionAndChanel(Channel channel, Connection conn){
try{
if(null!=channel){
channel.close();
}
if(null!=conn){
conn.close();;
}
}catch (Exception e){
e.printStackTrace();
}
}
}
1生产者:
public class provider {
@Test
public void send() throws IOException, TimeoutException {
//创建连接mq的连接工厂对象
/* ConnectionFactory connectionFactory=new ConnectionFactory();
//设置连接rabbitmq主机
connectionFactory.setHost("192.168.31.71");
//设置端口号
connectionFactory.setPort(5672);
//设置连接那个虚拟主机
connectionFactory.setVirtualHost("/ems");
//设置登录名
connectionFactory.setUsername("ems");
//设置登陆密码
connectionFactory.setPassword("123");
//获取连接对象
Connection connection = connectionFactory.newConnection();*/
Connection connection=RabbitMqUtils.getConnection();
//获取连接中通道
Channel channel=connection.createChannel();
//通道绑定对应消息队列
//参数1:队列名称,如果队列不存在,自动创建,队列名称为hello
//参数2:定义队列特性是否要持久化,true持久化队列,false 不持久化, 若为false,rabbitmq重启时队列丢失(不存在),这个参数只是保证对队列持久化,就算为true,消息也会丢失
//参数3:exclusive 是否独占队列true独占队列,false不独占,独占表示队列只能被当前通道(连接)绑定,如果有其他队列绑定则报错,一般为false
//参数4:autoable:是否在消费完成后自动删除队列,true自动删除,false不自动删除,自动删除是在断开连接时删除,不是消费完消息。
//参数5:额外附加参数
//注意:同一个通道可以向不同的队列发布消息,真正发布消息的是发布方法。
//消费者和生产者队列参数要保证严格一致
channel.queueDeclare("hello",false,false,false,null);
//发布消息
//参数1:交换机名称
//参数2:队列名称
//参数3:额外的消息设置,设置为MessageProperties.PERSISTENT_TEXT_PLAIN表示消息持久化
//参数4:消息的内容
channel.basicPublish("","hello", MessageProperties.PERSISTENT_TEXT_PLAIN,"hello rabbitmq".getBytes());
channel.close();
connection.close();
}
}
2.消费者:为什么消费者使用main函数呢,junit不支持程序一直运行,执行完毕就关闭程序,看不见效果。
public class customer {
public static void main(String[] args) throws IOException, TimeoutException {
/*ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setHost("192.168.31.71");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");*/
//创建链接对理想
Connection connection= RabbitMqUtils.getConnection();
//创建通道
Channel channel=connection.createChannel();
//通道绑定对象
channel.queueDeclare("hello",false,false,false,null);
//消费消息
channel.basicConsume("hello",true,new DefaultConsumer(channel){
public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties,byte[] body){
System.out.printf("new string(body)="+new String(body));
}
});
RabbitMqUtils.closeConnetionAndChanel(channel,connection);
}
}
这种模式适用于登陆发送验证,加积分等场景。
2.工作模式(work模型)
1.平均消费模型
work queues,也被称为(task queues)任务模型,当消息处理比较耗时的时候,可能生产消息的速度远远大于消费的速度,长此以往,消息就会堆积越来越多,无法及时处理,此时就可以使用work模型,让多个消费者绑定到一个队列,共同消费队列中的消息,队列中的消息一旦消费,就会消失,因此任务不会被重复执行。
注意:在默认的条件下,我们消费的消息时平均分配的。不管消费者处理的时间长短,处理的消息都是同样多的。
这种模式下:其实消费者已经拿到消息,只是再等待处理,而不是还存储在rabbitmq上的。
生产者:
public class provider {
public static void main(String[] args) throws IOException {
//获取连接对象
Connection connection = RabbitMqUtils.getConnection();
//获取通道对象
Channel channel=connection.createChannel();
//通过通道声明队列
channel.queueDeclare("work",true,false,false,null);
//循环生产消息
for(int i=1;i<=20;i++){
channel.basicPublish("","work",null,(i+"hello work quene").getBytes());
}
RabbitMqUtils.closeConnetionAndChanel(channel,connection);
}
}
消费者1:
public class customer1 {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//绑定队列
channel.queueDeclare("work",true,false,false,null);
channel.basicConsume("work",true,new DefaultConsumer(channel){
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body){
try{
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("消费者-1:"+new String(body));
}
});
}
}
消费者2:
public class customer2 {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//绑定队列
channel.queueDeclare("work",true,false,false,null);
channel.basicConsume("work",true,new DefaultConsumer(channel){
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body){
System.out.println("消费者-2:"+new String(body));
}
});
}
}
2.能者多劳
这个和消息确认机制有关。自动消费确认设置为true,接收到消息之后,马上就确认已经消费了,消费者还没有处理完,或者消费者挂了,消息就丢失了,不建议使用自动确认。
处理方法:
所以在实现能者多劳就是,不设置为自动消费同时将每次消费的条数设置为1条,并且处理完成之后,手动确认消息已经消费。
消费者1:
public class customer1 {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
channel.basicQos(1);//设置每次只能消费一条消息。
//绑定队列
channel.queueDeclare("work",true,false,false,null);
//参数1:队列名称
//参数2.自动确认,消费者自动向rabbitmq确认消息,。设置为false则不会
channel.basicConsume("work",false,new DefaultConsumer(channel){
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body) throws IOException {
try{
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("消费者-1:"+new String(body));
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
}
消费者2:
public class customer2 {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
channel.basicQos(1);//设置每次只能消费一条消息。
//绑定队列
channel.queueDeclare("work",true,false,false,null);
channel.basicConsume("work",false,new DefaultConsumer(channel){
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body) throws IOException {
System.out.println("消费者-2:"+new String(body));
//开启手动确认。
//参数1,确认列表中那个具体的消息,通过envelope.getDeliveryTag()拿到消息唯一id,
//参数2:是否开启多个消息同时确认。
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
}
3.fanout模型(发布订阅模型)也叫广播模型
消息发送到交换机,交换机会将消息发送到绑定了当前交换机的所有消费者。
广播模式下消息发送流程:
可以有多个消费者,
每个消费者有自己的queue(队列)(这里的队列是临时队列)
每个队列都要绑定exchange(交换机)
生产者发送的消息,只能发送到交换机,交换机来决定要发给那个队列,生产者无法决定。
交换机把消息发送给绑定过的队列(所有绑定交换机的队列)
队列的消费者都能拿到消息,是西安一条消息被多个消费者消费。
临时队列:消息消费之后就删除的队列
生产者:
public class provider {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//将通道声名指定交换机
//参数1:交换机名称
//参数2:交换机类型 fanout为广播类型
channel.exchangeDeclare("logs","fanout");
//发送消息
//在广播里面路由key无意义
channel.basicPublish("logs","",null,"fanout type message".getBytes());
RabbitMqUtils.closeConnetionAndChanel(channel,connection);
}
}
消费者:
public class Customer1 {
public static void main(String[] args) throws IOException {
//获取连接对上
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//通道绑定交换机
channel.exchangeDeclare("logs","fanout");
//创建临时队列
String queueName=channel.queueDeclare().getQueue();
//绑定交换机和队列
channel.queueBind(queueName,"logs","");
//消费消息
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.printf("消费者1 "+new String(body));
}
});
}
}
广播模型,多个消费者才体现出效果,将消费者多复制几份就行了。
4.direct模型(路由模型)
再fanout模式中,一条消息,会被所有订阅的队列消费,但是在某些场景下,我们希望不同的消息被不同的队列消费,这时就要用到direct类型的exchange。
再direct模型下:
队列与交换机的绑定,不能是任意绑定了,而是要指定一个routingkey(路由key)
消息的发送方在向exchange发送消息时,也必须指定消息的routingkey。
exchange不再把消息交给每一个绑定的队列,而是根据消息的routing key 进行判断,只有队列的routing key与消费者的routing key 完全一致,才会接受到消息
生产者:
public class provider {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//定义交换机的名称
String exchangeName="logs_direct";
//通过通道声名交换机,
//参数1:交换机名称
//参数2:direct 路由模式
channel.exchangeDeclare(exchangeName,"direct");
String routingkey="error";
channel.basicPublish(exchangeName,routingkey,null,("这是direct模型发布的routekey【"+routingkey+"】").getBytes());
RabbitMqUtils.closeConnetionAndChanel(channel,connection);
}
}
消费者1:
public class customer1 {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//定义交换机的名称
String exchangeName="logs_direct";
//通道声名交换机以及交换的类型
channel.exchangeDeclare(exchangeName,"direct");
String queueName=channel.queueDeclare().getQueue();
//基于路由key绑定队列和交换机
channel.queueBind(queueName,exchangeName,"error");
//获取消费者的消息
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1"+new String(body));
}
});
}
}
消费者2:
public class customer2 {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//定义交换机的名称
String exchangeName="logs_direct";
//通道声名交换机以及交换的类型
channel.exchangeDeclare(exchangeName,"direct");
String queueName=channel.queueDeclare().getQueue();
//基于routing key(info)绑定队列和交换机
channel.queueBind(queueName,exchangeName,"info");
//基于routing key(error)绑定队列和交换机
channel.queueBind(queueName,exchangeName,"error");
//基于routing key(warning)绑定队列和交换机
channel.queueBind(queueName,exchangeName,"warning");
//获取消费者的消息
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1"+new String(body));
}
});
}
}
5.topic模型(动态路由模型)
topic类型的exchange与direct相比,都可以根据routingkey 把消息路由到不同的队列,只不过topic类型的exchange可以让队列在绑定routingkey的时候使用通配符,这种模型routingkey 一般都是由一个或多个单词组成,多个单词之间以“.”分割,列如 item.insert
* 表示匹配一个单词
# 表示匹配多个单词
生产者:
public class provider {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//声名交换机
channel.exchangeDeclare("topics","topic");
//发布消息
String routingkey="user.save";
channel.basicPublish("topics",routingkey,null,("zheshi topic moxing").getBytes());
//关闭
RabbitMqUtils.closeConnetionAndChanel(channel,connection);
}
}
消费者1:
public class Customer1 {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//s声名交换机和交换机类型
channel.exchangeDeclare("topics","topic");
//创建一个临时队列
String queue=channel.queueDeclare().getQueue();
//绑定队列和交换机,同台通配符形式 routing key
channel.queueBind(queue,"topics","user.*");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.printf("消费者1 "+new String(body));
}
});
}
}
消费者2
public class Customer2 {
public static void main(String[] args) throws IOException {
Connection connection= RabbitMqUtils.getConnection();
Channel channel=connection.createChannel();
//s声名交换机和交换机类型
channel.exchangeDeclare("topics","topic");
//创建一个临时队列
String queue=channel.queueDeclare().getQueue();
//绑定队列和交换机,同台通配符形式 routing key
//前面匹配一个,后片匹配多个,中间必须由user
channel.queueBind(queue,"topics","*.user.#");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.printf("消费者1 "+new String(body));
}
});
}
}
6.rpc 模型