跳到主要内容

PubSub+ JCSMP API的最佳实践

本页讨论了在使用 PubSub+ JCSMP API 开发应用程序时可以采用的最佳实践。有关复制和冗余最佳实践的信息,请参阅:

  • PubSub+ JCSMP API 中的复制最佳实践
  • PubSub+ JCSMP API 的冗余最佳实践

通用最佳实践

保证消息传递的调整指南

当客户端接收大量保证消息(尤其是大消息)时,可能会降低客户端接收消息的速率。在这种情况下,使用的流的数量以及每个流使用的保证消息窗口大小会影响事件代理为保证消息使用的每个客户端优先级队列(称为 G-1 队列)的缓冲区使用情况。这些队列用于为等待从事件代理投递或已发送但等待客户端确认的消息保存保证消息。

每个 G-1 队列都被分配了一个最大深度缓冲区。此最大深度以工作单位为单位进行测量,其中 1 个工作单位代表消息的 2,048 字节。(默认情况下,每个 G-1 队列被分配了 20,000 个工作单位的最大深度。)

为了解决由于对 G-1 队列分配的缓冲区需求过高而导致的保证消息传递速率降低的问题,您应该减少每个流使用的保证消息窗口大小,并在可能的情况下减少使用的流的数量。

如果无法减少保证消息窗口大小或流的数量,您也可以通过调整事件代理使用的 min-msg-burst 大小来有效地增加 G-1 队列大小。

重新应用订阅

如果启用,API 会维护一个本地订阅缓存,并在重新建立订阅者连接时重新应用这些订阅。重新应用订阅仅适用于直接主题订阅。它不会重新应用持久和非持久端点上的主题订阅。

有关更多信息,请参阅订阅管理。

流的数量和保证消息窗口大小

客户端用于接收保证消息的缓冲区数量主要由每个会话使用的流数量 * 每个流的保证消息窗口大小决定。为了限制客户端的最大缓冲区使用量,您可以减少使用的流数量和/或减少每个流的保证消息窗口大小。(每个流的保证消息窗口大小是通过流属性设置的。有关更多信息,请参阅重要流(消息消费者)属性。)

例如,考虑一个客户端使用窗口大小为 255 的流绑定到 10 个队列,这些队列上的保证消息平均大小为 20kB。在这种情况下,客户端的流配置大小不合适,因为客户端的最大缓冲区使用量(大约 24,902 个工作单位)超过了事件代理提供的缓冲区(20,000 个工作单位)。然而,如果将流重新配置为窗口大小为 25,则客户端的最大缓冲区使用量将处于可接受范围内(大约 2,441 个工作单位)。

工作单位是事件代理上用于处理消息的固定大小缓冲区,根据设置的队列深度进行设置。1 个工作单位代表消息的 2,048 字节。

如果您使用 JCSMP,还需要调整 Java 消费者通知分发队列的大小,以便其足够大,能够缓冲上下文中所有会话中所有消费者流(保证消息流以及直接消费者)生成的最大通知数量。

img

其中:

GDFlows 是上下文中所有的保证消息消费者流。

FCL 是默认消费者流拥塞限制。

Nconsumers 是上下文中直接消息消费者的数量。

最小消息突发大小

如果无法减少流的数量或保证消息窗口大小,您可以调整 G-1 队列的大小。增加队列的最简单方法是调整 min-msg-burst 大小。min-msg-burst 大小指定始终允许进入队列的消息数量。min-msg-burst 大小是通过客户端配置文件在每个客户端基础上设置的。

在正常运行条件下,无需更改 G-1 队列的默认 min-msg-burst 值。然而,在客户端从多个端点消费消息的情况下,重要的是 G-1 队列的 min-msg-burst 大小至少等于客户端消费消息的所有流的保证消息窗口大小之和。例如,如果客户端连接到 1,000 个端点,并且流的窗口大小为 50,则 min-msg-burst 大小应设置为 50,000 或更大。

以这种方式调整 min-msg-burst 大小,确保 NAB 持有足够的消息来填充客户端上线时的组合保证消息窗口大小。如果没有足够的消息被持有,未投递到客户端的消息可能会被丢弃,然后需要再次尝试投递。这种丢弃然后重新发送消息的过程会导致慢速订阅者(即,不以快速率消费消息的客户端)恢复缓慢。

有关如何设置 min-msg-burst 大小的信息,请参阅配置出站每个客户端优先级队列。

基本规则

在使用 JCSMP 编程时,记住以下基本规则是有用的:

  • 持久和非临时对象(如持久端点)是在工厂级别创建的
  • 非持久和临时对象是在会话级别创建的
  • 流是在会话级别创建的

线程

API线程

