作者: 鸿湖万联(武汉) 许文龙
1、概述
Hiview是一个跨平台的终端设备维测服务集,由插件管理平台和基于平台上运行的服务插件来构成整套系统。Hiview维测服务是由HiSysEvent事件驱动的,其核心为分布在系统各处的HiSysEvent桩点,格式化的事件会通过HiSysEvent打点API上报至Hiview进行处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MxxbLkdS-1686048492639)(figures/image-20230606162340209.png)]
这张图没有完全按OpenHarmony的结构来讲述,而是采用三层架构讲述。
adaptor 层
适配不同系统。
base层
提供基础的hiview类型定义和代码框架。包括:
-
HiviewContex: 提供hiview上下文基类定义。
-
EventSource: 事件源。 事件源监听事件,并传递给绑定的流水线(pipelines)。一个事件源可以绑定多个流水线。
-
Pipeline: 流水线,接收来自事件源分发的事件。流水线根据插件顺序依次将事件交给插件处理。
-
插件:挂载在流水线上,用于监听流水线事件。用户可以将自己希望监听的事件类型开发成插件挂载在流水线上。这个过程称为订阅。插件可以是so的动态库形式,也可以是静态的。
-
编译脚本: 将plugin_build.json 转变成plugin_config ,并打包到系统。插件配置脚本配置了事件源、流水线、插件的关系。
系统服务层
这一层主要是基于base框架,实现了一套系统hiview平台。实现了系统的事件源SysEventSource,HiviewService、平台信息监视和提供了各种插件。
这一层还提供了各种配置文件,分为平台配置、插件配置、监视配置。
后面会详细讲解。
2、hiview基本原理
一个事件源可以配置多个流水线。一个hiview平台也可以配置多个事件源。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0IVpu1FM-1686048492640)(figures/image-20230606171255468.png)]
流程
1、分布在各个子系统、模块的服务使用HiSysEvent接口上报事件信息。
2、事件源读取到事件信息,并交给流水线
3、流水线打印日志。已被订阅的事件类型传递给订阅者处理。
3、hiview平台
这里指在hiview里实现的HiViewPlatform。
HiviewPlatform基于base提供的event、logger处理,和框架实现了一套系统的hiview平台。理解它的实现方式可以很好帮助我们理解整个hiview。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8IGReshn-1686048492641)(figures/image-20230606182950957.png)]
类图关系如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chrs1ZMi-1686048492641)(figures/image-20230606182257707.png)]
步骤(不重要内容有省略):
1、解析hiview_platform_config 配置文件,即平台配置
配置插件配置文件、插件配置目录、动态库路径、工作目录等。
/system/etc/hiview/hiview_platform_config
DEFAULT_PLUGIN_CONFIG_NAME = "plugin_config"
PLUGIN_CONFIG_FILE_DIR = "/system/etc/hiview/"
DYNAMIC_LIB_SEARCH_DIR = "/system/lib/"
DYNAMIC_LIB64_SEARCH_DIR = "/system/lib64/"
WORK_DIR = "/data/log/hiview/"
COMMERCIAL_WORK_DIR = "/log/LogService/"
PERSIST_DIR = "/log/hiview/"
2、解析插件配置
插件配置的路径在平台配置 DEFAULT_PLUGIN_CONFIG_NAME
可找到,这个配置文件定义了事件源、流水线、插件之间的关系。如下:
plugins:8 <- 8个插件
# 插件格式 : 插件名称[pool(所属的线程池):线程名称:线程数量(缺省1)]:0(延时,0表示立即) static(加载方式、静态或动态)
SysEventSource[thread:sysevent_source]:0 static <- 事件源, 这里配置的是系统事件源
EventLogger[thread:event_logger]:0 static <- logger 事件
FreezeDetectorPlugin[thread:freeze_detector]:0 static
HiCollieCollector[]:0 static
SysEventService[thread:sysevent_service]:0 static
Faultlogger[]:0 static
UsageEventReport[thread:usage_event_report]:0 static
BBoxDetectorPlugin[thread:bbox_detector]:0 static
pipelines:1 <- 流水线个数
SysEventPipeline:SysEventService Faultlogger EventLogger BBoxDetectorPlugin
pipelinegroups:1
SysEventSource:SysEventPipeline <- 关联事件源与流水线, 可以配置多个流水线
这个是运行的时候的配置文件。编译的时候,需要配置编译插件配置文件,在build/plugin_build.json。 这个文件以json格式定义,编译之后会生成plugin_config。 因此,他们表示的含义一样,只是形式不同。
因为有的插件是独立编译并配置到系统的,因此,独立编译的插件需要手动单独配置到plugin_config。
3、 启动系统事件源,会接收两个事件服务(EventServer),一个socket,另一个bbox。 同时会启动平台监视,收集平台数据。
监听配置主要是配置定时器周期等参数:
collectPeriod = 300
reportPeriod = 3600
totalSizeBenchMark = 209715200
realTimeBenchMark = 100000
processTimeBenchMark = 200000
4、根据插件配置加载插件
5、运行过程中接收订阅事件
4、开发指导
自己开发主要是需要构建一个hiview平台,事件源、监听插件,并提供事件源、流水线和插件的插件配置文件。
事件订阅
插件与插件之间,或者插件与单独的订阅者模块之间,可以通过事件订阅的方式进行交互。
事件订阅分为两种:
一种是单独的订阅者模式EventListener,该模式可以单独形成一个EventListener模块专门订阅事件、处理事件的小模块;也可以被插件继承用于插件监听事件,当插件选择这种监听模式将不能被动态加载和卸载
需要继承EventListener并且实现如下方法或者使用如下的接口:
class EventListener {
public:
/* 该方法已经弃用,为了兼容以前的代码没有删除,新代码中不需要实现 */
virtual bool OnOrderedEvent(const Event &msg);
/* 该方法为接收订阅来的外部驱动事件,并且处理事件 */
virtual void OnUnorderedEvent(const Event &msg) = 0;
/* 该方法为定义当前订阅者的名字 */
virtual std::string GetListenerName() = 0;
// 以下为接口
/* 添加需要订阅的事件。订阅的事件分为依靠eventName订阅和EventIdRange(eventId范围也可以是全范围或者单一id)订阅。type是事件类型 */
void AddListenerInfo(uint32_t type, const EventListener::EventIdRange &range = EventListener::EventIdRange(0));
void AddListenerInfo(uint32_t type, const std::set<EventListener::EventIdRange> &listenerInfo);
void AddListenerInfo(uint32_t type, const std::string& eventName);
void AddListenerInfo(uint32_t type, const std::set<std::string> &eventNames);
/* 可以获取当前订阅者订阅的事件信息 */
bool GetListenerInfo(uint32_t type, std::set<std::string> &eventNames);
bool GetListenerInfo(uint32_t type, std::set<EventListener::EventIdRange> &listenerInfo);
};
事件源
事件源是继承于EventSource实现特定的事件源功能。事件源也是一种特殊的插件,EventSource继承于Plugin可以实现或者使用其中公共的方法。事件源作为事件的驱动者,不能被代理加载,只能静态加载。
事件源需要继承EventSource这个类实现如下方法或者使用如下的接口,根据需求也需实现Plugin类中的虚方法:
class EventSource : public PipelineEventProducer, public Plugin {
public:
/* 开启事件源,当平台加载完成,加载事件源之后执行,可以用于初始化或者开启一些事件源数据或者事件 */
virtual void StartEventSource();
/* 通知事件生产者事件已完成其传递 */
virtual void Recycle(PipelineEvent* event);
/* 暂停调度并安排恢复调度 */
virtual void PauseDispatch(std::weak_ptr<Plugin> plugin);
//以下为接口
/* 将事件下发到各个流水线,开始当前事件源下流水线的运作 */
bool PublishPipelineEvent(std::shared_ptr<PipelineEvent> event);
/* 动态添加流水线到当前事件源 */
void AddPipeline(std::shared_ptr<Pipeline> pipeline);
};
事件源实例详见test/plugins/examples/event_source_example或者test/plugins/examples_bundle/bundle_event_source_example
插件
插件并不一定是so的动态库形式,也可以是一个静态编译的模块、类。参考列表中的例子。
插件可以编译时注册,也就是通过build/plugin_build.json配置。也可以独立编译,独立配置。独立编译的插件需要配置到/system/etc/hiview/plugin_config.
插件的注册方式有三种:
名称 | 说明 | 实例 |
---|---|---|
静态注册 | 通过使用宏定义REGISTER(xxx);注册,该种注册方式的插件不能被卸载 | 实例详见test/plugins/examples/event_processor_example1中的插件 |
代理注册 | 通过使用宏定义REGISTER_PROXY(xxx);注册,开机不加载运行时动态加载卸载 | 实例详见test/plugins/examples/event_processor_example3中的插件 |
代理注册且开机加载 | 通过使用宏定义REGISTER_PROXY_WITH_LOADED(xxx);注册,开机加载之后再动态卸载加载 | 实例详见test/plugins/examples/event_processor_example4中的插件 |
所有插件都有继承Plugin类,如下:
OnLoad,OnUnload 并不是加载/卸载动态库,而是用于注册服务。参考上表例子。
class Plugin {
public:
/* 插件生命周期中,当前插件被平台加载起来后调用,可以用于初始化一些数据 */
virtual void OnLoad();
/* 插件生命周期中,当前插件被平台卸载前调用,可以用于回收一些数据 */
virtual void OnUnload();
/* 运行期判断当前插件是否被加载,返回true是被需要被平台加载;false是将永远不会被平台加载。Plugin默认返回true */
virtual bool ReadyToLoad();
/* 当前插件在流水线上时,接收流水线上外部驱动事件,或者当前插件自己设置的延时事件,并且处理事件。其处理过程是当前插件定义是的线程中,若没有指定则在当前线程进行 */
virtual bool OnEvent(std::shared_ptr<Event>& event) override;
/* 当前插件在流水线上,且是当前流水线上第一个插件时,判断是否流转整条流水线。返回true是流转整条流水线;false是不流转整条流水线 */
virtual bool CanProcessEvent(std::shared_ptr<Event> event) override;
/* 当前插件在流水线上时,判断当前事件源是否进入暂停状态。返回true是进行处理,将会继续调用插件的OnEvent方法;false将进入事件源PauseDispatch方法,进行处理,然后继续执行后续操作 */
virtual bool CanProcessMoreEvents() override;
/* 可以通过hidumper命令行查询当前插件的dump信息,该接口就是实现能dump出来那些内容 */
virtual void Dump(int fd, const std::vector<std::string>& cmds);
/* 当前插件最为一个事件动态订阅者,接收通过动态订阅来的外部驱动事件,并且处理事件 */
virtual void OnEventListeningCallback(const Event &msg);
// 以下为接口
/* 当前插件作为动态订阅者时,添加需要订阅的事件。订阅的事件分为依靠eventName订阅和EventIdRange(eventId范围也可以是全范围或者单一id)订阅。type是事件类型 */
void AddEventListenerInfo(uint32_t type, const EventListener::EventIdRange &range = EventListener::EventIdRange(0));
void AddEventListenerInfo(uint32_t type, const std::set<EventListener::EventIdRange> &listenerInfo);
void AddEventListenerInfo(uint32_t type, const std::string& eventName);
void AddEventListenerInfo(uint32_t type, const std::set<std::string> &eventNames);
/* 设置延时执行事件 等时间到了以后在OnEvent中执行 */
void DelayProcessEvent(std::shared_ptr<Event> event, uint64_t delay);
/* 得到hiview插件管理平台接口的上下文 */
HiviewContext* GetHiviewContext();
const std::string& GetName();
const std::string& GetVersion();
void SetVersion(const std::string& version);
}
平台接口
平台提供的功能在插件中可以通过调用GetHiviewContext()获得平台接口上下文。平台提供的主要的功能接口如下:
class HiviewContext {
public:
/* 将继承了EventListener类的订阅者,注册到平台中,开启订阅 */
void RegisterUnorderedEventListener(std::weak_ptr<EventListener> listener);
/* 将作为插件中的动态订阅者,注册到平台中,开启订阅。动态插件和静态插件只要是想通过插件内部接口和方法定义的订阅者都可以使用这个注册 */
void RegisterDynamicListenerInfo(std::weak_ptr<Plugin> listener);
/* 发送事件到订阅者,该接口发送的事件,无论那种订阅方式都可以按规则被订阅到 */
void PostUnorderedEvent(std::shared_ptr<Plugin> plugin, std::shared_ptr<Event> event);
/* 卸载并从平台移除插件,之后再也不能被加载起来,慎用 */
void RequestUnloadPlugin(std::shared_ptr<Plugin> caller);
/* 获得平台中共享的EventLoop线程 */
std::shared_ptr<EventLoop> GetSharedWorkLoop();
/* 按照流水线名称获得流水线当中的插件 */
std::list<std::weak_ptr<Plugin>> GetPipelineSequenceByName(const std::string& name);
/* 检测平台是否已经全部加载完毕,返回true是全部初始化和加载完毕,返回false就是平台没有初始化和加载完成 */
bool IsReady();
/* 按照key值获得平台参数 */
std::string GetHiviewProperty(const std::string& key, const std::string& defaultValue);
/* 可以向平台中设置平台参数 key-value形式 */
bool SetHiviewProperty(const std::string& key, const std::string& value, bool forceUpdate);
/* 向平台的特定流水线中添加特定插件 */
void AppendPluginToPipeline(const std::string& pluginName, const std::string& pipelineName);
/* 通过插件名称获取插件或者插件的代理 */
std::shared_ptr<Plugin> GetPluginByName(const std::string& name);
};
5、 总结
hiview 其实就是一个维持服务平台,它可以集合dfx的许多功能,比如faullogger,hicollie,eventlogger等,将他们集合到一起又不会互相冲突,构成一整套可靠性维测系统,为我们系统的开发和运行提供了极大的帮助。