OpenHarmony开发者论坛
标题:
OpenHarmony4.0分布式任务调度浅析
[打印本页]
作者:
深开鸿_王奎
时间:
2023-10-31 09:52
标题:
OpenHarmony4.0分布式任务调度浅析
[md]本文以 OpenHarmony 4.0 Release 分布式任务调度模块源码进行解析。
作者:深开鸿-王奎
## 1 概述
OpenHarmony分布式任务调度是一种基于分布式软总线、分布式数据管理、分布式 Profile等技术特性的任务调度方式。它通过构建一种统一的分布式服务管理机制,包括服务发现、同步、注册和调用等环节,实现了对跨设备的应用进行远程启动、远程调用、绑定/解绑,以及迁移等操作的支持。此外,分布式任务调度还能够根据设备的能力、位置、业务运行状态、资源使用情况,以及用户的习惯和意图,选择最合适的设备来运行分布式任务,从而提高了任务的执行效率和质量。
### 1.1 应用场景举例
比如在用户的日常出行中,一段路程往往需要使用不同的交通工具,例如:从家里出发去某公园露营,需要先开车到达公园到公园停车场,然后骑单车进入公园,最终可能还需要步行一段距离才可以到达目的地。在这个过程中,用户需要分别打开车载导航APP和手机的导航APP,并分别输入起点和终点,最后才能完成一次出行导航。但是,借助于OpenHarmony分布式任务调度,可以将这些APP的功能进行整合,用户只需要在手机上输入起点和终点,然后点击出行按钮,借助于OpenHarmony分布式任务调度,应用就会根据使用场景自动地将导航信息流转到车机,手机,手表等不同的设备上。
![app_scene.png](data/attachment/forum/202310/31/093804u5yxud4uqh4uruhx.png "app_scene.png")
可以看到借助于OpenHarmony分布式任务调度,在物联网应用场景中可以实现设备间的资源共享,跨设备任务调度,从而创造出无限的可能性,提升用户的使用体验的同时,也提升了设备的使用效率。
### 1.2 系统架构
如图所示,分布式任务调度框架位于系统服务层,主要包括元能力子系统中的系统服务管理(safwk/samgr)和分布式组件管理部件(distributedsched)两个模块。其中,系统服务管理主要负责系统服务(SystemAbility)的启动、注册、发现、调用等功能。分布式组件管理主要负责分布式应用(Ability)调度的功能,包括元能力的远程绑定与调用、绑定关系的管理、分布式权限管理,元能力的远程启动与迁移等功能。
![system_arch.png](data/attachment/forum/202310/31/093818s9m89abz8jw6v75e.png "system_arch.png")
## 2 系统服务及其分布式特性
OpenHarmony的系统服务通常是一个常驻内存的进程,它可以通过IPC或RPC的方式向其他进程提供相关的系统能力,比如分布式数据管理、设备认证等。按照启动方式大体可将系统服务分为两种,原生的可执行程序(如设备认证服务)和SystemAbility(如分布式数据管理服务)。原生的服务通常比较底层,大多数使用C语言实现,其对应的编译目标也是可执行的二进制文件。比如deviceauth_service,它是设备认证的系统服务,主要负责设备的认证和鉴权。这种类型的服务通常在系统启动的时候由init进程根据服务的配置文件(json格式的文本文件)进行启动。
SystemAbility则是由samgr统一管理的so库,一般采用service.cfg + profile.xml + libservice.z.so的方式由init进程根据对应的service.cfg文件拉起相关系统服务能力进程。cfg配置文件为linux提供的native进程拉起策略,开机启动阶段由init进程解析该文件,调用sa_main(profile.xml)加载服务的so库文件。profile.xml是SA的描述文件,定义了服务的名称,so文件路径等信息。
本文的分布式任务调度主要指的是SystemAbility类型的系统服务,因此主要介绍SA的相关内容。SA类型的服务主要由safwk和samgr两部分组成,其中safwk定义了SystemAbility的实现方法,并且提供了SystemAbility的注册、调用等功能接口。samgr则是SystemAbility的管理者,通过IPC通信接收到safwk的消息,完成服务注册,调用,加载so库等功能。下面将分别从服务的启动,实现等方面介绍两个模块的实现原理。
![safwk.png](data/attachment/forum/202310/31/093831hqekq4vfpgpeep60.png "safwk.png")
### 2.1 SA启动流程
通过samgr管理的服务需要配置文件service.cfg和profile.xml,参考下图。其中name字段为服务的名称,path服务的启动路径。profile.xml定义了服务的名称,so文件路径等信息。需要注意的是,不同的SA可能会加载进同一个进程,因此process字段指定了要加载的进程名称,systemability字段定义了SA相关的信息。也就是说一个service进程可以加载多个SA的so库。
![cfg_profile.png](data/attachment/forum/202310/31/093844qw1cztbcbcbcc4fc.png "cfg_profile.png")
SA的启动流程如下:
- 在系统开机启动阶段,init进程会解析service.cfg文件,根据配置的path字段,调 用sa_main(profile.xml)启动服务。sa_main是safwk中编译得到的可执行文件,是SA的 启动入口。其首先会对参数进行预处理,判断profile.xml路径,saId等参数是否合法。
- 接着sa_main会调用lsamgr的DoStartSAProcess()方法启动服务进程。lsamgr(Local SystemAbility Manager)是safwk框架中的内部组件,主要用于在safwk内部对服务进行管理,注意与samgr服务区别。
- DoStartSAProcess()首先解析profile文件,配置服务进程,SA实例的名称,SaId等属性,然后通过IPC调用samgr服务的AddSystemProcess()方法将服务进程(本质上是一个绑定了服务相关信息的lsamgr实例)注册到samgr,然后启动服务进程。
- SA对应的系统进程启动之后,便需要加载启动SA自身。首先,samgr调用AddOnDemandSystemAbilityInfo(),将对应的SA实例与服务进程进行绑定,并准备启动对应的SA。
- 启动SA需要再次调用safwk中lsamgr的StartAbility()方法,该方法会加载SA对应的so库,并从中调用SA的Start()方法,完成服务的启动。在Start()方法中会触发钩子函数OnStart()的调用,OnStart()通常由SA自己实现,用来完成其的初始化工作,并调用Publish()发布SA。
- 最后Publish()方法会调用samgr的AddSystemAbility()方法,将SA注册到samgr中。
至此,服务启动完成,可以对外提供服务了。SA的整体管理流程较为复杂,上面省略了部分细节介绍了服务启动的核心流程,可以结合下图进行理解。
![sa_start.png](data/attachment/forum/202310/31/093853td9ipa31vapz9pa1.png "sa_start.png")
### 2.2 SA的实现
SA服务通过IPC通信向外提供服务,因此其除了需要继承SystemAbility外,实现其OnStart()等方法外,还需要实现IPC通信框架。OpenHarmony的IPC框架主要依赖下面几个类:
- IService:SA服务的接口类,用来定义并描述服务的能力,该类需要继承IRemoteBroker类,并使用宏命令DECLARE_INTERFACE_DESCRIPTOR声明接口描 述符。该类是IPC通信双方的公共接口,因此需要将其放在公共的头文件中,以便于双方都可以使用。
- ServiceStub:SA服务中IPC请求的接收类,该类需要同时继承IService和IRemoteStub类,并实现IRemoteStub类的OnRemoteRequest方法,该方法用来在接收 到IPC请求时处理请求。
- ServiceImpl:SA服务的实现类,继承ServiceStub和SystemAbility类,实现IService接口中定义的方法,该类用来实现服务的具体功能,并且需要在onStart方法中调用 Publish方法将服务发布出去。
- ServiceProxy:SA服务的代理类,继承IService和IRemoteProxy类,并实现IService中定义的同名方法,该类通过IRemoteProxy类的Remote()获取远程对象,并调用其SendRequest()方法向SA服务的Stub类发送IPC请求。
当其他进程或应用需要调用该SA服务的功能时,首先需要调用samgr的GetSystemAbility()方法获取服务的代理实例,即上述的ServiceProxy类的实例proxy。然后调用proxy中的同名方法,该方法通过Remote()->SendRequest()方法向SA服务的ServiceStub类发送IPC请求。在ServiceStub类的OnRemoteRequest()方法中接收到相应的IPC请求,从中读取参数,然后调用服务实现类ServiceImpl的具体方法去处理实际的业务逻辑,完成最后将结果写回IPC请求。
下面我们通过实现一个简单的HelloService服务来说明如何向系统中添加一个SA服务。该服务通过IPC通信向外提供一个SayHello接口,该接口接收一个string类型的参数name,然后生成字符串“Hello name”,将其通过hilog输出打印,并将结果返回给调用者。整体服务的类图如图。
![hello_service_class.png](data/attachment/forum/202310/31/093916worcqcrrdxzdbc4x.png "hello_service_class.png")
其中HelloService对应上述的ServiceImpl类,HelloServiceClient是服务的客户端,提供与服务同名的接口,在其中封装了获取服务代理对象以及调用代理类的同名接口,供外部程序调用。下面的流程图展示了外部进程调用该服务的流程。其中GetProxy方法会向samgr请求服务的代理实例,并通过接口转换函数iface_cast将其转换为自己进程内的HelloServiceProxy类型的实例proxy,图中的虚线对应IPC通信,其具体的实现机制在此不在展开介绍。
![hello_service_flow.png](data/attachment/forum/202310/31/093924lwpkpbn0c4dppp0z.png "hello_service_flow.png")
### 2.3 分布式SA
上述SA的启动及调用均为本地服务,下面来介绍SA服务的分布式特性,即本地设备调用其他设备的SA服务。注意,下文中的对端设备均指的是已经与本地设备完成组网认证的设备。分布式SA服务的启动与本地服务的启动类似,只需要在SA对应的profile文件中设置distributed属性为true即可。然后SA的Publish()方法中会检查注册阶段保存的该属性,若为true,则首先通过IPC框架的dbinder_service服务的RegisterRemoteProxy()接口对该SA服务的saId进行注册。dbinder_service服务会维护一个map类型的变量mapRemoteBinderObjects_以记录本地对外提供的分布式服务对象。在服务注册及启动阶段,分布式SA与本地SA并无太多区别,真正实现SA的分布式特性实在服务的访问调用阶段。其主要实现依赖于IPC/RPC框架,此处仅对和samgr相关的内容做简要介绍,更多关于RPC的细节请参考相关文章。
![dsa.png](data/attachment/forum/202310/31/093934y14gtrbrrzw4jm41.png "dsa.png")
如果本地设备需要调用对端设备SA服务的功能,则在获取SA代理时需要向samgr的GetSystemAbility()方法同时传入saId和deviceId。samgr检测到客户端请求的是其他设备的SA服务实例时,会调用IPC框架的dbinder_service服务的MakeRemoteBinder()方法来构建远程服务实例,该实例实际是一个指向DBinderServiceStub对象的指针。DBinderServiceStub主要用来实现RPC通信,这里不过多介绍。MakeRemoteBinder()方法会构造一个DBinderServiceStub类的实例,并绑定远程SA服务的saId和deviceId,然后记录在DBinderStubRegisted_(一个Vector类型的变量)中。接着与对端设备建立软总线会话,并通过软总线发送Entry消息到对端设备,挂起线程等待回复。若能够收到回复,则证明远程服务已经启动,将DBinderServiceStub类的对象返回给samgr,samgr进一步返回给远程SA服务的请求者。请求者收到该对象后,将其作为参数构造ServiceProxy代理类对象remoteProxy,之后的调用与本地SA服务调用类似,先调用remoteProxy中的同名接口,该接口通过Reomte()->SendRequest()方法发送RPC请求(通过软总线)。SendRequest()的跨端实现属于RPC框架部分,此处不再赘述。
由上述分析可以看到,相比于本地的SA,分布式SA不仅需要向samgr注册,还需要将其注册至IPC/RPC框架中以供后续跨设备调用。本地SA主要依赖于IPC通信提供服务,而分布式SA则通过RPC(依赖于软总线)向其他设备提供服务。
## 3 分布式组件管理
系统服务SA的分布式特性主要供框架层使用,几乎不会直接提供给上层应用。而实际应用中支持分布式调度的任务特性多是由上层应用实现。比如多设备联动,用户应用程序的流转,实现如邮件跨设备编辑、多设备协同健身、多屏游戏等分布式业务。上层应用的分布式调度主要由分布式组件管理服务(DistributedSched)服务提供。DistributedSched服务是OpenHarmony系统中的一个SA,可以向其他进程提供分布式任务调度的相关接口。分布式任务调度框架是分布式任务调度的核心功能所在,包括元能力的远程启动、流转等功能。
DistributedSched服务(dmsfwk框架)主要包括两个模块,dtbabilitymgr和dtbschedmgr。dtbabilitymgr主要负责元能力的分布式流转管理功能的实现,向上层应用提供流转管理的接口,也是SA服务的实例。dtbschedmgr主要负责分布式任务调度的具体功能实现,包括元能力的远程启动,迁移,绑定等具体功能的实现。
需要注意的是,在OpenHarmony3.2版本中,dtbschedmgr作为一个独立的so库,由dtbabilitymgr服务进程加载使用,但是在最新的4.0版本中,dtbschedmgr作为一个独立的SA服务运行,两模块间通过IPC通信。
### 3.1 远程启动Ability
Ability的远程启动主要依赖dtbschedmgr模块,在介绍dtbschedmgr模块前我们先简单了解一下OpenHarmony的应用启动流程。通常,我们通过应用的上下文类Context的StartAbility(want)方法启动新的Ability。Want是一种对象,用于在应用组件之间传递信息。例如,当UIAbilityA需要启动UIAbilityB并向UIAbilityB传递一些数据时,可以使用Want作为一个载体,将数据传递给UIAbilityB。如图所示:
![want.png](data/attachment/forum/202310/31/093944f3lud58a95kzv4vc.png "want.png")
Want类包含字段deviceid,给字段指明了启动新的Ability的设备。若该字段为空则表示在本地启动,反之表示在字段指定的设备上启动。(关于Ability启动的相关细节请参考Ability相关介绍: [应用模型](
https://docs.openharmony.cn/page ... opment-overview.md/
))。当ability_runtime框架判断需要处理的want的deviceid为其他设备时,便会调用dtbschedmgr的相关接口实现分布式的能力管理。
当AbilityManager服务需要远程启动Ability时,调用DistributedSched服务(dtbschedmgr模块)的StartRemoteAbility()方法处理远程启动的请求。StartRemoteAbility()方法首先会调用samgr的GetSystemAbility()方法获取对端设备上的DistributedSched服务的代理对象remoteProxy,这一步即是上文中提到的分布式SA服务的实现。然后设置abilityInfo,callerInfo,accountInfo等信息,并配置新的want对象。使用上述参数调用remoteProxy(对端设备服务代理)的StartAbilityFromRemote()方法。对端设备的DistributedSched服务收到请求后,会调用其StartAbilityFromRemote()方法,通过本地的AbilityManager服务启动本地Ability。具体流程参考下图。
![remote_start.png](data/attachment/forum/202310/31/093959g3566h6gpvk6zmwe.png "remote_start.png")
### 3.2 Ability流转
在OpenHarmony中,流转泛指跨多设备的分布式操作。流转能力打破设备界限,多设备联动,使用户应用程序可分可合、可流转,实现如邮件跨设备编辑、多设备协同健身、多屏游戏等分布式业务,如本章引言中提到的智能家居中视频播放流转及多场景导航应用的使用。流转为开发者提供更广的使用场景和更新的产品视角,强化产品优势,实现体验升级。流转按照使用场景可分为跨端迁移和多端协同。流转能力的实现主要依赖于dtbabilitymgr模块,整体的架构如图所示:
![continue.png](data/attachment/forum/202310/31/094007ch2o7exhmjqx7ujj.png "continue.png")
跨端迁移指在用户使用设备的过程中,当使用情境发生变化时(例如从客厅走到卧室或者周围有更合适的设备等),之前使用的设备可能已经不适合继续当前的任务,此时,用户可以选择新的设备来继续当前的任务,原设备退出任务,这就是跨端迁移场景。常见的跨端迁移场景实例:在平板上播放的视频,迁移到智慧屏继续播放,从而获得更佳的观看体验;平板上的视频应用退出。在应用开发层面,跨端迁移指在A端运行的UIAbility迁移到B端上,完成迁移后, B端UIAbility继续任务,而A端UIAbility退出。一次跨端迁移的流程大体如下图所示:
![migration.png](data/attachment/forum/202310/31/094018pla5wnyolxbznv7s.png "migration.png")
多端协同指用户拥有的多个设备,可以作为一个整体,为用户提供比单设备更加高效、沉浸的体验,这就是多端协同场景。常见的多端协同场景实例:平板侧应用A做答题板,智慧屏侧应用B做直播,为用户提供更优的上网课体验。在应用开发层面,多端协同指多端上的不同UIAbility/ServiceExtensionAbility同时运行、或者交替运行实现完整的业务;或者多端上的相同UIAbility/ServiceExtensionAbility同时运行实现完整的业务。一次多端协同的流程大体如下图所示:
![collaboration.png](data/attachment/forum/202310/31/094028qltr9wtzzalzkgra.png "collaboration.png")
由于在最新的OpenHarmony版本中(4.0)跨端迁移及多端协同的能力尚未具备,开发者当前只能开发具备跨端迁移能力的应用,但不能发起实际迁移。虽然流转功能的NAPI接口尚未完全实现,但其基本的流程和上述的远程启动流程类似,本地设备的DistributedSched服务的在ContinueRemoteMission()接口中远程调用对端设备的DistributedSched服务的ContinueMission()接口,从而实现流转功能。
流转功能的实现大体可分为两步,首先远程启动对端设备的Ability,然后同步本地设备的Ability的状态及数据至对端,从而实现流转。目前数据同步功能尚未完善,但是在应用层可通过分布式数据管理(KVDB或RDB)的相关接口实现数据的同步。这里可以参考OpenHarmony官网上的应用示例:
- 跨端迁移:[分布式音乐播放器](
https://gitee.com/openharmony/ap ... tributedMusicPlayer
)
- 多段协同:[分布式计算器](
https://gitee.com/openharmony/ap ... rkTSDistributedCalc
)
## 4 小结
本文带领读者了解分布式任务调度的基本概念和应用场景。结合OpenHarmony3.2源代码,简要介绍了分布式任务调度的实现原理和核心代码框架。了=包括SystemAbility的启动流程和实现原理,以及如何结合SA与IPC框架以向其他进程提供服务。在此基础上,介绍了分布式组件管理服务DistributedSched服务的实现原理,包括远程启动Ability和流转功能的实现。通过上述介绍,读者可以更深入地理解分布式任务调度的具体实现方式,以及其在实际应用开发中的使用。
[/md]
作者:
panda123
时间:
2023-11-30 09:34
[md]这个例子 可以在 本地模拟器上 跑,但感觉有点不正常 ?
[/md]
欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/)
Powered by Discuz! X3.5