[开源]KJFramework.ServiceModel 基于SOA模式的轻量级服务调用框架模型
框架介绍
KJFramework框架中内置了一套轻量级RPC服务调用框架,使用此框架,将使得用户不再关心底层协议转换和网络传输的繁琐过程,解放程序员的脑力劳动。
此框架的使用也非常简单,作为服务器端的使用人员,只需要完成一个契约的实现,就可以通过配置不同的底层通信信道来达到不同通信模式需求。
在客户端,我们可以有2种方法来完成对于服务器端契约的使用:
1. 直接引用服务器端契约文件(dll文件)
2. 使用内部专门编写的VS插件(VS Add-in)来完成对于远程服务端契约的动态生成
当然,我们推荐使用第二种方式,因为使用VS插件可以帮助用户自动生成针对于每一个契约操作的异步版本。
目前此内置框架被初步定义在命名空间:KJFramework.ServiceModel内,拥有独立Assembly.
目前此框架中,仍保留了内部扩展能力,日后扩展后,将会通过配置文件来增强配置能力。
适用场景
作为一个轻量级的SOA解决方案,对于此框架,我们建议的场景是内部的SERVER间服务调用。首先,对于外部公有式的解决方案,微软已经提出了WCF。
当然,还有一个老古董级别的WebService. 所以,对于部门间的服务调用,WCF和WebService可以作为整合不同技术平台的一个公有接口。
我在SERVER SIDE的开发已经有很多年了,据我了解和熟知,几乎大多数的SERVER SIDE开发,内部的服务通讯都会有属于自己的一套框架,内部的服务调用
,如果采用SOA方式的话,也不会选用WCF,因为它确实太重了,而REMOTING服务存在内部的BUG,这个BUG是只有固定场景才会出现的,更何况这些解决方案的可驾驭型不强。 一个私有的SOA解决方案,对于内部服务的调用是具有积极效应的。于是,我自己设计
并完成了这套轻量级的SOA解决方案, 目前这套框架支持2种通讯方式,TCP, IPC(命名管道),日后会支持更多的通讯方式。
内部设计&使用体验
这套框架,我遵从了类似于WCF技术的使用体验,因为这种设计体验会使得使用者以更少的成本去用它。首先,我们来举个例子,看一下服务器端对于服务契约的声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //以下为服务端契约接口和实现 [ServiceContract(ServiceConcurrentType = ServiceConcurrentTypes.Concurrent, Description= "这是一个契约的描述" , Name = "IHellowWorld 服务契约" , Version = "0.0.0.1" )] public interface IHelloWorld { [Operation(IsOneway = true )] void Increment(); [Operation] int [] Hello( int [] text); [Operation] String Hello( int x, int y, string z); [Operation] string TestMultiPackage(); [Operation] string TestIntellectObject(InObject inObject); [Operation] InObject3 TestReturnValue( string content); } //服务器端实现,具体方法的实现就不写了 :) public class HelloWorld : IHelloWorld { /*Some code here.*/ } |
从中,我们可以看到,每一个服务器端契约的操作,我们都为它打上了一个 OperationAttribute, 这个标签是至关重要的,它直接的标示了一个契约中的某些可用操作。
当然,一个OperationAttribute内部拥有很多属性,我们来介绍一下:
- IsOneway(该属性直接标示了当前的操作是单向操作,也就是说,客户端不会接收到服务器端的请求回馈Response,这样的话,直接能够节省服务器端的通信开销)
- IsAsync(该属性直接标示了当前的操作是异步的,由于我们的框架服务器端是不分异步操作或者同步操作的,所以此属性仅限于客户端使用)
- MethodToken(契约操作编号,此属性更多的代表了远端服务契约中某个操作的唯一编号,请不用担心,一般此属性都会由我们内置的插件自动生成)
好吧,我们再来看一下客户端拿到的服务器端契约接口会是个什么样子呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 | [ServiceContract(ServiceConcurrentType = ServiceConcurrentTypes.Singleton, Description= "这是一个契约的描述" , Name = "IHellowWorld 服务契约" , Version = "0.0.0.1" )] public interface IHelloWorld { [Operation(MethodToken = 10655301)] void Increment(); [Operation(MethodToken = 100663310)] string TestMultiPackage(); [Operation(MethodToken = 100663311)] string TestIntellectObject(InObject inObject); //可以看到,此方法是异步的,异步方法不会由服务器端声明,而是由客户端通过我们的插件生成的 [Operation(IsAsync = true , MethodToken = 100663308)] void TestIntellectObjectAsync(InObject inObject, AsyncMethodCallback callback); } |
来请注意,这里的AsyncMethodCallback是框架内部定义的,通常情况下,一个带参数的同步操作,在使用插件生成异步操作时,我们都会为其参数的末尾加入一个 AsyncMethodCallback。那么,我们接下来,看一下,服务器端的程序是如何发布一个属于自己的服务呢? 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | * *服务端用来公开一个服务的代码 *从此代码可知,公开服务的通讯方式为TCP *地址为本地的9999端口 *公开的服务全地址为: tcp: //localhost:9999/Test */ ServiceHost serviceHost = new ServiceHost( typeof (HellowWorld), new TcpBinding( "tcp://localhost:9999/Test" )); //如果设置此属性,则会让客户端从网页上直接预览此服务的契约 serviceHost.IsSupportExchange = true ; serviceHost.Opened += (sender, e) => { Console.WriteLine( "Service has been opened." ); }; serviceHost.Open(); |
再来看一下客户端调用服务器端指定契约操作的代码:
1 2 3 4 5 6 7 8 9 10 | //IClientProxy<T>接口是一个动态的客户端代理接口 IClientProxy<IHelloWorld> proxy= new DefaultClientProxy<IHelloWorld>( new TcpBinding( "tcp://localhost:9999/Test" )); InObject inObject = new InObject { Inf1 = 1, Info2 = DateTime.Now, Info = "Info" , Info3 = new [] { "1" , "2" }, Info4 = new [] { 3, 4 } }; //调用的使用使用proxy.Channel, 此对象的类型是服务器端公开的契约接口 //从这里看出,客户端调用的这个方法,是个异步版本,是VS插件自动生成的 proxy.Channel.TestIntellectObjectAsync(inObject, delegate (IAsyncCallResult callResult) { if (callResult.IsSuccess) Interlocked.Increment( ref callbackcount); else if (!callResult.IsSuccess && callResult.LastError != null ) throw callResult.LastError; }); |
可以看出什么吗? 哦!是的。 从客户端的调用代码来看,使用起来是多么的简单! 由于客户端使用的是一个服务器端契约的接口,所以对于方法的名称,
方法传入参数的数量,以及参数的强类型,编译器都会默认的进行支持和智能提示。 我们的框架,将会很智能的为服务器端契约接口中的每一个操作来编写动态的MSIL语言,
这样一来,我们的客户端就能直接使用服务器端的接口来进行调用了。
如果,在调用期间,产生了任何*业务逻辑*上的错误,那么客户端是能够知晓的。 我们框架能够支持远程的异常回传,这样的话,当我们调用了一个契约操作,
但是却出错的时候,会有如下2种场景:
- 对于同步操作的调用,会在该操作的调用处触发一个异常,该异常为远端所抛出的异常
- 对于异步操作的调用,会为callback函数内部IAsyncCallResult接口的LastError字段赋值,这样的话,我们需要每次在回调函数中检测IAsyncCallResult.IsSuccess字段
什么样子才算是好的使用体验?
为了让使用者,更好的使用我们的框架,我们特地为此框架设计了一个VS的插件,该插件的目的就是为了能够通过远端服务所发布的元数据,来自动的为客户端
生成远端服务的接口。 这个插件的界面看起来是这样的:
当然,此插件现在已经能够正常使用了,我会在日后慢慢的进行优化。
发布元数据
对于每一个RPC服务来说,我们框架内部都为其提供了开放元数据功能的支持。一个已开放的RPC服务,如果也开放了元数据,那么意味着,我们就可以使用
刚才提到的插件来生成这个远程服务的本地契约接口啦(当然,也包括传递的第三方类参数等等)。 让一个RPC服务开放其自身的元数据,是很简单的一件事情,我们仅仅需要设置一个属性即可。 就如下:
ServiceHost.IsSupportExchange = true;
如果一个RPC服务开放了元数据,那么我们就可以直接使用本地的浏览器进行远端RPC服务元数据的浏览啦。 效果如下:
服务器端的运行截图如下:
我们也可以查看每一个契约操作的具体细节:
页面的具体元素还没有做更多的细节处理,很多页面的Style都是照扒的,请放心,日后我会进行改进的 :)
服务的调用形式
- 单例模式
- 多线程模式(多服务实例)
- 并发模式(多服务实例)
目前只是完成了第一和第三个,第二个方式的调用正在开发中。
性能测试
我们已经为此框架做了初步的性能测试 这个测试的结果是: callback func 13K/s,也就是说,1万3千个请求 + 1万个3千个请求响应消息 = 1秒
当然,对于框架内部,我们也加入了很多的性能计数器,这样的话,可以以一种很直观的方式来查看当前服务的调用性能。 在 13K/s的性能压力上,
我们并没有看到内存有任何的波动,整体资源也使用正常,线程数量很稳定。 由于测试环境硬件的因素,所以这个结果,仅仅是取决于当时测试的环境而给出的,
这个值也会根据各位不同的测试环境而重新待定。
测试平台:
CPU: Intel(R)Xeon(R)CPU X5670 @2.93GHz @2.93GHz (2处理器)
System: Windows Server 2008 R2 Enterprise
以下是测试代码,各位也能从源代码的ClientTest项目中得到该段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public void Test1() { LightTimer.NewTimer(1000, -1).Start( delegate { Console.Title = String.Format( "Callbacks {0}" , callbackcount); }, null ); Console.WriteLine( "开始执行......" ) IClientProxy<IHelloWorld> instance0 = new DefaultClientProxy<IHelloWorld>( new TcpBinding( "tcp://localhost:9999/Test" )); InObject inObject = new InObject { Inf1 = 1, Info2 = DateTime.Now, Info = "Info" , Info3 = new [] { "1" , "2" }, Info4 = new [] { 3, 4 } }; CodeTimer.Initialize(); CodeTimer.Time( "Client Performane Test: 1000000" , 1000000, delegate { instance0.Channel.TestIntellectObjectAsync(inObject, delegate (IAsyncCallResult callResult) { if (callResult.IsSuccess) Interlocked.Increment( ref callbackcount); else if (!callResult.IsSuccess && callResult.LastError != null ) throw callResult.LastError; }); }); Console.ReadLine(); } |
附:
此框架目前已经能够完成基本需求了,但是距离商用级别还有待时日。我开源此项目也并不是为了能够将此项目商用,只是希望这个项目能够越来越好,
如果,看了上述的描述,您对此项目很感兴趣,不妨下载下来源代码研究下,又或者是来加入我们的这个项目,一起改进。
在日后,我会为此框架提供越来越多的能力,当然,性能上面也会越来越好。很多的新功能,我也正在策划中,争取能以最快的速度完成。
很感谢各位耐心的看完这篇很长很长的文章。
项目地址: http://servicemodel.codeplex.com/
目前此项目并没有发布二进制的文件,因为我感觉此框架还没有达到初步商用的级别。 但是,仍然可以下载源代码供各位研究。
如果在尝试此框架的途中,有任何问题,您也可以选择与我联系,我将提供最优的技术支持。
QQ:250623008
Email: Kevin.Jee@live.cn
谢谢.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述