MQ全称:MessageQueue(消息队列),是在消息的传输过程中保存消息的容器,大多数用于分布式系统之间进行通信。
MQ:消息队列,存储消息的中间件。
分布式系统通信的两种方式:
1.直接远程调用接口(例如:Dubbo-Zookeeper,Eureka-Feign,Nacos,Edas-Hsf)
2.借助第三方组件完成间接通信:mq,redis
发送方称:生产者
接受方称:消费者
2.1优点:
···2.1.1应用解耦:提升容错性,可维护性;
···2.1.2异步提速:提升用户体验,系统吞吐量(单位时间内处理请求的数量);
···2.1.3削峰填谷:提高系统的稳定性;
2.2劣势:
···2.2.1系统可用性降低;
···2.2.2系统的复杂度提高;
···2.2.3一致性问题;
MQ | 公司 | 开发语言 | 协议 | 支持语言 | 单机吞吐 | 消息延迟 |
---|---|---|---|---|---|---|
RabbitMQ | Rabbit | Erlang | AMQP,XMPP,SMTP,STOMP | Erlang,Java | 万级(其次) | 微秒级 |
ActiveMQ | Apache | Java | AMQP,XMPP,STOMP,REST, | Java,C,C++,Python | 万级(最差) | 毫秒级 |
RocketMQ | 阿里 | Java | 自定义 | Java,C++ | 十万级(最好) | 毫秒级 |
Kafka | Apache | Java&Scala | 自定义 | Java,Python,Php | 十万级(次之) | 毫秒级 |
总结:
3.1RabbitMQ:并发能力强,性能极好,延迟低;
3.2ActiveMQ:老牌产品,成熟度高,文档多;
3.3RocketMQ:功能完善,扩展性强;
3.4Kafka:只支持主要的MQ功能,大数据使用;
AMQP:即Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。
Rabbit基于AMQP标准开发的RabbitMQ1.0版本发布于2007年。
总结:
···1.Broker:接收和分发消息的应用,RabbitMQ Server就是Message Broker。
···2.Virtual host:出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组 中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ Server提供服务时,可以划分出多个vhost。
···3.Connection:producer/consumer和Broker之间的TCP链接;
···4.Channel:channel是在Connection内部建立的逻辑链接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以是完全隔离,极大减少了操作系统建立TCP connection的开销。
···5.Exchange:message到达broker的第一站,根据分发规则,匹配查询表中的routing key分发消息到queue中去。
······5.1.message分发规则常用类型:
··········5.1.1.direct(point–to–point)点到点
··········5.1.2.fanout(multicast)广播模式
··········5.1.3.topic(publish–subscribe)订阅模式
···6.Queue:消息最终被推送到队里这里等待Consumer取走。
···7.Binding:exchange和queue之间的虚拟链接,binding中可以包含routing key。Bingding信息被保存到exchange中的查询表中,用于message的分发依据。
JMS:Java消息服务(Java Message service)应用程序接口,是一个Java平台关于面向消息中间件的API。
JMS是JavaEE规范中的一种,很多消息中间件都实现了JMS规范。
6.1创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
6.2设置参数
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/douglas");//虚拟机,默认值1
factory.setUsername("test");
factory.setPassword("test");
6.3创建连接
Connection connection = factory.newConnection();
6.4创建Channel
Channel channel = connection.createChannel();
6.5创建队列Queue
channel.queueDeclare();
参数:1.queue队列名称
2.durable是否持久化,mq重启后还在;
3.exclusive是否独占,只能有一个消费者监听这个队列,当connection关闭时,否则删除队列。
4.autoDelete是否自动删除,当没有Consumer时,自动认为删除。
5.arguments:参数
6.6发送消息
channel.basicPublish();
参数:1.exchange:交换机名称
2.routingKey:路由名称
3.props:配置信息
4.body:消息体
6.7释放资源
channel.close();
connection.close();
7.1创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
7.2设置参数
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("");//虚拟机 默认值1
factory.setUsername("");
factory.setPassword("");
7.3创建TCP连接Connection
Connection connection = factory.newConnection();
7.4创建channel
Channel channel = connection.createChannel();
7.5创建队列
chanel.queueDeclare();//参数与生产者一致
7.6接受消息
chanel.basicConsumer();
例如:chanel.basicConsumer(queueName,true,consumer);
参数:1.queue:队列名称
2.autoAck:是否自动回复
3.callback:回调函数
回调方法:当收到消息后会自动执行如下方法:
Consumer consumer = new DefaultConsumer(channel){
public void handleDelivery(String consumerTag,Envelope envelope,AMQP.BasicProperties properties,byte[] body){
//处理业务逻辑
}
}
参数说明:consumerTag 标识
envelope 对象,获取一些配置信息,交换机,路由key
properties 协议配置
body 消息体
work queues:
图示:与入门程序的简单模式相比,多了一个或一些消费者,多个消费端共同消费一个队列中的消息。
应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理速度。
pub/sub:
图示:在订阅模式中,多了一个Exchange角色,而且过程略有变化。
···生产者:将消息直接发送到交换机。
···消费者:消息的接收者,会一直等待消息的到来。
···消息队列:接收消息,缓存消息。
···交换机:一方面接收生产者发送的消息;另一方面处理消息,例如:递交给指定队列,递交给所有队列,或者将消息丢弃,这将取决于Exchange的类型。
·········交换机类型:
···············1.Fanout广播:将消息交给所有绑定到交换机的队列。
···············2.Direct定向:把消息交给符合指定routing key的队列。
···············3.Topic通配符:把消息交给符合routing pattern(路由模式)的队列。
··········注意:交换机Exchange只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!!!
1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
2.设置参数
factory.setHost();//ip
factory.setPort();//端口
factory.setVirtualHost();//虚拟机名称
factory.setUsername();//用户
factory.setPassword();//密码
3.创建连接
Connection connection = factory.new Connection();
4.创建Channel
Channel channel = connection.createChannel();
5.创建交换机
channel.exchangeDeclare();
参数:
1.exchange:交换机名称 例如:String exchargeName = "test-fanout";
2.type:交换机类型 DIRECT("direct")定向。FANOUT广播。TOPIC通配符。
3.durable:是否持久化
4.autoDelete:自动删除
5.internal:内部使用,一般为false;
6.arguments:参数
6.创建队列
String queue1Name = "test_fanout_queue1";
String queue2Name = "test_fanout_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
7.绑定队列和交换机
channel.queueBind();
参数:
1.queue:队列名称
2.exchange:交换机名称
3.routingKey:路由键,绑定规则,如果交换机的类型为fanout,routingKey设置为"";
例如:
channel.queueBind(queue1Name,exchangeName,"");
channel.queueBind(queue2Name,exchangeName,"");
8.发送消息
String body ="消息内容";
channel.basicPublish(exchangeName,"",null,body.getBytes());
9.释放资源
channel.close();
connection.close();
与之前的消费者一致,只需要改变接受消息的队列名称
channel.basicConsumer(queue1Name,true,consumer);
编写多个消费者模块绑定接受不同队列消息。
Routing:
说明:
·······队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey路由key;
·······消息的发送方在向Exchange发送消息时,也必须指定消息的RoutingKey;
·······Exchange不再把消息交给每一个绑定的队列,而是根据消息的RoutingKey进行判断,只有队列的RoutingKey与消息的RoutingKey完全一致,才会接受到消息;
图解:
·······Producer:生产者向Exchange发送消息时会指定一个RoutingKey;
·······Exchange:交换机接受生产者的消息,把消息递交给与RoutingKey完全匹配的队列;
·······Cousumer1:消费者其所在队列指定了需要routingKey为error的消息;
·······Cousumer2:消费者其所在队列指定了需要routingKey为waring,error,info的消息;
小结:
······Routing模式要求队列在绑定交换机时需要指定routingKey,消息会转发到符合routingKey的队列;
······生产者/消费者代码:与之前订阅模式类似:
注意:设置对应的路由routingKey即可;交换机类型指定为:direct(定向);
Topics通配符模式:type=topic
图解:
······Queue1:绑定的是usa.#,因此凡是以usa.开头的routingKey都会被匹配到;
······Queue2:绑定的是*.news,因此凡是以.news结尾的routingKey都会被匹配到;
小结:
······Topic主题模式可以实现Pub/Sub发布订阅模式和Routing路由模式的功能,只是Topic在配置routingKey的时候可以使用通配符,显得更加灵活。
一个生产者,一个消费者,不需要设置交换机(使用默认的交换机);
WorkQueue:一个生产者,多个消费者(竞争关系),不需要设置交换机(使用默认的交换机);
publish/subscribe:需要设置交换机类型为fanout,交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列上。
Routing:需要设置交换机类型为direct,交换机和队列进行绑定,并且指定routingKey,当发送消息到交换机后,交换机会根据routingKey将消息发送到对应的队列上。
Topic:需要设置交换机类型为topic,交换机和队列进行绑定,并且指定通配符方式的routingKey,当发送消息到交换机后,交换机会根据routingKey将消息发送到对应的队列。