第一个问题,为什么要使用rust开发?
笔者在最近10于年主要开发语言一直是golang,在公司用golang开发了运维统一作业调度执行软件,这款软件暂时就叫小J。小J为定时任务,批量任务,编排任务提供了统一的执行解决方案,用户不再需要使用ssh,salt等执行脚本,小J提供了统一的解决方案,只需简易配置,即可再成千上万的机器上执行任务,收集结果。
话题回到为什么用rust开发
小J哪里都好,但是笔者担心小J(jiacrontab)会panic,所以笔者开发时处处小心,可是心里没底啊,想必每个golang开发人员都曾见识过空指针的威力。
而且写golang久了,会有一点点觉得golang过于啰嗦,由于缺乏对迭代器的强力支持,处理数据时到处都是for循环。满篇的if err != nil 看起来也不是那么舒服。
所以我决定使用rust把小J重新打扮,开发大J(jiaschedule)。
架构问题
我们第一时间先把架构图放出来,方便读者有个大致的印象,方便后续内容的展开和引用

我们的目标是开发一款可以同时调度数万节点执行作业的服务,所以不得不重点考虑稳定性和效能。
怎么同时调度数万节点呢,写一个for循环,然后后台通过ssh连接到目标服务执行脚本?这样也行,但是会有一些不好解决的缺陷。
- ssh是同步连接并执行作业的,如果正在执行时连接中断怎么处理?
- 如果执行耗时很久,我们要如何处理长连接呢,一直连着等待结束之后,那么连接数占用将会相当惊人
- 如果我们需要在目标实例后台长期异步做一些处理,ssh也不好满足
所以我们不能直接使用ssh,需要一个异步解耦的方案。考虑到经常需要在目标实例后台处理任务,所以我们需要一个Agent直接安装在目标实例。采用Agent的好处太多了,不仅可以用来执行作业,在控制服务失效时也能自己干活。这就好像国家治理,我们不仅需要中央政府,地方也要有管理机构,不然这指令也不好下达呀
Agent多了如何管理
-
第一个要考虑的是一个存活性问题:简言之我们要知道agent在运行,agent都挂了,我们在agent上部署的作业和脚本肯定执行不了了呀。
-
第二个要考虑的是一个实时性的问题:我们要第一时间知道agent挂掉了,第二,第三时间都不行,要第一时间!
-
第三个要考虑的海量agent实例的调度问题:我们需要同时把作业同送到数以万计的实例上运行
结合这些问题,我们需要一个注册中心,统一来管理海量的agent实例节点的状态,每当有agent上线/下线时,注册中心能够第一时间更新实例状态。这样控制服务才能在作业调度时第一时间获得实例的状态,进而把作业正确的部署到实例上
实际上项目在开发中并没有采用传统的注册中心方案(如etcd,consul等),而是用采用redis+stream消息事件的机制来实现状态的管理。这么做主要是为了减少依赖的中间件数量,增强项目整体的稳定性,减少需要维护的中间件数量,毕竟项目现在只有笔者一个人
通信和指令
我们期望这个调度软件能够管理不同子网的实例。就如我们现在做的,我们在腾讯云部署了控制台,这个控制台有一个公网域名jiascheduler.iwannay.cn,我们登录这个控制台即可管理来自阿里云,百度云,亚马逊云,企业内网.....的实例。所以这里抛出了一个问题,内网如何穿透。
用户在控制台选定一个作业后,可以继续选择执行的实例,如何高效的把这个执行的实例发送到成千上万的实例上? 除了执行作业,还有其他各类执行动作,这些执行动作如何正确的触达到目标实例上?
针对内网穿透,用户可能想到的第一个解决方案是采用vpn或者代理,实际上这会把问题复杂化。我们的需求是能够管理不同子网的实例,并不需要兼容其它复杂的场景,我们不需要在子网实例上翻墙上网不是?
实际上一个联通agent和broker的活跃的tcp连接就能满足这个需求,我们把broker端口暴露在公网,或者能够联通其它子网的一个特殊的网段,我们只需要把指令下达给broker,通过broker再把指令分发给具体的执行节点。
最终的链路看起来大致是这个样子
console<-->broker<-->agent
agent主动向broker发起websocket长连接(为什么是ws?),console下发指令到broker,然后broker分发指令给具体的agent执行,agent会把执行结果通过这条链路再反馈到console。
这里通信是双向的,即broker可以主动发消息给agent,agent也可以主动发消息给broker,这里又抛出了一个问题,双向通信是如何实现问答机制的?即一个request需要对应response。 这些问题会在后续的文章中详细介绍,这里就不再赘述
我们用的指令是类似redis底层实现的Command,一个指令就是一个Command,一个Command对应要给具体的功能,未来我们给agent拓展功能,只需要拓展对应的Command就行了。
为什么是websocket
weboskcet好处很多,可以直接复用tls实现加密通信,可以直接使用http的认证机制,rust支持也非常好,有很多现成的库也可以用。整个使用体验比较平滑,如果直接使用tcp那么要封装的东西就未免有点太多了,自定义协议,设计加密...
结束
这是第一篇文章,主要讲了架构设计和通信问题,下篇文章会讲如何基于websocket通信实现非阻塞应答机制和内网穿透
项目地址:github.com/jiawesoft/j...
放几张截图


