介绍一下我自己开发的全新Remoting技术。(本地调用远程代码)

前言

------------------

本文介绍了一种全新的调用远程代码的技术。参考了微软的remoting、webservice。  

 

基础知识

------------------

先抛开具体的代码,如果要实现远程代码调用,一个最简单的模型是:

1. 使用一个HttpHandler,当有请求的时候,调用对应的代码,返回。例如:

代码
    class RemoteHandler : IHttpHandler, IReadOnlySessionState
    {
        
public bool IsReusable
        {
            
get
            {
                
return true;
            }
        }

        
public void ProcessRequest(HttpContext context)
        {
              
//这里创建实例RemotingGreeting
                
                RemotingGreeting greeting 
= new RemotingGreeting();

               
byte[] response = greeting.Helloworld();


              
//这里返回调用结果到客户端

              context.Response.Clear();

              context.Response.ContentType 
= "application/octet-stream";

              BinaryWriter writer 
= new BinaryWriter(context.Response.OutputStream);

              writer.Write(response);

              writer.Flush();

              writer.Close();

            context.Response.End();              
        }
    }

 

2. 客户端使用Http去访问这个Handler,就实现了最原始的远程调用

 

这段代码,就实现了远程调用RemotingGreeting这个类,获取方法Helloworld();的返回值。

 

那么,这个过程如何实现通用呢?如何实现框架化?首先先看看实际代码的调用效果:

 

代码实例 

------------------ 

首先声明一个被远程调用的对象,RemotingGreeting. 以及一个接口IRemotingGreeing

代码
    class RemotingGreeting : IRemotingGreeting
    {
        
public string Greeting(string message)
        {
            
return "Hi! " + message;
        }
    }

    [Remote(
"Pixysoft.Framework.Remoting.Demo""Pixysoft.Framework.Remoting.Demo.RemotingGreeting")]//这里实际指定了接口具体实现的类的Assembly和Type
    
public interface IRemotingGreeting
    {
        
string Greeting(string message);
    }

 

 

然后本地实现远程调用:

代码
using System;
using System.Collections.Generic;
using System.Text;

namespace Pixysoft.Framework.Remoting.Demo
{
    
class testcase
    {
        
public void test()
        {
            
//指定了调用的入口点url
            string url = "http://localhost:1300/Apis/remoting.asmx";

            
//创建本地调用的透明代理
            IRemoteChannel<IRemotingGreeting> channel = RemotingManager.CreateRemoteChannel<IRemotingGreeting>(url);

            
//登录远程服务器
            channel.Login("xxxxxx""xxxxxxxxx");

            
//远程调用
            string greeting = channel.RemoteProxy.Greeting("pixysoft");

            
//登出
            channel.Logout();

            
//打印结果,就是“Hi!pixysoft”
            Console.WriteLine(greeting);
        }
    }
}

 

 

正文 

------------------ 

远程调用框架的思路是:

1. 本地创建一个透明代理(RealProxy.GetTransparentProxy())

2. 用户本地的请求,被透明代理序列化为XML

3. XML传递到服务器的Handler,被解析后,加载对应的对象(Spring? 动态加载)

4. Handler运行对象,获取返回值,再序列化为XML,返回本地。

5. 本地透明代理解析XML,获取返回值。

 

 

第一步,创建透明代理。请各位先阅读一篇相关的文章:

http://www.cnblogs.com/zc22/archive/2010/02/22/1671557.html

这里贴出核心代码的一个例子:

代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;

namespace Pixysoft.Framework.TestDrivens
{
    
public class Mock<TInterface> : RealProxy
    {
        
public Mock()
            : 
base(typeof(TInterface))
        {
        }

        
public TInterface Value
        {
            
get
            {
                
return (TInterface)this.GetTransparentProxy();
            }
        }

        
public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage methodCall 
= msg as IMethodCallMessage;

            
//我返回int = 1

            
return new ReturnMessage(1null0null, methodCall);
        }
    }

    
public interface IMock
    {
        
int Devide(int a, int b);
    }

    
public class testrealproxy //测试代码在这里!!!
    {
        
public void test()
        {
            IMock mock 
= new Mock<IMock>().Value;

            Console.WriteLine(mock.Devide(
12));

            
//输出 = 1
        }
    }
}

这篇文章讲解了如何实现一个接口的透明代理。本质在

public override IMessage Invoke(IMessage msg)

这里,对用户调用的方法进行序列化操作。

 

第二步,调用的序列化。

上文透明代理通过以下代码获取了用户调用的方法反射

            IMethodCallMessage methodCall = msg as IMethodCallMessage;

            MethodInfo method 
= methodCall.MethodBase as MethodInfo;

这里,要对调用方法MethodInfo进行序列化。当然,就是自己去建立一个MethodInfo的xml描述,例如:

代码
<method assembly="Pixysoft.Framework.Remoting" type="Pixysoft.Framework.Remoting.Core.RemotingHelloworld" method="HelloWorld" parametercount="4">
  
<parameter type="DateTime" parameter="para1">2010-4-12 下午 08:52:21</parameter>
  
<parameter type="String" parameter="para2">2</parameter>
  
<parameter type="Int32" parameter="para3">12</parameter>
  
<parameter type="IRemotingValue" parameter="para4" />
  
<return type="IRemotingValue" />
</method>

这个是我实际建立的MethodInfo的xml描述。如何建立就不说了吧,很简单,用StringBuilder去拼就行了。

 

第三步,httpHandler解析XML,加载对象运行结果。

客户端通过HttpPost到服务端,服务端获取了XML之后,只要根据对应的参数加载Assembly,然后获取对象即可。具体涉及到了一些反射的操作:

Assembly assembly = Assembly.LoadFrom(assemblyname);

Type type 
= assembly.GetType(typename);

MethodInfo method 
= type.GetMethod(methodname);

 

获取了MethodInfo之后,只要把参数放入,获取返回值即可。

代码
//实例化一个对象

            ConstructorInfo constructorInfo 
=
                type.GetConstructor(BindingFlags.Public 
| BindingFlags.NonPublic | BindingFlags.Instance,
                
nullnew Type[] { }, null);

            
object remoteObject = constructorInfo.Invoke(new object[] { });

            
//调用这个对象的方法 这里省略了如何获取parameters过程

            
object returnvalue = method.Invoke(remoteObject, parameters);

 

 

第四步 Handler序列化返回值为XML,返回本地。 

只要把returnvalue序列化为xml即可。具体就不叙述了。

 

第五步 本地透明代理解析XML,获取返回值。

本地透明代理把序列化的returnvalue再反序列化为对象即可,然后返回

return new ReturnMessage(returnvalue, null0null, methodCall);

 

 

难点讲解 

------------------ 

1. 整个调用过程最难的地方在于序列化操作。因为微软不支持接口的序列化、不支持内部类的序列化。这里需要自己实现。

 

2. 其次最难的在于值类型的操作。因为值类型进入了RealProxy之后,全部被装箱成为了对象(object)。这个时候直接把对象返回会抛异常,因此需要根据具体的method.ReturnType, 逐一用值类型解析返回。

 

3. 再次,就是动态加载问题。Assembly.LoadFrom会有很多问题,比如版本问题、路径问题。因此要实现一个事件

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

 

实现了这个event之后,能够代码指定搜索assembly的位置。具体代码我就不列举了。

 

后记 

------------------ 

写代码的过程,和拼装模型是一样的。只要我们手上的零件越来越多,能实现的功能和效果就越来越多!

 

 

posted @ 2010-04-12 19:48    阅读(3565)  评论(13编辑  收藏  举报
IT民工