建议

  • 如果优化延迟是最重要的,考虑直接从 I/O 线程分发消息。这是 C、.NET 和 Java RTO API 的默认行为。

API 使用上下文来组织客户端应用程序和 Solace PubSub+ 事件代理之间的通信。上下文是一个会话容器。上下文负责处理与会话相关的事件,并封装驱动会话的网络 I/O 和消息传递通知的线程。

Solace 的 JCSMP 实现本质上是阻塞的,也就是说,应用程序可用的调用可能会阻塞。为了解决这个问题,API 运行一个单独的分发线程,以便应用程序可以在回调中进行阻塞调用,同时仍然允许网络 I/O 线程继续从网络读取消息。随着消息继续被接受到 API 中,一个称为通知队列的存储机制被用来在分发线程将消息分发给客户端应用程序之前跟踪这些消息。

与 JCSMP 不同,C、.NET 和 Java RTO API 实现为本质上非阻塞的。因此,客户端应用程序不允许使用这些 API 在回调中进行阻塞调用。因此,这些实现不需要单独的分发和 I/O 线程。上下文因此有一个单一的处理线程,用于从套接字读取消息以及执行通知和消息分发。

直接从 I/O 线程分发消息的好处是优化延迟,因为消息不会被排队到通知队列中,这可能会引入延迟。因此,对于延迟敏感的应用程序,应考虑使用 C、.NET 和 Java RTO API。JCSMP 也可以配置为直接从其 I/O 线程(称为“反应器”线程)分发消息。为此,启用会话属性中的 MESSAGE_CALLBACK_ON_REACTOR。然而,在这种情况下,应用程序必须确保回调中不发生阻塞。否则,如果 API 正在等待事件代理的响应,但线程被阻塞以读取该响应,则可能会发生死锁。

尽管直接从 I/O 线程分发消息可以减少消息延迟,但它也会降低最大消息吞吐量的潜力,因为消息是逐个处理的,而不是批量处理。在决定实现哪种 API 线程模型时,也应该权衡这一方面。由于应用程序的典型消息大小是这一方面的主要决定因素,因此在必要时建议在模型选择前进行性能评估。

有关线程交互的更多详细信息,请参阅 PubSub+ JCSMP API 中的线程。

上下文和会话线程模型注意事项

建议

  • 尽可能使用“一个会话,一个上下文”的线程模型。“多个会话,一个上下文”和“多个会话,多个上下文”模型可能会增加消息处理吞吐量,但以增加额外处理为代价。

在设计应用程序时,需要考虑三种不同的线程模型:

  1. 一个会话一个上下文。使用单一会话与单个上下文。
  2. 多个会话一个上下文。使用单个上下文服务多个会话。
  3. 多个会话多个上下文。应用程序提供或使用多个线程,每个线程包含一个上下文,每个上下文包含一个或多个会话。

在大多数情况下,“一个会话,一个上下文”模型足以用于发布者和消费者应用程序设计。

如果需要对消息进行优先级排序,其中高价值消息可能通过不同会话发送/接收,例如通过不同的 TCP 连接,应用程序设计者可能希望转向“多个会话,一个上下文”。这种方法也可以潜在地增加吞吐量。这意味着可能需要将接收到的消息转发到下游应用程序内部队列,以便消息由额外的应用程序消息处理线程处理。所有接收到的消息可以由相同的消 息和事件回调函数处理,或者通过创建额外的回调函数来处理特定于会话的消息。

使用“多个会话,多个上下文”模型,设计者可以减轻“多个会话,一个上下文”模型中上下文线程处理负担,因为所有会话都必须在选择循环中等待才能被处理。在此模型中,每个会话可以分离到其自己的上下文线程中,并利用操作系统提供的多线程增强处理性能。然而,由于线程数量增加,这种方法需要昂贵的线程上下文切换,因此对 CPU 的负担更大,也更耗费资源。

总是清理资源

JCSMPSessionXMLMessageProducerXMLMessageConsumer 都与系统资源分配有关。当它们不再使用或发生错误时,应正确关闭它们。

关闭 JCSMPSession 会关闭与之关联的任何 ProducerConsumer

发布大消息时增加缓冲区大小

在某些 Java 虚拟机(JVM)上,一些用户报告在发布大消息时需要增加套接字发送和接收缓冲区大小。Java 使用的默认套接字缓冲区大小为 64 KB。

要修改发送套接字缓冲区大小,请调用 JCSMPChannelProperties#setSendBuffer(int so_sendbuf)。要修改接收套接字缓冲区大小,请调用 JCSMPChannelProperties#setReceiveBuffer(int so_rcvbuf)

