我们来讨论MQ,首先我们需要问下自己我们为什么要使用MQ?MQ为我们解决了哪些问题?
日常应用解决的是我们系统耦合的问题,如下举出外面场景 订单和派送系统
使用RPC 也会存在一些耦合一方系统修改或者调整,另一方也要跟着调整;
image.png通过队列传递消息,双方都是可以做到异步处理,且无耦合
加入公司决定自研MQ,那么如果你是架构设计组成员的话,你会从哪些方面考虑去设计一款高性能高可用的MQ呢?
1.首先是协议
因为任何通过网络收发数据,肯定是要协议的指定;
比如说浏览器的http协议,只不过http协议有点重量级了,消息头以及消息体字节数较大
不适用与轻量级的MQ消息传递;且http是短连接,不适合业务量较大的mq场景,此时频繁建立链接也是比较耗时的;
因此需要知道何时的协议,市面上的协议比如MQTT amqp,OpenWire...
2.消息的存取和持久化(刷盘规则)
我们分析一下消息存储时机,以及存储的规则
1.可以这样优化 消息总是先在内存-->缓存到指定大小再写到磁盘文件
2.如果保证消息正常写入,使用确认重试机制,如果生产者发送消息,mq没有收到消息会重试;
3.如何保证写入与读取的高效(不使用jvm内存,直接使用os的内存)
image.png
如图如果我们会多出2次拷贝,既浪费性能又浪费空间;
3.消息的分发策略
1.消息发送到哪个mq,消费者是主动pull还是mq push
2.消息收发确认机制(保证mq收到消息,消费者正确收到消息)
4.mq的高可用
我们的mq server如何高可用
从以下几个角度
1.能不能有备份
1.1可以一主多从,同步模式可以有异步或者同步 类似于db的数据冗余备份
image.png
1.2 多主多从的集群模式
image.png2 分片和副本
image.png原本一份数据现在分成3分,分布在不同server示例上,每个分片也有自己的副本,及时当前分片主节点挂了,也会选出在其他节点的salve1节点作为新的节点保证了高可用;slave和master保持异步或者同步数据;
这样的设计比如ES或者Mongo都会有看到;
4.如何做到支持高并发
1.首先网络通讯使用Nio的异步通讯,支持海量数据处理(很多中间件也是使用前面文章的netty作为高性能的通信框架);
2.合适的数据刷盘机制,使用合适的索引机制,类似于mysql索引;
3.读写的零拷贝,不使用jvm内存
4.动态方便的扩容
至此以上是总结的mq的一些通用设计点;下面我们来看下具体的MQ的设计架构,我们会拿Rabbitmq和Kafka作为对比,并总结一下MQ设计的一些共通的设计要点;
kafka架构
image.png
架构图中涉及到涉及概念
broker : server实例,多个实例组成集群
topic: 类似于传统mq的队列
partion: 分区相当于将topic的一整份大数据分成多个分区存储在多个实例上;
replica: pation数据的冗余副本,分布在其他节点实例;
consumer:消息发送者采用pull推送
consumer group:消费分组 一个partion只能被consumer group的一个消费者消费;
producer:采用push推送到broker上
kafka消息的分发策略:
具体发送哪个topic和partion的,由生产者决定;
具体的消息格式类似于key _value格式
轮询:
如果key为null,会采用轮休的策略发送到对应partion;
hash:
如果key不为null,采用hash策略;
所以在一个partition内要保证消息的有序性,需要设置指定的key;
kafka消息的持久化策略:
普遍任务写文件比内存慢很多;但如果采用顺序读写,可以达到
百兆/s,甚至比内存还快,但如果是随机读写的话只能达到100kb/s;
所以kafka采用顺序读写的文件流;
1.kafka数据存储从os 内存->磁盘,没有采用jvm内存中转,
1.原因是先写到jvm在写到os内存,会多一次拷贝,且内存空间会多一倍以上
2.jvm gc机制,很难保证消息正确写出;
我们看下具体消息格式
image.png
我们看到一个partition消息文件分为log和index文件;都是以offerset(每条消息都会分片对应的消息id)
所以我们只要根据index就可以以最快的速度查询到消息文件
消息格式如下
消息长度: 4 bytes (value: 1 + 4 + n)
版本号: 1 byte
CRC校验码: 4 bytes
具体的消息: n bytes
总结以上:
kafka高效的原因
1.协议:简易的消息格式类似于mqtt和amqp;没有复杂的消息传输格式;
网络基于netty消息传递,使用零拷贝机制,实现消息的高效传输;
2.分发策略
采用发布确认机制(ack机制);保证消息可靠收发;
3.消息持久化
基于顺序存储的文件日志储存,不使用jvm内存的零拷贝机制,
文件写入采用顺序追加机制,高效的实现文件写入和读取;
4.高可用设计
采用分区(可以水平扩展)同时提高partition读写能力;
使用replica同步机制,当节点故障,利用zk的临时节点机制,选出新的主节点;
使用kafka可以从以上几点优化配置;
网友评论