乐趣区

关于消息队列:Pulsar下一代消息引擎真的这么强吗

背景

咱们最近在做新业务的技术选型,其中波及到了对消息中间件的抉择;联合咱们的理论状况心愿它能满足以下几个要求:

  • 敌对的云原生反对:因为当初的主力语言是 Go,同时在运维上可能足够简略。
  • 官网反对多种语言的 SDK:还有一些 PythonJava 相干的代码须要保护。
  • 最好是有一些不便好用的个性,比方:延时音讯、死信队列、多租户等。

当然还有一些程度扩容、吞吐量、低提早这些个性就不必多说了,简直所有成熟的消息中间件都能满足这些要求。

基于以上的筛选条件,Pulsar 进入了咱们的视线。

作为 Apache 下的顶级我的项目,以上个性都能很好的反对。

上面咱们来它有什么过人之处。

架构

从官网的架构图中能够看出 Pulsar 次要有以下组件组成:

  1. Broker 无状态组件,能够程度扩大,次要用于生产者、消费者连贯;与 Kafka 的 broker 相似,但没有数据存储性能,因而扩大更加轻松。
  2. BookKeeper 集群:次要用于数据的长久化存储。
  3. Zookeeper 用于存储 brokerBookKeeper 的元数据。

整体一看仿佛比 Kafka 所依赖的组件还多,这样的确会提供零碎的复杂性;但同样的益处也很显著。

Pulsar 的存储于计算是拆散的,当须要扩容时会非常简单,间接新增 broker 即可,没有其余的心智累赘。

当存储成为瓶颈时也只须要扩容 BookKeeper,不须要人为的做重均衡,BookKeeper 会主动负载。

同样的操作,Kafka 就要简单的多了。

个性

多租户

多租户也是一个刚需性能,能够在同一个集群中对不同业务、团队的数据进行隔离。

persistent://core/order/create-order

以这个 topic 名称为例,在 core 这个租户下有一个 ordernamespace,最终才是 create-ordertopic 名称。

在理论应用中租户个别是依照业务团队进行划分,namespace 则是以后团队下的不同业务;这样便能够很清晰的对 topic 进行治理。

通常有比照才会有挫伤,在没有多租户的消息中间件中是如何解决这类问题的呢:

  1. 罗唆不分这么细,所有业务线混着用,当团队较小时可能问题不大;一旦业务减少,治理起来会十分麻烦。
  2. 本人在 topic 之前做一层形象,但其实实质上也是在实现多租户。
  3. 各个业务团队各自保护本人的集群,这样当然也能解决问题,但运维复杂度天然也就进步了。

以上就很直观的看出多租户的重要性了。

Function 函数计算

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 的根底之上封装了一个外部应用的 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 这类库所带来的益处:

  • 对象交由容器治理,很不便的实现单例。
  • 当各个对象之前依赖关系简单时,能够缩小许多创立、获取对象的代码,依赖关系更清晰。

同样的害处也有:

  • 跟踪浏览代码时没有那么直观,不能一眼看出某个依赖对象是如何创立的。
  • 与 Go 所推崇的简洁之道不符。

对于应用过 SpringJava 开发者来说必定直呼真香,毕竟还是相熟的滋味;但对于齐全没有接触过相似需要的 Gopher 来说貌似也不是刚需。

目前市面上各式各样的 Go 依赖注入库层出不穷,也不乏许多大厂出品,可见还是很有市场的。

我置信有很多 Gopher 十分恶感将 Java 中的一些简单概念引入到 Go,但我感觉依赖注入自身是不受语言限度,各种语言也都有本人的实现,只是 Java 中的 Spring 不仅仅只是一个依赖注入框架,还有许多简单性能,让许多开发者望而却步。

如果只是依赖注入这个细分需要,实现起来并不简单,并不会给带来太多复杂度。如果花工夫去看源码,在了解概念的根底上很快就能把握。

回到 SDK 自身来说,GoSDK 现阶段要比 Java 版本的性能少(精确来说只有 Java 版的性能最丰盛),但外围的都有了,并不影响日常应用。

总结

本文介绍了 Pulsar 的一些基本概念与长处,同时顺便讨论一下 Go 的依赖注入;如果大家和咱们一样在做技术选型,无妨考虑一下 Pulsar

后续会持续分享 Pulsar 的相干内容,有相干教训的敌人也能够在评论区留下本人的见解。

退出移动版