消息处理节点和解析器必须在多实例、多线程环境中工作。可以有许多节点对象或解析器对象,每个多具有许多语法元素,并可以有许多 在这些对象上执行方法的线程。消息代理设计确保消息对象和它拥有的任何对象仅由线程使用,该线程通过消息流接收和处理消息。
由服务消息流(在此定义节点)的所有线程共享和使用消息流处理节点的实例。对于解析器,仅由单个消息流线程使用解析器的实例。
用户定义的扩展名应该遵循此模型,并且应该避免使用需要信标来序列化跨越线程访问的全局数据或资源。这样的串行化会导致性能瓶颈。
用户定义的扩展实施函数必须是可重入的,并且它们调用的任何函数必须同样是可重入的。所有用户定义的扩展实用程序函数都似乎完全可重入的。
尽管用户定义的扩展可以衍生其他线程(如果必需),对于同一个线程将控制权返回到有关实施函数的完 成的代理这是必不可少的。如果此操作失败则将损害代理的完整性并会产生不可预测的结果。
以下消息流示例会帮助您理解在设计和实现用户定义的节点时应该注意的一些线程技术注意事项。它设想了一个用户定义的输入节点的示例。
消息流可以配置为在一组线程上运行。这由消息流中的一些输入节点,以及消息流的附加实例属性的值确定。这两个元素确定消息流可以使用的线程池的最大容量。因此,如果消息流具有指示单个线程执行的特殊处 理需求,则您需要确保就是这种情况。
使用 additionalInstances 属性来允许代理启动其他单独线程中的消息流的副本是防止输入队列成为瓶颈的最有效的方法。然而,创建单独线程允许消息队列的消息并行处理,因此您应该仅当处理的命令消息不重要的时候才使用。
线程作为输入节点构建和操作的结果创建。 线程在线程池中保持活动或空闲,且空闲线程保持在池中,直到输入节点分派它们,或关闭代理。
下图描述消息流中的线程分配。
消息流中的线程分配
最初,Thread 1 被需求(A),并等待消息。当消息到达(B),Thread 1 传播消息,并分派 Thread 2。Thread 2 立即接收消息(C),并传播和分派等待消息(D)的 Thread 3。Thread 1 完成(E),并返回到线程池。然后 Thread 3 接收消息(F),分派 Thread 1,并传播消息。现在 Thread 1 等待消息以到达队列(G)。
应当注意标记为 H 的点。在消息流的这个实例,Thread 1 获取消息,但因为所有其他线程在那时是活动的,所以它无法分派。则消息被传播。
在已传播此消息之后,Thread 2 完成(I),从队列接收新消息,并传播此新消息。然后 Thread 3 完成(J),并返回到池。然后 Thread 2 也完成(K)。因为它不分派,当 Thread 1 完成(L)时,它无法返回到线程池,即使队列上没有消息,相反它等待消息以到达队列。