跳到主要内容

C API最佳实践

以下是使用 PubSub+ C 消息 API 的一些最佳实践。这些实践分为以下类别:

  • 通用最佳实践
  • 线程
  • 文件描述符管理
  • 使用提供的宏初始化数据结构
  • 内存管理
  • 会话建立
  • 选择阻塞模式
  • 订阅管理
  • iOS 应用程序开发
  • 发送消息
  • 接收消息
  • 回调中不要阻塞
  • 队列和流
  • 错误处理和日志记录
  • 影响客户端应用程序行为的事件代理配置

通用最佳实践

保证消息的调整指南

当客户端接收大量保证消息(尤其是大消息)时,客户端接收消息的速率可能会降低。在这种情况下,使用的流数量和每个流的保证消息窗口大小会影响事件代理为保证消息使用的每个客户端优先队列(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 大小的更多信息,请参阅“配置每个客户端优先队列的出口”。


线程

选择线程模型

建议

  • 尽可能使用 API 提供的上下文线程,除非它无法满足所需的性能要求,在这种情况下应考虑使用应用程序提供的上下文线程技术。
  • 尽可能使用“一个会话,一个上下文”的线程模型。 “多个会话,一个上下文”和“多个会话,多个上下文”模型可能会增加消息处理吞吐量,但以增加额外的处理限制为代价。

C API 使用上下文来组织与 SolacePubSub+ 事件代理的通信。使用 C API 的每个客户端应用程序必须至少包含一个上下文,每个上下文可以包含一个或多个会话。

默认情况下,C API 提供了一个内部上下文线程,用于处理适合大多数常见应用程序模型和架构的工作。此线程可以处理应用程序通过 API 注册的应用程序计时器和文件描述符。

如果您希望自动创建上下文线程,而不是依赖应用程序创建和销毁上下文线程,请启用 SOLCLIENT_CONTEXT_PROP_CREATE_THREAD 上下文属性。API 提供的上下文线程在 solClient_context_processEvents(...) 中阻塞,并且是应用程序接收所有消息和事件的线程。当上下文被销毁时,API 提供的上下文线程会自动干净地退出。

应用程序提供的线程

可选地,当禁用 SOLCLIENT_CONTEXT_PROP_CREATE_THREAD 上下文属性时,应用程序可以提供上下文线程并自行管理文件描述符。使用此配置时,API 要求应用程序提供线程处理时间。

允许应用程序使用自己的线程和事件循环处理(要求该事件循环的一部分包括对 processEvents() 函数的调用)为开发人员提供了很大的灵活性。

在依赖应用程序提供的线程时,如何在上下文中配置会话是一个重要的应用程序设计考虑因素,因为它直接影响诸如应用程序主机上的 CPU 使用率、消息延迟和吞吐量等因素。

下表描述了可以用于应用程序提供线程的线程模型及其对应用程序设计和性能的影响。

线程模型描述实现注意事项
一个会话,一个上下文线程在这种情况下,使用一个会话和一个上下文在应用程序提供的线程上。这种方法将所有消息和事件处理强制到上下文线程中,而不是将部分或所有处理转发到其他应用程序线程。
这种简单直接的模型允许更简单的设计和调试,适用于仅作为发布者或消费者运行的应用程序。
在大多数情况下,“一个会话,一个上下文”模型足以用于发布者和消费者应用程序的设计。
多个会话,一个上下文线程在这种情况下,使用一个上下文在应用程序提供的线程上服务多个会话。您可以使用相同的回调函数处理所有会话接收的消息,也可以创建额外的回调函数。这种方法对上下文线程施加了相当大的处理压力,因为所有会话都必须在 select 循环中等待处理。
根据消息量,可能需要将消息转发到下游队列以供其他应用程序线程处理。
如果需要优先处理消息(例如,通过不同的 TCP 连接发送/接收高价值消息),应用程序设计者可能希望迁移到“多个会话,一个上下文”模型。这种方法也可以潜在地增加吞吐量。这意味着可能需要将接收到的消息转发到下游应用程序内部队列,以便由其他应用程序消息处理线程处理。所有接收到的消息可以由相同的回调函数处理,也可以通过创建额外的回调函数为每个会话创建特定的回调函数。
多个会话,多个上下文线程在这种情况下,应用程序提供多个线程,每个线程包含一个上下文,每个上下文可以包含一个或多个会话。这种方法允许将每个会话连接分离到自己的上下文线程中,从而使所有处理都在每个应用程序提供的线程中进行。
使用“多个会话,多个上下文”,设计者可以减轻“多个会话,一个上下文”模型中上下文线程的处理负担,因为所有会话都必须在 select 循环中等待处理。在这种模型中,每个会话可以分离到自己的上下文线程中,从而增强操作系统多线程提供的处理性能。然而,由于线程数量增加,这种方法需要广泛的
线程上下文切换,因此对 CPU 的负担更重,资源消耗更多。

线程模型注意事项:

  • 应用程序必须为每个上下文调用 solClient_context_timerTick()
  • 对于给定的上下文,消息 API 的文件描述符回调必须从调用该上下文的 solClient_context_timerTick() 的同一线程中调用。

上下文线程亲和性

建议

  • 使用 SOLCLIENT_CONTEXT_PROP_THREAD_AFFINITY_CPU_LIST 将 API 生成的上下文线程固定到 CPU。

当上下文线程由 C API 自动生成时,可以通过在上下文创建期间使用 SOLCLIENT_CONTEXT_PROP_THREAD_AFFINITY_CPU_LIST 参数设置上下文线程的线程亲和性。设置线程亲和性可以将特定的 CPU 专门用于处理改进,并防止上下文线程被其他进程中断。默认情况下,自动生成的上下文线程的线程亲和性未设置,允许操作系统在可用的 CPU 上最优地调度上下文线程。预期的字符串值是用逗号分隔的列表,可以是:

  • 数字 — 介于 0 和系统中 CPU 数量之间的十进制非负整数。

和/或

  • 范围 — 两个数字之间用短横线分隔。

以下示例展示了如何使用数字和范围为 CPU 列表设置线程亲和性:

const char* contextProps_simple[3] = {SOLCLIENT_CONTEXT_PROP_THREAD_AFFINITY_CPU_LIST, "0,1,2,4,8-10,13-15", NULL};

SOLCLIENT_CONTEXT_PROP_THREAD_AFFINITY_CPU_LIST 的默认值为空字符串,结果是没有设置线程亲和性。

如果应用程序自己创建上下文线程,则此属性无效。

有关更多信息和使用细节,请参阅 C API 参考和 solclient.h 头文件。


文件描述符管理

下表列出了可以在 C API 中用于处理文件描述符的管理模式。

管理模式描述
API 管理 API 文件描述符默认管理模式是 C API 内部管理自己的文件描述符事件。当应用程序的处理上下文调用 solClient_context_processEvents(...) 时,API 等待内部文件描述符事件。当至少发生一个事件或发生超时时,控制权返回到调用应用程序的处理线程。
应用程序管理 API 文件描述符应用程序管理通常由 C API 管理的事件生成逻辑。在处理上下文中,应用程序提供文件描述符事件注册和注销函数,API 使用这些函数为其文件描述符请求事件。然后应用程序负责轮询文件描述符以获取事件。当 API 的文件描述符上发生事件时,应用程序的事件生成逻辑必须调用 API 为其文件描述符注册的例程。
API 管理应用程序文件描述符应用程序在其自己的文件描述符上注册事件(如读取或写入事件),在 C API 处理上下文中。当应用程序文件描述符注册时,应用程序提供回调例程和指向应用程序数据的指针。然后应用程序调用 solClient_context_processEvents(...),或者依赖于默认管理模式下的内部 API 上下文线程,这可能导致在注册的应用程序文件描述符和 API 文件描述符上生成事件。

文件描述符限制

Linux 和 Solaris 平台上的文件描述符限制将每个进程可以管理的文件数量限制为 1,024。由于 C API 使用 select(2) 轮询设备,这些文件描述符限制阻止 API 管理任何单个文件描述符,其数值大于 1,023。

应用程序不应在每个进程中创建超过 1,023 个会话。由于应用程序打开的其他文件,可能进一步减少可以处理的会话数量。

同样,在 Windows 平台上,单个上下文不能管理超过 63 个连接到事件代理的会话。然而,与 Linux 平台上的应用程序不同,Windows 平台上的应用程序可以通过在进程中创建多个上下文来绕过操作系统限制。

注册了自己的文件描述符处理程序的应用程序(通过在 solClient_context_createRegisterFdFuncInfo_t 中提供非空函数指针)在 API 中不受限制,但它可能有自己的限制。

通过调用 solClient_context_registerForFdEvents() 将自己的文件描述符提供给 API 以进行管理的应用程序,进一步减少了可以在单个上下文(Windows 平台)或进程范围内(Linux 和 Solaris 平台)处理的会话数量。

对 Solaris/SunOS 的支持现在已废弃,最后一个版本是 v7.23.0(2022 年 9 月)。有关更多详细信息,请参阅产品生命周期策略页面上的“已废弃功能”列表。


使用提供的宏初始化数据结构

C API 为回调函数数据结构提供了静态初始化宏。使用提供的宏与配套的显式数据结构初始化代码是良好的编程实践,因为它初始化整个数据结构并为新字段提供适当的值。这确保了即使对 API 回调函数数据结构进行了更改,您的应用程序仍然可以编译而不会出现意外错误。

为以下回调函数数据结构提供了静态初始化宏:

solClient_context_createFuncInfo_t – 初始化器 SOLCLIENT_CONTEXT_CREATEFUNC_INITIALIZER
solClient_session_createFuncInfo_t – 初始化器 SOLCLIENT_SESSION_CREATEFUNC_INITIALIZER
solClient_flow_createFuncInfo_t - 初始化器 SOLCLIENT_FLOW_CREATEFUNC_INITIALIZER

例如,您可以如下初始化 solClient_session_createFuncInfo_t

solClient_session_createFuncInfo_t sessionFuncInfo = SOLCLIENT_SESSION_CREATEFUNC_INITIALIZER;
sessionFuncInfo.rxInfo.callback_p = rxLogCallbackFunc;
sessionFuncInfo.rxInfo.user_p = userPtr;
sessionFuncInfo.eventInfo.callback_p = eventLogCallbackFunc;
sessionFuncInfo.eventInfo.user_p = userPtr;

上述代码片段等同于并且优于声明中的立即初始化,如下例所示:

solClient_session_createFuncInfo_t sessionFuncInfo = {
{rxMsgCallback, userPtr},
{rxEventCallbac, userPtr},
{NULL, NULL}
};

如果不使用提供的宏进行初始化,将来对 C API 的增强和更改可能需要对您的程序进行代码更改。


内存管理

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

消息抽象

使用 C API 的应用程序使用 solClientMsg 接口,这是一个存储在内部内存缓冲区中的抽象数据结构,并为消息的每个部分提供访问器和修改器。

solClientMsg 接口提供以下功能:

  • 内部内存池,用于避免堆分配和碎片化。
  • 用于在消息的二进制数据负载(即二进制附件)中添加和获取结构化数据的函数。
  • 用于在消息的 XML 数据和用户数据负载中添加和获取非结构化数据的函数。
  • 用于添加和获取 Solace 定义和用户定义的消息头的函数。

修改全局池缓冲区大小

solClientMsg 接口是一个消息缓冲区 API,它使用堆内存分配。C API 从自己的池中分配特定大小的缓冲区,并在内部维护这些缓冲区。缓冲区从堆存储中分配,用于在应用程序空间中保存消息,直到应用程序释放它们。

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

当您调用 solClient_initialize 函数初始化 C API 时,您可以使用 SOLCLIENT_GLOBAL_PROP_DBQUANTASIZE_<0-4>(“GLOBAL_DBQUANTA_SIZE”)来指定每个池的数据缓冲区大小(以字节为单位)。

有关更多信息,请参阅 PubSub+ 消息 API C 参考。

配置消息缓冲区大小

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

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

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

  • SOLCLIENT_SESSION_PROP_SOCKET_RCV_BUF_SIZE
    订阅者数据套接字的接收缓冲区大小(以字节为单位)。默认值为 150,000。如果此属性设置为 0,则接收缓冲区大小使用操作系统默认值。

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

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

发布消息时的内存管理

为了确保在发布消息时达到高水平的操作性能,避免不必要的内存移动和复制。为了减少在移动和复制内存时使用的处理周期,请考虑以下指南:

  • 当消息负载或消息已在应用程序中存在时,使用 solClient_msg_setXmlPtrsolClient_msg_setBinaryAttachmentPtr 函数直接将负载指针设置为消息。 与之配套的 solClient_msg_setXmlsolClient_msg_setBinaryAttachment 函数会将消息复制到内部分配的内存中,只有在需要构建和保存消息时才应使用这些函数。
  • 如果主题或队列名称目标已经存在,使用 solClient_msg_setTopicPtrsolClient_msg_setQueueNamePtr

使用结构化数据类型(SDT)消息容器时,总是涉及内存复制。因此,为了在使用容器时节省内存,请考虑以下指南:

  • 如果要发送的头信息较少,用于描述大内容,考虑在 USER_PROPERTY 映射中设置头信息(通过 solClient_msg_createUserPropertyMap(...) 设置),并使用 solClient_msg_setBinaryAttachmentPtr() 添加内容。
  • 在构建容器时,始终尝试准确估计所需大小。 容器可以是用户属性映射(通过 createUserPropertyMap() 创建)、在消息中创建的映射(通过 solClient_msg_createBinaryAttachmentMap() 创建)或在消息中创建的流(通过 solClient_msg_createBinaryAttachmentStream(...) 创建)。
  • 在构建使用子映射或子流的复杂容器时,完全写入子映射或子流,并调用 solClient_container_createStream(...) 完成子映射或子流,然后向主容器中添加更多内容。

当二进制附件已在应用程序中存在时,您可以使用 solClient_msg_setBinaryAttachmentContainerPtr(...) 以避免内存复制。当消息发送时,二进制附件内容直接从应用程序内存复制到传输套接字或缓冲区。注意,当使用此函数时,修改容器或释放它引用的内存可能会导致内容损坏。

接收消息时的内存管理

回调接收到的消息缓冲区由 C API 所有,不得释放。然而,为了拥有这些消息缓冲区,应用程序可以为每个消息向 API 返回 SOLCLIENT_CALLBACK_TAKE_MSG。在这种情况下,应用程序必须在完成消息处理后调用 solClient_msg_free(...) 以释放内存。

TCP发送和接收缓冲区大小

建议

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

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

具体到 Windows 平台,接收套接字缓冲区大小必须比发送套接字缓冲区大小大得多,以防止在发送和接收消息时丢失数据。推荐的比例是发送缓冲区占 3 份,接收缓冲区占 5 份。

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

  • SOLCLIENT_SESSION_PROP_SOCKET_RCV_BUF_SIZE;默认值为 150,000 字节。
  • SOLCLIENT_SESSION_PROP_SOCKET_SEND_BUF_SIZE;默认值为 90,000 字节。

会话建立

阻塞连接

建议

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

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

阻塞连接属性为 SOLCLIENT_SESSION_PROP_CONNECT_BLOCKING

主机列表

建议

  • 作为最佳实践,使用主机列表(见下文注释)。 主机列表适用于您使用复制进行故障转移支持以及软件事件代理的主机列表高可用性(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 是复制感知型的,并且透明地处理会话重新建立。

事务性会话回调中的阻塞调用

建议

  • 允许在消息接收回调中进行阻塞调用,因为消息是从消息分发线程而不是上下文线程传递的。 事务性会话隐式地为消息传递创建了一个消息分发线程。

事务性会话的一个常见用法是从消息中消费并发布结果,并将这两个步骤作为原子步骤提交。通常情况下,不能在上下文线程中的消息回调中进行阻塞调用(即 send())。然而,对于事务性会话,API 隐式地为消息传递创建了一个消息分发线程,因此不是从上下文线程中进行的。因此,阻塞调用是在消息分发线程上进行的,而不是从上下文线程中进行的。

在创建事务性会话时,客户端应用程序可以选择拥有自己的分发线程,或者与其他使用相同上下文的事务性会话共享分发线程。

有关更多详细信息,请参阅 C API 参考中的“事务性会话”。

文件描述符限制

建议

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

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

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

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


选择阻塞模式

阻塞和非阻塞模式是可配置的会话属性参数。在创建会话时,应用程序可以配置在建立连接时、发送、订阅和取消订阅操作中使用阻塞模式还是非阻塞模式。下表列出了可用的阻塞模式会话属性。

即使为会话设置了阻塞模式,当在上下文消息接收、事件或计时器回调函数中进行调用时,也会忽略阻塞模式。在这种情况下,如果调用可以在上下文线程中立即处理,则阻塞调用成功,否则它将返回 SOLCLIENT_WOULD_BLOCK,就像非阻塞调用一样。

阻塞应用程序必须有单独的线程来处理事件。阻塞线程通过在 solClient_context_processEvent 函数中检测到的事件来解除阻塞。

参数描述
SOLCLIENT_SESSION_PROP_CONNECT_BLOCKING设置是否以阻塞或非阻塞模式连接会话。
使用 SOLCLIENT_PROP_ENABLE_VAL 以阻塞模式连接(默认值)。使用 SOLCLIENT_PROP_DISABLE_VAL 以非阻塞模式连接。默认为阻塞模式。
避免在上下文中配置多个会话时设置阻塞连接。此模式会序列化所有会话的连接过程的每个步骤,因此增加了连接时间。
SOLCLIENT_SESSION_PROP_SEND_BLOCKING设置是否使用阻塞或非阻塞发送操作。
使用 SOLCLIENT_PROP_ENABLE_VAL 以阻塞模式发送(默认值)。使用 SOLCLIENT_PROP_DISABLE_VAL 以非阻塞模式发送。
SOLCLIENT_SESSION_PROP_SUBSCRIBE_BLOCKING设置订阅/取消订阅操作是否以阻塞或非阻塞模式发生。
使用 SOLCLIENT_PROP_ENABLE_VAL 以阻塞模式订阅(默认值)。使用 SOLCLIENT_PROP_DISABLE_VAL 以非阻塞模式订阅。
SOLCLIENT_SESSION_PROP_BLOCKING_WRITE_TIMEOUT_MS在阻塞模式下发送消息或订阅/取消订阅时的超时时间(以毫秒为单位)。
如果发生超时,则返回代码为 SOLCLIENT_FAIL

阻塞模式会话属性参数

阻塞模式对发布的影响

solClient_session_sendMsg 函数调用的处理方式取决于为会话选择的阻塞模式。

  • 阻塞模式
    在这种模式下,每个 solClient_session_sendMsg(...) 函数调用的调用线程将被阻塞,直到 C API 接受消息。因此,solClient_session_sendMsg 调用自动限制为事件代理可以接受消息的速率。solClient_session_sendMsg 函数调用将保持阻塞,直到它被 C API 接受或相关联的定时器到期。

  • 非阻塞模式
    在这种模式下,无法立即被 C API 接受的 solClient_session_sendMsg 函数调用将立即向应用程序返回 SOLCLIENT_WOULD_BLOCK 错误代码。当它可以被接受时,API 会收到一个后续的 SOLCLIENT_SESSION_EVENT_CAN_SEND 事件,然后它可以重试发送请求。在此期间,它可以继续处理其他操作。

订阅管理

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

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

iOS应用程序开发

C API 的 iOS 发行版允许您创建新的或集成现有的 iOS 应用程序以用于 SolacePubSub+。本节提供了适用于 iOS 应用程序的特殊注意事项。

响应状态和连接性变化

为了获得最佳性能,应用程序应在状态发生变化或设备的网络连接发生变化时做出适当的响应。这可以确保应用程序在被操作系统置于后台时不会尝试维持与事件代理的连接。

正确响应状态和网络连接变化可以延长设备电池寿命并最小化数据连接使用。

状态变化

应用程序应在移至后台时关闭会话连接,并在返回前台时重新打开任何已关闭的连接。

因此,Solace 建议在状态转换发生时采取以下操作:

事件建议操作
applicationDidBecomeActive应用程序即将移至前台。通过调用 solClient_session_connect() 建立会话连接。
applicationWillResignActive应用程序即将离开前台。通过调用 solClient_session_disconnect() 关闭会话连接。
网络连接变化

为了获得最佳性能,应用程序应在 Wi-Fi 连接可用时优先使用 Wi-Fi 连接,而不是无线广域网(WWAN)连接,以降低数据使用量并延长电池寿命。

为了优先使用 Wi-Fi 连接,Solace 建议在网络连接变化发生时采取以下操作:

WWAN 状态Wi-Fi 状态事件建议操作
已连接未连接Wi-Fi 连接通过调用 solClient_session_disconnect() 后跟 solClient_session_connect() 强制会话重新连接。
已连接未连接WWAN 断开连接通过调用 solClient_session_disconnect() 关闭会话连接。
未连接已连接Wi-Fi 断
开连接通过调用 solClient_session_disconnect() 关闭会话连接。
未连接未连接WWAN 连接通过调用 solClient_session_connect() 建立会话连接。
未连接未连接Wi-Fi 连接通过调用 solClient_session_connect() 建立会话连接。
提供信任库以建立安全连接

为了建立与事件代理的安全连接,C API 的 iOS 发行版使用 OpenSSL,它包含在 API 中。通过 iOS 发行版的 C API 建立安全连接的过程与其他发行版相同。然而,需要为 iOS 发行版提供一些特殊考虑,以向 C API 提供信任库以验证事件代理的服务器证书。

在建立安全连接时,C API 尝试与远程事件代理建立信任。为此,应用程序必须提供包含 CA 或自签名证书的信任库的路径。可以通过以下方式之一提供信任库的路径:

  • 将静态信任库包含在应用程序包中作为资源,然后提供该位置的路径。

    例如,假设应用程序包包含一个名为 trustStore 的信任库文件夹,其中包含 .crt 文件,可以使用以下代码初始化具有此信任库的会话:

    // 将信任库的路径存储到 trustStorePath
    NSString* trustStorePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/trustStore"];
    // 必须将此路径传递给即将创建和连接的会话
    sessionProps[propIndex++] = SOLCLIENT_SESSION_PROP_SSL_TRUST_STORE_DIR;
    sessionProps[propIndex++] = [trustStorePath cStringUsingEncoding:NSASCIIStringEncoding];
  • 使用 <application_Home>/Documents/ 作为信任库,并通过文件共享使该目录对用户可用。 由于此目录最初为空,应用程序可以将默认信任库从应用程序包(如前面的项目中所示)复制到该目录中。然后,用户可以通过 iTunes 通过应用程序的文件共享功能配置信任库。

    哪种方法最佳取决于事件代理的证书签名方式。建议创建一个 CA 来签署事件代理的证书,然后将此 CA 证书包含在应用程序中。然后,应用程序设计者可以使用此 CA 为应用程序将要使用的事件代理创建证书。这种方法确保应用程序设计者可以控制应用程序信任的事件代理。

发送消息

阻塞发送

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

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

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

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

发送阻塞参数是 SOLCLIENT_SESSION_PROP_SEND_BLOCKING

批量发送

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

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

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

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

批量发送 API 调用是 solClient_session_sendMultipleMsg()

消息生存时间

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

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

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

配置尊重 TTL

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

接收消息

尽快消费消息

建议
  • 客户端应用程序不应在消息接收回调中阻塞,并应尽快消费接收到的消息,以避免接收性能下降。

为了优化消息吞吐量,应在接收后尽快消费消息。

如果从回调例程中进行 API 函数调用可能会阻塞其他线程,则客户端应用程序可以预期会收到返回失败代码。

应用程序应确保尽快返回回调,因为该 API 仅以异步模式运行。在回调例程中等待可能会导致应用程序死锁,或者至少严重降低接收性能。

处理重复消息发布

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

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

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

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

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

处理重新投递的消息

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

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

绑定到非独占队列的客户端应用程序也可能收到设置了 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 的说明,请参阅“配置每个端点允许的最大未确认消息数”。

流的数量和AD窗口大小

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

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

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

错误处理和日志记录

当会话意外终止时,可以收集错误信息并发送到应用程序。错误信息是为每个单独线程单独处理的。

日志和日志级别

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

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

处理会话事件/错误

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

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

| C

(solclient.h)描述
SOLCLIENT_SESSION_EVENT_ACKNOWLEDGEMENT已确认的最老传输的持久/非持久消息。
SOLCLIENT_SESSION_EVENT_ASSURED_DELIVERY_DOWN保证消息发布不可用。
SOLCLIENT_SESSION_EVENT_CAN_SEND发送不再阻塞。
SOLCLIENT_SESSION_EVENT_CONNECT_FAILED_ERROR会话尝试连接但未成功。
SOLCLIENT_SESSION_EVENT_DOWN_ERROR会话已建立,然后断开。
SOLCLIENT_SESSION_EVENT_MODIFYPROP_FAIL会话属性修改失败。
SOLCLIENT_SESSION_EVENT_MODIFYPROP_OK会话属性修改完成。
SOLCLIENT_SESSION_EVENT_PROVISION_ERROR端点创建/删除命令失败。
SOLCLIENT_SESSION_EVENT_PROVISION_OK端点创建/删除命令完成。
SOLCLIENT_SESSION_EVENT_RECONNECTED_NOTICE会话的自动重新连接成功,会话重新建立。
SOLCLIENT_SESSION_EVENT_RECONNECTING_NOTICE会话已断开,正在进行自动重新连接尝试。
SOLCLIENT_SESSION_EVENT_REJECTED_MSG_ERROR设备拒绝发布的消息。
SOLCLIENT_SESSION_EVENT_REPUBLISH_UNACKED_MESSAGES在成功重新连接已断开的会话后,API 在重新连接保证传输发布流时收到未知发布者流名称响应。
SOLCLIENT_SESSION_EVENT_RX_MSG_TOO_BIG_ERRORAPI 丢弃了一个超过会话缓冲区大小的接收消息。
SOLCLIENT_SESSION_EVENT_SUBSCRIPTION_ERROR应用程序拒绝订阅(添加或删除)。
SOLCLIENT_SESSION_EVENT_SUBSCRIPTION_OK订阅或取消订阅操作成功。
SOLCLIENT_SESSION_EVENT_TE_UNSUBSCRIBE_ERROR主题端点取消订阅命令失败。
SOLCLIENT_SESSION_EVENT_TE_UNSUBSCRIBE_OK主题端点取消订阅完成。
**SOLCLIENT_SESSION_EVENT_UP_NOTICE
**会话已建立。
SOLCLIENT_SESSION_EVENT_VIRTUAL_ROUTER_NAME_CHANGED在重新连接操作期间,设备的虚拟路由器名称发生了变化。

处理流事件/错误

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

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

| C

(solclient.h)描述
SOLCLIENT_FLOW_EVENT_UP_NOTICE流已建立。
SOLCLIENT_FLOW_EVENT_DOWN_ERROR流已建立,然后被设备断开,可能是因为操作员干预。
SOLCLIENT_FLOW_EVENT_BIND_FAILED_ERROR流尝试连接但未成功。
SOLCLIENT_FLOW_EVENT_SESSION_DOWN流的会话已断开。
SOLCLIENT_FLOW_EVENT_ACTIVE流已激活。
SOLCLIENT_FLOW_EVENT_INACTIVE流已停用。
SOLCLIENT_FLOW_EVENT_RECONNECTING当启用流重新连接时,而不是 DOWN_ERROR 事件,API 会生成此事件并尝试重新绑定流。
如果流重新绑定失败,API 会监控绑定失败,并在以下原因之外终止重新连接尝试:
  • 队列关闭
  • 主题端点关闭
  • 服务不可用
    有关流重新连接的更多信息,请参阅“流重新连接”。 | | SOLCLIENT_FLOW_EVENT_RECONNECTED | 流已成功重新连接。 |

错误处理函数

为了完成错误处理,请在您的事件处理代码中包含以下函数调用:

函数描述
solClient_getLastErrorInfo()返回指向 solClient_errorInfo_t 结构的指针。此数据结构包含调用线程最后捕获的错误信息。
solClient_resetLastErrorInfo()清除最后的错误信息。错误信息是按线程记录的。

子代码

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

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

最后生成的子代码是按线程记录的,可以通过应用程序线程检索。应用程序可以调用 solClient_subCodeToString() 将子代码转换为字符串。

子代码描述
SOLCLIENT_SUBCODE_INIT_NOT_CALLED由于未先调用 solClient_initialize(...),API 调用失败。
此子代码不会出现在允许在调用 solClient_initialize 之前调用的函数中。
SOLCLIENT_SUBCODE_PARAM_OUT_OF_RANGEAPI 调用使用了超出范围的参数。
SOLCLIENT_SUBCODE_PARAM_NULL_PTRAPI 调用使用了空指针或无效指针参数。
此子代码仅适用于接受指针参数的函数。
SOLCLIENT_SUBCODE_PARAM_CONFLICTAPI 调用使用了无效的参数组合。
此子代码仅适用于具有相互依赖参数的函数。
SOLCLIENT_SUBCODE_INTERNAL_ERRORAPI 调用发生内部错误(非应用程序故障)。
SOLCLIENT_SUBCODE_OS_ERROR由于操作系统调用失败,API 调用失败。
SOLCLIENT_SUBCODE_OUT_OF_MEMORY由于无法分配内存,API 调用失败。

通用子代码

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

最大重新投递

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

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

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

拒绝消息发送方在丢弃时

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

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

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