net开发框架never

【一】 摘要

never是纯c#语言开发的一个框架,同时可在netcore下运行。 该框架github地址:https://github.com/shelldudu/nevergit

同时,配合never_web,never_component,never_application (demo)可对比代码学习。github

引用其图片说明该构架所涉及到的工具web

never      

使用emit技术所实现的核心功能点    sql

never

其中使用包含了一些开发设计模式,好比message的订阅与发布,熔断机制等。shell

【二】总体设计

一、以ApplicationStartup开始,启动服务,注册不一样组件,这里是netcore的部分代码数据库

/// <summary>
/// 该方法被ConfigureServices里面的base.ConfigureServicese调用,因为ConfigureServices方法会使用不一样的组件方案,因此在其后面启支,是将这些组件方案所注册的ioc规则加入到本身的ioc规则里面去 /// 同时替换了系统IServiceCollection本身生成的IServiceProvider对象 /// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Startup_OnStarting(object sender, Never.StartupEventArgs e) { //ddd的command里面使用了恢复(即一些命令出错后被保存后过段时间再执行),当前使用sqlite本地数据库方式
    var commandfile = new FileInfo(AppContext.BaseDirectory + "\\App_Data\\command_demo.db"); //ddd的event跟上面的同样
    var eventfile = new FileInfo(AppContext.BaseDirectory + "\\App_Data\\event_demo.db"); //使用nlog组件
    var logfile = new FileInfo(AppContext.BaseDirectory + "\\App_Config\\nlog.config"); //配置文件的读取
    var configReader = new AppConfigReader(this.Configuration); }

 咱们先对程序集过滤与开启IoCjson

//注册程序集过滤,由于整个启动过程会分析程序集里面的Type对象,不少dll咱们不用分析,只焦点到咱们如今注入的2个规则就行,"Never" + "B2C",正则只要匹配到该字符就加加载到待分析的dll集合中
e.Startup.RegisterAssemblyFilter("B2C".CreateAssemblyFilter()).RegisterAssemblyFilter("Never".CreateAssemblyFilter()); //ioc分2种启动方法,主要缘由以下:(1)服务启动有前后顺序,不一样的系统组件所注册的顺序不一样的,但有些组件要求在全部环境下都只有第一或最后启动(2)因为使用环境自动注册这种设计下,一些组件要手动注册会带本身的规则就会被自动注册覆盖
e.Startup.UseEasyIoC( (x, y, z) => { //先启动该服务注册组件,
 }, (x, y, z) => { //再按本身的个性化注册组件,好比Controller在下面UseApiDependency后会自动注入,可是我想HomeController注入的时候使用memecahed,这种状况就要手动注入了 //x.RegisterType<Controllers.HomeController, Controllers.HomeController>().WithParameter<Never.Caching.ICaching>("memcached"); //注入query与repository实例,为何不用自动注入?哈哈,由于在framework或netcore等各类不一样的环境下你们读取配置文件是不一样的,一旦写死在B2C.Message.SqlData.Query里面读取配置文件,则使用不一样的host技术就出现极大问题, //好比netcore没有connectionString这种配置(或者有人说能够手动引用System.Configuration,这不是嫌麻烦吗)
        x.RegisterInstance(new B2C.Message.SqlData.Query.QueryDaoBuilder(Infrastructure.SqldbType.sqlserver, () => configReader["message_conn"])); x.RegisterInstance(new B2C.Message.SqlData.Repository.RepositoryDaoBuilder(Infrastructure.SqldbType.sqlserver, () => configReader["message_conn"])); });

注册各类组件c#

//使用环境下自动注册组件,
e.Startup.UseAutoInjectingAttributeUsingIoC(new IAutoInjectingEnvironmentProvider[] { //在message该环境下,全部单例注册组件只有匹配message的才注册,(1)有些组件是线程的,那么不会被描述和注入中,除非再加个线程provider;(2)即便是单例provider,但所运行不是message环境,因此也不会注入
    SingletonAutoInjectingEnvironmentProvider.UsingRuleContainerAutoInjectingEnvironmentProvider("message"), }) //使用统一配置中心读取配置文件,实用性在后面有讲到
