.Net下几个服务框架介绍

在公司的服务多了以后,为了调用上的方便,同时为了以后的服务治理,一般都会使用一些服务框架,这里主要介绍我知道的几个服务框架,简析一下这些服务框架的基本概念。
以下两个服务框架,我已经见过有公司投入到生产环境,所以对于稳定性,应该不需要有太大的担心。 
 
    ServiceStack可能没有用过,但是它的另外两个组件,大家应该都用过,ServiceStack.Redis( Redis 访问工具),ServiceStack.Text(Json序列化工具),ServiceStack就是一服务框架,可以很方便的用他来创建服务,服务是基于http的,另外提供了客户端调用, 数据的序列化方式包含Json , xml , 二进制,Protobuf ,并且创建出来的服务带有一定的描述。
    1个http请求,有两个东西很关键,请求路径和参数,对于ServiceStack, 参数即对象,即它要传递的参数都封装到一个类里面, 另外在类上打标签,标签内容就是请求路径,这样客户端在调用的时候,反射出请求路径和参数,即可发起调用。 
    因为ServiceStack本身已经提供了demo, 所以这里就不写demo了, 大家可以学习一下。 
 
    Hessian是一个序列化工具,同时也是一个服务框架,提供有多语言的实现,包括.net,这个组件在.Net领域貌似不怎么有名,可能是很久没有更新了。
    使用Hessian的时候,首先需要定义接口,然后一式两份,服务端来实现这些接口, 客户端引用这些接口,然后客户端使用了RealProxxy类做代理, 所有接口调用最终会调用代理类里面的Invoke方法, 在Invoke方法里面获取要调用的方法的名称, 参数等内容,经过序列化发送到服务端一个统一的url上, 这个url会以hessian结尾, 所以需要在web.config中配置一个handle来拦截请求, 拦截到请求后,反序列化参数, 然后通过反射发起调用。 
    这里调用可以参考这篇博客 http://www.cnblogs.com/lxsfg/archive/2008/08/27/1277777.html
 
其他的服务框架还有很多, 比如thrift,JSON-RPC 但是因为没有用过, 所以这里不做评论。 
以下三个框架是我在浏览博客园的时候发现的,并且都是开源的,对于学习服务框架是个不错的项目,另外我不经常上博客园,下面3个框架也是机缘巧合下看到的,博客园应该还有其他优秀的服务框架只是我没有发现而已,如果你有发现,不妨写到评论区,大家一起学习。 
    和Hessin的有些像,也是先定义服务接口,然后一式两份,服务端通过继承接口来实现具体的逻辑,对于客户端,他是通过反射,提取出接口内的所有方法,以及方法参数及返回值信息,然后通过动态生成代码的方式,生成一份代理类,再动态编译,放入到容器中。 关于生成的那个代理类,所有方法都是一个统一的实现,即调用一个Invoke方法把方法和参数发给底层,底层组装发送到服务端。这里生成代码的方式比较好玩,可以学习一下。
    另外,这个框架同时集成了服务治理,服务注册到Zookeeper, 客户端通过Zoopeeper拿到具体的服务地址进行调用。 其实到这里,这已经不仅仅是一个服务框架,同时包含了服务治理功能。 
    我也仅仅是粗读了代码, 细节还没理解透,想要详细了解的可以看源码。 
 
  看介绍基本的功能都有,但是看作者对这个项目的介绍是说这个项目属于研究性的,对于服务调用的客户端,也是通过代码生成的方式, 具体可以参看代码。
  另外再多介绍一下这位博友,他在博客园也有博客 http://www.cnblogs.com/chejiangyi/  ,其下开源了数个项目 http://git.oschina.net/chejiangyi 都是一些很不错的项目,比如调度服务,配置服务,监控服务,对于上点规模的互联网公司,这些服务都是需要的,更可贵的是这些服务都是基于.Net的, 所以对于一些使用.Net开发的互联网公司来说,有不错的借鉴意义。
 
  这个是我最近发现的,该框架使用Actor模型,序列化使用了protobuf-net,传输方式是基于tcp , tcp框架使用的他自己的Cowboy.WebSockets ,看介绍也实现了服务中心,但是因为我对Actor模型并不理解,所以这里就不画蛇添足了, 有兴趣的同学可以看作者的博客。
看了这么多的服务框架,自己手痒也写了一个,仅仅是一个服务调用的且是实验性质的,可以作为一个参考。 
框架的原理和Hession类似,先要定义接口,然后服务端实现接口, 这里我取了个巧,我直接让Web API中的Controller继承了接口,所以就不需要专门定义一个Handle来拦截请求,同时也不影响Web Api的访问,所以你即使使用了这个框架,也不妨碍你直接用http的方式进行访问。客户端的调用方式和Hession类似,也是需要定义代理,然后收集参数,
首先定义接口
 
有了接口,接下来就是实现,这里我使用的是Web API, 我们定义一个Controller, 这个Controller除了继承APIController, 还继承了接口,在这个Controller里,实现接口具体的逻辑。 
 
 
同时把这个接口所在的dll复制到客户端,这里要介绍一个功能,就是RealProxy, 这是.Net自带的代理类,同时也是Hession采用的机制。 
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using Newtonsoft.Json;
using RestSharp;

