平安保险基于spi机制的rocketmq 定制化应用

73 阅读10分钟

1、为什么选用RocketMQ

首先跟大家聊聊我们为什么会选用rocketMQ,在做技术选型的过程中,应用场景应该是最先考虑清楚的,只有确定好了应用场景在做技术选型的过程中才有明确的目标和衡量的标准。像异步、解耦、削峰填谷这些消息中间件共有的特性就不一一介绍了,这些特性是决定你的场景需不需要使用消息中间件,这里主要讲述下在确定使用消息中间件后,又是如何去选择哪款消息中间件的。

1.1同步双写,确保业务数据安全可靠不丢失

​我们在搭建消息中间件平台时的定位是给业务系统做业务数据的传输使用,对业务数据的很重要的一个要求就是不允许丢数据,所以选用rocketMQ的第一点就是他有同步双写机制,数据在主从服务器上都刷盘成功才算发送成功。同步双写条件下,MQ的写入性能与异步刷盘异步赋值相比肯定会有所下降,与异步条件下大约会有20%左右的下降,单主从架构下,1K的消息写入性能还是能达到8W+的TPS,对大部分业务场景而言性能是能完全满足要求的,另外对下降的这部分性能可以通过broker的横向扩招来弥补,所以在同步双写条件下,性能是能满足业务需求的。

1.2多topic应用场景下,性能依旧强悍

​ 第二点,业务系统的使用场景会特别多,使用场景广泛带来的问题就是会创建大量的topic,所以这时候就得去衡量消息中间件在多topic场景下性能是否能满足需求。我自己在测试的时候呢,用1K的消息随机往1万个topic写数据,单broker状态下能达到2W左右的TPS,这一点比kafka要强很多。所以多topic应用场景下,性能依旧强悍是我们选用topic的第二个原因。这点也是由底层文件存储结构决定的,像kafka、rocketMQ这类消息中间件能做到接近内存的读写能力,主要取决于文件的顺序读写和内存映射。rocketMQ中的所有topic的消息都是写在同一个commitLog文件中的,但是Kafka中的消息是以topic为基本单位组织的,不同的topic之间是相互独立的。在多topic场景下就造成了大量的小文件,大量的小文件在读写时存在一个寻址的过程,就有点类似随机读写了,影响整体的性能。

1.3支持事务消息、顺序消息、延迟消息、消息消费失败重试等

​ RocketMQ支持事务消息、顺序消息、消息消费失败重试、延迟消息等,功能比较丰富,比较适合复杂多变的业务场景使用

1.4 社区建设活跃,阿里开源系统

​ 另外,在选用消息中间件时也要考虑下社区的活跃度和源码所使用的开发语言,RocketMQ使用java开发,对java开发人员就比较友好,不管是阅读源码排查问题还是在MQ的基础上做二次开发都比较容易一点。社区里同学大都是国内的小伙伴,对大家参与RocketMQ开源贡献也是比较亲近的,这里呢也是希望更多的小伙伴能参与进来,为国内开源项目多做贡献。

2、SPI机制简介及应用

​ 介绍完为什么选用rocketMQ后,接下来给大家介绍下我们是如何基于SPI机制应用RocketMQ的。SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制,我个人简单理解就是面向接口编程,留给使用者一个扩展的点,像springBoot中的spring.factories也是SPI机制的一个应用。如图给大家展示的是rocketMQ中SPI的一个应用。我们基于SPI机制的RocketMQ客户端的应用的灵感也是来自于MQ中SPI机制的应用。RocketMQ在实现ACL权限校验的时候,是通过实现AccessValidator接口,PlainAccessValidator是MQ中的默认实现。权限校验这一块,可能因为组织架构的不一样会有不同的实现方式,通过SPI机制提供一个接口,为开发者定制化开发提供扩展点。在有定制化需求时只需要重新实现AccessValidator接口,不需要对源码大动干戈。

在这里插入图片描述 在这里插入图片描述

​ 接下来先给大家介绍下我们配置文件的一个简单模型,在这个配置文件中除了sendMsgService、consumeMsgConcurrently、consumeMsgOrderly这三个配置项外其余的都是RocketMQ原生的配置文件,发送消息和消费消息这三个配置项呢就是SPI机制的应用,是为具体实现提供的接口。可能有的同学会有疑问,SPI的配置文件不是应该放在META-INF.service路径下么?这里呢我们是为了方便配置文件的管理,索性就跟MQ配置文件放在了一起。前面也提到了,META-INF.service只是一个默认的路径而已,为了方便管理做相应的修改也没有违背SPI机制的思想。 ​ 我们再看下这个配置文件模型,这里的配置项呢囊括了使用MQ时所要配置的所有选项,proConfigs支持所有的MQ原生配置,这样呢也就实现了配置与应用实现的解耦,应用端只需呀关注的具体的业务逻辑即可,生产者消费者的实现和消费者消费的topic都可以通过配置文件来指定。另外该配置文件也支持多nameserver的多环境使用,在较复杂的应用中支持往多套RocketMQ环境发送消息和消费多套不同环境下的消息。消费者提供了两个接口主要是为了支持rocketMQ的并发消费和顺序消费。接下来呢给大家分享下如何根据这个配置文件来初始化生产者消费者。首先给大家先介绍下我们抽象出来的客户端加载的一个核心流程。

