跳到主要内容

.NET API最佳实践

通用最佳实践

保证消息的调整指南

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

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

为了应对因对 G-1 队列分配的缓冲区需求过高而导致的保证消息投递速率变慢的问题,您应该减小每个流的保证消息窗口大小,并尽可能减少使用的流数量。

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

重新应用订阅

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

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

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

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

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

最小消息突发大小

如果无法减少流的数量或保证消息窗口大小,您可以调整 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 大小的更多信息,请参阅“配置每个客户端优先队列的出口”。


基本规则

在使用 .NET API 编程时,记住以下基本规则会很有帮助:

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

线程

选择线程模型

.NET API 使用上下文来组织与事件代理的通信,并创建和管理自己的工作线程。使用 .NET API 的每个客户端应用程序必须至少包含一个上下文,每个上下文可以包含一个或多个会话。

使用 .NET API 的应用程序开发人员可以选择在一个上下文中创建一个或多个会话。如何在上下文中配置会话是应用程序设计中的一个重要考虑因素,因为它直接影响诸如应用程序主机上的 CPU 使用率、消息延迟和吞吐量等因素。

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

建议

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

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

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

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

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

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


内存管理

以下各节讨论如何在 .NET API 中管理内存,并提供一些优化 API 性能的指南。

修改全局池缓冲区大小

.NET API 从自己的池中分配特定大小的缓冲区并内部维护这些缓冲区。缓冲区从堆存储中分配,用于在应用程序空间中保存消息,直到应用程序释放消息缓冲区或稍后进行垃圾回收。

您可以在初始化 .NET API 时选择修改用于五个池的默认全局数据缓冲区大小。

当您调用 ContextFactory.Init(...) 方法初始化 .NET API 时,您可以修改以下 ContextFactoryProperties

  • MaxPoolMemory
    指定 .NET API 可以在其数据和消息池中保存的最大内存量。

  • DBQuantaSize_<0-4>
    指定每个五个池的数据缓冲区大小。一旦达到此大小,数据块将释放回堆,而不是保留在 API 池中。

有关更多信息,请参阅 C#/.NET API 参考。

配置消息缓冲区大小

在创建会话时,应用程序可以配置以下与内存和资源分配相关的会话属性:

  • SessionProperties.SdkBufferSize
    用于 TCP 会话传输消息的会话缓冲区大小。此参数指定要缓冲的消息的最大数量(以字节为单位)。为了获得最佳性能,当发送小消息时,会话缓冲区大小应设置为典型消息大小的多倍。

    .NET API 始终至少接受一个消息进行传输。因此,即使单个消息的大小超过了设置的缓冲区大小,只要当前缓冲区数据为零,它也会被接受并传输。然而,直到缓冲区中的数据量减少到低于设置的缓冲区大小之前,不会再接受其他消息。

  • SessionProperties.SocketReceiveBufferSizeInBytes
    订阅者数据套接字的接收缓冲区大小。默认值为 150,000。如果此属性设置为 0,则接收缓冲区大小使用操作系统默认值。

    在 Windows 平台上,接收套接字缓冲区大小必须比发送套接字缓冲区大小大得多,以防止在发送和接收消息时丢失数据。例如,发送套接字和内部缓冲区的默认大小设置为 90,000,而接收 套接字缓冲区的默认大小设置为 150,000。如果您更改了默认大小,建议保持类似的大小比例。

  • SessionProperties.SocketSendBufferSizeInBytes
    允许应用程序设置发布者数据套接字的发送缓冲区大小。默认值为 90,000。如果此属性设置为 0,则发送缓冲区大小使用操作系统默认值。

    您还可以修改 .NET API 使用的五个缓冲区池的全局数据缓冲区大小(DBQuantaSize_<0-4>)。修改全局数据缓冲区大小可以在初始化 API 时完成。有关更多信息,请参阅“修改全局池缓冲区大小”。

发布消息时的内存管理

为了确保在发布消息时达到高水平的操作性能,应避免不必要的内存移动和复制。因此,建议尽可能重用消息实例。

接收消息时的内存管理

接收到的消息缓冲区由应用程序所有。为了确保分配的内存被释放,建议您为每个接收到的消息显式调用 Dispose() 方法。


会话建立

阻塞连接

建议

  • 连接阻塞调用会序列化每个会话连接,增加会话建立延迟。如果序列化不是必要的,请禁用阻塞连接以提高整体会话连接速度。

启用阻塞连接会序列化上下文中配置的每个单独会话连接。这样做会增加连接时间。如果序列化不重要,请考虑禁用阻塞连接。

阻塞连接属性为 SessionProperties.ConnectBlocking

主机列表

