分布式微服務現在成為了很多公司架構首先項,據我了解,很多java公司架構都是 Maven+Dubbo+Zookeeper基礎上擴展的。
Dubbo是Alibaba開源的分布式服務框架,它最大的特點是按照分層的方式來架構,使用這種方式可以使各個層之間解耦合(或者最大限度地松耦合)。從服務模型的角度來看,Dubbo采用的是一種非常簡單的模型,要么是提供方提供服務,要么是消費方消費服務,所以基於這一點可以抽象出服務提供方(Provider)和服務消費方(Consumer)兩個角色。
關於更多Dubbo更多資料,可以查看Dubbo官方文檔。值得一提的是,Dubbo官方文檔也是一份非常好的分布式學習資料。
那么.net下能不能類似dubbo那樣玩呢?目前好像沒有找到.net下類似的實現。感覺很多公司應該有自己類似實現,但是沒有開源出來吧。
我這里先做一個類似的基於接口的遠程調用的實現,項目的服務實現使用WCF,IOC注入使用Autofac。
一,解決方案整體設計

(一)Cn.Code.Demo
如上圖,Cn.Code.Demo 解決方案包含了所有的項目。

如上圖: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

此解決方案只包含 Cn.Code.Core 和Cn.Code.FirstService 。
(三)Cn.Code.SecondService

此解決方案只包含 Cn.Code.Core 和Cn.Code.SecondService 。
二,接口層 --Cn.Code.Core