.UseConfigClient(new IPEndPoint(IPAddress.Parse(configReader["config_host"]), configReader.IntInAppConfig("config_port")), out var configFileClient); configFileClient.Startup(TimeSpan.FromMinutes(10), new[] { new ConfigFileClientRequest { FileName = "message_api" } }, (c, t) => { var content = t; if (c != null && c.FileName == "message_api") { System.IO.File.WriteAllText(System.IO.Path.Combine(this.Environment.ContentRootPath, "appsettings.app.json"), content); } }).Push("message_api").GetAwaiter().GetResult(); e.Startup .UseCounterCache() //使用countcache
    .UseConcurrentCache() //使用安全countcache
    .UseDataContractJson() //使用datacontract技术的序列化,实现了IJsonSerialize接口
    .UseEasyJson(string.Empty) //使用easyjson技术的序列化,实现了IJsonSerialize接口
    .UseNLog(logfile) //使用nlog
    .UseAppConfig(configReader) //将IConfigReader注入
    .UseForceCheckAggregateRootImplIHandle() //这几个Force都是为了检查ddd开发一些要求,好比是否继承某个类,某些接口
    .UseForceCheckCommandAppDomainAttribute() //检查全部的command是否带了特定attribute
    .UseForceCheckCommandEvenWithNoParamaterCtor() //检查全部的commandhandler所要的构造参数是否被注入中
    .UseForceCheckCommandHandlerCtor() //检查全部的eventhandler所要的构造参数是否被注入中
    .UseForceCheckEventAppDomainAttribute()//检查全部的event是否带了特定attribute
    .UseForceCheckEventHandlerCtor() //检查全部的eventhandler所要的构造参数是否被注入中
    .UseForceCheckMessageSubscriberCtor() //使用消息的订单与发布
    .UseInjectingCommandHandlerEventHandler(Never.IoC.ComponentLifeStyle.Singleton) //注入全部的commandhandler,在commandbus执行其对象行为
    .UseSqliteEventProviderCommandBus<DefaultCommandContext>(new SqliteFailRecoveryStorager(commandfile, eventfile)) //使用cqrs组件,指定sqlite做为恢复组件,
    .UseApiModelStateValidation() //mvc,webapi的模型参数验证
    .UseApiActionCustomRoute(e.Collector as IServiceCollection) //自定义路由,相同于在controller可使用httpget等route技术
    .UseApiDependency(e.Collector as IServiceCollection);//注入全部的controller

最后启动过程当中检查整个系统是否正常设计模式