如果您的应用程序在 Linux 系统上运行,您还必须更改其 TCP 缓冲区大小设置。将变量 rmem_maxwmem_maxtcp_rmemtcp_wmem 如下所示添加到 /etc/sysctl.conf 文件中。

# 增加 TCP 最大缓冲区大小
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# 增加 Linux 自动调整 TCP 缓冲区限制
# 最小、默认和最大字节数
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

根据最大消息大小和网络链路适当调整缓冲区大小,然后运行 sysctl -p。系统启动时会设置这些值。

TCP发送和接收缓冲区大小

建议

  • 调整 TCP 发送和接收缓冲区大小,以优化 TCP 性能,特别是在发布大消息或优化 WAN 性能时。

对于 TCP,带宽延迟乘积是指数据链路的容量与其往返延迟时间的乘积。结果表示网络在任何给定时间可以接受的最大数据量。由于 WAN 环境固有的长往返延迟,因此可以预期带宽延迟乘积较大,因此只有当发送方发送足够大量的数据以填满网络可以接受的最大数据量时,TCP 才能达到最佳吞吐量。这意味着需要调整 TCP 发送和接收缓冲区大小。

特定于 Windows 平台,接收套接字缓冲区大小必须远大于发送套接字缓冲区大小,以防止在发送和接收消息时丢失数据。推荐的比例是 3 部分发送缓冲区对应 5 部分接收缓冲区。

TCP 的套接字发送和接收缓冲区大小可以通过 API 的会话属性设置进行配置。会话属性参数及其默认值如下所示。如果将这些属性的值设置为零,则使用操作系统的默认大小。

  • JCSMPChannelProperties.setSendBuffer(int);64,000 字节
  • JCSMPChannelProperties.setReceiveBuffer(int);64,000 字节

不要缓存 XMLMessages

如果使用生产者消息池中的消息,请始终调用 XMLMessageProducer.create<TYPE>XMLMessage() 来获取一个新的 XMLMessage 实例以供发布。应用程序不应缓存或重用消息,因为 JCSMP 可能会自动回收这些消息。

超低延迟

对于超低延迟应用程序,您可以启用 MESSAGE_CALLBACK_ON_REACTOR 会话属性以减少消息延迟。启用此会话属性时,异步传递到 XMLMessageListener 的消息将直接从 I/O 线程传递,而不是从消费者通知线程传递。

尽管启用此会话属性可以减少消息延迟,但它也会降低最大消息吞吐量。

会话建立

主机列表

建议

作为最佳实践,使用主机列表(见下文说明)。主机列表适用于您使用复制进行故障转移支持以及软件事件代理的主机列表高可用性(HA)支持的情况。

不应在主动/主动复制部署中使用主机列表。

对于复制故障转移支持,客户端应用程序必须配置一个包含两个地址的主机列表,每个站点的启用保证消息的虚拟路由器各一个。如果一个主机的连接失败,客户端将尝试连接到待激活的复制主机,然后才会重试同一个主机。因此,建议将每个主机的重连尝试次数设置为 1。

在主动/主动复制部署中,客户端应用程序从两个站点的复制活动消息 VPN 上的端点消费消息,不应使用主机列表。

同样,对于软件事件代理 HA 故障转移支持,如果切换机制设置为主机列表而不是 IP 地址接管,客户端应用程序必须提供一个包含两个地址的主机列表。

有关主机列表配置的更多详细信息,请参阅 HA 组配置。

客户端API保持活动

建议

  • 客户端保持活动间隔应与客户端配置文件上的 TCP 保持活动设置处于同一数量级。

客户端应用程序和事件代理之间存在两种类型的保持活动机制。

有一种是 TCP 保持活动,它在 TCP 层面上运行,由事件代理发送给客户端应用程序。这是 RFC 1122 中描述的 TCP 保持活动机制。客户端应用程序的 TCP 堆栈响应事件代理的 TCP 保持活动探测。默认情况下,事件代理在检测到空闲连接 3 秒后发送保持活动消息。然后,它以每秒 1 次保持活动探测的间隔发送 5 次探测。因此,如果事件代理在 8 秒后未收到响应,则会将客户端标记为 TCP 保持活动失败。

还有一种是客户端 API 保持活动,它与 TCP 保持活动同时发生。这是 API 的内置保持活动机制,在 TCP 之上在 API 层面上运行。这是从 API 发送到事件代理的。默认情况下,客户端保持活动以每 3 秒一次的间隔发送,并且在 API 声明事件代理无法到达之前可以错过最多 3 次保持活动响应,即 9 秒后。