namespace SimleRPC
{
    public class SimpleProxy : RealProxy
    {
        private readonly Uri _uri;

        public SimpleProxy(Type type, Uri uri) : base(type)
        {
            this._uri = uri;
        }

        public override IMessage Invoke(IMessage msg)
        {
            //msg参数包含调用方法的信息,这里通过封装,使信息更丰富一些
            IMethodCallMessage methodMessage = new MethodCallMessageWrapper((IMethodCallMessage)msg);
            MethodInfo methodInfo = (MethodInfo)methodMessage.MethodBase;

            ParameterInfo[] paramsInfo = methodInfo.GetParameters();  //获取方法调用参数

            //拼装path  类名即controller名, 方法名即action名  这样就表示出具体的url
            string path = "/api/" + methodInfo.DeclaringType.Name + "/" + methodInfo.Name;

            //请求类型,post or get
            bool isHttpGet = methodInfo.CustomAttributes.Any(customAttributeData => customAttributeData.AttributeType.Name == "HttpGetAttribute");

            var client = new RestClient(_uri);
            var request = new RestRequest(path, isHttpGet ? Method.GET : Method.POST);

            //构建参数
            //web api对于传参的一切规则  参考 http://www.cnblogs.com/babycool/p/3922738.html    http://www.cnblogs.com/landeanfen/p/5337072.html 两篇博客
            if (isHttpGet)
            {
                //这里默认get请求的参数都是基本类型
                for (int i = 0; i < paramsInfo.Length; i++)
                {
                    request.AddParameter(paramsInfo[i].Name, methodMessage.Args[i]);
                }
            }
            else
            {
                //对于post请求,要么没有参数(当然基本没有这种情况),要么只有一个参数(这些是受web api的限制)
                if (paramsInfo.Length > 0)
                {
                    request.AddJsonBody(methodMessage.Args[0]);
                }
            }

            // 发送请求 拿到结果
            IRestResponse response = client.Execute(request);
            var content = response.Content;

            Type returnType = methodInfo.ReturnType;     //获取调用方法返回类型,根据类型反序列化结果

            object returnValue;
            if (IsBaseType(returnType))
            {
                returnValue = Convert.ChangeType(content, returnType);
            }
            else
            {
                returnValue = JsonConvert.DeserializeObject(content, returnType); //如果是一个对象,则用json进行反序列化
            }

            return new ReturnMessage(returnValue, methodMessage.Args, methodMessage.ArgCount, methodMessage.LogicalCallContext, methodMessage);
        }

        /// <summary>
        /// 判断是否是基础类型(int long  double), string 是引用类型,但是也归到基础类型里面
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static bool IsBaseType(Type type)
        {
            if (type == typeof(string) || type.IsPrimitive)
                return true;
            return false;
        }
    }
}
所有的方法调用都会调用Invoke方法,在Invoke方法内,可以拿到调用方法的具体信息,比如参数,返回值类型等。 然后通过反射和拼装,组成一个Http请求,这里我默认接口类名即Controller的名字, 接口方法名即Action的名字。,最后再通过RestSharp把请求发送出去。 最后根据http结果反序列化为方法返回值需要的值。 
 
其实对于服务端来首,Web API是否继承了接口都不重要,如果不继承,则接口的签名要和Web API中方法的签名保持一致。 
通过我写的Demo,方法是可以调的通的, 如果有人对这个感兴趣,可以再多测试一些情况。  
现在很多的互联网公司都有自己的RPC框架,有些是采用开源的,有些因为历史问题,自己写的,对于通信方式,有基于Http的,也有基于TCP的, 还有两种协议都兼容的。 序列化方式也是多种多样, 我上面只列举了5个,其实在github上搜索,还有很多优秀的RPC。
RPC仅是项目发展过程中一个阶段。 有了RPC以后,可以在此基础上做很多的事情,比如:
   服务治理 所有的服务在启动的时候注册到服务中心,客户端在启动的时候,从注册中心获取真实的地址,直接调用,不经过Nginx等代理,这里可以在获取真实地址上做一些权限限制,比如哪些客户端能用,哪些客户端不能用,能用多少个,这里可以参考dubbo。
   Http请求路径 现在微服务很流行, 前端一个请求,可能要经过后端好几个服务,可以在http头上加上RequestId和RequestIndex, 把这些服务串起来,例如 A->B->C,A服务调用B服务的时候,如果发现http head里面没有RequestId, 则可以通过GuId生成一个,同时RequestIndex加1 ,B服务调用C服务端时候, 因为RequestId已经有了,就直接传递下去,同时RequestIndex加1 ,把这些信息记录到日志中,通过分析,整个调用就串起来了,通过完整的数据就可以绘制出整个服务的调用链路图。 
 除了链路图,因为我们可以拦截到每个服务的调用,所以我们可以记录服务调用耗时,再加上链路图,整个的服务信息会更加完善。 
 
 
 
posted @ 2016-11-02 10:31  秋夜  阅读(12344)  评论(19编辑  收藏  举报