JAVA RMI线程模型及内部实现机制
1 RMI内部实现
JAVA RMI是JAVA分布式结构的基础。远程对象的通信过程中,RMI使用标准机制:stub和skeleton。远程对象的stub担当远程对象的客户本地代表或代理人角色,调用程序将调用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。在RMI中,远程对象的stub与该远程对象所实现的远程接口集相同。调用stub的方法时将执行下列操作:
(1)初始化与包含远程对象的远程虚拟机的连接;
(2)对远程虚拟机的参数进行编组-传输参数;
(3)等待远程方法调用结果;
(4)解编(读取)返回值或返回的异常;
(5)将值返回给调用程序。
为了向调用程序展示比较简单的调用机制,stub将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以有相应的skeleton。skeleton负责将调用分配给实际的远程对象实现。它在接收方法调用时执行下列操作:
(1)解编(读取)远程方法的参数;
(2)调用实际远程对象实现上的方法;
(3)将结果(返回值或异常)编组(写入并传输)给调用程序。
stub和skeleton由rmic编译器生成。在最新的JDK中,不需要手工生产stub和skeleton,用动态代理生成的Proxy代替了stub,而skeleton则取消了。
我们可以查看源代码来了解RMI的内部实现。Server端调用UnicastRemoteObject的export方法输出远程对象,export方法会在一个线程里监听某个TCP端口上的方法调用请求:
- publicvoidexportObject(Targettarget)throwsRemoteException{
- //othercode
- while(true){
- ServerSocketmyServer=server;
- if(myServer==null)
- return;
- ThrowableacceptFailure=null;
- finalSocketsocket;
- try{
- socket=myServer.accept();
- /*
- *Findclienthostname(or"0.0.0.0"ifunknown)
- */
- InetAddressclientAddr=socket.getInetAddress();
- StringclientHost=(clientAddr!=null
- ?clientAddr.getHostAddress()
- :"0.0.0.0");
- /*
- *Spawnnon-systemthreadtohandletheconnection
- */
- Threadt=(Thread)
- java.security.AccessController.doPrivileged(
- newNewThreadAction(newConnectionHandler(socket,
- clientHost),
- "TCPConnection("+++threadNum+
- ")-"+clientHost,
- true,true));
- t.start();
- }catch(IOExceptione){
- acceptFailure=e;
- }catch(RuntimeExceptione){
- acceptFailure=e;
- }catch(Errore){
- acceptFailure=e;
- }
- }
- //othercode
上面的代码已被修改以展示主要的要点,Server端就是在ServerSocket的accept方法上面监听到来的请求,如果有新的方法调用请求到来,Server产生一个单独的线程来处理新接收的请求:
- publicvoiddispatch(Remoteobj,RemoteCallcall)throwsIOException{
- //positiveoperationnumberin1.1stubs;
- //negativeversionnumberin1.2stubsandbeyond...
- intnum;
- longop;
- try{
- //readremotecallheader
- ObjectInputin;
- try{
- in=call.getInputStream();
- num=in.readInt();
- if(num>=0){
- if(skel!=null){
- oldDispatch(obj,call,num);
- return;
- }else{
- thrownewUnmarshalException(
- "skeletonclassnotfoundbutrequired"+
- "forclientversion");
- }
- }
- op=in.readLong();
- }catch(ExceptionreadEx){
- thrownewUnmarshalException("errorunmarshallingcallheader",
- readEx);
- }
- /*
- *Sinceonlysystemclasses(withnullclassloaders)willbeon
- *theexecutionstackduringparameterunmarshallingforthe1.2
- *stubprotocol,telltheMarshalInputStreamnottobothertrying
- *toresolveclassesusingitssuperclasses'sdefaultmethodof
- *consultingthefirstnon-nullclassloaderonthestack.
- */
- MarshalInputStreammarshalStream=(MarshalInputStream)in;
- marshalStream.skipDefaultResolveClass();
- Methodmethod=(Method)hashToMethod_Map.get(newLong(op));
- if(method==null){
- thrownewUnmarshalException("invalidmethodhash");
- }
- //ifcallsarebeinglogged,writeoutobjectidandoperation
- logCall(obj,method);
- //unmarshalparameters
- Class[]types=method.getParameterTypes();
- Object[]params=newObject[types.length];
- try{
- unmarshalCustomCallData(in);
- for(inti=0;i<types.length;i++){
- params[i]=unmarshalValue(types[i],in);
- }
- }catch(java.io.IOExceptione){
- thrownewUnmarshalException(
- "errorunmarshallingarguments",e);
- }catch(ClassNotFoundExceptione){
- thrownewUnmarshalException(
- "errorunmarshallingarguments",e);
- }finally{
- call.releaseInputStream();
- }
- //makeupcallonremoteobject
- Objectresult;
- try{
- result=method.invoke(obj,params);
- }catch(InvocationTargetExceptione){
- throwe.getTargetException();
- }
- //marshalreturnvalue
- try{
- ObjectOutputout=call.getResultStream(true);
- Classrtype=method.getReturnType();
- if(rtype!=void.class){
- marshalValue(rtype,result,out);
- }
- }catch(IOExceptionex){
- thrownewMarshalException("errormarshallingreturn",ex);
- /*
- *Thisthrowisproblematicbecausewhenitiscaughtbelow,
- *weattempttomarshalitbacktotheclient,butatthis
- *point,a"normalreturn"hasalreadybeenindicated,
- *somarshallinganexceptionwillcorruptthestream.
- *Thiswasthecasewithskeletonsaswell;thereisno
- *immediatelyobvioussolutionwithoutaprotocolchange.
- */
- }
- }catch(Throwablee){
- logCallException(e);
- ObjectOutputout=call.getResultStream(false);
- if(einstanceofError){
- e=newServerError(
- "Erroroccurredinserverthread",(Error)e);
- }elseif(einstanceofRemoteException){
- e=newServerException(
- "RemoteExceptionoccurredinserverthread",
- (Exception)e);
- }
- if(suppressStackTraces){
- clearStackTraces(e);
- }
- out.writeObject(e);
- }finally{
- call.releaseInputStream();//incaseskeletondoesn't
- call.releaseOutputStream();
- }
- }
- protectedstaticvoidmarshalValue(Classtype,Objectvalue,
- ObjectOutputout)
- throwsIOException
- {
- if(type.isPrimitive()){
- if(type==int.class){
- out.writeInt(((Integer)value).intValue());
- }elseif(type==boolean.class){
- out.writeBoolean(((Boolean)value).booleanValue());
- }elseif(type==byte.class){
- out.writeByte(((Byte)value).byteValue());
- }elseif(type==char.class){
- out.writeChar(((Character)value).charValue());
- }elseif(type==short.class){
- out.writeShort(((Short)value).shortValue());
- }elseif(type==long.class){
- out.writeLong(((Long)value).longValue());
- }elseif(type==float.class){
- out.writeFloat(((Float)value).floatValue());
- }elseif(type==double.class){
- out.writeDouble(((Double)value).doubleValue());
- }else{
- thrownewError("Unrecognizedprimitivetype:"+type);
- }
- }else{
- out.writeObject(value);
- }
- }
- rotectedstaticObjectunmarshalValue(Classtype,ObjectInputin)
- throwsIOException,ClassNotFoundException
- {
- if(type.isPrimitive()){
- if(type==int.class){
- returnnewInteger(in.readInt());
- }elseif(type==boolean.class){
- returnnewBoolean(in.readBoolean());
- }elseif(type==byte.class){
- returnnewByte(in.readByte());
- }elseif(type==char.class){
- returnnewCharacter(in.readChar());
- }elseif(type==short.class){
- returnnewShort(in.readShort());
- }elseif(type==long.class){
- returnnewLong(in.readLong());
- }elseif(type==float.class){
- returnnewFloat(in.readFloat());
- }elseif(type==double.class){
- returnnewDouble(in.readDouble());
- }else{
- thrownewError("Unrecognizedprimitivetype:"+type);
- }
- }else{
- returnin.readObject();
- }
- }
dispatch方法处理接收的请求,先从输入流中读取方法的编号来获得方法名称:op = in.readLong(),然后读取方法的所有参数:params[i] = unmarshalValue(types[i], in),接着就可以执行方法调用了:result = method.invoke(obj, params),最后把方法执行结果写入到输出流中:marshalValue(rtype, result, out)。一个方法调用就执行完成了。
Client端请求一个远程方法调用时,先建立连接:Connection conn = ref.getChannel().newConnection(),然后发送方法参数: marshalValue(types[i], params[i], out),再发送执行方法请求:call.executeCall(),最后得到方法的执行结果:Object returnValue = unmarshalValue(rtype, in),并关闭接:
ref.getChannel().free(conn, true)。
- publicObjectinvoke(Remoteobj,
- java.lang.reflect.Methodmethod,
- Object[]params,
- longopnum)
- throwsException
- {
- if(clientRefLog.isLoggable(Log.VERBOSE)){
- clientRefLog.log(Log.VERBOSE,"method:"+method);
- }
- if(clientCallLog.isLoggable(Log.VERBOSE)){
- logClientCall(obj,method);
- }
- Connectionconn=ref.getChannel().newConnection();
- RemoteCallcall=null;
- booleanreuse=true;
- /*Ifthecallconnectionis"reused"early,remembernotto
- *reuseagain.
- */
- booleanalreadyFreed=false;
- try{
- if(clientRefLog.isLoggable(Log.VERBOSE)){
- clientRefLog.log(Log.VERBOSE,"opnum="+opnum);
- }
- //createcallcontext
- call=newStreamRemoteCall(conn,ref.getObjID(),-1,opnum);
- //marshalparameters
- try{
- ObjectOutputout=call.getOutputStream();
- marshalCustomCallData(out);
- Class[]types=method.getParameterTypes();
- for(inti=0;i<types.length;i++){
- marshalValue(types[i],params[i],out);
- }
- }catch(IOExceptione){
- clientRefLog.log(Log.BRIEF,
- "IOExceptionmarshallingarguments:",e);
- thrownewMarshalException("errormarshallingarguments",e);
- }
- //unmarshalreturn
- call.executeCall();
- try{
- Classrtype=method.getReturnType();
- if(rtype==void.class)
- returnnull;
- ObjectInputin=call.getInputStream();
- /*StreamRemoteCall.done()doesnotactuallymakeuse
- *ofconn,thereforeitissafetoreusethis
- *connectionbeforethedirtycallissentfor
- *registeredrefs.
- */
- ObjectreturnValue=unmarshalValue(rtype,in);
- /*wearefreeingtheconnectionnow,donotfree
- *againorreuse.
- */
- alreadyFreed=true;
- /*ifwegottothispoint,reusemusthavebeentrue.*/
- clientRefLog.log(Log.BRIEF,"freeconnection(reuse=true)");
- /*Freethecall'sconnectionearly.*/
- ref.getChannel().free(conn,true);
- returnreturnValue;
- }catch(IOExceptione){
- clientRefLog.log(Log.BRIEF,
- "IOExceptionunmarshallingreturn:",e);
- thrownewUnmarshalException("errorunmarshallingreturn",e);
- }catch(ClassNotFoundExceptione){
- clientRefLog.log(Log.BRIEF,
- "ClassNotFoundExceptionunmarshallingreturn:",e);
- thrownewUnmarshalException("errorunmarshallingreturn",e);
- }finally{
- try{
- call.done();
- }catch(IOExceptione){
- /*WARNING:Iftheconnhasbeenreusedearly,
- *thenitistoolatetorecoverfromthrown
- *IOExceptionscaughthere.Thiscodeisrelying
- *onStreamRemoteCall.done()notactually
- *throwingIOExceptions.
- */
- reuse=false;
- }
- }
- }catch(RuntimeExceptione){
- /*
- *Needtodistinguishbetweenclient(generatedbythe
- *invokemethoditself)andserverRuntimeExceptions.
- *ClientsideRuntimeExceptionsarelikelytohave
- *corruptedthecallconnectionandthosefromtheserver
- *arenotlikelytohavedoneso.Iftheexceptioncame
- *fromtheserverthecallconnectionshouldbereused.
- */
- if((call==null)||
- (((StreamRemoteCall)call).getServerException()!=e))
- {
- reuse=false;
- }
- throwe;
- }catch(RemoteExceptione){
- /*
- *Somefailureduringcall;assumeconnectioncannot
- *bereused.MustassumefailureevenifServerException
- *orServerErroroccurssincethesefailurescanhappen
- *duringparameterdeserializationwhichwouldleave
- *theconnectioninacorruptedstate.
- */
- reuse=false;
- throwe;
- }catch(Errore){
- /*Iferrorsoccurred,theconnectionismostlikelynot
- *reusable.
- */
- reuse=false;
- throwe;
- }finally{
- /*alreadyFreedensuresthatwedonotlogareusethat
- *mayhavealreadyhappened.
- */
- if(!alreadyFreed){
- if(clientRefLog.isLoggable(Log.BRIEF)){
- clientRefLog.log(Log.BRIEF,"freeconnection(reuse="+
- reuse+")");
- }
- ref.getChannel().free(conn,reuse);
- }
- }
- }
2 RMI线程模型
在JDK1.5及以前版本中,RMI每接收一个远程方法调用就生成一个单独的线程来处理这个请求,请求处理完成后,这个线程就会释放:
- Threadt=(Thread)
- java.security.AccessController.doPrivileged(
- newNewThreadAction(newConnectionHandler(socket,
- clientHost),
- "TCPConnection("+++threadNum+
- ")-"+clientHost,
- true,true));
在JDK1.6之后,RMI使用线程池来处理新接收的远程方法调用请求-ThreadPoolExecutor。
下面是一个简单的RMI程序的执行线程抓图,我们可以更好的了解RMI的线程机制。这个简单的RMI程序是服务端有一个远程方法实现,一个客户端同时请求执行这个远程方法100次。在JDK1.5中执行时生成的线程如下图所示,每个方法调用请求都是在一个单独的线程里执行,即 A Thread per Request。
在JDK1.6中执行时生成的线程如下图所示,这些线程都是在ThreadPoolExecutor线程池中执行的。
3 RMI线程池参数
在JDK1.6中,RMI提供了可配置的线程池参数属性:
sun.rmi.transport.tcp.maxConnectionThread - 线程池中的最大线程数量
sun.rmi.transport.tcp.threadKeepAliveTime - 线程池中空闲的线程存活时间
转自:http://blog.csdn.net/sureyonder/article/details/5653609