前言
------------------
本文介紹了一種全新的調(diào)用遠(yuǎn)程代碼的技術(shù)。參考了微軟的remoting、webservice。
基礎(chǔ)知識(shí)
------------------
先拋開具體的代碼,如果要實(shí)現(xiàn)遠(yuǎn)程代碼調(diào)用,一個(gè)最簡單的模型是:
1. 使用一個(gè)HttpHandler,當(dāng)有請(qǐng)求的時(shí)候,調(diào)用對(duì)應(yīng)的代碼,返回。例如:
代碼 class RemoteHandler : IHttpHandler, IReadOnlySessionState
{
public bool IsReusable
{
get
{
return true;
}
}
public void ProcessRequest(HttpContext context)
{
//這里創(chuàng)建實(shí)例RemotingGreeting
RemotingGreeting greeting = new RemotingGreeting();
byte[] response = greeting.Helloworld();
//這里返回調(diào)用結(jié)果到客戶端
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去訪問這個(gè)Handler,就實(shí)現(xiàn)了最原始的遠(yuǎn)程調(diào)用。
這段代碼,就實(shí)現(xiàn)了遠(yuǎn)程調(diào)用RemotingGreeting這個(gè)類,獲取方法Helloworld();的返回值。
那么,這個(gè)過程如何實(shí)現(xiàn)通用呢?如何實(shí)現(xiàn)框架化?首先先看看實(shí)際代碼的調(diào)用效果:
代碼實(shí)例
------------------
首先聲明一個(gè)被遠(yuǎn)程調(diào)用的對(duì)象,RemotingGreeting. 以及一個(gè)接口IRemotingGreeing
代碼 class RemotingGreeting : IRemotingGreeting
{
public string Greeting(string message)
{
return "Hi! " + message;
}
}
[Remote("Pixysoft.Framework.Remoting.Demo", "Pixysoft.Framework.Remoting.Demo.RemotingGreeting")]//這里實(shí)際指定了接口具體實(shí)現(xiàn)的類的Assembly和Type
public interface IRemotingGreeting
{
string Greeting(string message);
}
然后本地實(shí)現(xiàn)遠(yuǎn)程調(diào)用:
代碼using System;
using System.Collections.Generic;
using System.Text;
namespace Pixysoft.Framework.Remoting.Demo
{
class testcase
{
public void test()
{
//指定了調(diào)用的入口點(diǎn)url
string url = "http://localhost:1300/Apis/remoting.asmx";
//創(chuàng)建本地調(diào)用的透明代理
IRemoteChannel<IRemotingGreeting> channel = RemotingManager.CreateRemoteChannel<IRemotingGreeting>(url);
//登錄遠(yuǎn)程服務(wù)器
channel.Login("xxxxxx", "xxxxxxxxx");
//遠(yuǎn)程調(diào)用
string greeting = channel.RemoteProxy.Greeting("pixysoft");
//登出
channel.Logout();
//打印結(jié)果,就是“Hi!pixysoft”
Console.WriteLine(greeting);
}
}
}
正文
------------------
遠(yuǎn)程調(diào)用框架的思路是:
1. 本地創(chuàng)建一個(gè)透明代理(RealProxy.GetTransparentProxy())
2. 用戶本地的請(qǐng)求,被透明代理序列化為XML
3. XML傳遞到服務(wù)器的Handler,被解析后,加載對(duì)應(yīng)的對(duì)象(Spring? 動(dòng)態(tài)加載)
4. Handler運(yùn)行對(duì)象,獲取返回值,再序列化為XML,返回本地。
5. 本地透明代理解析XML,獲取返回值。
第一步,創(chuàng)建透明代理。請(qǐng)各位先閱讀一篇相關(guān)的文章:
http://www.cnblogs.com/zc22/archive/2010/02/22/1671557.html
這里貼出核心代碼的一個(gè)例子:
這篇文章講解了如何實(shí)現(xiàn)一個(gè)接口的透明代理。本質(zhì)在
public override IMessage Invoke(IMessage msg)
這里,對(duì)用戶調(diào)用的方法進(jìn)行序列化操作。
第二步,調(diào)用的序列化。
上文透明代理通過以下代碼獲取了用戶調(diào)用的方法反射
IMethodCallMessage methodCall = msg as IMethodCallMessage;
MethodInfo method = methodCall.MethodBase as MethodInfo;
這里,要對(duì)調(diào)用方法MethodInfo進(jìn)行序列化。當(dāng)然,就是自己去建立一個(gè)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>
這個(gè)是我實(shí)際建立的MethodInfo的xml描述。如何建立就不說了吧,很簡單,用StringBuilder去拼就行了。
第三步,httpHandler解析XML,加載對(duì)象運(yùn)行結(jié)果。
客戶端通過HttpPost到服務(wù)端,服務(wù)端獲取了XML之后,只要根據(jù)對(duì)應(yīng)的參數(shù)加載Assembly,然后獲取對(duì)象即可。具體涉及到了一些反射的操作:
Assembly assembly = Assembly.LoadFrom(assemblyname);
Type type = assembly.GetType(typename);
MethodInfo method = type.GetMethod(methodname);
獲取了MethodInfo之后,只要把參數(shù)放入,獲取返回值即可。
代碼//實(shí)例化一個(gè)對(duì)象
ConstructorInfo constructorInfo =
type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
null, new Type[] { }, null);
object remoteObject = constructorInfo.Invoke(new object[] { });
//調(diào)用這個(gè)對(duì)象的方法 這里省略了如何獲取parameters過程
object returnvalue = method.Invoke(remoteObject, parameters);
第四步 Handler序列化返回值為XML,返回本地。
只要把returnvalue序列化為xml即可。具體就不敘述了。
第五步 本地透明代理解析XML,獲取返回值。
本地透明代理把序列化的returnvalue再反序列化為對(duì)象即可,然后返回
return new ReturnMessage(returnvalue, null, 0, null, methodCall);
難點(diǎn)講解
------------------
1. 整個(gè)調(diào)用過程最難的地方在于序列化操作。因?yàn)槲④洸恢С纸涌诘男蛄谢⒉恢С謨?nèi)部類的序列化。這里需要自己實(shí)現(xiàn)。
2. 其次最難的在于值類型的操作。因?yàn)橹殿愋瓦M(jìn)入了RealProxy之后,全部被裝箱成為了對(duì)象(object)。這個(gè)時(shí)候直接把對(duì)象返回會(huì)拋異常,因此需要根據(jù)具體的method.ReturnType, 逐一用值類型解析返回。
3. 再次,就是動(dòng)態(tài)加載問題。Assembly.LoadFrom會(huì)有很多問題,比如版本問題、路徑問題。因此要實(shí)現(xiàn)一個(gè)事件
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
實(shí)現(xiàn)了這個(gè)event之后,能夠代碼指定搜索assembly的位置。具體代碼我就不列舉了。