MQ概述
1.MQ简介
MQ,Message Queue,是一种提供消息队列服务的中间件,也称为消息中间件,是一套提供了消息生产、储、消费全过程API的软件系统。消息即数据。一般消息的体量不会很大。
2.MQ用途
限流削峰
MQ可以将系统的超量请求暂存其中,以便系统后期可以慢慢进行处理,从而避免了请求的丢失或系统被压垮。
异步解耦
上游系统对下游系统的调用若为同步调用,则会大大降低系统的吞吐量与并发度,且系统耦合度
太高。而异步调用则会解决这些问题。所以两层之间若要实现由同步到异步的转化,一般性做法就是,在这两层间添加一个MQ层。
数据收集
分布式系统会产生海量级数据流,如:业务日志、监控数据、用户行为等。针对这些数据流进行实时或批量采集汇总,然后对这些数据流进行大数据分析,这是当前互联网平台的必备技术通过MQ完成比类数据收集是最好的选择。
3.常见的MQ产品
ActiveMQ
ActiveMQ是使用Java语言开发一款MQ产品。早期很多公司与项目中都在使用。但现在的社区活跃度已经很低。现在的项目中已经很少使用了。
RabbitMQ
RabbitMQ是使用ErLang语言开发的一款MQ产品。其吞吐量较Kafka与RocketMQ要低,且由于其不是Java语言开发,所以公司内部对其实现定制化开发难度较大。
Kafka
Kafka是使用Scala/Java语言开发的一款MQ产品。其最大的特点就是高吞吐率,常用于大数据领域的实时计算、日志采集等场景。其没有遵循任何常见的MQ协议,而是使用自研协议。对于Spring CloudNetcix,其仅支持RabbitMq与Kafka.
RocketMQ
RocketMQ是使用Java语言开发的一款MQ产品。经过数年阿里双 11 的考验,性能与稳定性非常高。其没有遵循任何常见的MQ协议,而是使用自研协议。对于Spring Cloud Alibaba,其支持RabbitMQ、Kafka,但提倡使用RocketMQ.
RocketMQ的安装与启动
下载与安装
1.下载RocketMQ
https://rocketmq.apache.org/download/
2.windows下解压安装,配置环境变量ROCKETMQ_HOME
注意路径不要有中文或者空格等特殊字符
RocketMQ部署
1.启动nameserver
鼠标双击执行bin目录下的mqnamesrv.cmd 文件 或者 cmd 打开控制台,切换目录到rocket的bin目录下,执行命令:
start mqnamesrv.cmd
注:如果出现"找不到主类"的问题,需要将ROCKMQ_HOME和JAVA _HOME两个变量路径改掉,换成没有空格的路径
2.启动broker
执行bin目录下的 mqbroker.cmd 文件 或者 cmd 打开控制台,切换到bin目录下,执行命令:
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
默认端口是9876,autocreateTopicEnable=true表示允许自动创建topic
注:rocketMq运行环境中的内存不足会导致启动失败,可以修改配置文件
runserver.sh/runserver.cmd,runbroker.sh/runbroker.cmd中的配置参数
3.RocketMQ插件安装
访问 https://github.com/apache/rocketmq-externals/tags
下载rocketmq-console-1.0.0,并解压
在Eclipse中导入项目:
修改pom.xml文件中的java环境为当前环境1.8并执行compile指令
构建成功
在application.properties中设置地址namesrvAddr为localhost:9876
右键项目更新Maven
通过启动类App.java启动
在浏览器中打开
这里可以通过下面的指令将项目打包为jar包,失败可以尝试使用管理员身份运行
clean package -Dmaven.test.skip=true
打包成功后刷新项目可以在target文件夹下看到刚生成的jar包
此时复制jar包到到工作文件夹下通过cmd可以直接启动项目
java -jar rocketmq-console-ng-1.0.0.jar
同样可以在浏览器中打开
在bin目录下通过cmd输入指令mqadmin
mqadmin是一个命令行工具,用于管理Apache RocketMQ消息队列系统。它提供了一组命令,用于创建、删除、查询和管理消息队列和主题,以及监控和诊断RocketMQ集群的状态和性能。
基本概念
消息(Message)
消息是指,消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于个主题。
主题(Topic)
Topic表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位。topic:message 1:n message:topic 1:1
一个生产者可以同时发送多种Topic的消息;而一个消费者只对某种特定的Topic感兴趣,只可以订阅和消费一种Topic的消息。producer:topic 1:n consumer:topic 1:1
标签(Tag)
为消息设置的标签,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签。标签能够有效地保持代码的清晰度和连贯性,并优化RocketMQ提供的查询系统。消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性。
Topic是消息的一级分类,Tag是消息的二级分类。
队列(Queue)
存储消息的物理实体。一个Topic中可以包含多个Queue,每个Queue中存放的就是该Topic的消息。一个Topic的Queue也被称为一个Topic中消息的分区(Partition)。
一个Topic的Queue中的消息只能被一个消费者组中的一个消费者消费。一个Queue中的消息不允
许同一个消费者组中的多个消费者同时消费。
消息标识(MessageId/Key)
RocketMQ中每个消息拥有唯一的MessageId,且可以携带具有业务标识的Key,以方便对消息的查询。
不过需要注意的是,MessageId有两个:在生产者send()消息时会自动生成一个Messaged(msgId),当消息到达Broker后,Broker也会自动生成一个Messageld(offsetMsgId)。
msgId、offsetMsgId与key都称为消息标识。
- msgId:由producer端生成,其生成规则为:producerIp+进程pid+MessageClientIDSetter类的ClassLoader的hashCode +当前时间+AutomicInteger自增计数器
- offsetMsgId:由broker端生成,其生成规则为:brokerIp+物理分区的offset(Queue中的偏移量)
- key:由用户指定的业务相关的唯一标识
发送消息
同步发送
1.新建Maven项目并在pom.xml中导入依赖
XML
<!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-client -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>5.3.0</version>
</dependency>
2.创建测试类同步发送消息
java
public class EasyA {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("easygroup");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
//发送消息
Message message = new Message("easytopic","rocketmq message".getBytes());
SendResult result = producer.send(message);
System.out.println(result);
producer.shutdown();
}
}
发送成功,在RocketMQ中,默认情况下一个Topic会创建4个队列。
异步发送
java
public class EasyB {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException {
DefaultMQProducer producer=new DefaultMQProducer("easygroup");
producer.setNamesrvAddr("127.0.0.1:9876");
Message message=new Message("easytopic","Helloworld".getBytes());
producer.start();
producer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println(sendResult);
System.out.println("OK");
}
@Override
public void onException(Throwable e) {
e.printStackTrace();
System.out.println("Error");
}
});
System.out.println("发送了一个消息");
Thread.sleep(3000);
producer.shutdown();
}
}