使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。

分布式微服务现在成为了很多公司架构首先项,据我了解,很多java公司架构都是 Maven+Dubbo+Zookeeper基础上扩展的。

Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色。

关于更多Dubbo更多资料,可以查看Dubbo官方文档。值得一提的是,Dubbo官方文档也是一份非常好的分布式学习资料。

那么.net下能不能类似dubbo那样玩呢?目前好像没有找到.net下类似的实现。感觉很多公司应该有自己类似实现,但是没有开源出来吧。

我这里先做一个类似的基于接口的远程调用的实现,项目的服务实现使用WCF,IOC注入使用Autofac。

     一,解决方案整体设计

使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。

      (一)Cn.Code.Demo 

       如上图,Cn.Code.Demo 解决方案包含了所有的项目。

       使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。

如上图:1 Cn.Code.Common 是项目一些公共方法的实现。

2 Cn.Code.Core 是项目所有的对外接口,此项目只包含接口。

3 Cn.Code.Demo 是一个MVC项目,用于测试我们的接口调用。

4 Cn.Code.FirstService 第一个服务实现,实现了Cn.Code.Core 部分接口。

5 Cn.Code.SecondService 第二个服务实现,实现了Cn.Code.Core 部分接口。

        (二)Cn.Code.FirstService

使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。

此解决方案只包含 Cn.Code.Core 和Cn.Code.FirstService 。

       (三)Cn.Code.SecondService

使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。

此解决方案只包含 Cn.Code.Core 和Cn.Code.SecondService 。

       二,接口层 --Cn.Code.Core 

        使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。

        接口层只包含如下2个接口,这里我使用WCF进行测试,所以需要添加System.ServiceModel 模块引用。

IFirstService 接口如下:

     [ServiceContract]
public interface IFirstService
{
[OperationContract]
string GetData();
}

ISecondService接口如下:

     [ServiceContract]
public interface ISecondService
{
[OperationContract]
string GetData();
}

       三,服务实现 --Cn.Code.FirstService 和Cn.Code.SecondService

        为了方便测试,这里的服务实现都非常简单。

     public class FirstService :IFirstService
{
public string GetData()
{
return string.Format("您调用了 FirstService !");
} }
     public class SecondService :ISecondService
{
public string GetData()
{
return string.Format("您调用了 SecondService !");
}
}

         四,公共方法实现 --Cn.Code.Common

         首先是WCF动态调用的构建工厂。

    /// <summary>
/// Wcf动态调用构建
/// </summary>
public class WcfInvokeFactory
{
#region WCF服务工厂
public static T GetService<T>(string url)
{
return GetService<T>(url, "basicHttpBinding");
} public static T GetService<T>(string url, string bing)
{
try
{
if (string.IsNullOrEmpty(url)) throw new NotSupportedException("This url is not Null or Empty!");
EndpointAddress address = new EndpointAddress(url);
Binding binding = CreateBinding(bing);
ChannelFactory<T> factory = new ChannelFactory<T>(binding, address);
return factory.CreateChannel();
}
catch (Exception ex)
{
throw new Exception("创建服务工厂出现异常.");
}
}
#endregion #region 创建传输协议
/// <summary>
/// 创建传输协议
/// </summary>
/// <param name="binding">传输协议名称</param>
/// <returns></returns>
private static Binding CreateBinding(string binding)
{
Binding bindinginstance = null;
if (binding.ToLower() == "basichttpbinding")
{
BasicHttpBinding ws = new BasicHttpBinding();
ws.MaxBufferSize = ;
ws.MaxBufferPoolSize = ;
ws.MaxReceivedMessageSize = ;
ws.ReaderQuotas.MaxStringContentLength = ;
ws.CloseTimeout = new TimeSpan(, , );
ws.OpenTimeout = new TimeSpan(, , );
ws.ReceiveTimeout = new TimeSpan(, , );
ws.SendTimeout = new TimeSpan(, , ); bindinginstance = ws;
}
else if (binding.ToLower() == "nettcpbinding")
{
NetTcpBinding ws = new NetTcpBinding();
ws.MaxReceivedMessageSize = ;
ws.Security.Mode = SecurityMode.None;
bindinginstance = ws;
}
else if (binding.ToLower() == "wshttpbinding")
{
WSHttpBinding ws = new WSHttpBinding(SecurityMode.None);
ws.MaxReceivedMessageSize = ;
ws.Security.Message.ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows;
ws.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
bindinginstance = ws;
}
return bindinginstance; }
#endregion
}

WcfInvokeFactory

         其次是扩展Autofac一个注册接口的方法。

     /// <summary>
/// Autofac 扩展注入方法
/// </summary>
public static class RegistExtensions
{
/// <summary>
///
/// </summary>
/// <typeparam name="T">需要注入的接口类型</typeparam>
/// <param name="builder"></param>
/// <param name="url">接口服务调用URL</param>
/// <returns></returns>
public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> RegisterService<T>(this ContainerBuilder builder, string url)
{
return builder.Register(c => WcfInvokeFactory.GetService<T>(url)).As<T>();
}
}

RegistExtensions

五,MVC项目测试 --Cn.Code.Demo

         首先运行2个解决方案Cn.Code.FirstService与Cn.Code.SecondService,得到2个服务地址,将地址配置到项目的web.config中。

    <add key="FirstService" value="http://localhost:9970/FirstService.svc"/>
<add key="SecondService" value="http://localhost:10014/SecondService.svc"/>

其次在全局文件Global.asax中,利用自定义Autofac注册扩展,分别注入不同的服务地址。

 using Autofac;
using Autofac.Integration.Mvc; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Web.Security;
using System.Web.SessionState;
using System.Configuration; using Cn.Code.Core;
using Cn.Code.Common; namespace Cn.Code.Demo
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var builder = new ContainerBuilder();
SetupResolveRules(builder);
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles); } private void SetupResolveRules(ContainerBuilder builder)
{
//利用自定义注册扩展,分别注入不同的服务地址
builder.RegisterService<IFirstService>(ConfigurationManager.AppSettings["FirstService"]);
builder.RegisterService<ISecondService>(ConfigurationManager.AppSettings["SecondService"]);
}
}
}

Global.asax

接下来就是调用我们的接口了。

 using Cn.Code.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc; namespace Cn.Code.Demo.Controllers
{
public class HomeController : Controller
{
private IFirstService _iFirstService;
private ISecondService _iSecondService;
public HomeController() { }
public HomeController(IFirstService iFirstService, ISecondService iSecondService)
{
this._iFirstService = iFirstService;
this._iSecondService = iSecondService;
}
public ActionResult Index()
{
//第一个接口
ViewBag.FirstText = _iFirstService.GetData();
//第二个接口
ViewBag.SecondText = _iSecondService.GetData();
return View();
}
}
}

HomeController

视图页面输出结果。

@{
ViewBag.Title = "Home Page";
}
<div class="row">
@ViewBag.FirstText
<br />
@ViewBag.SecondText
</div> 使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。
 

调用成功。

 五,总结

       这样的话,我们的远程调用服务接口就和平时正常调用使用项目内接口方法一样了。同时这样拆分,可以将不同的服务注册到不同的节点。这个节点也可以是一个集群。

当然,这里只是个测试小demo,后续应该结合Zookeeper或者其他的分布式服务框架,实现均衡负载的管理。

注:代码比较low,望见谅。代码下载地址Cn.Code.Demo.zip

上一篇:命令行编译vs2013项目


下一篇:ELK-elkstack-使用消息队列