深入理解Moby Buildkit系列 #23 - jobs scheduler

308 阅读3分钟

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

果然不出袁小白所料,这scheduler还真不简单 - 因为没看懂,自己琢磨了一个星期,愣是没弄明白调度器是scheduler是怎么调度的。

没耐住性子,又求助了龙飞。 龙飞一如既往的没有让袁小白失望,没过多久,就按约定和袁小白讲起了自己的理解。 还是从一张图开始: scheduler.jpg 看着这张密集的流程图,袁小白不禁暗自感慨,这才不到两周啊,就又梳理完了?!

指着流程图,龙飞开始娓娓道来。 我是从solver/scheduler_test.go中的测试用例开始看的:

  • 创建Solver,创建的时候需要带上ResolveOpFunc,我的理解是对于scheduler而言,她是一套标准化流程,传入的Op需要提前做好封装,按接口实现,这里需要ResolveOpFunc帮助解析。袁小白想起了在上一步j.list.load中的newSharedOp,真如龙飞所说,提前封装了标准化的实例SharedOp。
  • 在初始化Solver也就是咱们的jobs时,实例化了如MemoryCache,jobs,actives状态的state集以及Scheduler。
  • 在初始化Scheduler时,除了初始化一些公用属性waitq, incoming, outgoing, stopped, closed, edgeFactory,最后还启动了s.loop,一直循环着监听着edge dispatch的信息。这里就相当于是消息总线,所有的edge dispatch事件都将从这里分发出去。
  • 接下来在测试中,我们初始化了一个Edge和自定义的Vertex;与此同时,我们需要用solver - jobs,新创建一个job,用来构建我们的Edge
  • 正式进入Build环节 - job.Build(ctx, Edge),这里就是SharedOp真正创建的地方,在loadUnlocked中,通过getEdge,对原生op进行了封装。
  • 而j.list.s.build,是scheduler真正build Edge的地方。首先创建了一个Pipe实例,这里的pipe我理解就是linux中的pipe管道,像./example | buildctl build一样,中间的这一竖,通过这个简单的运算符,大大的提高了效率。在这里我们将通过管道来管理和接收子请求。在这里调度器通过s.signal,发出了构建目标的信号,并一直等待着构建立的结果,<-wait就是会一直等在这,等到状态是Completed时,再继续走下去。

原来就是在这里,袁小白没弄清楚的地方也正在这,那这个管道到底是怎么管理所有的依赖和请求的呢? 没有着急去打断龙飞,继续顺着龙飞的思路梳理下去。

  • 通过这个s.signal,我们新创建了一个dispatcher,并将s.next指针,指向了她。到了这里还记得s.loop吗,这时她会监听到这个新的edge的到来,并开始触发edge的unpark操作。

说着龙飞又指向了中间这一块。 原来这个图得先看上,再看下,这时跳到了中间。 袁小白想,原来是这样,这跳来跳去,如果不是直接调用的话,确实容易迷失掉。

  • 在edge的unpark中,做了两件重要的事情。一件是e.op.CacheMap,在执行op前通过缓存获取所需要的信息,并且是根据e.op的不同而有不同的处理。我们可以设想一下SourceOp和ExecOp分别会怎么去处理自己的CacheMap。还有一件就是e.createInputRequests,这里会将我们的edge.Vertex分解成多个请求,并发送dispatch信号,由scheduler统一协调,那这些dispatch之间就需要通过pipe进行关联。

说完,龙飞看着袁小白,好像在问听懂了没有。 袁小白连自己也不清楚怎么回答这个问题,是有解决一些疑惑,但现在要用自己的话来总结,好像又不知从何处说起。

下一篇:深入理解Moby Buildkit系列 #24 - 神奇的Pipe