本章介绍了 DSDL 在设计和实现资源类型方面的典型用法, 还着重介绍了设计资源类型以验证资源配置以及启动、停止和监视该资源等内容。 在本章的最后还介绍了如何使用 DSDL 实现资源类型回叫方法。
有关其它信息,请参阅 rt_callbacks(1HA) 手册页。
要完成这些任务,您需要存取资源的特性设置。 DSDL 公用程序 scds_initialize() 向您提供了一种用来存取资源特性的统一方法。 本函数设计成在每个回叫方法的开头部分进行调用。 此公用程序函数用来从群集框架检索资源的所有特性,并使其可供 scds_getname() 系列函数使用。
资源类型登记 (RTR) 文件是资源类型的重要组成部分。 此文件用来向 Sun Cluster 指定资源类型的详细信息。 这些详细信息包括实现所需的特性、这些特性的数据类型以及缺省值、资源类型实现的回叫方法的文件系统路径以及系统定义的特性的各种设置。
DSDL 附带的 RTR 文件样例应该能够满足大多数资源类型实现的要求。 您仅需编辑一些基本的元素,例如资源类型的名称和该资源类型回叫方法的路径名。 如果实现该资源类型时需要一个新的特性,您可以在该资源类型实现的资源类型登记 (RTR) 文件中将该特性声明为扩展特性,然后使用 DSDL scds_get_ext_property() 公用程序存取新特性。
RGM 会在两种情况下调用资源类型实现的 Validate 方法: 1) 创建资源类型的新资源时;2) 更新资源或资源组的特性时。 这两种方案可以根据传送到资源的 Validate 方法的命令行选项 -c(创建)或 -u(更新)是否存在来区分。
对一组节点中的每一个节点都调用 Validate 方法,其中该组节点都是由资源类型特性 INIT_NODES 的值定义的。 如果 INIT_NODES 设置为 RG_PRIMARIES,则将对每一个可以承载含有该资源的资源组的节点(主节点)调用 Validate。 如果 INIT_NODES 设置为 RT_INSTALLED_NODES,则将对每一个安装有资源类型软件的节点(通常是群集中的所有节点)调用 Validate。 INIT_NODES 的缺省值为 RG_PRIMARIES(请参阅 rt_reg(4))。 调用 Validate 方法时,RGM 尚未创建资源(在创建回叫的情况下)或尚未应用所更新特性的更新后的值(在更新回叫的情况下)。 资源类型实现的 Validate 回叫方法的用途是检查所建议的资源设置(按照该资源的建议特性设置指定)是否适用于该资源类型。
如果您使用 HAStoragePlus 管理的本地文件系统,则使用 scds_hasp_check 检查 HAStoragePlus 资源的状态。使用为该资源定义的 Resource_dependencies 或 Resource_dependencies_weak 系统特性,从资源依赖的所有 SUNW.HAStoragePlus(5) 资源中获取此信息。 有关 scds_hasp_check 调用返回的状态码的完整列表,请参阅 scds_hasp_check(3HA)。
DSDL 函数 scds_initialize() 按以下方式处理这些情况:
针对创建资源的情况,它将分析传送到命令行的所建议的资源特性, 从而资源类型开发者可以使用所建议的资源特性值,就象已经在系统中创建了该资源一样。
针对更新资源或资源组的情况,将从命令行读取管理员所更新的特性的建议值;对于其余特性(值未进行更新),将使用资源管理 API 从 Sun Cluster 读取。 使用 DSDL 的资源类型开发者无需自己执行所有这些内务处理任务, 资源的验证就可以完成,就象该资源的所有特性都可供开发者使用一样。
假设实现资源特性验证的函数为 svc_validate(),它使用 scds_get_name() 系列函数来查看它要验证的特性。 假设可接受的资源设置由此函数的 0 返回代码表示,则该资源类型的 Validate 方法可以通过以下代码段表示:
int main(int argc, char *argv[]) { scds_handle_t handle; int rc; if (scds_initialize(&handle, argc, argv)!= SCHA_ERR_NOERR) { return (1); /* Initialization Error */ } rc = svc_validate(handle); scds_close(&handle); return (rc); } |
该验证函数还应记录资源验证失败的原因。 如果不考虑相应细节内容(有关对验证函数的更为实际的处理方法,请参阅下一章),简单的 svc_validate() 函数实例可用以下方式实现:
int svc_validate(scds_handle_t handle) { scha_str_array_t *confdirs; struct stat statbuf; confdirs = scds_get_confdir_list(handle); if (stat(confdirs->str_array[0], &statbuf) == -1) { return (1); /* Invalid resource property setting */ } return (0); /* Acceptable setting */ } |
因此,资源类型的开发者仅需执行 svc_validate() 函数的实现任务。 资源类型实现的典型实例可以确保名为 app.conf 的应用程序配置文件存在于 Confdir_list 特性中。 通过对源自 Confdir_list 特性的相应路径名进行 stat() 系统调用,就可以很方便地实现此目的。
RGM 将对所选群集节点调用资源类型实现的 Start 回叫方法,以启动该资源。 通过命令行传送资源组名称、资源名称和资源类型名称。 Start 方法应执行在群集节点上启动数据服务资源时所需的操作。 这些操作通常涉及检索资源特性、定位应用程序特定的可执行文件和/或配置文件以及使用相应的命令行变量启动该应用程序。
借助 DSDL,资源配置已可以被 scds_initialize() 公用程序检索。 应用程序的启动操作可以包含在函数 svc_start() 中。 可以调用另一个函数 svc_wait() 来检验是否确实启动了应用程序。 Start 方法的简化代码为:
int main(int argc, char *argv[]) { scds_handle_t handle; if (scds_initialize(&handle, argc, argv)!= SCHA_ERR_NOERR) { return (1); /* Initialization Error */ } if (svc_validate(handle) != 0) { return (1); /* Invalid settings */ } if (svc_start(handle) != 0) { return (1); /* Start failed */ } return (svc_wait(handle)); } |
该启动方法实现调用 svc_validate() 来验证资源配置。 如果失败,可能是因为资源配置和应用程序配置不匹配,也可能是因为当前与系统相关的这个群集节点上存在问题。 例如,当前此群集节点上可能没有提供该资源所需的全局文件系统。 在这种情况下,在此群集节点上尝试启动该资源是无用的, 最好是让 RGM 在其它节点上启动该资源。 注意,以上假设 svc_validate() 是十分保守的(因此它仅检查群集节点上应用程序绝对需要的资源),否则资源可能无法在所有群集节点上启动并处于 START_FAILED 状态。 有关对此状态的说明,请参阅 scswitch(1M) 和《Sun Cluster 数据服务规划和管理指南(适用于 Solaris OS)》。
如果资源在节点上成功启动,则 svc_start() 函数必须返回 0。 如果启动函数遇到了问题,则它必须返回一个非零值。 此函数失败后,RGM 将尝试在其它群集节点上启动该资源。
为了尽可能充分地利用 DSDL,svc_start() 函数可以使用 scds_pmf_start() 公用程序在进程管理工具 (PMF) 控制之下启动该应用程序。 此公用程序还将利用 PMF 的失败回叫操作功能(请参阅 pmfadm( 1M) 中的 -a 操作标志)来实现进程失败检测。
RGM 将对群集节点调用资源类型实现的 Stop 回叫方法,以停止该应用程序。 Stop 方法的回叫语义要求:
Stop 方法必须具有幂等性,因为即使 Start 方法在节点上未成功完成,RGM 也能调用 Stop 方法。 因此,即使应用程序未在群集节点上运行并且没有要执行的操作,Stop 方法也必须成功(在返回零的情况下退出)。
如果资源类型的 Stop 方法在群集节点上失败(在返回非零值的情况下退出),则要停止的资源将在 STO_FAILED 状态下停止。 是否会导致 RGM 对群集节点进行硬重新引导,这取决于资源的 Failover_mode 设置。 因此,设计 Stop 方法尤为重要,使其可以在应用程序无法终止时尽力尝试真正停止该应用程序,甚至使用硬终止或突然终止该应用程序的方法(例如,使用 SIGKILL)。 还应确保它及时地执行此操作,因为框架将 Stop_timeout 期满视作停止失败,并使资源处于 STOP_FAILED 状态。
对于大多数应用程序来说,DSDL 公用程序 scds_pmf_stop() 应该就满足要求,因为它首先试图软(通过 SIGTERM)停止应用程序(假设它是通过 scds_pmf_start() 在 PMF 控制下启动的),然后向进程发送 SIGKILL。 有关此公用程序的详细信息,请参阅PMF 函数。
以下是迄今为止我们一直在使用的代码模型,其中假设用来停止应用程序的应用程序特定函数为 svc_stop()(svc_stop() 的实现是否使用 scds_pmf_stop() 与此处介绍的内容无关,而是取决于该应用程序是否是在 PMF 控制下通过 Start 方法启动的),Stop 方法可用以下方式实现:
if (scds_initialize(&handle, argc, argv)!= SCHA_ERR_NOERR) { return (1); /* Initialization Error */ } return (svc_stop(handle));
svc_validate() 方法不用于 Stop 方法的实现,因为即使系统当前存在问题,Stop 方法应试图停止此节点上的应用程序。
RGM 将调用 Monitor_start 方法来启动资源的故障监视器。 故障监视器用来监视资源管理的应用程序的运行情况。 资源类型实现通常将故障监视器作为在后台运行的独立守护程序来实现。 Monitor_start 回叫方法用来通过合适的变量启动此守护程序。
因为监视器守护程序本身易于失败(例如,它可能毁坏,从而使应用程序处于不受监视的状态),所以您应该使用 PMF 启动该监视器守护程序。 DSDL 公用程序 scds_pmf_start() 具有启动故障监视器的内置支持。 此公用程序使用监视器守护程序的相对路径名(相对于资源类型回叫方法实现的位置的 RT_basedir)。 它使用由 DSDL 管理的 Monitor_retry_interval 和 Monitor_retry_count 扩展特性,以避免会无限多次地重启该守护程序。 虽然 RGM 从不直接调用监视器守护程序,但它仍然将与为所有回叫方法定义的命令行语法相同的语法(即 -R resource -G resource_group -T resource_type)强加到该监视器守护程序中。 它允许监视器守护程序实现本身利用 scds_initialize() 公用程序来设置其自己的环境。 主要用于设计监视器守护程序本身方面。
RGM 将调用 Monitor_stop 方法来停止原先通过 Monitor_start 方法启动的故障监视器守护程序。 此回叫方法的的故障处理方式与 Stop 方法完全相同,因此 Monitor_stop 方法必须像 Stop 方法一样等幂和强大。
如果您使用 scds_pmf_start() 公用程序启动故障监视器守护程序,则请使用 scds_pmf_stop() 公用程序停止该守护程序。
可以对指定资源的某节点调用资源中的 Monitor_check 回叫方法,以确定该群集节点是否可以控制该资源(即该资源所管理的应用程序是否可以在该节点上成功运行)。 通常此情况涉及到要确保该群集节点上确实提供了应用程序所需的所有系统资源。 如Validate 方法中所述,由开发者实现的函数 svc_validate() 可用来确定此方面的情况。
根据资源类型实现所管理的具体应用程序,也可以编写 Monitor_check 方法,以执行一些附加任务。 必须实现 Monitor_check 方法,以免该方法与同时运行的其它方法发生冲突。 对于使用 DSDL 的开发者,建议 Monitor_check 方法利用为实现资源特性的应用程序特定验证这一目的而编写的 svc_validate() 函数。
RGM 调用资源类型实现的 Update 方法来应用系统管理员对活动资源的配置进行的更改。 仅对资源当前处于联机状态的节点(如果有)调用 Update 方法。
刚才对资源配置所进行的更改一定会适用于资源类型实现,因为 RGM 在运行 Update 方法之前先运行了该资源类型的 Validate 方法。 Validate 方法在更改资源或资源组特性之前调用,Validate 方法可以否决所建议的更改。 Update 方法在应用更改内容之后调用,以便有机会向活动(联机)资源通知这些新设置。
作为资源类型开发者,您需要谨慎决定您希望能够动态更新的特性,并在 RTR 文件中用 TUNABLE = ANYTIME 设置来标记这些特性。 通常,如果 Update 方法的实现可以重启故障监视器守护程序,您就可以指定您希望能够动态更新该故障监视器守护程序所使用的资源类型实现的任意特性。
可能要更改的特性包括:
Thorough_Probe_Interval
Retry_Count
Retry_Interval
Monitor_retry_count
Monitor_retry_interval
Probe_timeout
这些特性会影响故障监视器守护程序对该服务的运行情况进行检查的方式以及频率、用来跟踪错误的历史间隔和 PMF 对其设置的重启阈值。 为实现这些特性的更新,在 DSDL 中提供了公用程序 scds_pmf_restart()。
如果您需要能够动态更新资源特性,而对该特性进行修改又可能会影响运行应用程序,则您需要执行相应操作,以便对该特性进行的更新可以正确地应用到该应用程序的所有运行实例中。 当前无法通过 DSDL 使该操作变得更容易。 Update 不向命令行传递修改后的特性(虽然 Validate 进行了传递)。
这些是按照资源管理 API 规范定义的一次性操作方法。 DSDL 附带的实现样例中并未说明这些方法的用法。 然而,如果资源类型开发者需要使用这些方法,也可以使用 DSDL 中的所有工具。 通常对于资源类型实现来说,Init 和 Boot 方法完全相同,都可以实现一次性操作。 Fini 方法通常用来执行撤消 Init 或 Boot 方法的操作的操作。
使用 DSDL 的资源类型实现通常都具有一个故障监视器守护程序,该守护程序可执行以下任务。
定期监视所管理的应用程序的运行情况。 监视器守护程序的这一特定方面主要由应用程序决定,各个资源类型之间可能具有较大差别。 DSDL 具有一些内置公用程序函数,用来检查基于 TCP 的简单服务的运行情况。 应用程序实现基于 ASCII 码的协议,例如 HTTP、NNTP、IMAP 和 POP3 都可以通过这些公用程序实现。
使用资源特性 Retry_interval 和 Retry_count 跟踪该应用程序遇到的问题。 当该应用程序完全失败时,确定 PMF 操作脚本是否应重启该服务,或确定是否已迅速积累了较多应用程序失败从而需要考虑进行故障切换。 DSDL 公用程序 scds_fm_action() 和 scds_fm_sleep() 可用来帮助您实现此机制。
采取相应的操作(通常是重启该应用程序或尝试故障切换包含的资源组)。 DSDL 公用程序 scds_fm_action() 可实现这样的算法。 为此它将计算过去 Retry_interval 时间(以秒为单位)内探测失败的当前积累值。
更新资源状态以使应用程序的运行状态可供 scstat 命令和群集管理 GUI 使用。
已对 DSDL 公用程序进行了设计,因此故障监视器守护程序的主循环可以用以下伪代码表示。
对于使用 DSDL 实现的故障监视器,
scds_fm_sleep() 检测到应用程序进程遭到破环的速度相当快,因为通过 PMF 发出的进程破环通知是异步的。 将该情况与频繁唤醒故障监视器以检查服务的运行情况并查找破环的应用程序的情况相比较, 故障检测时间大幅度减少了,从而提高了该服务的可用性。
如果 RGM 拒绝了通过 scha_control(3HA) API 对该服务进行故障切换的尝试操作,则 scds_fm_action() 将重置(遗忘)其当前失败历史记录。 原因在于失败历史记录已超出 Retry_count,如果故障监视器守护程序在下一个重复期内被唤醒并无法成功完成守护程序的运行情况检查,则将再次试图调用 scha_control() ,这可能仍然被拒绝,因为上一个重复期内导致这被拒绝的状态仍然有效。 重置历史记录可以保证故障监视器至少会在下一个重复期内尝试在本地改善该情况(例如,通过应用程序重启的方式)。
在重启失败的情况下,scds_fm_action() 不会重置应用程序失败历史记录,因为如果情况本身没有进行改善,通常会立即尝试使用 scha_control()。
根据失败历史记录的不同,公用程序 scds_fm_action() 会相应地将资源状态更新为 SCHA_RSSTATUS_OK、SCHA_RSSTATUS_DEGRADED 或 SCHA_RSSTATUS_FAULTED。 这样此状态就可用于群集系统管理。
在大多数情况下,应用程序特定运行情况检查操作可以在独立的公用程序(例如 svc_probe())中实现并与此普通主循环相集成。
for (;;) { / * sleep for a duration of thorough_probe_interval between * successive probes. */ (void) scds_fm_sleep(scds_handle, scds_get_rs_thorough_probe_interval(scds_handle)); /* Now probe all ipaddress we use. Loop over * 1. All net resources we use. * 2. All ipaddresses in a given resource. * For each of the ipaddress that is probed, * compute the failure history. */ probe_result = 0; /* Iterate through the all resources to get each * IP address to use for calling svc_probe() */ for (ip = 0; ip < netaddr->num_netaddrs; ip++) { /* Grab the hostname and port on which the * health has to be monitored. */ hostname = netaddr->netaddrs[ip].hostname; port = netaddr->netaddrs[ip].port_proto.port; /* * HA-XFS supports only one port and * hence obtaint the port value from the * first entry in the array of ports. */ ht1 = gethrtime(); /* Latch probe start time */ probe_result = svc_probe(scds_handle, hostname, port, timeout); /* * Update service probe history, * take action if necessary. * Latch probe end time. */ ht2 = gethrtime(); /* Convert to milliseconds */ dt = (ulong_t)((ht2 - ht1) / 1e6); /* * Compute failure history and take * action if needed */ (void) scds_fm_action(scds_handle, probe_result, (long)dt); } /* Each net resource */ } /* Keep probing forever */ |