前言
对于一个互联网项目来说,一般除了在语言层面的技术选型外,也会引入各种中间件,比如缓存、消息队列、搜索、NoSQL等,但是跳出语言层面来看的话,其实我们庖丁解牛之后,每个中间件都解决了特定的问题。
缓存中间件:解决了数据读的问题。
消息队列:解决数据写的问题,通过异步方式。
搜索:利用倒排索引来解决数据的搜索,以及日志搜索。
网关、代理中间件:解决流量分发和代理。
但是针对消息队列来说,早期可能使用ActiveMQ比较多,现在国内主流的就是Kafka、RabbitMQ、RocketMQ,但是除此之外,其实还有一个新型消息队列就是Apache Pulsar。
Pulsar和其他消息队列的不同点
针对于Kafka、RabbitMQ、RocketMQ来说消息都是存储在Broker对应的磁盘或者内存中的,而消费的时候需要先通过topic找到对应的Broker,然后才可以进行消费消息或者生产消息。
为避免单点故障,一般都是部署Broker集群,但是当一个Broker集群宕机后,并不是另外别的Broker就可以承担数据读写的任务,而是需要相应的具备相同数据的Broker可以,这样其实Broker就是一个有状态的节点,不利于横向扩容。
而在Pulsar中,是不存储元数据和消息数据的,计算和存储进行了分离。
Bookie和ZK
右侧中可以看到分别代表的是一个ZK集群和一个BookKeeper集群,ZK集群存储的是Broker的元数据,而BookKeeper集群存储的是具体的消息数据。
BookKeeper的存储单元是Ledger,它就是一段WAL(write Ahead log),可以理解为某个队列的一段,包含若干条数据。消息在Ledger称为Entry,为了保证Ledger中Entry的严格顺序,Pulsar增加了一次写的限制,比如当某个Broker创建了一个Ledger,只有这个Broker可以写入,当出现异常或者正常关闭的时候,这个Leder就不可以在写入,只能只读。
为什么采用一次性写入的设计?
我们知道在分布式存储中,想要保证数据的一致性,那么就需要加分布式锁,但是性能肯定会下降,所以为了保证数据的一致性,采用这种局部数据文件,可以不被共享,也就不需要加锁。
所以可以看到Broker不存储任何节点的元数据和消息数据,所以Broker只进行计算,这样Broker就是无状态的服务。
Load Balancer负责动态的分配,哪些Broker管理哪些主题分区,Managed Ledger负责管理本节点需要用到的Ledger,数据都保存在Bookie中,为了提升性能,采用了cache缓存。
存储计算分离优点
上述Pulsar就是采用存储计算想分离的思想,其实在我们实际业务中,一般也是采用存储计算分离,数据库存储数据,而系统进行处理请求,逻辑等。
这种方式可以很好的进行横行拓展,天然支持拓展。比如增加一台服务,动态的负载均衡都是比较容易的。
优点
1.计算节点上可以专注于计算业务逻辑开发,不需要关注数据一致性、数据可靠性、故障恢复和数据读写性能等比较麻烦的存储问题。
2.对于存储系统来说,只需要保证如何高效的存储数据,比如ZK、MySQL基本都是在优化存储引擎、提升性能、数据可靠性、可用性等。
缺点
1.针对于分布式数据一致性问题,并没有解决,而是在存储节点上要保证。
2.性能上可能会增加网络耗时等。
总结,存储计算分离模式其实还是从一定程度上减少了系统的复杂性,在做架构设计和技术选型的时候可以考虑。
小结
其实将计算和存储分离的思想,对应于软件设计层面来说,有点单一指责的味道,就是每个系统专注于自己的事情,存储系统就保证系统数据的存储,而计算系统保证系统的计算。对于开发、运维、测试的时候,以及部署层面来说,都是可以很好的进行调度和开发。但是具体到对应的常见,需要评估我们所针对的问题,然后进行选择,而不能一股脑就认为这就是黄金油。所以无论是编码还是技术选型、架构设计都是需要根据业务场景综合判断取舍。而架构师不就是这样一个角色嘛,trade-off 艺术。