这些保持活动机制的存在是为了能够在通知相应方之前告知应用程序或事件代理其对等方已死亡。保持活动机制还用于防止因网络不活动而导致的断开连接。然而,如果其中一种机制设置得比另一种更具侵略性,即检测时间更短,那么连接可能会过早断开。例如,如果客户端 API 保持活动设置为 500 毫秒间隔,有 3 次保持活动响应,而 TCP 保持活动保持默认值不变,那么客户端 API 保持活动将触发激进的断开连接。

高可用性故障转移和重连尝试

建议

  • 在设计支持高可用性(HA)的应用程序时,应将重连持续时间设置为至少 300 秒。

在使用高可用性(HA)设备设置时,从一个设备故障转移到其配对设备通常需要 30 秒。然而,应用程序应尝试至少重连 5 分钟。以下是使用以下会话属性值将重连持续时间设置为 5 分钟的示例:

  • 连接尝试次数:1
  • 重连尝试次数:20
  • 重连尝试等待:3,000 毫秒
  • 每个主机的连接尝试次数:5

有关如何设置连接尝试、重连尝试、重连尝试等待和每个主机的连接尝试参数的说明,请参阅配置连接超时和重试。

复制故障转移和重连尝试

建议

  • 应将重连尝试次数设置为 -1,以便在复制故障转移期间 API 无限期重试。

一般来说,复制故障转移的持续时间是不确定的,因为它可能需要操作干预来进行切换,这可能需要数十分钟甚至数小时。因此,建议将重连尝试次数设置为 -1,以便复制感知型客户端应用程序无限期地尝试重连。

有关如何设置重连尝试参数的说明,请参阅重连尝试。

复制故障转移和会话重新建立

建议

  • API 版本高于 7.1.2 的是复制感知型的,并且在复制故障转移发生时自动处理会话重新建立。运行较低 API 版本的客户端应用程序必须在重新连接时重新建立会话。

在 7.1.2 之前,当客户端在已断开连接的会话中发布保证消息时,复制故障转移后需要重新建立会话,因为尽管重新连接成功,但需要重新建立流,因为新连接的事件代理在复制站点中没有流状态信息,这与 HA 故障转移的情况不同,后者会同步此信息。建议捕获 unknown_flow_name 事件并重新建立新会话以创建流。从版本 7.1.2 开始,API 是复制感知型的,并且透明地处理会话重新建立。

文件描述符限制

建议

  • 应用程序创建的 Solace 会话数量不应超过底层操作系统每个进程支持的文件描述符数量。对于 Unix 变体,此数字为 1024,对于 Windows 为 63。

Unix 平台上的文件描述符限制限制了每个进程可以管理的文件数量,默认为 1024。因此,应用程序不应在每个上下文中创建超过 1023 个会话。会话代表一个 TCP/IP 连接,此类连接占用一个文件描述符。文件描述符是一个元素——通常是一个数字——允许您识别,此处为来自套接字的数据流。从磁盘读取信息打开文件也占用一个文件描述符。

文件描述符被称为“文件”,因为最初它们只标识文件,尽管最近,它们也可以用于标识磁盘上的文件、套接字、管道等。

同样,在 Windows 平台上,单个上下文最多只能管理 63 个会话。

订阅管理

可以使用以下最佳实践来管理订阅:

  • 如果您正在添加或删除大量订阅,请仅在最后一个订阅上设置等待确认标志(JCSMPSession.WAIT_FOR_CONFIRM)。这确保了事件代理已处理所有订阅,并提高了事件代理的性能。
  • 在会话断开连接的情况下,您可以在重新连接时让 API 重新应用最初由应用程序添加的订阅。要重新应用订阅,请启用重新应用订阅会话属性(JCSMPProperties.REAPPLY_SUBSCRIPTIONS)。建议使用此设置。

以下两种场景展示了在订阅失败时 WAIT_FOR_CONFIRM 如何影响订阅:

  • 如果发生订阅失败,REAPPLY_SUBSCRIPTIONS 设置为 true,并且您使用 带有 WAIT_FOR_CONFIRM 标志的主题进行订阅,则在重新连接时 不会 重新应用该订阅。

  • 如果发生订阅失败,REAPPLY_SUBSCRIPTIONS 设置为 true,并且您使用 不带 WAIT_FOR_CONFIRM 标志的主题进行订阅,则在重新连接时 重新应用该订阅。

发送消息

在发送消息时,您应考虑以下与您使用的消息所有权模型相关的最佳实践——会话无关消息模型或会话相关消息模型。

有关会话无关和会话相关消息所有权模型的信息,请参阅直接消息的消息所有权和保证消息的消息所有权。

发送会话无关消息

概述

在发送消息时,始终使用会话无关消息模型。会话相关消息模型仅支持向后兼容。

实践细节

如果消息尽可能预先分配并重用,则使用会话无关消息所有权模型不会带来性能损失。在会话无关模型中,消息是按需分配的。