//配置中心更新配置文件后,系统不必定立刻能从新加载
e.Startup.Startup(TimeSpan.FromSeconds(1), (x) => { //咱们在此启动看看所使用组件是否正常启动
    using (var sc = x.ServiceLocator.BeginLifetimeScope()) { sc.Resolve<ICommandBus>(); sc.Resolve<ILoggerBuilder>(); sc.Resolve<IJsonSerializer>(); var home = sc.Resolve<Controllers.MessageController>(); var logger = sc.Resolve<ILoggerBuilder>().Build(typeof(Startup)); logger.Info("startup at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } }); }

二、Controller的注入,使用构造函数的方法注入api

private readonly IEmailCodeQuery emailCodeQuery = null; private readonly IMobileCodeQuery mobileCodeQuery = null; private readonly ICommandBus commandBus = null; private readonly ILoggerBuilder loggerBuilder = null; private readonly IJsonSerializer jsonSerializer = null; public VCodeController(ICommandBus commandBus, ILoggerBuilder loggerBuilder, IJsonSerializer jsonSerializer, IEmailCodeQuery emailCodeQuery, IMobileCodeQuery mobileCodeQuery) { this.commandBus = commandBus; this.loggerBuilder = loggerBuilder; this.jsonSerializer = jsonSerializer; this.emailCodeQuery = emailCodeQuery; this.mobileCodeQuery = mobileCodeQuery; }

三、Action代码处理

/// <summary>
/// 校验邮箱验证码 /// </summary>
/// <param name="reqs"></param>
/// <returns></returns>
[ApiActionRemark("a9a900aee8c6", "HttpPost"), HttpPost] public ApiResult<string> CheckEmailValidateCode(CheckEmailValidateCodeReqs reqs) { if (!this.TryValidateModel(reqs)) { return Anonymous.NewApiResult(ApiStatus.Fail, string.Empty, this.ModelErrorMessage); } //实际上不用try + catch了,由于在startup统一日志处理了。 //发送命令后交给commandhandler去处理领域,commandbus + eventbus
    var handler = this.commandBus.Send(new DestroyEmailCodeCommand(NewId.GenerateGuid()) { Email = reqs.Email, UsageType = reqs.UsageType, VCode = reqs.VCode, }); if (handler == null) { return Anonymous.NewApiResult(ApiStatus.Fail, string.Empty, "验证失败"); } if (handler.Status != CommandHandlerStatus.Success) { return Anonymous.NewApiResult(ApiStatus.Error, string.Empty, this.HandlerMerssage(handler)); } return Anonymous.NewApiResult(ApiStatus.Success, string.Empty); }

【三】组成部分

  1. ApplicationStartup 整个系统的初始化中心点,能够是Web环境,也能够是Service环境。
  2. Emit 避免使用反射带来的损耗,而且对OpCode的使用封装变成方法的调用,可容易理解与使用,是后面全部技术的支撑点。
  3. IoC 简单实现三种生命周期,单例 + 做用域 + 短暂,注入指定参数,能够生成代理注入拦截器。
  4. Aop 加入上下文日志跟踪(如LoggerAttribte自动写日志);Mock对象等。
  5. CQRS 实现了一套commandbus + eventbus设计,commandbus执行命令后,若聚合对象有事件,则经过eventbus发布到订阅者;中间使用sqlite来保存订阅失败的队列,用于后期的恢复发布订阅。
  6. SqlClient 配置极其简单,使用也很容易的一个sql执行方法,使用xml文件配置管理sql语句,可执行事务,对xml内容进行缩进使得好看;也能够直接写sql语句。使用typehander,用于处理阻抗失败的状况。
  7. Mapper 直接映射对象,效率比emitmapper差一点。
  8. Message 消息的发布与订阅,能够在内存,mq方式发布到不一样的机器。
  9. Socket 使用SocketAsyncEventArgs实现的一套高性能方案,读取与发送分开队列,能够设置心跳。
  10. Remoting 在socket的基础上实现一套通信。
  11. Configuration 配置中心,对文件(夹)进行监控,修改文件会触发全部应用程序的配置更新;设置了共享级+应用级配置文件,不用的应用级配置文件能够直接link共享级的配置,共享级的配置能够读取文件,也能够到数据库查询。
  12. Deployment 对WebApi里面的Service直接生成代理类,封装了web请求的参数,路由等信息,还可使用熔断机制,在客户端发现服务不可用的时候自动返回友好结果。
  13. Workflow 实现了一套工做流内容,每一步骤均可以独立为插件或一个类,而且可组合不一样步骤,包含等待,重试,中断等不一样状态。
  14. Memcached 一个memcached客户端,文本协议+二进制协议,还有Gzip压缩,Binary序列化;定义的接口能够很方便使用protobuf等技术的自由扩展。
  15. JsonSerializerjson 序列化,可动态配置不用类型的输出结构,经过emit后缓存提升性能,还能支持用户自定义序列接口。

【四】快速开发

咱们打开startup文件global文件来看看,整个构架的初始化都在global或startup里面实现的,环境搭建比较简单,能够直接开发业务而不关心组件实现方式。

摘要里面一些代码展现:

一、接口与实现使用IoC管理,加上灵活的AOP,可统一日志管理的管理

[Logger] public class EmailCodeCommandHandler : ICommandHandler<CreateEmailCodeCommand>, ICommandHandler<DestroyEmailCodeCommand> { }

二、对远程方法的调用,封装成本地调用方式

//实际上这里是web远程方法,使用代理生成类,带熔断,
var api = this.validateCodeService.CreateMobileValidateCode(new Message.Contract.Request.CreateMobileValidateCodeReqs() { Mobile = model.UserName, ClientIP = this.GetAppIP(), Platform = this.GetAppPlatform(), Length = 4, UsageType = Message.Contract.EnumTypes.UsageType.注册, });

三、友好的参数验证,用户本身加验证参数规则。

/// <summary>
/// 用户Model /// </summary>
[Serializable, Validator(typeof(RequestValidator))] public class UserViewModel { #region prop

    /// <summary>
    /// 用户名 /// </summary>
    [DisplayName("用户名")] public string UserName { get; set; } #endregion prop

    #region validator

    private class RequestValidator : Validator<UserViewModel> { public override IEnumerable<KeyValuePair<Expression<Func<UserViewModel, object>>, string>> RuleFor(UserViewModel target) { if (target.UserName.IsNullOrWhiteSpace()) yield return new KeyValuePair<Expression<Func<UserViewModel, object>>, string>(m => m.UserName, "手机号码为空"); } } #endregion validator }

四、可靠的性能:json的序列化与反序列化,在反序列化timespan下(字符串:"00:10:00"), 2700x + 32g内存1000万次测试,jsonnet 使用12.6秒(GC=3.7万),easyser使用2.6秒(GC=3.7K),jil使用0.8秒(GC=1.2k)

五、简单的配置:系统初始化过程风格统一,还有组件eqsysql只须要xml文件 + 连接字符串,就能够实现ORM管理(如QueryForObject<T>,QueryForEnumerable<T>)

相关文章
相关标签/搜索