在系列文章前面几篇中,介绍了 NSQ 改造的过程和几个基础特性,本文中咱们继续介绍几个高级特性及其使用场景,这些都是结合有赞业务场景总结提炼出来的重要功能。html
有赞中间件在 NSQ 中引入了支持拓展内容的消息格式,经过支持拓展的消息格式。业务方可以在消息体外定义额外的数据,拓展了应用功能,支持更多的场景。git
相比较于 Kafka 等消息中间件,NSQ 的消息格式在内容和数量上较为简单。一条消息除了基本的元数据以外,其他内容为消息体。消息的元数据主要包括了消息在服务端产生时的时间戳,服务端对于该消息的下发次数,消息 ID。Kafka消息格式(record batch,control record,record)中出现的部
分元数据例如压缩格式(snappy),NSQ 在客户端建连的过程当中经过 IDENTIFY 确认,而部分元数据,如 CRC,事务属性等,在 NSQ 中则没有对应实现。github
消息格式的相对简单,使得 NSQ 传输消息内容上有更高的效率,同时使得编写 NSQ 客户端时更为容易。而简单格式所带来的缺点就是 NSQ 消息除了消息体自己以外,没法携带更多的额外信息。在传输一些能够和业务流程解耦的数据时,依然须要修改已有消息格式,而且因为缺乏重用性,每一个须要传输拓展数据的业务方都须要从新改造本身的业务消息格式。golang
为了使 NSQ 支持更多的场景,有赞中间件在原有 NSQ 消息格式的基础上进行了改进,设计并实现了一种支持拓展的消息格式。sql
能够看到新消息格式在已有消息格式上增长了 3 个部分(绿色字体):apache
长度为 1 个字节,用于区分拓展内容的类别和格式。例如,0x01 为 json 拓展;json
长度为 2 个字节,表示拓展内容的字节长度;数组
经过在消息格式中引入以上附加信息,NSQ 在消息传输过程当中可以在不修改原有消息格式的前提下附带额外的信息,业务方或者应用框架可以经过拓展消息格式支持新的场景和新的功能。在此咱们以有赞业务中使用的几个典型场景为例, 详细描述下扩展消息的使用。安全
链路压测是生产环境中的典型场景。压测器在短期内生产大量线上压测数据,用以检测线上链路的性能以及可用性。针对压测链路上使用消息中间件的应用,经过拓展消息设计,在链路压测场景中,消息中间件能够提供以下功能。服务器
fig1. 消息使用场景之链路压测
生产者应用在处理压测消息时,在拓展消息头中标记该消息为压测消息。NSQ 将线上消息以及压测消息统一下发至下游消费者(线上 Consumer),下游消费者经过检查拓展消息中的压测字段来判断该消息是否为压测流量,由应用框架根据拓展消息头内容决定是否下发至应用,或者对压测消息进行拦截。
该方案的优点在于,应用方无需对已有 NSQ 的 topic 生产/消费配置进行变动,新版 NSQ 经过对已有 topic 进行升级,使 topic 支持拓展消息格式。业务方仅须要关注压测消息的处理。该方案的缺点在于,线上消息和压测消息共用一个 topic,未进行隔离。一旦生产者对于压测消息的处理出现错误,或者下游消费应用超过负载时,此时隔离压测数据的操做较为复杂,须要业务方修改代码,新版 NSQ 经过回溯消费功能来“洗掉”压测消息。
拓展消息的另一种场景为应用链路隔离。场景以下:QA 环境总存在两类应用,第一类是 QA 环境中应用的稳定版本,另一类是应用在 QA 上进行新功能开发/验证的版本。QA 环境中应用经过 NSQ 进行解耦。新功能版本中增长了新的消息处理逻辑来消费稳定 QA 环境中不支持的消息,在 NSQ 不支持链路隔离前,开发须要:
fig2. QA 环境中应用使用 NSQ 场景
经过在 NSQ 服务端实现基于拓展消息头内容的投递优先级,新版 NSQ 支持业务上链路隔离的需求。
fig3. 新版 NSQ 支持链路隔离应用场景
供新功能验证的消息将经过在拓展消息头上的附带信息进行标记,NSQ 服务端在投递消息时根据消息头中的投递信息(Tag)按照如下规则进行路由:
经过实现该规则,新版 NSQ 支持业务方实现环境链路隔离。
NSQ 消息的消费模式为,消息在 channel 之间为组播,channel 内的客户端(Consumer)竞争一条消息。
fig4.NSQ 消息投递机制
与链路隔离的思路相似,经过对消息拓展头的指定值进行过滤,新版 NSQ 能够支持 channel 内的消息过滤。
订阅到相同 channel 上的消费者附带相同的拓展消息关键字,当 NSQ 投递消息时:
fig5. NSQ 基于 channel 的消息过滤
该功能的实现基于消息拓展头,能够在服务端,客户端单独实现,或由服务端和客户端共同实现。
对于正在使用开源版本 NSQ 的用户,NSQ migrate proxy 提供将开源版本 NSQ 迁移到有赞自研版本 NSQ 的能力。借助于该迁移工具,可在用户无感知的状况下对 topic 进行迁移。NSQ migrate proxy 在迁移过程当中做为开源 NSQ 和自研 NSQ 的代理,根据迁移阶段的变化将 lookup 请求代理至开源 NSQ 和自研 NSQ,整合 nsqlookupd 的结果后返回给客户端。使用迁移代理须要链接客户端实现读写策略,迁移代理须要根据读(r)写(w)参数对对生产者和消费者进行区分。
fig6. nsq迁移结构图
结合自研版 NSQ 的读写策略(r/w),NSQ migrate proxy定义了 3 个迁移阶段,到达最后阶段后,topic 的生产消费便迁移到自研版本
1.第 1 阶段中,代理将在返回给客户端的 lookup 结果中包含两个 NSQ 集群的节点信息。消费者将在两个集群间创建消费链接。生产继续向开源 NSQ 进行生产。
fig7.迁移阶段1
2.第 2 阶段中,代理对于生产者的 lookup 请求,只返回迁移目标集群的 lookup 结果。此时消息生产将指向目标 NSQ 集群。消费者继续维持双集群消费。
fig8.迁移阶段2
3.当确认开源 NSQ 集群中的消息已经消费完后,迁移进入最后阶段。代理对于消费者的 lookup 请求只返回目标 NSQ 节点信息。消费和开源 NSQ 的链接将断开。此时消息的生产和消费都迁移到自研 NSQ 集群。迁移完成。
fig9.迁移阶段3
除了围绕 NSQ 自己的的改造,咱们针对 spark 和 flume 尝试了经过拓展与 NSQ 进行集成。
spark consumer 做为 NSQ 的消费者,从 NSQ 消费消息后经过 spark streaming API 进行处理。
flume nsq sink 做为 apache flume sink 拓展,用于链接 flume 和 NSQ,并经过本地文件序列化保存发送失败的 event body 并重试。经过插件的方式,用户在 flume 中的配置文件中指定 NSQ 做为 flume 的下游。
为了支撑更多样的业务需求,有赞 NSQ 还在继续完善和丰富更多新特性, 这些特性包括 NSQ 自己的特性开发,也包括基于 NSQ 作的外部扩展系统的开发。将来的一段时间,咱们计划增长以下值得期待的重要特性。
目前有赞有大量的 topic 都部署在一个大的集群,受益于 golang 的goroutine模型,每一个topic基本都是独立的处理,互相直接影响不大, 可是碰到一些数据量大的状况, 仍是会对其余topic形成必定的影响,特别是一些网络流量很是大的 topic,为了下降这种topic流量影响,咱们须要限制一些topic的流量上限, 对整个集群的稳定性提供保障。 设计方案上, 咱们计划使用业界经常使用的令牌桶方案。
目前的 NSQ 仍是沿用每条消息 ack 的模式, 保持兼容特性。 性能上虽然知足目前以及将来一段时间的业务需求,可是还有改进的空间。特别是在某些网络延迟较高的场景下,批量订阅能够大大提升吞吐量。批量订阅将会支持一次消费一组消息而且能够一次性 ack 一组消息,从而减小必定的网络开销。
原版的 NSQ 已经支持一部分的安全审计功能, 包括使用安全连接以及使用验证服务器,咱们后面将会针对 topic 的生产和 channel 的一些操做提供独立的安全验证服务,并作好审计日志,防范一些安全问题。另外针对 nsqadmin 也会打通内部的统一登陆验证,针对性的限制业务的一些危险操做。
微服务拆分的痛点就是多个系统之间的一致性保证问题,所以急需一个统一的框架能解决此类问题。分布式事务协调器将会是构建在 NSQ 基础之上的一个重要产品, 该产品将会充分利用 NSQ 的一些特性去解决业务的痛点。
虽然目前已经有支持基于消息扩展头进行初步过滤的功能,可是也有些业务需求很是定制化,须要更加复杂的过滤规则,这种状况为了不给 NSQ 核心代码带来影响,咱们也计划在 NSQ 之上构建一个更加复杂的过滤系统去作和业务耦合的事情,避免给 NSQ 注入过多的业务耦合功能.
本文中,先展现了有赞中间件在 NSQ 中引入的支持拓展的消息格式,并经过 3 个业务场景来展现新的消息格式的玩法。以后的部分介绍了围绕自研版本 NSQ 开发的拓展工具,包括了用于迁移的代理,以及能够将 NSQ 与 spark 和 flume 进行集成的拓展。最后对于将来计划进行了介绍,展望了部分计划中的新特性。
点击连接,可直达《How we redesigned the NSQ》系列全部文章:
https://tech.youzan.com/tag/p...