在会话相关模型中,应用程序必须在创建消息后立即调用 send,以避免可能的内存资源耗尽。此外,会话相关消息池中的消息可能在首次使用后自动回收,从而防止创建的消息被重用。

有关如何发送会话无关消息的信息,请参阅 Ask Solly AI。

发送会话相关消息

在会话相关消息模型中,消息是从 Producer 消息池中获取的,您必须在创建 XMLMessage 后使用 send() 发布,以避免资源耗尽。例如,以下代码示例错误地允许在获取消息后继续执行,而没有发送消息:

while (keepPublishing) {
TextMessage message = producer.createTextMessage();
if (messageToPublish) {
message.setText("<xml>hello!</xml>");
producer.send(message);
}
}

为确保资源不耗尽,请在创建 XMLMessage 后立即调用 send() 方法。例如:

while (keepPublishing) {
TextMessage message = producer.createTextMessage();
message.setText("<xml>hello!</xml>");
producer.send(message);
}

会话相关消息所有权模型主要用于与使用 JCSMP 的现有应用程序向后兼容。如果可能,建议新的 Java 应用程序使用会话无关消息。如果消息预先分配并重用,则使用会话无关消息所有权模型不会带来性能损失。

批量发送

建议

  • 使用批量发送功能来优化发送性能。这特别适用于对客户端应用程序进行性能基准测试。

使用批量发送功能来优化发送性能。这特别适用于对客户端应用程序进行性能基准测试。

可以通过单个 API 调用发送多达 50 条消息。这允许批量发送消息。消息可以是直接消息或保证消息。当通过发送多个 API 批量发送消息时,应为批次中的所有消息设置相同的投递模式,即直接或持久模式。批次中的消息可以设置为不同的目标。

除了使用批量发送 API 外,还应尽可能预先分配消息并重用它们以进行批量发送。具体来说,不要为每次调用批量发送 API 重新分配新消息。

批量发送 API 调用是 XMLMessageProducer.sendMultiple()

高性能批量发送

在批量发送消息时,只使用会话无关消息;不要使用会话相关消息。有关会话无关和会话相关消息所有权模型的信息,请参阅直接消息的消息所有权和保证消息的消息所有权。

为了优化性能,还建议您预先分配消息并尽可能重用它们进行批量发送。(也就是说,避免为每次调用 JCSMPSendMultipleEntry() 重新分配新消息。)

有关直接消息批量发送的更多信息,请参阅一次发送多条消息。有关保证消息批量发送的更多信息,请参阅一次发送多条消息。

设置消息的生存时间(TTL)

建议

  • 如果用例允许丢弃旧的或过时的消息,则在发布的保证消息上设置 TTL 属性,以减少未消费消息意外堆积的风险。

发布应用程序应考虑使用保证消息传递中可用的 TTL 功能。发布者可以在将消息发送到事件代理之前,在每条消息上设置 TTL 属性。一旦消息被暂存,如果消息在指定的 TTL 内未被消费,消息将被自动丢弃(或者如果队列配置了死信队列,则移动到队列的死信队列)。这种常见做法减少了未消费消息意外堆积的风险。

或者,队列有一个 max-ttl 设置,这可以用来代替发布者在每条消息上设置 TTL。有关如何为队列设置 max-ttl 的说明,请参阅配置队列。

配置尊重 TTL

应将队列配置为尊重 TTL,因为默认情况下,此功能在所有队列上是禁用的。有关如何设置队列以尊重 TTL 的说明,请参阅强制尊重 TTL。

接收消息

处理重复消息发布

建议

  • 如果客户端应用程序使用最后值队列(LVQ)来确定事件代理成功暂存的最后一条消息,则可以避免发布重复消息,以便在重新启动时正确恢复发布,而不会引入重复消息。

当客户端应用程序意外重新启动时,它可能会与消息发布序列不同步。应该有一种机制,使它能够确定事件代理成功发布和接收的最后一条消息,以便正确恢复发布,而不会引入重复消息。

一种方法是发布应用程序维护一个数据库,该数据库关联发布的消息标识符和从事件代理收到的确认。这种方法完全在客户端应用程序端进行,但如果管理不当,可能会引入处理延迟。

另一种方法是利用最后值队列(LVQ)功能,LVQ 存储队列上暂存的最后一条消息。然后,发布客户端应用程序可以浏览 LVQ 以确定事件代理暂存的最后一条消息。这允许发布者在不引入重复消息的情况下恢复发布。

有关如何设置 LVQ 的说明,请参阅配置最大暂存使用值。

处理重新投递的消息

建议

  • 当从端点消费时,客户端应用程序应适当处理重新投递的消息。

