ZeroC ICE的远程调用框架 ThreadPool

ThreadPool提供Reactor/Proactor服务,并且强偶合了Reactor(反应器)/Proactor(前摄器)。不同于Reactor/Proactor使用线程池
进行事件处理的设计。如ACE框架的ACE_TP_Reactor。
同时ThreadPool提供一个共享的工作分派队列,可以用作Half-Async/Half-sync并发模式的线程池。
ThreadPool为池中每个线程定制了一至的线程循环,运行在池中的线程者必须进行这个循环,接受线程池的统一控制。
此外ThreadPool还强偶合了每一个事件处理类,线池程的相关控制也偶合进事件处理程序中去。

ThreadPool组件包含下面的协作类:
ThreadPoolCurrent,是ThreadPool和EventHandlerThread的一个关联
EventHandlerThread,运行在ThreadPool的线程。
Selector,反应器(当不使用IOCP),或前摄器(当使用IOCP)。
ThreadPoolWorkQueue,工作分派队列,优先级别比网络IO事件低。使用ThreadPool进行dispatch的任务都入队到ThreadPoolWorkQueue


EventHandler,定义了可以在线程池执行的事件处理程序的抽象接口。
ThreadPool组件只处理(或者说提供现成的)三种具体的事件:
ThreadPoolWorkQueue,工作队列消费事件。
ConnectionI,网络连接上读写事件,它的事件处理程序强偶合了Ice协议。
IncomingConnectionFactory,被动网络连接事件。
这里要指出ThreadPool还强偶合了上面每一个事件处理类,线池程的相关控制也偶合进事件处理程序"EventHandler::message"中去。

下面还会进一步说明。

相信这时你一定很想听到ThreadPool使用了高性能的Leader/Followers并发模式了。但是要清楚不是使用了LF模式就代表你的软件就高

性能了,LF模式虽然说高性能但也不是什么情况都使用,LF模式是设计用来解决某些模式在某些特定情况的问题的。在Ice的

ThreadPool组件设计中,在使用IOCP前摄器的版本就没有使用LF模式。为什么?因为LF模式不是最佳的选择,在使用IOCP的前摄器没有

LF模式针对解决的问题发生,反而帮倒忙。
ThreadPool组件在使用Reactor(反应器)的版本使用Leader/Followers并发模式,而在Proactor(前摄器)的版本不使用

Leader/Followers并发模式。

ThreadPool组件使用单方法分派接口策略,同一句柄的所有事件类型的事件处理集中在一个单独的事件处理方法。而ACE的设计则是使

用多方法分派接口策略,同一句柄不同事件类型的事件处理分别对应特定的事件处理钩子方法。ThreadPool组件使用单方法分派接口策

略,将具体的事件类型分派交给事件处理方法内进行,而不是由Reactor(反应器)或Proactor(前摄器)最终决定。

EventHandler是一个抽象类,虽然定义了事件处理抽象方法message,却同时也定义事件状态。
事件类型包含Read,Write等,由SocketOperation枚举类型来定义。
EventHandler定义许多由SocketOperation组成的状态。
用于控制的状态:_registered,_ready,_disable。
_registered:一个具体的事件处理类关联到ThreadPool(反应器或前摄器强偶合在ThreadPool)。
_ready:从反应器或前摄器接收事件的逻辑之外,对事件进行激活。
_disable:使用反应器版本的ThreadPool的动态控制。
用于前摄器异步操作状态:_pending,_started,_completed。
_pending:
_started:发起了一次异步I/O操作
_completed:发起了的一次异步I/O操作完成了
此外,EventHandler不保存由反应器或前摄器接收到事件的接收状态,而是在ThreadPool的map容器中,由线程池线程共享。

ThreadPool将事件处理程序分为两阶段,第一阶段是IO,第二阶段是User。在使用Reactor(反应器)版本中,网络指示事件通知了句

柄不再阻塞,可以放心进行同步I/O操作,所以事件处理程序首先要对指示事件进行响应进行同步I/O操作,然后才是事件用于I/O以外

的操作,也就是真正的dispatch。在使用Proactor(前摄器)版本中,事件处理程序并不包含第一阶段,因为异步I/O操作由系统进行

