一、前言
至今為止程式設計開發已經11個年頭,從 VB6.0,ASP時代到ASP.NET再到MVC, 從中見證了.NET技術發展,從無畏無知的懵懂少年,到現在的中年大叔,從中的酸甜苦辣也只有本人自知。隨著歲月的成長,技術也從原來的三層設計到現在的領域驅動設計,從原來的關係型資料庫SQL 2000到現在的NOSQL (mongodb,couchbase,redis),從原來基於SOAP協議的web service到現在基於restful 協議的web api,wcf,再到現在rpc微服務。技術的成長也帶來歲月的痕跡。
現在微軟又出了.NET CORE,為了緊跟微軟的步伐,研究了將近1年,從中看了不少開原始碼,如NetEscapades.Configuration,eShopOnContainers,rabbit.RPC等等,從中學到了不少知識,後面利用所學加上自己的想法,開發出分散式微服務框架surging。開源地址:點選開啟連結。下面會以三篇文章的形式介紹
surging
1.基於.NET CORE微服務框架 -surging的介紹和簡單示例 (開源)
2.剝析surging的架構思想
3.後續surging的架構完善工作
二、什麼是surging
surging從中文譯義來說,衝擊,洶湧,也可以翻譯成風起雲湧。我所希望的是.net core 能成為最流行的技術。
surging從技術層面來說就是基於RPC協議的分散式微服務技術框架,框架依賴於Netty 進行非同步通訊,採用Zookeeper作為服務註冊中心,整合了雜湊,隨機和輪詢作為負載均衡演算法
1.服務化應用基本框架
框架的執行過程如下:
1.服務提供者啟動,根據RPC協議通過配置的IP和port繫結到netty上
2.註冊服務資訊儲存至Zookeeper
3.客戶端CreateProxy呼叫服務時,從記憶體中拿到上次通知的所有效的服務地址,根據路由資訊和負載均衡機制選擇最終呼叫的服務地址,發起呼叫
2.簡單示例
建立IModuleServices
IUserService.cs:
[ServiceBundle] //服務標記 public interface IUserService { Task<string> GetUserName(int id); Task<bool> Exists(int id); Task<int> GetUserId(string userName); Task<DateTime> GetUserLastSignInTime(int id); Task<UserModel> GetUser(int id); Task<bool> Update(int id, UserModel model); Task<IDictionary<string, string>> GetDictionary(); Task TryThrowException(); }
建立領域物件
UserModel:
[ProtoContract] public class UserModel { [ProtoMember(1)] public string Name { get; set; } [ProtoMember(2)] public int Age { get; set; } }
AssemblyInfo.cs,擴充套件AssemblyModuleType來標識模組,根據AssemblyModuleType進行相關規則的反射註冊
[assembly: AssemblyTitle("Surging.IModuleServices.Common")] [assembly: AssemblyDescription("業務模組介面")] [assembly: AssemblyModuleType(ModuleType.InterFaceService)] // 如果此專案向 COM 公開,則下列 GUID 用於型別庫的 ID [assembly: Guid("2103624d-2bc2-4164-9aa5-1408daed9dee")]
建立Domain Service
PersonService.cs
[ModuleName("Person")] //標識例項化名稱 public class PersonService : ServiceBase,IUserService { #region Implementation of IUserService private readonly UserRepository _repository; public PersonService(UserRepository repository) { this._repository = repository; } public Task<string> GetUserName(int id) { return GetService<IUserService>("User").GetUserName(id); } public Task<bool> Exists(int id) { return Task.FromResult(true); } public Task<int> GetUserId(string userName) { return Task.FromResult(1); } public Task<DateTime> GetUserLastSignInTime(int id) { return Task.FromResult(DateTime.Now); } public Task<UserModel> GetUser(int id) { return Task.FromResult(new UserModel { Name = "fanly", Age = 18 }); } public Task<bool> Update(int id, UserModel model) { return Task.FromResult(true); } public Task<IDictionary<string, string>> GetDictionary() { return Task.FromResult<IDictionary<string, string>>(new Dictionary<string, string> { { "key", "value" } }); } public async Task Try() { Console.WriteLine("start"); await Task.Delay(5000); Console.WriteLine("end"); } public Task TryThrowException() { throw new Exception("使用者Id非法!"); } #endregion Implementation of IUserService } }
UserService.cs
[ModuleName("User")]//標識例項化名稱 public class UserService: IUserService { #region Implementation of IUserService private readonly UserRepository _repository; public UserService(UserRepository repository) { this._repository = repository; } public Task<string> GetUserName(int id) { return Task.FromResult($"id:{id} is name fanly."); } public Task<bool> Exists(int id) { return Task.FromResult(true); } public Task<int> GetUserId(string userName) { return Task.FromResult(1); } public Task<DateTime> GetUserLastSignInTime(int id) { return Task.FromResult(DateTime.Now); } public Task<UserModel> GetUser(int id) { return Task.FromResult(new UserModel { Name = "fanly", Age = 18 }); } public Task<bool> Update(int id, UserModel model) { return Task.FromResult(true); } public Task<IDictionary<string, string>> GetDictionary() { return Task.FromResult<IDictionary<string, string>>(new Dictionary<string, string> { { "key", "value" } }); } public async Task Try() { Console.WriteLine("start"); await Task.Delay(5000); Console.WriteLine("end"); } public Task TryThrowException() { throw new Exception("使用者Id非法!"); } #endregion Implementation of IUserService } }
AssemblyInfo.cs,擴充套件AssemblyModuleType來標識模組,根據AssemblyModuleType進行相關規則的反射註冊
[ModuleName("User")]//標識例項化名稱 public class UserService: IUserService { #region Implementation of IUserService private readonly UserRepository _repository; public UserService(UserRepository repository) { this._repository = repository; } public Task<string> GetUserName(int id) { return Task.FromResult($"id:{id} is name fanly."); } public Task<bool> Exists(int id) { return Task.FromResult(true); } public Task<int> GetUserId(string userName) { return Task.FromResult(1); } public Task<DateTime> GetUserLastSignInTime(int id) { return Task.FromResult(DateTime.Now); } public Task<UserModel> GetUser(int id) { return Task.FromResult(new UserModel { Name = "fanly", Age = 18 }); } public Task<bool> Update(int id, UserModel model) { return Task.FromResult(true); } public Task<IDictionary<string, string>> GetDictionary() { return Task.FromResult<IDictionary<string, string>>(new Dictionary<string, string> { { "key", "value" } }); } public async Task Try() { Console.WriteLine("start"); await Task.Delay(5000); Console.WriteLine("end"); } public Task TryThrowException() { throw new Exception("使用者Id非法!"); } #endregion Implementation of IUserService } }
3.服務端
using Autofac; using Autofac.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Surging.Core.Caching.Configurations; using Surging.Core.CPlatform; using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.DotNetty; using Surging.Core.ProxyGenerator.Utilitys; using Surging.Core.System.Ioc; using Surging.Core.Zookeeper; using Surging.Core.Zookeeper.Configurations; using System; using System.Net; using System.Text; using System.Threading.Tasks; namespace Surging.Services.Server { public class Program { static void Main(string[] args) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var services = new ServiceCollection(); var builder = new ContainerBuilder(); ConfigureLogging(services); builder.Populate(services); ConfigureService(builder); ServiceLocator.Current = builder.Build(); ConfigureCache(); ServiceLocator.GetService<ILoggerFactory>() .AddConsole((c, l) => (int)l >= 3); StartService(); Console.ReadLine(); } /// <summary> /// 配置相關服務 /// </summary> /// <param name="builder"></param> /// <returns></returns> private static void ConfigureService(ContainerBuilder builder) { builder.Initialize(); builder.RegisterServices(); builder.RegisterRepositories(); builder.RegisterModules(); builder.AddCoreServce() .AddServiceRuntime() .UseSharedFileRouteManager("c:\\routes.txt")//配置本地路由檔案路徑 .UseDotNettyTransport()//配置Netty .UseZooKeeperRouteManager(new ConfigInfo("192.168.1.6:2181", "/dotnet/unitTest/serviceRoutes"));//配置ZooKeeper builder.Register(p => new CPlatformContainer(ServiceLocator.Current)); } /// <summary> /// 配置日誌服務 /// </summary> /// <param name="services"></param> public static void ConfigureLogging(IServiceCollection services) { services.AddLogging(); } /// <summary> /// 配置快取服務 /// </summary> public static void ConfigureCache() { new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) .AddCacheFile("cacheSettings.json", optional: false); } /// <summary> /// 啟動服務 /// </summary> public static void StartService() { var serviceHost = ServiceLocator.GetService<IServiceHost>(); Task.Factory.StartNew(async () => { await serviceHost.StartAsync(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 98)); Console.WriteLine($"服務端啟動成功,{DateTime.Now}。"); }).Wait(); } } }
4.客戶端
using Autofac; using Autofac.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Surging.Core.CPlatform; using Surging.Core.DotNetty; using Surging.Core.ProxyGenerator; using Surging.Core.ProxyGenerator.Utilitys; using Surging.Core.System.Ioc; using System.Text; namespace Surging.Services.Client { public class Program { static void Main(string[] args) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var services = new ServiceCollection(); var builder = new ContainerBuilder(); ConfigureLogging(services); builder.Populate(services); ConfigureService(builder); ServiceLocator.Current = builder.Build(); ServiceLocator.GetService<ILoggerFactory>() .AddConsole((c, l) => (int)l >= 3); } /// <summary> /// 配置相關服務 /// </summary> /// <param name="builder"></param> /// <returns></returns> private static void ConfigureService(ContainerBuilder builder) { builder.Initialize(); builder.RegisterServices(); builder.RegisterRepositories(); builder.RegisterModules(); var serviceBulider = builder .AddClient() .UseSharedFileRouteManager("c:\\routes.txt") .UseDotNettyTransport(); } /// <summary> /// 配置日誌服務 /// </summary> /// <param name="services"></param> public static void ConfigureLogging(IServiceCollection services) { services.AddLogging(); } /// <summary> /// 配置服務代理 /// </summary> /// <param name="builder"></param> /// <returns></returns> public static IServiceProxyFactory RegisterServiceProx(ContainerBuilder builder) { var serviceProxyFactory = ServiceLocator.GetService<IServiceProxyFactory>(); serviceProxyFactory.RegisterProxType(builder.GetInterfaceService().ToArray()); return serviceProxyFactory; } } }
遠端服務呼叫
ServiceLocator.GetService<IServiceProxyFactory>().CreateProxy<T>(key)
本地模組和服務呼叫
ServiceLocator.GetService<T>(key)
5.負載均衡
surging提供3種負載均衡方式:
Random:隨機,呼叫量越大分佈越均勻,預設是這種方式
Polling:輪詢,存在比較慢的機器容易在這臺機器的請求阻塞較多
HashAlgorithm:一致性雜湊,對於相同引數的請求路由到一個服務提供者上。
6.其他功能
surging還會提供分散式快取,AOP資料攔截,基於rabbitmq訂閱釋出, 監控服務,後續完善後再來講解。
6.效能測試
測試環境
CPU:Intel Core i7-4710MQ
記憶體:16G
硬碟:1T SSD+512G HDD
網路:區域網
7.總結
surging 0.0.0.1版本的釋出意味著分散式微服務已經走出了第一步,以後還有很多工作需要完善。我會花很多空餘時間去完善它。如果大家還有任何疑問或者感興趣的話,可以加入QQ群:542283494