当客户端应用程序重新启动,无论是意外还是正常,重新绑定到队列时,它可能会收到已经处理并确认的消息。这可能是因为由于网络问题,确认在发送到事件代理的途中丢失。重新投递的消息将标记有 redelivered 标志。

消息的 redelivered 标志:

  • 在高可用性(HA)故障转移之间主备事件代理之间持续存在。
  • 当消息复制到灾难恢复(DR)站点时不会持续。

绑定到非独占队列的客户端应用程序也可能收到设置了 redelivered 标志的消息,即使这是客户端应用程序第一次收到这些消息。这是由于其他客户端连接到同一个非独占队列,并且在没有应用程序确认收到的消息的情况下断开连接。然后,这些消息将重新投递给绑定到同一非独占队列的其他客户端应用程序。

消费应用程序应包含一个消息处理机制,以处理上述提到的情况。

处理意外消息格式

建议

  • 客户端应用程序应能够处理意外消息格式。在从端点消费的情况下,客户端应用程序应确认收到的消息,即使这些消息的格式出乎意料。

客户端应用程序应能够应对意外消息格式。不应假设消息的有效载荷;例如,有效载荷可能包含一个空附件。应用程序应编写为避免崩溃,同时记录消息内容并向事件代理发送确认(如果使用保证消息传递)。如果客户端应用程序在没有发送确认的情况下崩溃,那么当它们重新连接时,相同的消息将被重新投递,导致应用程序再次失败。

客户端确认

建议

  • 当使用客户端确认模式时,客户端应用程序应在完成处理这些消息后立即确认收到的消息。

一旦应用程序完成处理消息,它应向事件代理确认收到该消息。只有当事件代理收到保证消息的确认时,该消息才会从其消息暂存区永久删除。如果客户端在没有发送某些收到消息的确认的情况下断开连接,那么这些消息将被重新投递。对于独占队列的情况,这些消息将被投递给下一个连接的客户端。对于非独占队列的情况,这些消息将被重新投递给绑定到该队列的其他客户端。

有两种确认类型:

  • API(也称为传输)确认。这是 API 和事件代理之间的内部确认,不暴露给应用程序。保证交付(AD)窗口大小、确认计时器和确认阈值设置控制 API 确认。未进行传输确认的消息将由事件代理自动重新投递。
  • 应用程序确认。这种确认机制是在 API 确认之上。其主要目的是确认消息处理已完成,并且相应的消息可以从事件代理永久删除。有两种应用程序确认模式:自动确认和客户端确认。当使用自动确认模式时,API 代表应用程序自动生成应用程序级确认。当使用客户端确认模式时,客户端应用程序必须明确发送每个收到消息的消息 ID 的确认。

有关不同确认模式的详细讨论,请参阅在 PubSub+ JCSMP API 中接收保证消息。

尽快消费消息并从回调中返回

为了确保最高的消息吞吐量,应在收到后尽快消费收到的消息。

当使用 XMLMessageConsumer 的同步操作模式时,应用程序应尽可能频繁地调用 receive() 以检索 XMLMessageConsumer 收到的消息。如果在 XMLMessageConsumer 的队列中积累了太多消息,XMLMessageConsumer 将被视为拥塞,消息投递将被暂停。

当使用 XMLMessageConsumer 的异步操作模式时,应用程序应确保 XMLMessageListener 中定义的回调方法能够迅速返回,以便调用线程不会被阻塞,从而能够处理后续消息。

应用程序也应尽可能快地从 API 事件回调中返回。

接收消息时的内存管理

API 为每条收到的消息动态分配内存。如果应用程序使用自动确认模式,API 会保留消息的引用,直到消息回调返回。然后,API 使用该引用来确认消息,之后消息内存将被垃圾回收器释放。如果应用程序正在接收直接消息或使用客户端确认模式,API 在调用消息回调后不会保留消息的引用。只要应用程序不创建新的引用,当回调返回且其包含的引用超出作用域时,消息内存将有资格进行垃圾回收。

队列和流

每次接收一条消息

建议

  • max-delivered-unacked-msgs-per-flow 设置为 1,并将保证交付窗口大小设置为 1,以确保消息从事件代理到客户端应用程序一次投递一条,并且以时间一致的方式进行。

API 只有在满足以下条件之一时才会发送传输确认:

  1. 它收到了配置的保证交付窗口消息数量的配置确认阈值(例如,60%)。
  2. 收到消息并且自上次确认发送以来已过配置的保证交付确认时间(例如,1 秒),以先到者为准。

应用程序确认会随着传输确认一起发送,用于从客户端应用程序到事件代理的投递。事件代理只有在收到确认后才会释放更多消息。