建议

  • 作为最佳实践,使用主机列表(见下文注释)。 主机列表适用于您使用复制进行故障转移支持以及软件事件代理的主机列表高可用性(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 为复制感知型客户端应用程序无限期地重新连接。

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

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

建议

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

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

文件描述符限制

建议

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

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

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

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


订阅管理

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

  • 如果您正在添加或删除大量订阅,请在最终订阅上设置等待确认标志(SubscribeFlag.WaitForConfirm),以确保所有订阅都已由事件代理处理。 为了提高性能,建议在所有其他订阅上不设置等待确认。
  • 在会话断开连接时,您可以配置消息 API 在重新连接时重新应用最初由应用程序添加的订阅。 为了重新应用订阅,请启用会话属性 SessionProperties.ReapplySubscriptions。建议使用此设置。

发送消息

阻塞发送

建议
  • 发送阻塞调用会自动限制事件代理可以接受消息的发布速率。使用非阻塞发送以提高应用程序性能。

在发送阻塞模式下,每个发送函数调用的调用线程将被阻塞,直到 API 接受消息,因此,发送速率自动限制为事件代理可以接受消息的速率。发送调用将保持阻塞,直到:

  • 被 API 接受,或者
  • 相关联的阻塞写入超时到期。

与非阻塞模式相比,无法被 API 接受的发送调用将立即向客户端应用程序返回 would_block 错误代码。在此期间,客户端应用程序可以继续处理其他操作。随后,API 将收到一个 can_send 事件,这 表明客户端应用程序可以再次重试 send() 函数调用。

发送阻塞参数是 SessionProperties.SendBlocking

批量发送

建议
  • 使用批量发送功能以优化发送性能。 这对于性能基准测试客户端应用程序尤其有用。

使用批量发送功能以优化发送性能。这对于性能基准测试客户端应用程序尤其有用。

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

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

批量发送 API 调用是 ISession.Send()

消息生存时间

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

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

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

配置尊重 TTL

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

接收消息

处理重复消息发布

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

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

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

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

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

处理重新投递的消息

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

消息的 redelivered 标志:

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

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

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

处理意外的消息格式

建议
  • 客户端应用程序应能够处理意外的消息格式。 在从端点消费时,即使消息格式意外,客户端应用程序也应确认收到的消息。

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

客户端确认

建议
  • 当使用客户端确认模式时,客户端应用程序应在完成处理消息后尽快确认收到的消息。

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

有两种确认方式:

  • API(也称为传输)确认。这是 API 和事件代理之间的内部确认,不暴露给应用程序。保证传输确认窗口大小、确认定时器和确认阈值设置控制 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,则无论客户端应用程序的保证传输窗口大小设置为 10,事件代理实际上一次只能发送 5 条消息。

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

流的数量和保证传输窗口大小

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

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

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

错误处理和日志记录

日志和日志级别

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

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

错误处理

当 .NET API 会话意外终止时,可以收集错误信息并发送到应用程序。以下会话事件枚举会触发错误信息:

  • DownError — 会话已建立,然后断开。
  • ConnectFailedError — 会话尝试连接但未成功。
  • RejectedMessageError — 事件代理拒绝发布的消息。
  • SubscriptionError — 事件代理拒绝订阅添加或删除。
  • MessageTooBigError — API 丢弃了一个超过设置的会话缓冲区大小的接收消息。
  • TEUnsubscribeError — 主题端点取消订阅请求成功。

错误信息是为每个单独线程单独处理的。

为了配置错误处理,请在您的事件处理代码中包含以下方法调用:

  • ContextFactory 单例上调用 GetLastSDKErrorInfo()

返回一个 SDKErrorInfo,其中包含调用线程最后捕获的错误信息。

子代码

子代码提供了更详细的错误信息。API 调用可能产生的基本子代码如下表所示。

某些 API 调用还可以生成更具体的错误子代码。有关这些子代码的更多信息,请参阅 C#/.NET API 参考。

最后生成的子代码是按线程存储的,可以通过应用程序线程检索。

子代码描述
FactoryInitNotCalled由于未先调用 ContextFactory.Init(),API 调用失败。
ParamOutOfRangeAPI 调用使用了超出范围的参数。
ParamConflictAPI 调用使用了无效的参数组合。
此子代码仅适用于具有相互依赖参数的方法。
InternalErrorAPI 调用发生内部错误(非应用程序故障)。
OperatingSystemError由于操作系统调用失败,API 调用失败。
OutOfMemory由于无法分配内存,API 调用失败。

通用子代码

处理会话事件/错误

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

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

| .NET

(SessionEvent 枚举)描述
Acknowledgment已确认的最老传输的持久/非持久消息。
AssuredDeliveryDown保证消息发布不可用。
CanSend发送不再阻塞。
ConnectFailedError会话尝试连接但未成功。
DownError会话已建立,然后断开。
DTEUnsubscribeError已弃用的名称。与 TE_UNSUBSCRIBE_ERROR 相同。
DTEUnsubscribeOK已弃用的名称。与 TE_UNSUBSCRIBE_OK 相同。
ModifyPropertyFail会话属性修改失败。
ModifyPropertyOK会话属性修改完成。
ProvisionError端点创建/删除命令失败。
ProvisionOK端点创建/删除命令完成。
Reconnected会话的自动重新连接成功,会话重新建立。
Reconnecting会话已断开,正在进行自动重新连接尝试。
RejectMessageError设备拒绝发布的消息。
RepublishUnackedMessages在成功重新连接已断开的会话后,API 在重新连接保证传输发布流时收到未知发布者流名称响应。
MessageTooBigErrorAPI 丢弃了一个超过会话缓冲区大小的接收消息。
SubscriptionError应用程序拒绝订阅(添加或删除)。
SubscriptionOK订阅或取消订阅操作成功。
TEUnsubscribeError主题端点取消订阅命令失败。
TEUnsubscribeOK主题端点取消订阅完成。
UpNotice会话已建立。
VirtualRouterNameChanged在重新连接操作期间,设备的虚拟路由器名称发生了变化。

处理流事件/错误

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

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

| .NET

(SessionEvent 枚举)描述
UpNotice流已建立。
DownError流已建立,然后被设备断开,可能是因为操作员干预。
BindFailedError流尝试连接但未成功。
ParentSessionDown流的会话已断开。
FlowActive流已激活。
FlowInactive流已停用。
Reconnecting当启用流重新连接时,而不是 DownError 事件,API 会生成此事件并尝试重新绑定流。
如果流重新绑定失败,API 会监控绑定失败,并在以下原因之外终止重新连接尝试:
  • 队列关闭
  • 主题端点关闭 | Kimi: - 服务不可用
    有关流重新连接的更多信息,请参阅“流重新连接”。
  • Reconnected | 流已成功重新连接。

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

最大重新投递

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

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

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

拒绝消息发送方在丢弃时

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

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

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