1. 背景
高可用需求,在系统故障时减小故障的影响,尽量保证系统正常运行。在汽车电子尤为重要,核心服务/进程需要实时监听,具备死亡复活机制。
2. 技术框架-HAM
HAM(High Availability Manager)是一个“智能看门狗”——一个高弹性的管理进程,它可以在系统服务或进程失败或不再响应时监视您的系统并执行多级恢复。作为一个自我监控的管理者,HAM对内部故障具有弹性。不管出于什么原因,如果HAM本身被异常地停止,它可以通过移交给一个称为“监护人”的镜像进程,立即并完全地重建自己的状态。
2.1 HAM基本元素
术语 | 描述 |
---|---|
entry | 实体,指的是HAM监测的具体实体,可理解为进程 |
condition | 条件,条件与实体相关联,类似进程死亡这种时候 |
action | 动作,动作与条件相关联,一个条件可以关联多个动作,类似重新启动等具体行为 |
2.2 功能介绍
进程自我监控
进程可以自已选择监测的开始和结束时间,选择相关的触发条件和执行动作,类似“当我挂掉时,做什么事情”。
例:由于APP不是常驻内存,在执行一些可能CRASH的代码时,可请求HAM,当我出现异常,重启我。
监控外部应用
进程可以监控外部进程的状态,类似“当B挂掉了,做什么事情”。
例:Audio守护进程监控到Audio进程挂掉后,重启Audio。
监控全局
系统中一种虚拟进程,可以监控所有被监控的进程,类似“当任意进程挂掉,做什么事情”。
例:注册行为,在任意进程挂掉后,写日志。
QNX Neutrino架构的三个关键因素直接促成了内在 HA:
- 微内核,单个内核逻辑更少,部分重启不太影响全局
- POSIX 进程模型,单独的内存地址,可创建动态进程
- 消息传递,标准消息传递,有利于任务解耦、任务简化和服务分发。
原理: HAM启动时会有一个Guardian进程,并且将状态数据保存在共享内存,若HAM进程挂掉,则Guardian进程会取代HAM进程。
是谁启动的进程
QNX启动HAM进程,HAM管理需要监控的进程。
可以直接执行ham以启动ham进程。
不需要使用ham时可通过hamctrl -stop 停止ham,也可以调用ham_stop()
2.3 条件condition
Condition | Description |
CONDDEATH | 进程中止。 |
CONDABNORMALDEATH | 进程异常中止。 |
CONDDETACH | 断开HAM。 |
CONDATTACH | 连接上HAM。 |
CONDHBEATMISSEDHIGH | 进程失去心跳达最大次数。 |
CONDHBEATMISSEDLOW | 进程失去心跳达最小次数。 |
CONDRESTART | 进程重新启动。 |
CONDRAISE | |
CONDSTATE | 进程状态发生变化。 |
CONDANY | 任何condition改变。 |
2.4 动作Action
Action | Description |
ham_action_restart() | 重启。 |
ham_action_execute() | 执行一段脚本。 |
ham_action_notify_pulse() | 发送PULSE事件。 |
ham_action_notify_signal() | 发送系统信号事件。 |
ham_action_notify_pulse_node() | |
ham_action_notify_signal_node() | |
ham_action_waitfor() | 等待。 |
ham_action_heartbeat_healthy() | |
ham_action_log() | 输出日志。 |
2.5 使用
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <thread>
#include <chrono>
#include <iostream>
#include <process.h>
#ifdef QNX
#include <ha/ham.h>
#endid
int main(int argc, char** argv)
{
#ifdef QNX
printf("HAM Prepar~\n");
int status;
char* inetdpath;
ham_entity_t* ehdl;
ham_condition_t* chdl;
ham_action_t* ahdl;
int inetdpid;
// 新启一个进程
inetdpath = strdup("/usr/bin/picherdemo/helloworld -D");
// 连接HAM
ham_connect(0);
// 将进程交给HAM监控
ehdl = ham_attach("inetd", ND_LOCAL_NODE, inetdpid, inetdpath, 0);
if (ehdl != NULL)
{
//添加条件,当进程“death”时
chdl = ham_condition(ehdl, CONDDEATH, "death", HREARMAFTERRESTART);
if (chdl != NULL) {
//添加动作,重启进程
ahdl = ham_action_restart(chdl, "restart", inetdpath,
HREARMAFTERRESTART);
if (ahdl == NULL)
printf("add action failed\n");
}
else
printf("add condition failed\n");
}
else
printf("add entity failed\n");
//关闭与HAM的连接
ham_disconnect(0);
#endif // QNX
while (true) {
printf("Process id: %d\n ", getpid());
std::cout << "Thread id:\n"<< std::this_thread::get_id();
std::this_thread::sleep_for(std::chrono::seconds(5));
}
return 0;
}
2.6 案例:
#ifndef HAS_NO_HAM
static void bridgechip_ham_thread(void)
{
ham_entity_t *ehdl = NULL;
ham_condition_t *chdl = NULL;
ham_action_t *ahdl = NULL;
const char *line = BRIDGECHIP_RESMGR_PROCESS_PATH;
bool32 bHAMReady = FALSE;
uint32 uMaxRetries = BRIDGECHIP_RESMGR_HAM_THREAD_MAX_RETRIES;
LOG_TRACE("RM", "ENTER");
/* Check for availability of HAM. If not available within specified number
* of checks, exit the thread.
*
* Note: This approach is preferred over Resource_BlockWait() as it allows us
* to release the thread resource once re-attempts are exhausted.
*/
while ((FALSE == bHAMReady) && (0 < uMaxRetries))
{
BridgeChip_OSAL_IsPathExist(BRIDGECHIP_RESMGR_HAM_PATHNAME, &bHAMReady, NULL);
if (FALSE == bHAMReady)
{
BridgeChip_OSAL_SleepMs(BRIDGECHIP_RESMGR_HAM_THREAD_SLEEP_IN_MS);
uMaxRetries--;
}
else
{
LOG_INFO("RM", "HAM is ready");
}
}
if (TRUE == bHAMReady)
{
ham_connect(0);
ehdl = ham_attach(BRIDGECHIP_RESMGR_PROCESS_NAME, 0, getpid(), line, 0);
if (ehdl != NULL)
{
chdl = ham_condition(ehdl,CONDDEATH, "server_death", HREARMAFTERRESTART);
if (chdl != NULL)
{
ahdl = ham_action_restart(chdl, "server_restart", line, HREARMAFTERRESTART);
if (ahdl == NULL)
{
LOG_ERROR("RM", "add action failed strerror(errno=%d)=%s", errno, strerror(errno));
}
}
else
{
LOG_ERROR("RM", "add condition failed strerror(errno=%d)=%s", errno, strerror(errno));
}
}
else
{
/* If this isn't the first instance of bridgechip_server, HAM will already have an entity to track
* the liveness of bridgechip_server. Ignore "entity already exists" error.
*/
if (EEXIST != errno)
{
LOG_WARNING("RM", "add entity failed strerror(errno=%d)=%s", errno, strerror(errno));
}
}
}
else
{
LOG_WARNING("RM", "can't find %s", BRIDGECHIP_RESMGR_HAM_PATHNAME);
}
LOG_TRACE("RM", "EXIT");
}
#endif
3. 小结
HAM机制是QNX原生机制,通过监控进程状态做到进程的重启。