3、客户端核心流程详解

在这里插入图片描述

​ 图中大家可以看到,客户端的核心流程我们抽象成了三部分,分别是启动期、运行期和终止期。首先加载配置文件呢就是加载刚刚介绍的那个配置文件模型,在配置与应用完全解耦的状态下,必须先加载完配置文件才能初始化后续的流程。在初始化生产者和消费者之前应当先创建好应用实现的生产者和消费者的业务逻辑对象 供生产者和消费者使用。在运行期监听配置文件的变化,根据变化动态的调整生产者和消费者实例。这里还是要再强调下配置与应用的解耦为动态调整提供了可能。终止期就比较简单了,就是关闭生产者和消费者,并从容器中移除。这里的终止期指的生产者和消费者的终止,并不是整个应用的终止,生产者和消费者的终止可能出现在动态调整的过程中,所以终止了的实例一定要从容器中移除,方便初始化后续的生产者和消费者。介绍完基本流程后,接下来给大家介绍下配置文件的加载过程。

3.1如何加载配置文件

在这里插入图片描述

​ 配置文件加载这一块的话,流程是比较简单的。这里主要讲的是如何去兼容比较老的项目。RocketMQ客户端支持的JDK最低版本是1.6,所以在封装客户端时应该要考虑到新老项目兼容的问题。在这里呢我们客户端的核心包是支持JDK1.6的,spring早期的项目配置文件一般都是放在在resources路径下,我们是自己实现了一套读取配置文件的和监听配置文件的方法,具体的大家可以参考acl中配置文件的读取和监听。在核心包的基础上用springBoot又封装了一套自动加载配置文件的包供微服务项目使用,配置文件的读取和监听都用的spring的那一套。配置文件加载完之后, 配置文件中应用实现的生产者和消费者是如何与rocketMQ的生产者和消费者相关联的呢?接下来给大家分享下这方面的内容。

3.2如何将生产消费者与业务实现关联

在这里插入图片描述

​ 首先先看下消费者是如何实现关联的,上图是MQ消费者的消息监听器,需要我们去实现具体的业务逻辑处理。通过将配置文件中实现的消费逻辑关联到这里就能实现配置文件中的消费者与rocketMQ消费者的关联。消费者的接口定义也是很简单,就是去消费消息。消费消息的类型可以通过泛型指定,在初始化消费者的时候获取具体实现的参数类型,并将MQ接受到的消息转换为具体的业务类型数据。由客户端统一封装好消息类型的转换。对消费消息的返回值大家可以根据需要与MQ提供的status做一个映射,这里的demo只是简单显示了下。在获取具体的应用消费者实例的时候,如果你的消费逻辑里使用了spring管理的对象,那么你实现的消费逻辑对象也要交给spring管理,通过spring上下文获取初始化好的对象;如果你的消费逻辑里没有使用spring进行管理,可以通过反射的方式自己创建具体的应用实例。

在这里插入图片描述

与消费者不一样的是生产者需要将初始化好的producer对象传递到应用代码中去,而消费者是去获取应用中实现的逻辑对象,那如何将producer传递到业务应用中去呢? 业务代码中实现的生产者需要继承SendMessage,这样业务代码就获得了RmqProducer对象,这是一个被封装后的生产者对象,该对象对发送消息的方法进行的规范化定义,使之符合公司的相应规范制度,该对象中的方法也会对topic的命名规范进行检查,规范topic有一个统一的命名规范。

3.3如何动态调整生产消费者

在这里插入图片描述

首先谈到动态调整就需要谈一下动态调整发生的场景,如果没有合适的使用场景的话实现动态调整就有点华而不实了。这里我列举了四个配置文件发生变化的场景:

  • nameserver发生变化的时候,需要重新初始化所有的生产者和消费者,这个一般是在MQ做迁移或者当前MQ集群不可用是需要紧急切换MQ;

  • 增减实例的场景只要启动或关闭相应的实例即可,增加应用实例的场景一般是在需要增加一个消费者来消费新的topic的,减少消费者一般是在某个消费者发生异常时需要紧急关闭这个消费者,及时止损。

  • 调整消费者线程的场景中我们对源码进行了一点修改,让应用端能获取到消费者的线程池对象,以便对线程池的核心线程数进行动态调整。这个的应用场景一般是在当某个消费者消费的数据比较多,占用过多的CPU资源时,导致优先级更高的消息得不到及时处理,可以先将该消费者的线程调小一些。

4、应用的优点

在这里插入图片描述