笔者之前的系列文章,详细介绍了使用 ABAP Push Channel(简称 APC)来开发 TCP Socket 和 Web Socket 服务器端和客户端的实现步骤。
在早期 ABAP 不支持 APC 技术的时代,作为客户端的浏览器,如果想获取来自服务器端的最新数据,比如消息,通知,提示,警报等,就必须以一个时间间隔,不停地向服务器端发起 HTTP 轮询(Poll),即使服务器没有任何新的数据产生。
这种低效的轮询会导致大量不必要的请求,浪费带宽和服务器资源。
ABAP 7.40 SP5 正式发布的 APC 技术,解决了 HTTP 轮询机制带来的弊端。APC 能够在服务器有新数据就绪时,立即推送通知到客户端,从而避免了频繁的轮询请求,减轻了服务器和网络的负担,真正实现了服务器和客户端的实时双向通讯。
笔者之前的 ABAP 开发教程,曾经详细介绍过 ABAP 编程环境的 Internal Session,ABAP Session 和 User Session 三个概念的辨析。比如一个 ABAP Session 内可以通过调用 ABAP 程序,执行关键字 SUBMIT 和 CALL TRANSACTION 等方式,开启新的 Internal Session.
同一 ABAP Session 之内的多个 Internal Sessions 之间,可以通过 ABAP Memory 来共享和交换数据,如下图所示。
ABAP Messaging Channels (AMC) 是 ABAP 程序之间,使用`消息`进行通信的另一种方法。任何 ABAP 程序之间都可以通过 AMC 交换消息,包括不同 User Sessions 和 ABAP 应用程序服务器之间的通信。
ABAP Messaging Channel 采用了大家熟知的设计模式中的发布/订阅者(Publisher & Subscriber)模式,来实现 ABAP 程序间的消息交换。
参与消息交互的双方,在特定的 Communication Channel 上,通过同步或者异步的方式发布和订阅消息。
下面是一张简单的示意图。
User Session P1 作为消息发布者,将消息发布到 ABAP Message Channel Framework 管理的 Channel X 中去。另外三个 User Session S1,S2 和 Sn 订阅了这个 Channel X,因此它们能够从 Channel X 接收到消息。
随后的某个时间点,Sn 取消了消息订阅。从这一时刻起,它不会再收到来自 Channel X 的消息转发。
下面介绍基于 ABAP Message Channel 的消息发布者和订阅者的详细开发步骤。
事务码 SE80 的右键菜单,选择创建一个新的 ABAP Messaging Channel:
给这个新建的 ABAP Messaging Channel 应用,维护名称和描述信息。下面是新建 AMC 应用需要维护的关键属性的详细解释。
一个 AMC 应用可以维护多个 Channel,本例 Channel 名称为 /ping.
AMC 应用的名称和 Channel 名称这一对字段,唯一标识一个 ABAP Message Channel,可以看成通过 AMC 机制进行发布和订阅消息的主键。
Message Type ID 即在 Channel 通道里流动消息的数据类型,本例选择 TEXT 即文本类型。
Activity Scope 即 Message Channel 的作用范围,这里选择 Client 即消息的发送者和接受者必须在 ABAP 应用服务器的同一个 Client 上。选择 System 则支持 Cross Client 即跨 Client 的消息交换。
Authorized Program 即能够使用当前 AMC 应用的白名单列表,如上图图例5 所示。
只有 ZDEMO_SEND_AMC 才有权限,往这个 Message Channel 里发送消息。同理,只有 ZDEMO_RECEIVE_AMC 才有权限从 Message Channel 接收消息。其他任何没有声明在这个白名单上的 ABAP 程序,试图通过该 Message Channel 发布或者订阅消息时,会遇到运行时错误。
使用 ABAP 发布 AMC 消息
实现上图白名单里声明的 ZDEMO_SEND_AMC 程序。
简单的 17 行代码:
REPORT zdemo_send_amc.
DATA text_message TYPE string VALUE `I am a text message`.
DATA session_id TYPE amc_consumer_session_id.
cl_demo_input=>new(
EXPORTING text = 'Text message'
CHANGING field = text_message
).
CAST if_amc_message_producer_text(
cl_amc_channel_manager=>create_message_producer_by_id(
i_consumer_session_id = session_id
i_communication_type = cl_amc_channel_manager=>co_comm_type_synchronous
i_application_id = 'ZAMC_SIMPLE_TEST'
i_channel_id = '/ping' )
i_message = text_message ).
这个简单的 ABAP 程序,定义了一个输入字段 text_message,作为待发布到 Message Channel 中的消息内容。因为我们之前创建 AMC 时,Message Type 指定为 Text,所以这里的输入字段的 ABAP 类型选择为 string.
通过 cl_amc_channel_message 的静态方法 create_message_producer_by_id, 创建一个新的消息发布者。Communication Type 选择为同步方式,当然 AMC 也支持异步消息发布。
该静态方法还需要传入 AMC 应用名称和 Channel 名称,告知创建好的 Producer 实例,其发布消息的目的地所在。
静态方法返回的类型为 IF_AMC_MESSAGE_PRODUCER,我们使用 CAST 关键字,将其类型转换成更具体的 IF_AMC_MESSAGE_PRODUCER_TEXT, 以便进行 TEXT 类型消息的发布。
最后调用 IF_AMC_MESSAGE_PRODUCER_TEXT 的 send 方法,将消息发布到指定的 Message Channel 中去。
使用 ABAP 接收 AMC 消息
实现 AMC 应用创建时,白名单里维护的 ZDEMO_RECEIVE_AMC 程序。
试图从 Message Channel 里读取其他发布者写入消息的订阅者,需要实现 ABAP 接口 IF_AMC_MESSAGE_RECEIVER_TEXT. 这个接口提供了一个 receive 方法。当订阅的 ABAP Message Channel 有消息到达时,receive 方法会自动触发。
在本例的接收方实现里,receive 方法触发时,将通过输入参数 i_message 传递进来的消息文本内容,存储到类实例的 text_message 成员变量里。
使用 ABAP 代码,订阅 ABAP Message Channel 发布消息的三大要点,如下图图例所示。
1. 新建一个实现了 IF_AMC_MESSAGE_RECEIVER_TEXT 接口的类实例。该类的成员变量 text_message 用来存储 Message Channel 里订阅的消息的文本内容。该类实例作为消息的订阅者。
2. 使用 cl_amc_channel_manager 的 create_message_consumer 静态方法,创建一个 Message Channel 订阅者。需要订阅的具体 Channel 信息,通过指定的 AMC 名称和 Channel 名称来区分。
该静态方法返回一个 consumer 实例,调用该实例的 start_message_delivery 方法,把上一步骤创建的订阅者实例,传递给该方法的输入参数 i_receiver. 至此消息订阅就完成了。
3. 调用 ABAP 关键字 WAIT FOR MESSAGING CHANNELS UNTIL XXX UP TO YYY, 等待订阅的 ABAP Message Channel 中有消息发布进来。
YYY 是指定 WAIT 语句的超时时间,而 XXX 处需要指定一个逻辑表达式,本例使用的是 receiver->text_message IS NOT INITIAL.
如果逻辑表达式的值为 FALSE, 该 ABAP 程序将一直处于 WAIT 状态。
当订阅的 Message Channel 上有其他 ABAP 程序发布消息时,订阅者实例的 receive 方法会被 AMC 框架触发,消息内容会写入实例的成员方法 text_message 内。这会导致 receiver->text_message IS NOT INITIAL 表达式的值变为 TRUE,因此 WAIT 语句完成其执行,将 sy-subrc 设置为 0,然后 ABAP 程序从 WAIT 语句的下一行继续执行。
我们对迄今为止编写的发布者 & 订阅者场景做一个简单的测试。
依次开启两个 SAPGUI 窗口登录系统,这样我们就有了两个不同的 User Sessions.
在第一个窗口里执行 ZDEMO_RECEIVE_AMC,弹出对话框,指定 WAIT 操作的超时时间,默认为 60 秒。点击 Enter 按钮触发程序的执行。
此时我们可以观察到,该程序进入了等待状态,等待订阅的 Message Channel 上消息的到来。
在第二个 SAPGUI 窗口里,执行 ZDEMO_SEND_AMC. 在输入字段里随便维护一些文本。点击 Enter 按钮之后,这些文本将会被写入 ABAP Message Channel.
点击 Enter 按钮之后,第一个 SAPGUI 窗口里的 ZDEMO_RECEIVE_AMC 程序,从 WAIT 状态中被唤醒,打印出来自第一个 SAPGUI 窗口发布的消息内容。
总结
本文演示了通过 ABAP Messaging Channel 技术,在不同的 User Sessions 里通过消息发布和订阅的方式,进行数据交换的开发步骤。
在笔者另一篇文章 我用 ABAP 做过的那些无聊的事情 里介绍过一个 Function Module TH_POPUP, 可以通过点对点的方式,给不同 User Session 的其他用户,以弹出框的方式发送文本消息。
只需要通过该 Function Module 的 USER 输入参数,指定期望接收到该弹出框的用户名,以及 MESSAGE 参数来维护弹出的文本内容。
执行 Function Module 之后,另一个 User Session 的 SAPGUI 窗口,就会弹出下面这种对话框。
当然,同这种最简陋的对话弹出框相比,ABAP Messaging Channel 不仅支持包括 Text,自定义结构和二进制流的各种消息格式,也支持同步和异步两种消息发布和订阅方式,功能比 TH_POPUP 要强太多了。