因此,虽然将 max-delivered-unacked-msgs-per-flow 设置为 1 可以确保消息一次投递给客户端应用程序,但如果保证交付窗口大小不是 1,那么条件 1 将不会立即满足。这可能会导致接收延迟变化,因为 API 只有在条件 2 满足后才会发送确认。这与预期的端到端消息接收投递延迟不一致。为了避免这种情况,事件代理会通知 API 端点的 max-delivered-unacked-msgs-per-flow 设置。然后,API 使用此信息自动调整其确认阈值,以防止接收延迟变化。

有关如何在队列上配置 max-delivered-unacked-msgs-per-flow 的说明,请参阅配置最大允许的未确认投递消息数量。

设置临时端点暂存大小

建议

  • 如果客户端应用程序频繁创建临时端点,请确保所有临时端点暂存大小的总和不超过为消息 VPN 配置的总暂存大小。

默认情况下,消息 VPN 和端点的消息暂存配额基于过度订阅模型。例如,可以将多个端点的消息暂存配额设置为与整个消息 VPN 相同的配额。客户端应用程序创建的临时端点默认为 Solace 应用程序 4000 MB,软件事件代理 1500 MB。当客户端应用程序广泛使用临时端点时,如果临时端点是按需创建的,消息暂存过度订阅模型可能会迅速失控。因此,建议客户端应用程序覆盖端点的默认消息暂存大小,将其设置为与预期使用相符的值,特别是如果临时端点被大量使用。

建议

  • 在 API 上配置的保证交付窗口大小不应大于事件代理上为队列设置的 max-delivered-unacked-msgs-per-flow 值。

max-delivered-unacked-msgs-per-flow 控制事件代理可以在没有收到确认的情况下向客户端应用程序投递多少消息。保证交付窗口大小控制在事件代理和客户端应用程序之间传输中的消息数量。因此,如果保证交付窗口大小大于 max-delivered-unacked-msgs-per-flow,那么 API 可能无法及时确认其收到的消息。实际上,保证交付窗口大小受到 max-delivered-unacked-msgs-per-flow 设置值的限制。例如,如果保证交付窗口大小设置为 10,而 max-delivered-unacked-msgs-per-flow 设置为 5,那么事件代理实际上将限制一次发送 5 条消息,无论客户端应用程序的保证交付窗口大小设置为 10。

有关如何在队列上设置 max-delivered-unacked-msgs-per-flow 的说明,请参阅配置最大允许的未确认投递消息数量。

流的数量和AD窗口大小

建议

  • 根据客户端应用程序主机的可用内存限制以及事件代理上每个客户端出站队列分配的默认工作单位,确定每个会话的预期流数量及其相关的保证交付窗口大小。

API 为收到的保证消息缓冲,并且通常也拥有这些消息并负责释放它们。客户端使用的缓冲区数量主要由每个会话使用的流数量 * 每个流的保证交付窗口大小决定。例如,如果一个接收客户端应用程序使用窗口大小为 255 的流绑定到事件代理上的 10 个不同队列,那么在平均消息大小为 1 MB 的情况下,最大缓冲区使用量将为 2560 MB。如果在同一主机上有 10 个这样的客户端,那么将需要 25.6 GB 的内存。

同样,事件代理为每个客户端出站队列分配一个缓冲区,用于缓冲要传输到客户端应用程序的消息。默认情况下,这是 20,000 个工作单位,相当于 40 MB 的缓冲区,因为每个工作单位是 2048 字节。如果一个客户端出站队列要支持 2560 MB 的缓冲,那么这个特定客户端的工作单位数量需要增加到 130,560。因此,根据应用程序的使用情况,建议您根据每个会话预期的流数量调整保证交付窗口大小,使其在每个客户端连接的默认 20,000 个工作单位的缓冲区内。

错误处理和日志记录

日志记录和日志级别

建议

  • 不应在生产环境中启用客户端应用程序的调试级别日志记录。

客户端应用程序的事件日志记录可能会对性能产生重大影响,因此在生产环境中不建议启用调试级别日志记录。

处理会话事件/错误

建议

  • 客户端应用程序在创建会话时应注册会话事件处理程序接口/委托/回调的实现,以接收会话事件。

客户端应用程序在创建会话时应注册会话事件处理程序接口/委托/回调的实现,以接收会话事件。下表列出了完整的会话事件列表。随后,应根据客户端应用程序的使用情况适当处理会话事件。

这些会话事件中的许多是通过异常处理向客户端应用程序提供的反馈。有关详细信息,请参阅 JCSMPException 及其子类,JCSMPTransportException、JCSMPStateException、JCSMPOperationException。例如,JCSMPTransportException 在多次(重新)连接尝试后被抛出。

