演化阶段二
渐渐的,你设计的引擎还不错。那么新的挑战来了。
1、更多的业务方找到你,希望也使用你的项目进行任务制作,但是他们并不想共享资源,而是希望有自己的独立资源,和独立的队列。但并不是所有的资源都需要独立,一些可以支持高并发的资源,是可以共享的。简而言之,更多的业务方,由业务方为维度的独立队列,独立和共享的资源分配。
2、业务方找到你,说如果把任务1的结果给到任务2,其实就能拿到我要的结果。问题来了,原子任务要具备可以编排成复杂任务的能力。
3、任务的状态过程无法监控。OK,任务状态机。
4、既然大家都需要对接你的项目,能不能提供标准的sdk,我只需要引入就可以完美的对接你的系统。
5、相同的任务参数,是不是制作出来的结果一致呢?那么是否需要增加结果缓存,降低对资源的消耗呢?
6、完正的生产项目必然需要将日志、告警等关键信息传递出来,一旦发生问题可以马上定位到问题的起因。
这些问题对于新人来说还是很有挑战的,需要对系统深层的含义有充足的理解。没事,我来好好来说下设计所需要掌握的知识点。
设计说明:
1、需要在资源表中区别资源类型,共享资源组所有业务组都可以使用,独立资源则资源具备业务标识。在执行引擎的队列管理中,也需要区分业务组,避免共用排队。这里给一个建议,共享的资源一定要是可以支持并发或者可以部署多个实例的,避免所有的业务组产品制作瘫痪。
2、增加了高级任务概念,高级任务可以将任意的原子任务进行组合编排,形成全新的任务。需要定义专属于TEE的语法规则。对语法规则引擎的开发,有一些建议。你可能会选择规则引擎,建议其实可以自己开发,毕竟语法不会太过复杂,没必要引入三方的引擎。
3、增加任务状态机,执行引擎在提交线程的同时,也想任务状态机提交任务线程信息。任务的进度状态可以同步给任务状态机中,同时一旦任务执行过长的时间,除了任务自己的超时机制外,也可由状态机的看门狗程序将卡死线程释放、资源回收。
4、研发属于TEE的SDK,作为内部系统不建议SDK增加鉴权模块。毕竟你对接的往往都是业务后端,鉴权不通过的话根本渗透不到TEE层面。给开发SDK一些建议,尽量引用较少的包,避免业务端引入带来的包冲突。SDK也需要添加一些回调Consumer或者Function,尽可能让业务端对接起来代码简单。
5、增加了缓存策略,可以设想一下,大部分情况下,相同的参数制作出来的结果也必然相同。使用redis,将任务参数与任务结果进行缓存,主键可以采用任务参数的MD5值。任务在提交给任务执行引擎前,检查缓存中是否已经存在结果。缓存的过期时间按照具体情况而定。
6、增加日志系统和监控系统的对接,状态机与任务执行中的信息接入到日志系统中。对于日志系统的建议是,最好采用成熟的ELK架构。可以考虑两种方式
a、将日志异步推送到消息队列(例如:kafka),使用flink将kafka存入es。
b、使用logstash将日志内容清洗处理,推送到es。
两种方式都可以,但是一定要异步推送日志,避免任务阻塞。
告警系统的接入可以使用Prometheus,将TEE的指标信息开放出来,特别是告警信息。在Prometheus的告警监控规则中,可以将告警信息按照某些策略发送邮件或者短信,通知运维人员。
小结:
做到这里,我们再看看我们的技术架构图,是不是感到很满足。但是这真的够了吗?
演化阶段三
随着业务愈发繁重,一个新的问题出现。
1、TEE在本机运行很顺利,但是每个任务都是需要消耗线程的,单台模式线程必然不是无限的,总有吃满的时候。问题来了,TEE得支持分布式部署结构。但是有资源管理的存在,你无法通过加实例的方式来实现,因为资源调度必然混乱。
2、假设TEE挂掉,则等于业务组此刻提交的任务均失败,容灾机制需要建立。
到了这一步,很多小伙伴可能觉着一阵头大,分布式不是大数据的东西吗?不是的,不是大数据就不能分布式部署吗?就不能有主从节点吗?就不能有注册中心吗?要跨过内心的固有思想,我们往下看。
设计说明:
1、需要先将TEE项目做一下代码分解,将管理调度模块与任务执行模块拆解开了。消耗性能和线程较高的是任务执行模块,定义为TEE执行节点。消耗性能和线程较低,却需要参数校验、任务封装、资源调度、队列管理的是管理调度模块,定义为TEE管理节点。
2、TEE执行节点可以多实例,形成节点池。管理节点可以考虑做成共享任务队列的管理池。这里有个难点,如何共享任务队列。建议使用zookeeper或者缓存方式,但是不管哪种方式都要注意使用集群,避免单点故障导致队列数据丢失。
3、关于注册中心,可以使用开源组件,nacos、zookeeper都可以。在该架构设计中,其实注册中心并没有太多功能,如果你对自己有信心,可以尝试自己写一个注册中心,核心功能就是服务注册与心跳检测。可以用netty架构做一做,提高一下自己的代码能力。
4、在管理池的调用方面,增加网关代理,可以使用nginx、konga等。主要功能是业务端调用的时候,随机打到管理节点上,就算一个管理节点挂了,也不会影响使用,保证了生产线的稳定。
小结:
分布式架构,主从节点模式也好、哨兵模式也好、选举模式也好,按照自己的业务需求选择最适合自己的。不是大数据才有分布式,它是一种设计思想,要知道开发大数据组件的大佬们,也是一行行java写出来的。大佬们可以,为什么你不可以呢?
代码设计
在着手开发该系统的时候,我给大家一些代码开发的建议:
1、定时任务的实现,从最简单的while死循环加sleep,到定时线程池,或者springboot的@Scheduled注解,都可以实现。我在这里推荐一下时间轮算法TimeWheel,有兴趣的可以去了解一下。
2、异步消息处理,如果你只是想在项目内部使用消息总线,推荐使用guava包内的EventBus。按照消息的数量级,考虑使用RabbitMQ或者Kafka作为消息中间件。
3、任务执行,推荐使用CompletableFuture进行异步非阻塞任务编程。
4、在资源的使用中,可能存在多种协议类型http、ws、tcp等。所以代码设计中,尽可能提供完整全面的协议工具类。
总结
最近和一个同事闲聊的时候,他和我说了说最近面试高级研发的情况。总结一下,现在想招一个做过高并发场景的太难了,大部分人选只做业务相关。这让我想到了十年前,我刚工作的时候。那时候没有那么多框架,大部分功能需要看很多资料研究底层的原理与算法。随着软件行业的日益成熟,从以前拿着谷歌翻译看国外的组建说明,到从阿里云直接对接功能,软件研发的成本和时间也在大大缩短。渐渐失去了研究分析的动力,框架什么都有为什么要抓破脑袋自己写。
怎么成长?怎么进步?
上面给出的架构图,看上去都挺好理解。但真要自己去代码实现,中间各个环节出现的问题真的好解决吗?冰冻三尺,非一日之寒。没有日积月累的学习,是很难成功的。不要惧怕那些你看上去遥远的东西,获取你的几个晚上的学习,它就成了你最趁手的武器。对学习而言,难的永远不是过程,而是踏出第一步。
高并发任务执行架构中,有一个模块看上去很不起眼,但是在你研发的过程就会发现他会给你制造大麻烦-资源调度。以后有时间我会单独做一篇关于资源调度的架构设计,与大家说道说道里面的坑。
最后说一句:为什么不ban猛犸?