ICE系列培训(三)_第1页
ICE系列培训(三)_第2页
ICE系列培训(三)_第3页
ICE系列培训(三)_第4页
ICE系列培训(三)_第5页
已阅读5页,还剩157页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

ICE系列培训(三),鲍齐权qqbao统一网管平台专项,培训内容,1.ICE运行时2.动态ICE3.连接管理4.FreezeMap,1.1概述,本章主要介绍ICE运行时的一些内部细节:通信器ICE上下文对象适配器连接超时对象标识单向、数据报、批量调用Current对象定位服务Servant自动重试ICE线程模型字符串编码代理插件开发,1.ICE运行时,1.2通信器,通信器Ice:Communicator是Ice运行时的主入口点,其实例与一些运行时资源关联在一起:客户端线程池对象适配器服务端线程池配置属性对象工厂日志记录器统计对象缺省路由器缺省定位器插件管理器,1.ICE运行时,1.2通信器,注意:不同通信器的对象适配器以及ICE对象是相互独立的;不同通信器使用其自己的线程池,互不干扰;通常情况下,一个服务器中使用一个通信器即可;某些情况下多通信器更有用:例如,IceBox对每个Ice服务使用不同的通信器这样保证不同服务间互不干扰;,1.ICE运行时,1.2通信器,通信器接口:moduleIcelocalinterfaceCommunicatorstringproxyToString(Object*obj);Object*stringToProxy(stringstr);通信器提供大量接口来操作它所关联的运行时资源,后面介绍各个资源时再介绍。,1.ICE运行时,1.3通信器的初始化,在创建通信器时,ICE运行时初始化了一系列通信器特性,这些特性影响通信器的行为;注意:这些特性生命期随通信器生命期一起,通信器创建后就不能更改了。因此,你必须在创建通信器时设置好这些特性;初始化使用Ice:initialize函数:namespaceIceCommunicatorPtrinitialize(int,1.ICE运行时,1.3通信器的初始化,通信器创建时可以定制下列特性:属性集合日志接口状态接口宽/窄字符集转换器线程通知hook分派器类装载器(仅java),1.ICE运行时,1.3通信器的初始化,要建立这些特性,你需要填充一个InitializationData结构:namespaceIcestructInitializationDataPropertiesPtrproperties;LoggerPtrlogger;StatsPtrstats;StringConverterPtrstringConverter;WstringConverterPtrwstringConverter;ThreadNotificationPtrthreadHook;DispatcherPtrdispatcher;;,1.ICE运行时,示例:运行时属性集可从文件中读取InitializationDatainitData;initDperties=createProperties();initDperties-load(configFile);,1.3通信器的初始化,例如:定制一个类型为MyLogger的日志记录器Ice:InitializationDataid;id.logger=newMyLoggerI;Ice:CommunicatorPtric=Ice:initialize(argc,argv,id);,1.ICE运行时,例如,我们的总线客户端的通信器是在下面所示函数内初始化的:voidCBusClientForRpcClient:ClientInit(conststring,1.4对象适配器,对象适配器是位于Ice运行时和实际服务提供对象(Servant)之间,实现以下职责:(1)将Ice对象(培训一中有介绍什么是Ice对象和Servant)映射到Servant,并分派请求给Servant;(2)辅助Ice对象和Servant的生命期操作;(3)提供传输端点;注意:每个通信器可拥有一个或多个对象适配器;每个对象适配器可拥有一个或多个Servant;每个对象适配器可拥有多个传输端点,这些传输端点代表的是通向同一组对象的不同路径;每个对象适配器可非强制性拥有自己的线程池(通过.ThreadPool.Size设置)如果设置了适配器自己的线程池,那么给对象适配器的分配操作将使用自己线程池中的线程,而不会使用通信器中的线程池。,1.ICE运行时,1.4.1活动Servant映射表,每个对象适配器都维护有一个活动Servant映射表(ASM)。对于收到的客户端请求,对适配器会根据对象标识到ASM中查找正确的Servant来分派这个请求。注意:如果客户端请求包含的对象标识无法在ASM中找到正确的Servant,则适配器会返回ObjectNotExistException异常给客户端(适配器没有使用Servant定位器时),1.ICE运行时1.4对象适配器,1.4.2Servants,Servant是位于服务端,为客户端提供实际服务业务的Ice对象实例,它使用具体语言实现Ice接口。注意:同一Servant可注册给多个对象适配器;Ice:ObjectAdapterPtradapter=communicator()-createObjectAdapter(Hello);/ServantDemo:HelloPtrhello=newHelloI;adapter-add(hello,communicator()-stringToIdentity(hello);adapter-activate();,1.ICE运行时1.4对象适配器,HelloI实现的接口Slice定义:moduleDemointerfaceHelloidempotentvoidsayHello(intdelay);voidshutdown();接口实现:classHelloI:publicDemo:Hello;,1.4.3对象适配器的创建及提供的接口,对象适配器的创建:使用通信器提供的方法::Ice:ObjectAdapterPtrcreateObjectAdapter(const:std:string,1.ICE运行时1.4对象适配器,示例:Ice:ObjectAdapterPtradapter=ic-createObjectAdapterWithEndpoints(SimplePrinterAdapter,default-p10000);,1.4.4Servant的激活与解除激活,Servant激活:告知Ice运行时某个Servant的存在(加入ASM)。解除激活:将Servant从ASM中移除。对象适配器提供了add函数来激活和解除Servant:Object*add(Objectservant,Identityid);Object*addWithUUID(Objectservant);voidremove(Identityid);,1.ICE运行时1.4对象适配器,1.4.4Servant的激活与解除激活,注意:不允许使用同一标识激活多次;不同标识可用来激活同一Servant多次,这种情况下同一Servant将表现为多个Ice对象;addWithUUID会自动为Servant生成一个UUID作为对象标识。Demo:HelloPtrhello=newHelloI;/Servantadapter-add(hello,communicator()-stringToIdentity(hello);,1.ICE运行时1.4对象适配器,1.4.5对象适配器状态,对象适配器具有三种状态:保持状态(holding);活动状态(active);不活动状态(inactive)。,1.ICE运行时1.4对象适配器,1.4.5对象适配器状态,保持状态(holding)处于保持状态的适配器,任何到来的请求客户端都会收到TimeoutException或ConnectTimeoutException异常。注意:出于保持状态下面向流协议的适配器(TCP/IP),服务端运行时会停止从相应的传输端点读数据,同时也会拒绝客户端的连接请求。对象适配器提供的操作:voidIce:ObjectAdapter:hold();该操作或将适配器置为hold状态,但是不会等待已有请求的完成,会立即返回。voidIce:ObjectAdapter:waitForHold();该操作会阻塞调用线程,知道所有已有请求完成。,1.ICE运行时1.4对象适配器,1.4.5对象适配器状态,活动状态(active)活动状态下的适配器可以接收并分派请求到servant注意:新创建的适配器初始化状态是Holding状态;双向连接的适配器不需要激活(双向连接参见1.7);并置调用(collocatedinvocation)即使适配器没有激活调用也会成功,除非你禁用了并置调用优化。对象适配器提供的操作:voidIce:ObjectAdapter:activate();激活对象适配器,例如:Ice:ObjectAdapterPtradapter=communicator()-createObjectAdapter(Hello);Demo:HelloPtrhello=newHelloI;/Servantadapter-add(hello,communicator()-stringToIdentity(hello);adapter-activate();/激活适配器,1.ICE运行时1.4对象适配器,1.4.5对象适配器状态,不活动状态(inactive)不活动状态下,适配器在概念上已经被销毁了。一旦适配器处于不活动状态,那么它将不能再被重新激活对象适配器提供的操作:voidIce:ObjectAdapter:deactivate();voidIce:ObjectAdapter:waitForDeactivate();voidIce:ObjectAdapter:isDeactivated();voidIce:ObjectAdapter:destroy();,1.ICE运行时1.4对象适配器,1.4.6Endpoints(端点),Endpoints:由:传输协议、主机名、端口组成;格式:protocalhhostaddr1pportNo.:protocalhhostaddr2pportNo.一个对象适配器维护有两组传输端点:一组用于对象适配器监听新的连接物理传输端点;一组嵌入适配器创建的代理对象中,用于客户端通信使用(发布端点)publishedendpoints;(大多数情况下,这两组端点是一样的,但它们是可以独立配置的),1.ICE运行时,1.ICE运行时1.4对象适配器,1.4.6Endpoints(端点),物理端点(PhysicalEndpoints)物理传输端点是对象适配器用来接收客户端请求用的;可由:name.Endpoints属性或createObjectAdapterWithEndpoints方法进行设定;例如:Hello.Endpoints=tcp-p10000:udp-p10000:ssl-p10001注意:(1)如果端点中定义了主机名,那么对象适配器只能监听该网络接口上的请求;(2)如果端点中未定义主机名,但是属性Ice.Default.Host定义了,那么对象适配器将监听Ice.Default.Host定义的主机名的端点;(3)如果上面两者都未定义,那么对象适配器将监听所有网络接口;,1.ICE运行时,1.ICE运行时1.4对象适配器,1.4.6Endpoints(端点),注意:(4)如果要强制对象适配器监听所有网络接口,则可定义端点中的主机名为或*;(5)如果端点中指定了主机名,但是没有指定回环接口,那么回环接口是不会被监听的,必须加入后才能监听回环接口:MyAdapter.Endpoints=tcphp9999:tcphp9999(6)如果端点中没有指定端口,则对象适配器会使用系统选择的一个端口进行监听(该端口不固定);,1.ICE运行时,1.ICE运行时1.4对象适配器,1.4.6Endpoints(端点),发布端点(PublishedEndpoints)对象适配器创建代理对象时,需要发布它的传输端点,但是它不能发布它的物理传输端点;例如,一个服务运行在防火墙后的私有网络内,它的适配器的物理端点使用的是私有网络的地址,如果将物理端点发布在对象适配器创建的代理中,那么公共网络中的客户端是无法使用的。这种情况下适配器必须在它的代理中发布能够指引客户端通过防火墙的端点。使用name.PublishedEndpoints配置发布端点;如果未配置上述属性,则适配器会使用物理端点(注意:除非只有回环接口,否则回环接口不会被发布)例如:MyAdapter.Endpoints=tcphp9999MyAdapter.PublishedEndpoints=tcphcorpfwp25000表示连接在主机名为corpfw端口25000的客户端会被转发到适配器的物理端点上(私有网络上的端点)。,1.ICE运行时,1.ICE运行时1.4对象适配器,1.4.6Endpoints(端点),发布端点还可用于多服务负载平衡时:假设有两个状态无关的服务运行在两个不同的主机上以实现负载平衡。一般情况下客户端使用的代理对象的端点同时包含两个服务的端点,这样Ice运行时可以随机选择一个服务建立连接。但是,如果客户端调用了服务的某操作后,该操作返回了一个该服务的代理对象。那么,客户端使用这个返回代理对象调用操作时Ice运行时会始终选择返回该代理的服务端(因为返回的代理中只有创建它的服务端端点),无法负载平衡。这种情况下要负载平衡可以配置服务端适配器的发布端点,让发布端点同时包含两服务:(假设两个服务一个叫sun1,一个叫sun2),1.ICE运行时,1.ICE运行时1.4对象适配器,对于sun1MyAdapter.Endpoints=tcphsun1p9999MyAdapter.PublishedEndpoints=tcphSun1p9999:tcphSun2p9999对于sun2MyAdapter.Endpoints=tcphsun2p9999MyAdapter.PublishedEndpoints=tcphSun1p9999:tcphSun2p9999,1.4.6Endpoints(端点),刷新端点主机的网络接口列表可能会动态改变。例如,笔记本电脑从一个无线网络移动到另外的无线网络时。这时对象适配器提供刷新网络接口列表的方法:localinterfaceObjectAdaptervoidrefreshPublishedEndpoints();/;注意:刷新时机由应用程序自己决定;只有那些发布端点中没有配置主机地址或配置的是全部地址(*或)的对象适配器,该刷新才有效。,1.ICE运行时,1.ICE运行时1.4对象适配器,1.4.6Endpoints(端点),端点的超时属性作为防备恶意客户端的手段,建议在物理端点中指定超时时间。超时会影响那些Ice运行时通常不希望长时间阻塞的任务:向socket写回应消息或等待SSL磋商完成等。如果没有设置超时,那么运行时会无限等待。这样一些恶意客户端就能够大量消耗资源。端点中使用-t参数设置超时MyAdapter.Endpoints=tcpp9999t5000,1.ICE运行时,1.ICE运行时1.4对象适配器,1.4.6Endpoints(端点),路由器如果对象适配器配置了路由器,那么适配器的发布端点会影响路由器(详见手册42章关于如何使用路由器配置对象适配器),1.ICE运行时,1.ICE运行时1.4对象适配器,1.4.7创建代理,对象适配器接口提供多种方法创建代理对象(不管对象id表示的Servant是否是激活的):Object*ObjectAdapter:createProxy(Identityid);它返回直接代理还是间接代理由对象适配器的配置决定。如果对象适配器是用的适配器id,则返回间接代理;如果对象适配器没有id,那么该接口使用适配器发布端点返回一个直接代理;Object*ObjectAdapter:createDirectProxy(Identityid);该接口直接返回一个直接代理,代理端点为适配器的发布端点;Object*ObjectAdapter:createIndirectProxy(Identityid);返回一个间接代理。直接代理其内部保存有某个对象的标识,以及它的服务器的运行地址;间接代理其内部保存有某个对象的标识,以及对象适配器名(objectadaptername)。,1.ICE运行时,1.ICE运行时1.4对象适配器,1.4.8使用多对象适配器,一个典型的服务实现很少需要使用多个对象适配器。如果你正考虑使用多个对象适配器,则建议你考虑是否用到了下列情况:(1).你需要一个细粒度的对象访问控制。例如,已可能有一个适配器仅绑定一个安全的端点来限制访问一些管理对象,其它的适配器绑定到非安全端点上用来访问其它对象;(2).针对不同集合的对象,你需要控制线程池中线程数量。例如:某个特定的适配器上的对象不需要并发处理,而是设置多个适配器,每个适配器有各自的线程池,这样可用于解决死锁;(3).你需要针对某一组对象能够暂时禁用它上面的新请求。这可以通过设置相应的对象适配器的状态为Holding来完成;(4).当你在Glacier2中使用一个Ice路由器时能设置不同请求路径时可以考虑使用多适配器;如果你的应用中没有上述情形,则你无须使用多适配器;,1.ICE运行时,1.ICE运行时1.4对象适配器,1.5对象标识(ObjectIdentity),每一个Ice对象都有如下定义的一个对象标识:moduleIcestructIdentitystringname;stringcategory;标识可以用字符串标示:category/name通信器成员函数:stringidentityToString(Identityid);IdentitystringToIdentity(stringid);可用于相互转换,1.ICE运行时,示例:Demo:HelloPtrhello=newHelloI;/Servant/下面用“hello”字符串作为对象标识adapter-add(hello,communicator()-stringToIdentity(hello);,1.6Ice:Current对象,每个传给服务端骨架操作的最后一个参数都是Ice:Current对象;moduleIcelocaldictionaryContext;enumOperationModeNormal,Nonmutating,Idempotent;localstructCurrentObjectAdapteradapter;分派该操作的对象适配器Connectioncon;接收该请求的连接信息Identityid;处理当前请求的对象标识stringfacet;(参见33章)stringoperation;当前调用的操作名OperationModemode;调用模式Contextctx;当前调用上下文(参见手册32.12)intrequestId;请求ID用来关联请求和请求回应;,1.ICE运行时,例如:virtualvoidsayHello(:Ice:Int,const:Ice:Current,1.7Servant定位器,Servant定位器是一个注册到对象适配器的对象,当到来的请求在ASM中无法找到正确的Servant时,会调用该定位器对象,由定位器对象返回一个正确的Servant来处理这个请求。没有定位器时:(1)每个Ice对象都由一个不同的Servant表示;(2)每个Ice对象对应的Servant必须驻留在内存中;如果Servant的数量很多时,会面临内存压力问题;使用Servant定位器:允许Servant实例数量小于Ice对象数量;运行时由定位器根据需要实例化Servant。,1.ICE运行时,1.7Servant定位器,要实现定位器,你需要继承自Ice:ServantLocator,并实locate,finished,deactivate方法。如果到来的请求在ASM中找不到Servant,则Ice运行时会掉调用locate方法;locate返回一个Servant,Ice运行时会将请求分派给该Servant,请求完成后,Ice运行时会调finished;定位器销毁时deactivate会被调用。,1.ICE运行时,1.7Servant定位器,定位器的线程保证:同步操作Ice运行时保证,调用locate和finished是配对的且在同一线程内的;异步分派的操作Ice运行时不保证调用locate和finished是同一线程;如果在locate和finished中访问了共享数据,则必须加以保护。,1.ICE运行时,1.7Servant定位器,向对象适配器注册一个定位器:使用对象适配器方法voidaddServantLocator(ServantLocatorlocator,stringcategory);进行注册。注意:必须提供category实参,它决定这个定位器要负责的Ice对象标识(那些对象标识与定位器category匹配的Ice对象才会触发该定位器的locate调用),1.ICE运行时,1.8缺省Servants,还可以使用缺省Servants来解决Servants量很大的问题。它根据请求对象标识的不同改变其行为。可通过对象适配器方法:voidaddDefaultServant(Objectservant,stringcategory);添加缺省Servant。注意:缺省Servant需要保护共享数据。,1.ICE运行时,1.9服务器实现技术,1.9.1渐进初始化如果使用了Servant定位器,locate方法返回的Servant实例只能用于当前请求(Ice运行时不会将该Servant加入ASM)。所以,Servant的初始化是分散到每次请求调用中的,而不是服务启动时(也可以在locate首次实例化了某Servant后将其加入ASM,这样后面的调用请求不再需要初始化该Servant了);内存需求也有所降低,因为只有那些被访问到的Servant才会被实例化。,1.ICE运行时,1.9服务端实现技术,1.9.2缺省Servants针对不同的请求,缺省Servant能充当不同角色。根据访问对象标识改变其行为。例如:我们目前的服务端中使用了类似的实现方式(虽然不是使用的缺省Servant,但只使用了一个Servant对象来处理所有请求)boolCBusClientForRpcServer:RegisterRpcService(conststring,1.ICE运行时,1.9服务端实现技术,1.9.3混合途径及缓存(HybridApproachesandCaching)即:一些性能要求较高的Servant可直接加入ASM中,其它不经常访问的对象可通过缺省Servant实现。,1.ICE运行时,1.9服务端实现技术,1.9.4Servant逐出器(ServantEvictors)逐出器是维护有servants缓存的servant定位器。只要有请求到达(也就是说,Iceruntime调用了locate),逐出器就在它的缓存中检查,看是否能找到可用于该请求的servant。如果有,它就返回这个已经在缓存中实例化的servant;否则,它实例化一个servant,并把它增加到缓存中。缓存是一个按照“最近最少使用”(LRU)顺序维护的缓存:最近最少使用的servant处在队列的尾部,最近使用最多的servant处在队列的头部。如果某个servant被从缓存中返回,或增加到缓存中,它就会从当前的队列位置移到队列头部,也就是说,“最新”的servant总是在头部,“最老”的servant总是在尾部。队列的长度可以配置,并决定会有多少servant存放在缓存中;如果针对某个Ice对象的请求在内存中没有对应的servant,且缓存满了,逐出器就会在队尾移除最近最少使用的servant,给要在队头实例化的servant腾出空间。,1.ICE运行时,1.9服务端实现技术,1.9.4Servant逐出器(ServantEvictors)逐出器的实现和实现定位器一样,只是该定位器中需要增加一个“最近最少使用”(LRU)队列来缓存实例化过的Servant。classEvictorBase:publicIce:ServantLocator/;,1.ICE运行时,1.10Ice线程模型,Ice天生是一个多线程平台,不存在单线程的Ice服务。你必须考虑并发问题。例如:请求可能被并发分派。,1.ICE运行时,1.10.1线程池介绍,每个通信器创建两个线程池:1.客户端线程池;(响应请求回应、AMI回调)2.服务端线程池;(分派请求、双向连接回应)缺省情况下,这两个线程池被通信器的所有适配器共享。如果需要,你可以为某个对象适配器配置私有的线程池;如果线程池中线程耗尽,则新的请求会被阻塞;默认池中线程数为1.,1.ICE运行时1.10ice线程模型,1.10.2配置线程池,每个线程池有一个唯一的名字;有下列属性可设置:Name.SizeName.SizemaxName.SizeWarnName.StackSizeName.SeralizeName.ThreadIdleTime客户端和服务端线程池的名字为:Ice.ThreadPool.Client和Ice.ThreadPool.Server如果要监视线程池活动情况,可打开属性Ice.Trace.ThreadPool属性。动态线程池:由name.Size、name.Sizemax、name.ThreadIdleTime决定(Ice运行时会按这些参数配置,动态决定池中的线程数),1.ICE运行时,1.ICE运行时1.10ice线程模型,例如,我们的Ice服务配置如下:,1.10.3适配器线程池,缺省情况下对象适配器和通信器是共享线程池的。但是,在某些特殊情况下配置对象适配器自己的线程池也是很有用的:1.当对象适配器的并发要求和通信器的并发要求不一致时;2.确保对某个适配器的Servants的请求分派有最小数量的线程可用;使用adapter.ThreadPool属性进行配置:adapter.ThreadPool.Sizeadapter.ThreadPool.SizeMax,1.ICE运行时,1.ICE运行时1.10ice线程模型,1.10.4设计考虑,不合理的线程池配置可能会导致严重的性能问题。设计应用时可考虑如下一些问题:(1)只有1个线程的线程池的影响.一次只有一个消息能被分派。这样做可以不必考虑线程安全问题,但是它消除了可能的并行分派,这在多CPU的系统上将带来性能瓶颈;.一次仅有一个AMI响应可被处理。你必须增加客户端线程池大小以便多个AMI回调能并发处理;.嵌套双向调用将受到限制。单线程的情况下最多只能有1层双向嵌套调用;需要注意:通信器的客户端和服务端线程池缺省的最大线程数是1个,使用时需要注意。,1.ICE运行时,1.ICE运行时1.10ice线程模型,1.10.4设计考虑,(2)使用多线程的线程池当配置线程池支持多个线程时也就意味着ICE运行时会并发的分派操作调用以及并发的处理AMI回调。这虽然会增加线程安全问题上的设计考虑,但是它所带来的可伸缩性和更高的吞吐量也是显而易见的。具体线程池最大线程数是多少也是需要精心考虑的。例如,超过实际处理器数量的线程数可增加系统的响应性。但是过多的线程数也会起副作用。建议在真实使用环境下来测试系统最佳的线程数。,1.ICE运行时,1.ICE运行时1.10ice线程模型,1.10.4设计考虑,(3)序列化时的考虑当使用多线程线程池时,线程调度的不确定性会导致调用分派顺序可能和调用到来时的接收顺序不一致。有些应用不允许这种情况,例如一些事物处理服务必须保证顺序。有两种方式可以解决这个问题:1.使用单线程的线程池;2.使用Serialize属性(poolname.Serialize)配置多线程的线程池来处理序列化请求。在客户端避免并发调用也可以避免上述问题,但会降低客户端的性能。打开Serialize属性会增加处理延迟减小服务吞吐量。如果服务端必须保证客户端请求顺序,一个比较好的做法是序列化配合使用异步分派排队到来的请求,将请求排队放入一个队列,然后由其它线程来执行这些请求。,1.ICE运行时,1.ICE运行时1.10ice线程模型,1.10.5嵌套调用,嵌套调用:一个Ice操作发生在另一个Ice操作的上下文中。注意:如果有嵌套调用,则需要注意避免潜在的死锁肯能。例如,调用时路径出现环路。这张图中操作A嵌套双向调用了操作B,但是操作B尝试嵌套回调时发生死锁。因为,通信器缺省最大线程数是1个,所以服务A中仅有的一个线程正忙于等待操作B的调用完成,因此没有线程来处理服务B的回调操作。这时客户端因为服务A阻塞,所以它也被阻塞。在这种场景下有多种方法可以避免死锁发生:(1)增加服务A的线程池的最大线程数量;(2)使用单向调用;(3)另外再创建一个对象适配器用于回调;(另外的适配器配置有自己的线程池)(4)操作A实现为异步分派和调用:将操作A实现为AMD方式,调用操作B实现为AMI方式。这样服务A中的线程不用一直阻塞去等待操作B的完成。,1.ICE运行时,1.ICE运行时1.10ice线程模型,1.10.5嵌套调用,是否使用嵌套调用:是否设计成嵌套调用需要考虑一些因素:(1)线程池的配置是否能满足嵌套调用要求;(2)双向连接所带来的复杂性,因此你必须小心处理线程的使用;(3)各通信模块中的同步问题也需要考虑,为了同步所加入的锁也可能导致死锁。所以,在嵌套调用的情况下,跟踪调用路径来避免死锁会变得很复杂。,1.ICE运行时,1.ICE运行时1.10ice线程模型,1.10.6向用户线程分派调用,缺省情况下操作调用和AMI回调以及操作分派所使用的线程都是Ice运行时线程池中的线程。这样做很方便,不用考虑线程的创建和销毁。但是Ice运行时线程数量有限,当运行时线程耗尽后将无法继续接收新的调用和分派。例如,我们的目前的服务端实现中使用了一个自己的线程池,所有到来的调用请求会先放入一个请求队列中,然后由服务端自己的线程池去处理请求。这样,Ice运行时的线程池中始终有空闲线程可用于接收新的请求。virtualboolDispatch(constCRpcDispatchResultPtr,1.ICE运行时,1.ICE运行时1.10ice线程模型,1.11代理的使用及配置(Proxies),代理对象能够使得远程调用像本地调用一样简单。事实上,处理远程调用只是代理对象的众多职责之一。代理还封装了连接远程对象所需的足够信息,包括标识、地址信息(传输端点)等。代理方法还提供了访问配置及连接信息的功能,以及创建新代理对象的功能。代理负责所需的新连接的建立。代理分成两类:直接代理其内部保存有某个对象的标识,以及它的服务器的运行地址;(ObjectIdent:tcphp9999)间接代理其内部保存有某个对象的标识,以及对象适配器名(objectadaptername)(ObjectIdent或ObjectIdentAdapterName)。,1.ICE运行时1.11代理,1.11.1获取代理,字符串表示的代理:通信器的stringToProxy方法可从代理的字符串标识创建一个代理对象:Ice:ObjectPrxp=communicator-stringToProxy(“ident:tcpp5000”);代理属性:除了上述硬编码方式表示代理对象,还可通过配置属性将代理外部化。例如,可以定义一个属性包含代理字符串标识:MyApp.Proxy-ident:tcpp5000可以使用通信器的propertyToProxy方法从配置属性得到代理对象。,1.ICE运行时1.11代理,例如:配置文件中配置了代理Hello.Proxy=hello:tcp-p10000,通信器初始化时加载了这个该配置文件initDperties-load(configFile);,使用通信器方法propertyToProxy创建代理HelloPrxtwoway=HelloPrx:checkedCast(communicator()-propertyToProxy(Hello.Proxy);,1.11.1获取代理,通过工厂方法创建代理代理提供一些方法允许改变已有的代理的某些特性,得到新的代理。因为代理本身是不可变的,工厂方法会返回新的代理对象。例如:ice_oneway方法返回一个单向调用代理对象。注意:如果原代理的配置和新代理配置不一样,则返回的是一个新代理对象实例。checkCast和uncheckedCast方法也可以认为是工厂方法,因为他们也会返回新代理对象。HelloPrxtwoway=HelloPrx:checkedCast(communicator()-propertyToProxy(Hello.Proxy)-ice_twoway()-ice_timeout(-1)-ice_secure(false);if(!twoway)cerrice_oneway();HelloPrxbatchOneway=twoway-ice_batchOneway();HelloPrxdatagram=twoway-ice_datagram();HelloPrxbatchDatagram=twoway-ice_batchDatagram();,1.ICE运行时1.11代理,1.11.3代理的端点,代理传输端点是位于客户侧的并等同于对象适配器的传输端点。代理传输端点标识了同远程对象通信用的协议信息。例如:tcpp10000这个端点标示一个远程对象,改对象可通过tcp协议连接,该对象处于主机上,端口10000。代理对象必须至少能获取到一个端点。直接代理对象可包含1个或多个端点:MyObject:tcpp10000:sslo10001如果直接代理不含有-h项,则Ice运行时将使用属性Ice.Default.Host。如果Ice.Default.Host也未定义,则localhost网络接口将被使用。间接代理只有对象标识或还有适配器名,它使用定位服务来动态获取传输端点。MyObject或MyObjectAdapterName,1.ICE运行时1.11代理,1.11.4端点过滤,代理的配置决定了它的端点如何使用。例如,一个配置为安全通信的代理它的端点必须使用安全协议,如SSL。如下表中列出了一些工厂方法返回的代理对象其端点必须根据该代理对象的配置来使用。,1.ICE运行时1.11代理,1.11.4端点过滤,例如:Ice_secure()返回的代理对象只使用安全协议的端点.如果没有正确协议的端点可用,则连接建立时会收到NoEndpointException异常。,1.ICE运行时1.11代理,1.11.5缺省值与覆盖,理解代理是如何受Ice配置属性设置影响的是很重要的。相关属性可分为两类:缺省值和覆盖值(defaultsandoverrides)。缺省属性缺省属性影响那些由Ice调用、stringToProxy或propertyToProxy创建的代理对象,不影响工厂方法创建的代理。覆盖属性设置覆盖属性将导致Ice运行时忽略与之对应的代理设置而直接使用覆盖属性设置。例如:Ice.Override.Secure=1指示Ice运行时只使用安全端点,相当于对每个代理调用ice_secure(true)。但是这个属性不会改变已存在的代理的设置,而是指示Ice运行时使用安全端点而不去理会代理自身的安全设置。,1.ICE运行时1.11代理,1.12Ice:Context,moduleIcelocaldictionaryContext;上下文就是一个把串映射到串的词典,或者从概念上说,上下文就是一系列名-值对。每当有请求要发往服务器时,这个词典的内容(如果有的话)都会随同请求一起整编,也就是说,如果客户在上下文中放入一些名-值对,并在发出调用时使用这个上下文,服务器就将能使用客户所发送的这些名-值对。上下文提供了一种手段,可以把数量不限的参数从客户发往服务器,而不必在操作的型构中提到这些参数。例如:PersonPrxp=.;Addressa=.;Ice:Contextctx;ctxwritepolicy=immediate;p-setAddress(a,ctx);/在服务端,Currentc;Ice:Context:const_iteratori=c.ctx.find(writepolicy);,1.ICE运行时,1.12Ice:Context,注意:通过使用Ice:Context,在处理每一个请求时,它们可以向服务端传入一些上下文信息。上下文的设计用途是传送简单的token(比如事务标识符),你应该只把上下文用于这样的目的,而不要把它用于其他目的。例如,如果服务端设计上必须接收上下文,但是它是无法强制客户端调用时一定会传入上下文的,如果客户端没有发送上下文信息,服务端可能无法工作或异常。,1.ICE运行时,1.13具有超时的代理,代理方法Ice:ObjectPrxice_timeout(Ice:Intt)const;会根据一个已有代理创建一个具有超时的代理,参数t以毫秒为单位,-1表示没有超时。如果在超时代理上调用的操作没有在超时时间内完成,客户端会收到异常。注意:连接的建立和关闭也有超时:ConnectTimeoutException:连接无法在制定时间内建立;CloseTimeoutException:连接无法在指定时间内关闭。同样支持覆盖属性来设置超时时间:Ice.Override.TimeoutIce.Override.ConnectTimeout(设置了覆盖参数后,代理自身的超时设置会被覆盖),1.ICE运行时,1.13具有超时的代理,注意:这些超时是“软”超时,也就是说,它们不是精确的、实时的超时(精确度受制于底层操作系统的能力)。Iceruntime把超时当作是严重的出错:例如,超时会导致客户端的连接关闭。超时应该被用于防止客户在“服务器发生错误”的情况下无限期地阻塞;,1.ICE运行时,1.14单向调用,单向调用客户端发送调用后不关心任何服务端回应,服务端也不会给客户端任何回应,客户端不关心调用是否成功。单向调用(客户端侧)在客户端,调用的发送是通过把请求写到客户的本地传输缓冲区来完成的;一旦本地传输机制接受了调用,调用就会完成,并把控制返回给应用代码。这意味着,单向调用是不可靠的:它可能根本没有发送出去(例如,因为网络故障),也可能没有被服务器接受(例如,因为目标对象不存在)。如果出了什么问题,客户端应用代码不会收到失败通知;只有那些在调用过程中,发生在客户端的本地错误,才会报告给客户(例如,没有能建立连接)。,1.ICE运行时,1.14单向调用,单向调用(服务端侧)在服务器端,接收和处理单向调用的方式与其他请求是一样的。特别地,服务器端应用代码无法区分单向调用和双向调用,也就是说,在服务器端,单向调用是透明的。在使用单向调用时,不会有任何数据从服务器返回客户:服务器不会发出答复消息来响应单向调用。这意味着,单向调用可能会带来很大的效率提升,特别是在发送大量小消息的情况下,因为客户无需等待对每条消息的答复,就可以发送下一条消息了。注意:尽管单向调用在理论上是不可靠的,在实践中,它们是可靠的(但并非不会失败的):它们是通过面向流的传输机制发送的,所以它们不可能丢失,除非连接完全失败了。特别地,面向流的传输机制将使用平常所用的流控制,所以客户不会向服务器发送过多的消息。在客户端,如果客户的传输缓冲区满了,Iceruntime就会阻塞,所以客户端应用代码不可能把过多消息发往它的本地传输缓冲区。但要注意,服务端的多线程派发机制可能导致单向调用的处理顺序和发送顺序不一致。,1.ICE运行时,1.14单向调用,单向调用使用条件:(1)操作的返回类型必须是void,不能有任何out参数,也不能有异常规范。(2)操作所属代理必须支持面向流的传输机制(比如TCP或SSL)创建单向调用代理:使用代理方法:Ice:ObjectPrxice_oneway()const;可从一个已有代理创建一个新的单向调用代理。例如:HelloPrxtwoway=;HelloPrxoneway=twoway-ice_oneway();oneway-sayHello(delay);,1.ICE运行时,1.15数据报调用,数据报调用使用UDP协议进行传输。和单向调用一样,只有对那些返回类型为void、并且没有out参数或异常规范的操作,才能进行数据报调用.此外,代理的端点必须至少包括一个UDP的,才能使用数据报调用;传输机制和单向调用类似;另外,个别的调用可能会丢失,也可能会不按次序到达。传输机制可能会造成UDP包的重复。UDP包的尺寸是有限的。适用场景:因为数据报有着不可靠的本质,它们最适用于简单的更新消息,这些消息本来没有状态。此外,由于在广域网上数据报调用丢失的可能性很高,你应该只在局域网上使用数据报调用,在局域上网上它们丢失的可能性比较小(当然,不管丢失的可能性有多大,你都必须设计你的应用,让它能容忍消息丢失或重复)。,1.ICE运行时,1.15数据报调用,创建数据报调用代理:使用代理方法:Ice:ObjectPrxice_datagram()const;可从一个已有代理创建一个新的数据报调用代理。HelloPrxtwoway=;HelloPrxdatagram=twoway-ice_datagram();datagram-sayHello(delay);,1.ICE运行时,1.16批量调用,单向调用和数据报调用通常会作为单条消息发送,也就是说,只要客户发出调用,Iceruntime就会立刻把单向或数据报调用发给服务器。如果客户接连发送多个单向调用或数据报调用,这会有比较昂贵的开销。为了避免这样的开销,你可以成批地发送单向调用和数据报调用:Iceruntime不是把它们作为单条消息发送,而是把一批调用放在客户端缓冲区中,在客户端累积起来,直

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论