一、前言
至今為止程式設計開發已經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
網絡:區域網路
測試結果如下:
1萬次調用,也就2290MS,平均單次也就0.229毫秒,性能不錯。
7.總結
surging 0.0.0.1版本的釋出意味着分布式微服務已經走出了第一步,以後還有很多工作需要完善。我會花很多空餘時間去完善它。如果大家還有任何疑問或者感興趣的話,可以加入QQ群:615562965