原文地址:Create a simple Daemon in ABAP
目录
一、ABAP语言中的守护进程是什么?
守护程序是一种实用程序,它自身连续运行,无需交互式用户在后台多任务操作系统上的控制,以监视和处理某些子系统或对事件立即做出反应。守护进程在预定义的时间或响应某些事件执行显式操作。
ABAP 守护进程框架 (ADF-ABAP Daemon Framework) 提供的 ABAP 守护进程功能与 Linux 或 Windows 中的对应功能更加相似。它们通过在具有无限生命周期的会话中运行来以可靠的方式处理事件。ABAP 程序可以通过使用 ABAP 消息传递通道和消息类型推送通道协议 (PCP) 向守护程序发送消息来与守护程序进行通信,PCP 是一种类似于简单 HTTP 消息的通信格式。
守护进程对错误的应对非常可靠,即守护进程会话在遇到错误(E/A/X 消息)后不会终止。但是,每次运行时错误或类型 E、A 或 X 的消息导致程序终止时,都会自动再次创建 ABAP 守护程序。当其 AS 实例关闭时,可以通过创建包含与前面的守护程序相同的上下文信息的新守护程序来将守护程序移动到另一个 AS 实例。这使得新的守护进程能够完成相同的工作。
二、ABAP 守护进程框架 (ADF)
SAP Netweaver 7.52 将 ABAP Daemon Framework 或简称 ADF 作为一项新功能。ADF 是一个 API,允许您在 ABAP 中创建和管理守护程序。ABAP 守护进程是 ABAP 守护进程类的实例,位于应用程序服务器的特殊 ABAP 守护进程会话中。ABAP程序本身使用ABAP守护进程管理器来访问ABAP守护进程。
如前所述,ABAP 守护进程的处理发生在后台并使用事件进行控制。守护程序或ABAP运行时框架的用户可以引发ABAP守护程序事件,守护程序使用预定义的接口方法对此做出反应。守护进程必须始终准备好对入站事件做出反应,这是通过以非阻塞模式运行 ABAP 守护进程处理来确保的,以防止 ABAP 守护进程处理中出现运行时错误 DAEMON_ILLEGAL_STATEMENT 以及随后重新启动守护进程。
有关ADF实现和 ABAP 守护进程类的详细信息扩展可以在 ABAP 关键字文档和本博客文章系列中找到。在这里我想更多地关注如何通过实现守护进程类来创建守护进程的过程。
三、ABAP 守护进程类
ABAP守护进程类是一个全局类,继承自抽象系统类CL_ABAP_DAEMON_EXT_BASE。ABAP 守护程序类继承了接口 IF_ABAP_DAEMON_EXTENSION 的方法,如果在 ABAP 守护程序类中实现了这些事件,则ABAP 守护程序类将使用这些方法对ABAP 守护程序事件做出反应。这些方法的详细解释可以在文档的相关部分找到。
ABAP 守护程序类可以包含更多辅助方法,并且可以在其方法中调用任意数量的其他过程。还可以实现接口 IF_ABAP_TIMER_HANDLER 将 ABAP 守护程序类转换为 ABAP 计时器处理程序,从而对 ABAP 计时器事件做出反应。例如,等待某些事件或在一段时间后停止守护进程。当从 ABAP 守护进程类实例化 ABAP 守护进程时,ABAP 守护进程框架将创建 ABAP 守护进程会话。
四、创建一个简单的ABAP守护进程
现在让我们创建 ABAP 守护进程并使用 PCP 消息与它们交互。如前所述,ABAP 守护进程是扩展基类 CL_ABAP_DAEMON_EXT_BASE 的 ABAP 守护进程类的实例。因此,首先我们需要按照本教程定义类。
步骤1:创建一个新的ABAP Daemon类
第一步,创建一个 ABAP 类 ZCL_ABAP_SIMPLE_DAEMON 并将 CL_ABAP_DAEMON_EXT_BASE 设置为超类。从这个超类中,我们的守护进程将从 IF_ABAP_DAEMON_EXTENSION 接口继承不同的方法,这将允许我们响应不同的事件。现在您会看到必须重新定义的 9 个继承方法的列表。
图1 : 必须重新定义的 IF_ABAP_DAEMON_EXTENSION 接口的继承方法列表。
步骤2:实现ON_ACCEPT方法
在创建 ABAP 守护程序类的新实例之前,将调用其 ON_ACCEPT 方法来确定是否应启动该守护程序。它接受来自您自己的类的所有启动请求,并拒绝来自任何其他程序的所有请求。这是通过检查发起启动的调用程序来实现的。
METHOD if_abap_daemon_extension~on_accept.
TRY.
DATA lv_program_name TYPE program.
lv_program_name = cl_oo_classname_service=>get_classpool_name( 'ZCL_ABAP_SIMPLE_DAEMON' ).
IF i_context_base->get_start_caller_info( )-program = lv_program_name.
e_setup_mode = co_setup_mode-accept.
ELSE.
e_setup_mode = co_setup_mode-reject.
ENDIF.
CATCH cx_abap_daemon_error.
" to do: for productive applications, error handling, e.g. write error log!
e_setup_mode = co_setup_mode-reject.
ENDTRY.
ENDMETHOD.
第三步:实现ON_START方法
要启动ABAP守护进程,需要执行ON_START方法。您可以包含一条 PCP 消息,其中包含可在 ON_START 方法中访问的任意启动参数。
在这个简单的场景中,您将通过 PCP 传递参数超时。然后,守护进程将设置一个计时器,以便在每次达到超时时显示弹出消息。计数器将用于在五个消息后停止计时器。
首先,您需要创建一些私有成员变量来存储必要的数据。将这些行添加到类的 PRIVATE SECTION 中:
private section.
DATA: mv_timeout TYPE i,
mo_timer TYPE REF TO if_abap_timer_manager,
mv_counter TYPE i.
您还需要添加 IF_ABAP_TIMER_HANDLER 作为守护程序类的接口。在 ON_START 方法中,您现在可以从 PCP 检索超时值,然后启动计时器。为此,请复制以下实现:
METHOD if_abap_daemon_extension~on_start.
TRY.
" retrieve timeout from PCP start parameters
mv_timeout = i_context->get_start_parameter( )->get_field( 'timeout' ).
" start timer for displaying messages
mo_timer = cl_abap_timer_manager=>get_timer_manager( ).
mo_timer->start_timer( i_timeout = mv_timeout i_timer_handler = me ).
CATCH cx_abap_daemon_error cx_ac_message_type_pcp_error cx_abap_timer_error.
" to do: for productive applications, error handling, e.g. write error log!
ENDTRY.
ENDMETHOD.
第四步:实现ON_TIMEOUT方法
当你的ABAP守护进程在后台运行时,当超时时间过去时,ON_TIMEOUT方法将被触发。因此,您需要为类的 PUBLIC SECTION 实现一个处理程序接口 if_abap_timer_handler。通过在类中实现以下 ON_TIMEOUT 方法,将显示使用功能模块 TH_POPUP 的弹出消息,并且计时器将重新启动五次:
METHOD if_abap_timer_handler~on_timeout.
" increment the loop counter
ADD 1 TO mv_counter.
" display popup message
CALL FUNCTION 'TH_POPUP'
EXPORTING
client = sy-mandt
user = sy-uname
message = CONV th_popup( |Timeout triggered. Number of loops: { mv_counter }| ).
" restart the timer if any loops are remaining
IF mv_counter < 5.
TRY.
mo_timer->start_timer( i_timeout = mv_timeout i_timer_handler = me ).
CATCH cx_abap_timer_error.
" to do: for productive applications, error handling, e.g. write
error log!
ENDTRY.
ENDIF.
ENDMETHOD.
当守护进程在后台持续运行时,您还可以向它发送 PCP 消息。
第5步:实现ON_MESSAGE方法
ON_MESSAGE 是一个有用的方法,守护进程通过它接收用户输入并做出响应。每次守护进程收到 PCP 消息时都会调用 ON_MESSAGE 方法。当守护进程接收到用户输入时,将弹出一个弹出对话框,该对话框显示在 SAPGUI 中。
METHOD if_abap_daemon_extension~on_message.
TRY.
" get text from PCP message
DATA(lv_text) = i_message->get_text( ).
" display popup
CALL FUNCTION 'TH_POPUP'
EXPORTING
client = sy-mandt
user = sy-uname
message = CONV th_popup( |Message received: { lv_text }| ).
CATCH cx_ac_message_type_pcp_error.
" to do: for productive applications, error handling, e.g. write error log!
ENDTRY.
ENDMETHOD.
您必须考虑这样一个事实:只有运行守护程序的程序才能通过管理器守护程序使用该守护程序。这个想法是通过守护进程类本身创建守护进程。因此,您需要向该类添加另外 3 个方法:START、SEND 和 STOP 来管理守护进程。
第6步:实现静态方法START
这个简单的 ABAP 守护进程将通过静态方法启动。因此,您需要在守护程序类中创建一个带有两个导入参数的新方法 START:
IV_DAEMON_NAME:守护进程的名称
IV_TIMEOUT:两个连续定时器事件之间的超时时间(毫秒)
为此,请将以下代码粘贴到类定义的 PUBLIC SECTION 中:
CLASS-METHODS start
IMPORTING
iv_daemon_name TYPE string
iv_timeout TYPE i
RAISING
cx_abap_daemon_error
cx_ac_message_type_pcp_error.
使用 ABAP 守护程序管理器实例化 ABAP 守护程序。此方法包装类 CL_ABAP_DAEMON_CLIENT_MANAGER 的方法 START 并启动 ABAP 守护程序类的 ABAP 守护程序。其静态方法 CL_ABAP_DAEMON_CLIENT_MANAGER=>START 需要守护程序的名称和优先级作为参数。此外,您还可以将启动参数作为 PCP 消息传递。
METHOD start.
" set ABAP Daemon start parameters
DATA(lo_pcp) = cl_ac_message_type_pcp=>create( ).
lo_pcp->set_field( i_name = 'timeout' i_value = CONV #( iv_timeout ) ).
" start the daemon application using the ABAP Daemon Manager
cl_abap_daemon_client_manager=>start(
i_class_name = 'ZCL_ABAP_SIMPLE_DAEMON'
i_name = CONV #( iv_daemon_name )
i_priority = cl_abap_daemon_client_manager=>co_session_priority_low
i_parameter = lo_pcp ).
ENDMETHOD.
检查所有调用者以确保该方法仅在相关程序和 ABAP 守护程序类中使用。已启动守护进程的 ID 会被保存以供进一步使用。如果ABAP守护进程类在启动时被接受,则返回值不是初始值。
第7步:实现静态方法SEND并运行守护进程
实现一个静态方法 SEND,它将用于向守护进程发送文本,从而触发其 ON_MESSAGE 方法。该方法需要以下导入参数:
IV_DAEMON_NAME:将接收消息的守护进程的名称。
IV_TEXT:将发送到守护程序的文本消息。
将以下代码粘贴到类定义的公共部分中。
CLASS-METHODS send
IMPORTING
iv_daemon_name TYPE string
iv_text TYPE string
RAISING
cx_abap_daemon_error
cx_ac_message_type_pcp_error.
要将 PCP 消息发送到守护程序,您需要检索类 ZCL_ABAP_SIMPLE_DAEMON 的所有正在运行的 ABAP 守护程序实例的列表。然后,您可以将每个实例的名称与 IV_DAEMON_NAME 进行比较,并相应地发送 PCP 消息。
METHOD send.
" retrieve the list of ABAP Daemon instances
DATA(lt_ad_info) = cl_abap_daemon_client_manager=>get_daemon_info( i_class_name = 'ZCL_ABAP_SIMPLE_DAEMON').
" create PCP message with text
DATA(lo_pcp) = cl_ac_message_type_pcp=>create( ).
lo_pcp->set_text( iv_text ).
" for each running daemon instance of this class
LOOP AT lt_ad_info ASSIGNING FIELD-SYMBOL(<ls_info>).
" send a message if the names match
IF iv_daemon_name = <ls_info>-name.
cl_abap_daemon_client_manager=>attach( <ls_info>-instance_id )->send( lo_pcp ).
ENDIF.
ENDLOOP.
ENDMETHOD.
现在,您只需调用之前创建的静态 START 方法即可启动守护程序。因此,只需一行代码即可创建一个新的ABAP程序Z_ABAP_SIMPLE_DAEMON_START:
REPORT Z_ABAP_SIMPLE_DAEMON_START.
zcl_ABAP_simple_daemon=>start( iv_daemon_name = 'simple_daemon' iv_timeout = 10000 ).
您应该每 10 秒看到一个弹出通知:
第 8 步:停止 ABAP 守护进程
您可以使用 SAPGUI 中的事务 SMDAEMON 监视所有正在运行的 ABAP 守护程序。在那里你可以看到它们的状态,检查错误,甚至重新启动,并终止它们。
要停止守护程序,请从列表中选择它,然后转至 ABAP 守护程序 > 终止守护程序。或者,您也可以创建静态 STOP 方法。因此,将其添加到类定义的公共部分:
CLASS-METHODS stop
IMPORTING
iv_daemon_name TYPE string
RAISING
cx_abap_daemon_error.
并实现方法
METHOD stop.
" retrieve the list of ABAP Daemon instances
DATA(lt_ad_info) = cl_abap_daemon_client_manager=>get_daemon_info( i_class_name = 'ZCL_ABAP_SIMPLE_DAEMON').
" for each running daemon instance of this class
LOOP AT lt_ad_info ASSIGNING FIELD-SYMBOL(<ls_info>).
" stop the daemon if the names match
IF iv_daemon_name = <ls_info>-name.
cl_abap_daemon_client_manager=>stop( i_instance_id = <ls_info>-instance_id ).
ENDIF.
ENDLOOP.
ENDMETHOD.
该方法包装了 CL_ABAP_DAEMON_CLIENT_MANAGER 类的 STOP 方法,用于停止 ABAP 守护进程。现在你可以通过执行来停止你的守护进程
zcl_ABAP_simple_daemon=>stop( iv_daemon_name = 'simple_daemon' ).
来自任何 ABAP 程序。更多示例也可以在文档中找到。
五、守护进程用例。
正如这篇博客文章中很好地解释的那样,守护进程的主要用例是处理事件或管理简单的后台活动以及频繁的定期任务,例如跟踪、日志记录、资源清理等,而不会损害 LUW(逻辑工作单元-Logical Unit of Work)。
六、结论
我希望这个分步说明可以帮助您对守护进程以及如何创建守护进程有一些了解。您还可以通过重新定义其他方法来扩展此示例,以应对系统重新启动或服务器关闭的情况。例如,当服务器关闭时,会触发开发者实现的ON_SERVER_SHUTDOWN方法。当当前 AS 实例关闭时,此方法调用帮助器方法 RELOCATE 将守护程序移动到不同的 AS 实例。
method IF_ABAP_DAEMON_EXTENSION~ON_SERVER_SHUTDOWN.
relocate( ).
endmethod.
relocate 方法必须从当前 AS ABAP 的列表中随机选择一个 AS 实例。然后,该应用程序服务器用作启动当前 ABAP 守护程序类的守护程序的目标。