Java (SessionEvent 枚举)描述
DOWN_ERROR会话建立后中断。
RECONNECTED会话的自动重新连接成功,并且会话重新建立。
RECONNECTING会话中断,正在进行自动重新连接尝试。
SUBSCRIPTION_ERROR应用程序拒绝订阅(添加或移除)。
VIRTUAL_ROUTER_NAME_CHANGED在重新连接操作期间,设备的虚拟路由器名称发生了变化。
UNKNOWN_TRANSACTED_SESSION_NAME重新建立事务会话的尝试失败。
INCOMPLETE_LARGE_MESSAGE_RECEIVED由于未能及时收到所有片段,消费者收到不完整的大型消息。

会话事件

处理流事件/错误

建议

  • 客户端应用程序在创建流时应注册流事件处理程序接口/委托/回调的实现,以接收流事件。

客户端应用程序在创建流时应注册流事件处理程序接口/委托/回调的实现,以接收流事件。应根据客户端应用程序的使用情况适当处理流错误/事件。

通过抛出 JCSMPFlowTransportException 来指示错误事件,并且对于异步模式下的流,错误条件将传递给 XMLMessageListener 的 onException()。

Java (FlowEvent 枚举)描述
FLOW_ACTIVE流已变为活动状态。
FLOW_INACTIVE流已变为非活动状态。
FLOW_RECONNECTING当启用流重新连接时,API 生成此事件并尝试重新绑定流,而不是生成 INACTIVE 事件。如果流重新绑定失败,API 将监控绑定失败,并在非以下原因之一的情况下终止重新连接尝试:队列关闭、主题端点关闭、服务不可用。有关流重新连接的更多信息,请参阅流重新连接。
FLOW_RECONNECTED流已成功重新连接。

流事件

自定义重新连接事件处理

建议

  • 使用会话消息消费者的应用程序在获取消费者时还应注册 JCSMPReconnectEventHandler 实例,以接入 API 的重新连接逻辑。

使用会话消息消费者(通过 JCSMPSession.getMessageConsumer(JCSMPReconnectEventHandler.XMLMessageListener) 获取)的应用程序在获取该消费者时还应注册 JCSMPReconnectEventHandler 实例。此处理程序在客户端 TCP 连接在连接失败后重新连接之前接收 preReconnect() 回调,在重新连接之后接收 postReconnect() 回调。通过 preReconnect()postReconnect(),客户端应用程序可以在 JCSMP 尝试重新建立连接之前和之后执行一系列操作,例如,通知系统内其他方连接失败。

仅发布应用程序的传输层事件通知

建议

  • 对于仅发布应用程序,需要创建一个空的 XMLMessageConsumer,以便从 JCSMPReconnectEventHandler 接收事件,从而捕获传输层事件。

JCSMPReconnectEventHandler 仅通过 XMLMessageConsumer 暴露。对于获取 XMLMessageProducer 的仅发布应用程序,需要创建一个空的 XMLMessageConsumer,以便从 JCSMPReconnectEventHandler 接收事件。可以通过 session.getMessageConsumer() 获取该消费者。无需通过调用 consumer.start() 方法启动该消费者。这样,就可以为仅发布应用程序捕获传输层事件。

影响客户端应用程序行为的事件代理配置

最大重新投递

建议

  • 默认情况下,消息将无限期地从端点重新投递给客户端。在适当时,应在事件代理上的端点设置最大重新投递选项,以限制每条消息的最大重新投递次数。

可以在端点上设置最大重新投递选项,以控制该端点上每条消息的投递次数。端点的重新投递次数超过最大值后,消息将被丢弃,或者如果已配置并且消息被设置为死信队列(DMQ)合格,则移动到死信队列。

当端点上的重新投递次数不是无限的(默认情况下,重新投递模式设置为永远重新投递)时,客户端应用程序会受益。例如,如果客户端应用程序无法处理意外的毒药消息,该消息最终将被丢弃或移动到 DMQ,以便进行进一步检查。

拒绝消息发送方在丢弃时

建议

  • 除非有充分理由,否则应启用端点上的 reject-msg-to-sender-on-discard

当向事件代理发布保证消息时,由于消息暂存区满、消息大小超过最大值、端点关闭等原因,消息可能会被丢弃。如果端点上的消息丢弃选项(即 reject-msg-to-sender-on-discard)已启用,那么客户端应用程序将能够检测到正在发生丢弃,并采取纠正措施,例如暂停发布。API 没有明确支持暂停发布;这应由客户端应用程序逻辑执行。

考虑禁用 reject-msg-to-send-on-discard 的一个原因是,当有多个队列订阅发布保证消息的同一主题,并且意图是即使其中一个队列被认为无法接受消息,其他队列也能继续接收消息时。