了,并且事件是异步I/O操作的完成事件。ThreadPool统计这两个阶段的事件处理状态的线程数量,用来对线程池整体的控制。
ThreadPool使用_inUseIO对事件处理进行IO阶段的线程统计,_inUse对事件处理进行dipatch阶段的线程统计。
_inUseIO统计数决定是否要进行Leader的推选(promote),只有在反应器版本有效。每次线程以leader身份取出一个事件处理对象,

对相关事件进行同步I/O之前,都要参加统计计数即加1。在完成同步I/O之后,释放统计计数即减1,并根据统计是否要推选。因为反应

器不希望有句柄进行同步I/O的同时而进行阻塞等待句柄的事件。当还有已得事件未处理时,则不用理会这个统计计数而进行推选唤醒

线程处理已得事件。
_inUse统计数决定是否要新增线程,如果正在dispatch的线程数量已经到达了当前ThreadPool拥有的线程数目,还未到达最大限制数目

的时候,必须新增线程。
因为ThreadPool组件在使用LF模式的版本中,引入了_inUseIO统计数来控制竞选的进程(progress,是否允许新一轮的竞选)。并且这

个_inUseIO统计数的功能强偶合进了事件处理类(EventHandler的继承类)的事件处理程序(EventHandler::message)中,事件处理

程序必须对_inUseIO统计数进行释放,如果这个_inUseIO统计数发生了意外,你的LF竞选可能就永远不会进行新一轮的选举,以至候选

线程不可能被唤醒。如果你不明白这一点,去添加自己的事件处理类的话,就可能因没有在事件处理程序中释放这个_inUseIO统计数而

惹上麻烦。虽然Ice在ThreadPool的线程循环里,在事件处理程序执行之后小心地照料了这个_inUseIO统计数,但还是有可能事件处理

程序异常等其它原因,使得你没的程序运行没有按照Ice设计的流程进行,只要有一次_inUseIO统计数没有被正确释放,你的线程池就

会惹上麻烦。
此外,ThreadPool引入的另一个用于线程池控制的统计数_inUse,同样也强偶合在事件处理程序。并且由这个_inUse统计数感知压力而

新增线程的工作也是偶合在事件处理程序。当这个统计数发生错乱后,你可以失去以下便利。尽管你的线程池线程数目没有达到上限,

当前可用线程都用尽了,但也不能新添加线程。而你池中有线程因为异常面退出了,线程池规模缩小,却不能新添加线程补充。

由于ThreadPool引入了上面一些统计数来控制线程池,因此线程池的线程对象EventHandlerThread有下面几种状态:Idle,ForIO,

ForUser等。当线程在上面的状态切换时,都要通知相关的observer。ThreadPool的线程执行的整个流程中,还有许多的事件监视点,

即有许多各类的Observer,这将分开一篇独立写。

ThreadPool提供默认dispatch策略,以及支持自定义dispatcher策略。默认dispatch策略,事件处理执行在当前线程。自定义

dispatcher策略则由用户自定义,并在Ice环境Communicator创建时指定。

时间: 2024-11-03 22:51:03

ZeroC ICE的远程调用框架 ThreadPool的相关文章

ZeroC ICE的远程调用框架 AMD

继上一篇<ZeroC ICE的远程调用框架>,本篇再来说其中的AMD. 当在ice文件中声明某个接口方法Method为["amd"]后,接口方法在stub类生成的远程调用框架代码不会变,但在skeleton类生成的就不是_iceD_Method和Method,而是_iceD_Method和Method_async.而amd模式和非amd模式的代码生成模板区别在于,_iceD_Method调用Method_async代替Method,并且在调用后不进行out方向参数的处理.另

ZeroC ICE的远程调用框架 ASM与defaultServant,ServantLocator

ASM与defaultServant,ServantLocator都是与调用调度(Dispatch)相关的. ASM是ServantManager中的一张二维表_servantMapMap,默认Servant则由_defaultServantMap和_locatorMap两张一维表维护.一个对 象可由这样的字符串指定"Category/Identity -f Facet".ASM是根据Identity和Facet对进行查找,而defaultServant和 ServantLocator

