本章介绍了数据服务开发库 (DSDL) 在设计和实现资源类型方面的典型应用,还着重介绍了设计资源类型以验证资源配置以及启动、停止和监视该资源等内容。另外,本章介绍了如何使用 DSDL 来实现资源类型回调方法。
有关附加信息,请参见 rt_callbacks(1HA) 手册页。
要完成这些任务,您需要访问资源的属性设置。DSDL 实用程序 scds_initialize() 提供了访问这些资源属性的统一方法。本函数设计成在每个回调方法的开头部分进行调用。此实用程序函数用于从群集框架中检索资源的所有属性,并使其可用于 scds_getname() 函数系列。
本章包括以下主题:
资源类型注册 (RTR) 文件指明有关 Sun Cluster 软件中的资源类型的详细信息。包括以下详细信息:
实现所需要的属性
这些属性的数据类型和默认值
用于资源类型实现的回调方法的文件系统路径
系统定义的属性的各种设置
DSDL 附带的样例 RTR 文件对于大多数资源类型实现来说是足够的。您只需编辑一些基本元素,例如,资源类型回调方法的资源类型名称和路径名称。如果需要新属性才能实现资源类型,可以将其声明为资源类型实现的 RTR 文件中的扩展属性,并可通过 DSDL scds_get_ext_property() 实用程序访问该属性。
资源类型实现的 Validate 回调方法用于检查提议的资源设置(由对资源提议的属性设置指定)对于资源类型来说是否可以接受。
在以下两种情况下,资源组管理器 (RGM) 将调用资源类型实现的 Validate 方法:
创建属于该资源类型的新资源时
更新资源或资源组的属性时
这两种情况可以通过传递给资源的 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 尚未创建资源(在创建回调的情况下)或尚未应用正被更新的属性的更新值(在更新回调的情况下)。
如果使用的是由 HAStoragePlus 资源类型管理的本地文件系统,请使用 scds_hasp_check() 函数检查该资源类型的状态。此信息来自该资源所依赖的所有 SUNW.HAStoragePlus 资源的状态(联机或脱机),它是通过使用为该资源定义的 Resource_dependencies 或 Resource_dependencies_weak 系统属性获得的。有关 scds_hasp_check() 函数返回的状态代码的完整列表,请参见 scds_hasp_check(3HA) 手册页。
DSDL 函数 scds_initialize() 按照以下方式处理这些情况:
如果正在创建资源,则通过命令行传递提议资源属性时,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); }
验证函数还应当记录资源验证失败的原因。不过,如果忽略细节(第 8 章,DSDL 资源类型实现样例包含更实际的验证函数处理),可以实现更简单的示例 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() 函数的实现。
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 Data Services Planning and Administration Guide for Solaris OS》。
如果资源在节点上成功启动,则 svc_start() 函数必须返回 0。如果启动函数遇到问题,则该函数必须返回非零值。此函数失败后,RGM 将尝试在其它群集节点上启动该资源。
要尽量利用 DSDL,svc_start() 函数可以调用 scds_pmf_start() 实用程序以启动进程监视器工具 (PMF) 下的应用程序。此实用程序还使用 PMF 的故障回调操作功能来检测进程故障。有关更多信息,请参见 pmfadm(1M) 手册页中 -a 操作参数的说明。
RGM 将对群集节点调用资源类型实现的 Stop 回调方法,以停止该应用程序。Stop 方法的回调语义学需要以下因素:
Stop 方法必须是幂等方法,因为即使 Start 方法在节点上没有成功完成,Stop 方法也可以被 RGM 调用。因此,即使应用程序当前没有在群集节点上运行并且没有需要它执行的工作,Stop 方法也必须成功(退出零)。
如果资源类型的 Stop 方法在群集节点上失败(退出非零值),则正被停止的资源将进入 STOP_FAILED 状态。根据资源上的 Failover_mode 设置,这种情况可能会导致 RGM 执行群集节点的硬重启。
因此,必须设计 Stop 方法让此方法一定要停止应用程序。如果采用其他方法无法终止应用程序,您甚至可能需要采用 SIGKILL 突然中止应用程序。
您还必须确保此方法能够及时停止应用程序,因为框架将 Stop_timeout 属性的终止视为停止失败,并因而将资源置入 STOP_FAILED 状态。
DSDL 实用程序 scds_pmf_stop() 在第一次尝试使用 SIGTERM 软停止应用程序时应当足以用于大多数应用程序。此函数随后将 SIGKILL 传递给进程。此函数假定应用程序是使用 scds_pmf_start() 在 PMF 下启动的。有关此实用程序的详细信息,请参见PMF 函数。
假定停止应用程序的应用程序特定函数名为 svc_stop(),它采用以下代码实现了 Stop 方法:
if (scds_initialize(&handle, argc, argv)!= SCHA_ERR_NOERR) { return (1); /* Initialization Error */ } return (svc_stop(handle));
前述 svc_stop() 函数的实现与是否包括 scds_pmf_stop() 函数是不相关的。确定是否包括 scds_pmf_stop() 函数取决于应用程序是否通过 Start 方法在 PMF 下启动。
Stop 方法的实现中没有使用 svc_validate() 方法,因为即使系统当前遇到问题,Stop 方法也应当尝试在此节点上停止应用程序。
RGM 将调用 Monitor_start 方法以启动资源的故障监视器。故障监视器将监视正受该资源管理的应用程序的运行状况。资源类型实现通常将故障监视器作为单独在后台运行的守护进程实现。Monitor_start 回调方法用于使用正确的参数启动此守护进程。
由于监视器守护进程本身很容易失败(例如,该守护进程可能会停止,而使应用程序处于不受监视的状态),因此您应当使用 PMF 启动监视器守护进程。DSDL 实用程序 scds_pmf_start() 内置了对启动故障监视器的支持。此实用程序使用相对于 RT_basedir 的路径名来确定监视器守护进程的资源类型回调方法实现的位置。此实用程序使用由 DSDL 管理的 Monitor_retry_interval 和 Monitor_retry_count 扩展属性防止无限制地重新启动守护进程。
此实用程序还将为所有回调方法定义的相同命令行语法(即,-R resource -G resource-group -T resource-type)强加到监视器守护进程上,尽管监视器守护进程决不会被 RGM 直接调用。最后,此实用程序还允许监视器守护进程实现自身启用 scds_initialize() 实用程序来设置自己的环境。主要用于设计监视器守护进程本身方面。
RGM 将调用 Monitor_stop 方法来停止通过使用 Monitor_start 方法启动的故障监视器守护进程。处理此回调方法失败与处理 Stop 方法失败的方式刚好相同。因此,Monitor_stop 方法必须是幂等的并且要像 Stop 方法一样强健。
如果您使用 scds_pmf_start() 公用程序启动故障监视器守护进程序,则请使用 scds_pmf_stop() 公用程序停止该守护进程。
RGM 将在指定资源的节点上运行针对资源的 Monitor_check 回调方法以确定群集节点是否能够控制资源。换言之,RGM 运行此方法来确定正受资源管理的应用程序是否能够在节点上成功运行。
通常,这种情况涉及确保应用程序所需的所有系统资源在群集节点上确实可用。正如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 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 */