zoukankan      html  css  js  c++  java
  • 介绍一下我自己开发的全新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的位置。具体代码我就不列举了。

     

    后记 

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

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

     

     

  • 相关阅读:
    JS Array转JSON
    js数组转字符串并用,分割
    java枚举类-根据key获取value及根据value获取key
    CSS文件引入顺序
    git pull之前要先commit
    FastJson中@JSONField注解使用
    @JsonFormat与@DateTimeFormat注解的使用
    Jackson 时间格式化,时间注解 @JsonFormat 用法、时差问题说明
    shell脚本使用
    ubuntu12.04 安装redis
  • 原文地址:https://www.cnblogs.com/zc22/p/1710493.html
Copyright © 2011-2022 走看看