Hessian轻量级二进制远程调用框架

Hessian轻量级二进制远程调用框架 Hessian是一个轻量级的二进制远程调用框架,官方文档地址,它主要包括Hessian远程调用协议.Hessian序列化协议以及客户端服务端代理等几部分,关于Hessian协议可以看下另外一篇文章Hessian远程调用及序列化协议.Hessian远程调用框架构建在Http协议之上,下面是示意图. 下面这个图是一次远程调用的过程 其中步骤3.4.5.6是核心过程,还要细化下, 步骤3:将远程方法调用转换为hessian调用,具体为,客户端首先要先和服务器端建

ZooKeeper伪分布集群安装及使用 RMI+ZooKeeper实现远程调用框架

使用 RMI + ZooKeeper 实现远程调用框架,包括ZooKeeper伪集群安装和代码实现两部分.  一.ZooKeeper伪集群安装: 1>获取ZooKeeper安装包 下载地址:http://apache.dataguru.cn/zookeeper 选择一个稳定版本进行下载,我这里下载的是zookeeper-3.4.6版本. 2>ZooKeeper伪分布式集群安装 伪分布式集群:在一台Server中,启动多个ZooKeeper的实例. 3>上传并解压安装包 4>创建实例

alibaba远程调用框架dubbo原理

alibaba有好几个分布式框架,主要有:进行远程调用(类似于RMI的这种远程调用)的(dubbo.hsf),jms消息服务(napoli.notify),KV数据库(tair)等.这个框架/工具/产品在实现的时候,都考虑到了容灾,扩展,负载均衡,于是出现一个配置中心(ConfigServer)的东西来解决这些问题. 基本原理如图: 在我们的系统中,经常会有一些跨系统的调用,如在A系统中要调用B系统的一个服务,我们可能会使用RMI直接来进行,B系统发布一个RMI接口服务,然后A 系统就来通过RM

使用 RMI + ZooKeeper 实现远程调用框架

在 Java 世界里,有一种技术可以实现"跨虚拟机"的调用,它就是 RMI(Remote Method Invocation,远程方法调用).例如,服务A 在 JVM1 中运行,服务B 在 JVM2 中运行,服务A 与 服务B 可相互进行远程调用,就像调用本地方法一样,这就是 RMI.在分布式系统中,我们使用 RMI 技术可轻松将 服务提供者(Service Provider)与 服务消费者(Service Consumer)进行分离,充分体现组件之间的弱耦合,系统架构更易于扩展. 本

RPC远程调用框架rsf和dubbo

1.rsf(Remote service framework)框架整体的架构 思考点: 1.注册中心使用的zookeeper,多机房部署,各注册中心要求数据一致,如何在一个节点发生异常情况下,不影响其他节点? 服务发现模块会定时的将最新的服务提供方列表刷新到注册中心,如PUMP定时的将提供方的接口列表写入到注册中心.注册中心考虑到 ZK 的优势.局限和 Redis 优势,通过 Pump 定时批量刷新数据到 ZK 集群,减少 ZK 写入压力:通过 Redis 集群管理提供方上下线,由 Pump 订

自研发RPC调用框架

自主研发设计RPC远程调用框架,实现服务自动注册,服务发现,远程RPC调用,后续实现服务负载均衡 主要包括:客户端服务,服务端,服务发现,服务注册 github地址:https://github.com/btshoutn/rpc_project 原文地址:https://www.cnblogs.com/shoutn/p/8297345.html

【PHP】远程调用以及RPC框架

前言 一个项目,从开始到版本更新,一直到最后的版本维护.功能在不断增多,对应的代码量也在不断增加,也就意味着项目变得更不可维护,这时候,我们需要用拆分的方式将一个项目打散,以便开发团队更好的对项目进行维护. 分模块 这个阶段,一般也是项目的初级阶段,由于人手不够,一个服务端的接口项目只有一个开发进行维护,根据开发的习惯,会把项目分成若干个模块进行开发,在一个项目下进行部署. 这样做的缺点在于项目会随着版本更新而变得不可维护. 分项目 随着每个模块功能的不断完善,代码变得更加臃肿.这时候需要对项目