接口層只包含如下2個接口,這里我使用WCF進行測試,所以需要添加System.ServiceModel 模塊引用。
IFirstService 接口如下:
1 [ServiceContract] 2 public interface IFirstService 3 { 4 [OperationContract] 5 string GetData(); 6 }
ISecondService接口如下:
1 [ServiceContract] 2 public interface ISecondService 3 { 4 [OperationContract] 5 string GetData(); 6 }
三,服務實現 --Cn.Code.FirstService 和Cn.Code.SecondService
為了方便測試,這里的服務實現都非常簡單。
1 public class FirstService :IFirstService 2 { 3 public string GetData() 4 { 5 return string.Format("您調用了 FirstService !"); 6 } 7 8 }
1 public class SecondService :ISecondService 2 { 3 public string GetData() 4 { 5 return string.Format("您調用了 SecondService !"); 6 } 7 }
四,公共方法實現 --Cn.Code.Common
首先是WCF動態調用的構建工廠。
1 /// <summary> 2 /// Wcf動態調用構建 3 /// </summary> 4 public class WcfInvokeFactory 5 { 6 #region WCF服務工廠 7 public static T GetService<T>(string url) 8 { 9 return GetService<T>(url, "basicHttpBinding"); 10 } 11 12 13 public static T GetService<T>(string url, string bing) 14 { 15 try 16 { 17 if (string.IsNullOrEmpty(url)) throw new NotSupportedException("This url is not Null or Empty!"); 18 EndpointAddress address = new EndpointAddress(url); 19 Binding binding = CreateBinding(bing); 20 ChannelFactory<T> factory = new ChannelFactory<T>(binding, address); 21 return factory.CreateChannel(); 22 } 23 catch (Exception ex) 24 { 25 throw new Exception("創建服務工廠出現異常."); 26 } 27 } 28 #endregion 29 30 #region 創建傳輸協議 31 /// <summary> 32 /// 創建傳輸協議 33 /// </summary> 34 /// <param name="binding">傳輸協議名稱</param> 35 /// <returns></returns> 36 private static Binding CreateBinding(string binding) 37 { 38 Binding bindinginstance = null; 39 if (binding.ToLower() == "basichttpbinding") 40 { 41 BasicHttpBinding ws = new BasicHttpBinding(); 42 ws.MaxBufferSize = 2147483647; 43 ws.MaxBufferPoolSize = 2147483647; 44 ws.MaxReceivedMessageSize = 2147483647; 45 ws.ReaderQuotas.MaxStringContentLength = 2147483647; 46 ws.CloseTimeout = new TimeSpan(0, 30, 0); 47 ws.OpenTimeout = new TimeSpan(0, 30, 0); 48 ws.ReceiveTimeout = new TimeSpan(0, 30, 0); 49 ws.SendTimeout = new TimeSpan(0, 30, 0); 50 51 bindinginstance = ws; 52 } 53 else if (binding.ToLower() == "nettcpbinding") 54 { 55 NetTcpBinding ws = new NetTcpBinding(); 56 ws.MaxReceivedMessageSize = 65535000; 57 ws.Security.Mode = SecurityMode.None; 58 bindinginstance = ws; 59 } 60 else if (binding.ToLower() == "wshttpbinding") 61 { 62 WSHttpBinding ws = new WSHttpBinding(SecurityMode.None); 63 ws.MaxReceivedMessageSize = 65535000; 64 ws.Security.Message.ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows; 65 ws.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows; 66 bindinginstance = ws; 67 } 68 return bindinginstance; 69 70 } 71 #endregion 72 }
其次是擴展Autofac一個注冊接口的方法。
1 /// <summary> 2 /// Autofac 擴展注入方法 3 /// </summary> 4 public static class RegistExtensions 5 { 6 /// <summary> 7 /// 8 /// </summary> 9 /// <typeparam name="T">需要注入的接口類型</typeparam> 10 /// <param name="builder"></param> 11 /// <param name="url">接口服務調用URL</param> 12 /// <returns></returns> 13 public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> RegisterService<T>(this ContainerBuilder builder, string url) 14 { 15 return builder.Register(c => WcfInvokeFactory.GetService<T>(url)).As<T>(); 16 } 17 }
五,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注冊擴展,分別注入不同的服務地址。
1 using Autofac; 2 using Autofac.Integration.Mvc; 3 4 using System; 5 using System.Collections.Generic; 6 using System.Linq; 7 using System.Reflection; 8 using System.Web; 9 using System.Web.Http; 10 using System.Web.Mvc; 11 using System.Web.Optimization; 12 using System.Web.Routing; 13 using System.Web.Security; 14 using System.Web.SessionState; 15 using System.Configuration; 16 17 using Cn.Code.Core; 18 using Cn.Code.Common; 19 20 21 namespace Cn.Code.Demo 22 { 23 public class MvcApplication : System.Web.HttpApplication 24 { 25 protected void Application_Start() 26 { 27 var builder = new ContainerBuilder(); 28 SetupResolveRules(builder); 29 builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired(); 30 builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces(); 31 var container = builder.Build(); 32 DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 33 34 35 36 AreaRegistration.RegisterAllAreas(); 37 GlobalConfiguration.Configure(WebApiConfig.Register); 38 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 39 RouteConfig.RegisterRoutes(RouteTable.Routes); 40 BundleConfig.RegisterBundles(BundleTable.Bundles); 41 42 43 } 44 45 private void SetupResolveRules(ContainerBuilder builder) 46 { 47 //利用自定義注冊擴展,分別注入不同的服務地址 48 builder.RegisterService<IFirstService>(ConfigurationManager.AppSettings["FirstService"]); 49 builder.RegisterService<ISecondService>(ConfigurationManager.AppSettings["SecondService"]); 50 } 51 } 52 }
接下來就是調用我們的接口了。
1 using Cn.Code.Core; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Web.Mvc; 7 8 namespace Cn.Code.Demo.Controllers 9 { 10 public class HomeController : Controller 11 { 12 private IFirstService _iFirstService; 13 private ISecondService _iSecondService; 14 public HomeController() { } 15 public HomeController(IFirstService iFirstService, ISecondService iSecondService) 16 { 17 this._iFirstService = iFirstService; 18 this._iSecondService = iSecondService; 19 } 20 public ActionResult Index() 21 { 22 //第一個接口 23 ViewBag.FirstText = _iFirstService.GetData(); 24 //第二個接口 25 ViewBag.SecondText = _iSecondService.GetData(); 26 return View(); 27 } 28 } 29 }
視圖頁面輸出結果。
@{ ViewBag.Title = "Home Page"; } <div class="row"> @ViewBag.FirstText <br /> @ViewBag.SecondText </div>
調用成功。
五,總結
這樣的話,我們的遠程調用服務接口就和平時正常調用使用項目內接口方法一樣了。同時這樣拆分,可以將不同的服務注冊到不同的節點。這個節點也可以是一個集群。
當然,這里只是個測試小demo,后續應該結合Zookeeper或者其他的分布式服務框架,實現均衡負載的管理。
注:代碼比較low,望見諒。代碼下載地址Cn.Code.Demo.zip

