咱们最近在作新业务的技术选型,其中涉及到了对消息中间件的选择;结合咱们的实际状况但愿它能知足如下几个要求:架构
Go
,同时在运维上可以足够简单。SDK
:还有一些 Python
、Java
相关的代码须要维护。固然还有一些水平扩容、吞吐量、低延迟这些特性就不用多说了,几乎全部成熟的消息中间件都能知足这些要求。框架
基于以上的筛选条件,Pulsar
进入了咱们的视野。运维
做为 Apache
下的顶级项目,以上特性都能很好的支持。ide
下面咱们来它有什么过人之处。函数
从官方的架构图中能够看出 Pulsar
主要有如下组件组成:工具
Broker
无状态组件,能够水平扩展,主要用于生产者、消费者链接;与 Kafka 的 broker 相似,但没有数据存储功能,所以扩展更加轻松。BookKeeper
集群:主要用于数据的持久化存储。Zookeeper
用于存储 broker
与 BookKeeper
的元数据。总体一看彷佛比 Kafka 所依赖的组件还多,这样确实会提供系统的复杂性;但一样的好处也很明显。spa
Pulsar
的存储于计算是分离的,当须要扩容时会很是简单,直接新增 broker
便可,没有其余的心智负担。code
当存储成为瓶颈时也只须要扩容 BookKeeper
,不须要人为的作重平衡,BookKeeper
会自动负载。中间件
一样的操做,Kafka
就要复杂的多了。对象
多租户也是一个刚需功能,能够在同一个集群中对不一样业务、团队的数据进行隔离。
persistent://core/order/create-order
以这个 topic 名称为例,在 core
这个租户下有一个 order
的 namespace
,最终才是 create-order
的 topic
名称。
在实际使用中租户通常是按照业务团队进行划分,namespace
则是当前团队下的不一样业务;这样即可以很清晰的对 topic 进行管理。
一般有对比才会有伤害,在没有多租户的消息中间件中是如何处理这类问题的呢:
以上就很直观的看出多租户的重要性了。
Pulsar
还支持轻量级的函数计算,例如须要对某些消息进行数据清洗、转换,而后再发布到另外一个 topic 中。
这类需求就能够编写一个简单的函数,Pulsar
提供了 SDK
能够方便的对数据进行处理,最后使用官方工具发布到 broker
中。
在这以前这类简单的需求可能也须要本身处理流处理引擎。
除此以外的上层应用,好比生产者、消费者这类概念与使用你们都差很少。
好比 Pulsar
支持四种消费模式:
Exclusive
:独占模式,同时只有一个消费者能够启动并消费数据;经过 SubscriptionName
标明是同一个消费者),适用范围较小。Failover
故障转移模式:在独占模式基础之上能够同时启动多个 consumer
,一旦一个 consumer
挂掉以后其他的能够快速顶上,但也只有一个 consumer
能够消费;部分场景可用。Shared
共享模式:能够有 N 个消费者同时运行,消息按照 round-robin
轮询投递到每一个 consumer
中;当某个 consumer
宕机没有 ack
时,该消息将会被投递给其余消费者。这种消费模式能够提升消费能力,但消息没法作到有序。KeyShared
共享模式:基于共享模式;至关于对同一个topic
中的消息进行分组,同一分组内的消息只能被同一个消费者有序消费。第三种共享消费模式应该是使用最多的,当对消息有顺序要求时可使用 KeyShared
模式。
官方支持的 SDK
很是丰富;我也在官方的 SDK
的基础之上封装了一个内部使用的 SDK
。
由于咱们使用了 dig 这样的轻量级依赖注入库,因此使用起来大概是这个样子:
SetUpPulsar(lookupURL) container := dig.New() container.Provide(func() ConsumerConfigInstance { return NewConsumer(&pulsar.ConsumerOptions{ Topic: "persistent://core/order/create-order", SubscriptionName: "order-sub", Type: pulsar.Shared, Name: "consumer01", }, ConsumerOrder) }) container.Provide(func() ConsumerConfigInstance { return NewConsumer(&pulsar.ConsumerOptions{ Topic: "persistent://core/order/update-order", SubscriptionName: "order-sub", Type: pulsar.Shared, Name: "consumer02", }, ConsumerInvoice) }) container.Invoke(StartConsumer)
其中的两个 container.Provide()
函数用于注入 consumer
对象。
container.Invoke(StartConsumer)
会从容器中取出全部的 consumer
对象,同时开始消费。
这时以我有限的 Go
开发经验也在思考一个问题,在 Go
中是否须要依赖注入?
先来看看使用 Dig
这类库所带来的好处:
一样的坏处也有:
对于使用过 Spring
的 Java
开发者来讲确定直呼真香,毕竟仍是熟悉的味道;但对于彻底没有接触过相似需求的 Gopher
来讲貌似也不是刚需。
目前市面上各式各样的 Go 依赖注入库层出不穷,也不乏许多大厂出品,可见仍是颇有市场的。
我相信有不少 Gopher
很是反感将 Java
中的一些复杂概念引入到 Go
,但我以为依赖注入自己是不受语言限制,各类语言也都有本身的实现,只是 Java 中的 Spring 不只仅只是一个依赖注入框架,还有许多复杂功能,让许多开发者望而生畏。
若是只是依赖注入这个细分需求,实现起来并不复杂,并不会给带来太多复杂度。若是花时间去看源码,在理解概念的基础上很快就能掌握。
回到 SDK
自己来讲,Go
的 SDK
现阶段要比 Java
版本的功能少(准确来讲只有 Java
版的功能最丰富),但核心的都有了,并不影响平常使用。
本文介绍了 Pulsar
的一些基本概念与优势,同时顺便讨论一下 Go
的依赖注入;若是你们和咱们同样在作技术选型,不妨考虑一下 Pulsar
。
后续会继续分享 Pulsar 的相关内容,有相关经验的朋友也能够在评论区留下本身的看法。