上一篇,我们介绍了Log2Net的需求和整体框架,我们接下来介绍我们是如何用代码实现Log2Net组件的功能的。

一、整体介绍

  Log2Net组件本身是一个Dll,供其他系统调用。

  本部分由以下几部分组成:

  1. 日志平台实体定义;
  2. 工具方法定义,包括ComUtil(例如缓存帮助类、序列化帮助类、消息队列帮助类等)和DBUtil(例如Sql server帮助类、Oracle帮助类、MySql帮助类、EF帮助类等);
  3. 日志信息获取类(例如如获取客户端、服务器端信息,写日志数据到消息队列等);
  4. .NetCore中间件定义(例如HttpContext中间件、错误消息处理中间件等);
  5. Config配置类(包括Log2NetConfigurationSectionHandler类、消息队列管理类等);
  6. 日志追加器类(FileAppender、DirectDBAppender、Queue2DBAppender、MQ2DBAppender等);
  7. 外部接口LogApi类(例如组件注册类、写日志类等);

  使用的第三方类库有RabbitMQ访问类库RabbitMQ.Client、InfluxDB访问类库InfluxData.Net、缓存组件CacheManager、对象映射组件AutoMapper、缓存工具Microsoft.Extensions.Caching、Microsoft.AspNetCore.Session等。使用NuGet工具下载安装这些类库,会自动检测和匹配当前.NET版本并安装其他依赖。请尽量不要手动下载类库安装,可能会出现各种各样的不兼容、缺少依赖库的情况。

  本组件使用VS2017开发,为类库项目,支持.net4.5~netCore2.2(此项目初始使用VS2017开发,于2019-10为支持.NetCore3.0改用了VS2019。若您未安装VS2019,将依赖项中的.NetCoreApp 3.0(或项目文件中的netcoreapp3.0)移除即可使用VS2017打开)。若您把源码下载下来,而您的电脑上缺少某个.Net版本,请在csproj文件中的TargetFrameworks中移除该net版本。

  为了测试该组件,分别添加了一个.NET4.5的MVC项目和.netCore2.0的MVC项目。项目文件图如下图所示:

  本项目代码已开源,地址为 https://github.com/yuchen1030/Log2Net ,您可以参照代码理解下述的设计。

二、模型实体Models类库

  模型实体包括定义在ModelsInDB.cs中数据库中使用的模型、定义在ModelsUI.cs中外部接口使用的模型、定义在ModelsInCode.cs中本类库代码中使用的模型。

  系统中的操作轨迹数据的数据库实体为Log_OperateTrace,监控数据数据库实体为Log_SystemMonitor。代码中以这两个实体为核心定义了其他数据实体,具体参见代码。

三、工具方法Util定义

  本部分包括公共工具ComUtil类和DBUtil类。

3.1 ComUtil类

  该类库(Util类库)中,封装了了一些公共的方法和类,如下表所示:

文件

用途描述

AppConfig

配置文件读写类

AutoMapperHelper

对象映射帮助类

CacheHelper.cs

缓存操作类

DtModelConvert.cs

泛型Model和DataTable互转操作类

LambdaToSqlHelper

Lambda表达式转Sql帮助类

RabbitMQHelper.cs

RabbitMQ消息队列帮助类

SerializerHelper.cs

序列化反序列化帮助类

StringEnum.cs

字符串枚举类

XmlSerializeHelper.cs

Xml和实体转换类

  这些类是通用的方法封装,与具体业务逻辑无关,其他系统可以借鉴使用。

3.2 DBUtil类

  这些类是用来访问各种数据库的方法的封装。包括对Sql Server、Oracle、MySql、InfluxDB等4种数据库的访问。若您需要添加对其他数据(如Access、SQLite、PostgreSQL等)的支持,请在此部分下添加。

  对常用的数据库,本代码中使用了两种方式进行访问:ADO.net方式和EF方式,如果您需要使用NHibernate/SqlSugar/Dapper等其他方式,也请在该部分下添加。

3.2.1 AdoNet方式访问数据库

  该部分是使用ADO.Net方法直接访问数据库,因为要支持SqlServer,Oracle,MySql等多种数据库,支持多个数据库实体,它们需要遵循相同的接口契约,有一些共同的实现方法,因此定义了泛型接口类和泛型基础类。类图如下所示:

  在泛型接口IAdoNetBase中,定义了添加和获取数据的方法,如下所示:

     internal interface IAdoNetBase<T> where T : class
{
ExeResEdm Add(string tableName, T model, params string[] skipCols);
ExeResEdm GetListByPage(string tableName, PageSerach<T> para);
}

  数据库访问基础类AdoNetBase为抽象类,定义了各种数据库共用的一些基础方法,如下图所示:

  在实现这些公共方法的时候,各种数据库的实现方法不同,因此需要定义抽象方法,子类需要实现它。

  例如接口的public ExeResEdm Add(string tableName, T model, params string[] skipCols)方法需要调用私有方法ExecuteNonQuery,而该私有方法的定义如下:

         ExeResEdm ExecuteNonQuery(string cmdText, params DbParameter[] parameters)
{
ExeResEdm dBResEdm = SqlCMD(cmdText, cmd => cmd.ExecuteNonQuery(), parameters);
if (dBResEdm.ErrCode == )
{
dBResEdm.ExeNum = Convert.ToInt32(dBResEdm.ExeModel);
}
return dBResEdm;
}

  该ExecuteNonQuery方法中要调用SqlCMD方法,而各种数据库中SqlCMD方法方法实现不同,因此需要SqlCMD方法为抽象方法,各子类需要各自实现之。以下分别列出SqlServer和MySql中SqlCMD方法的实现:

         protected override ExeResEdm SqlCMD(string sql, Func<DbCommand, object> fun, params DbParameter[] pms)
{
ExeResEdm dBResEdm = new ExeResEdm();
try
{
pms = ParameterPrepare(pms);
using (SqlConnection con = new SqlConnection(connstr))
{
using (SqlCommand cmd = new SqlCommand(sql, con))
{
con.Open();
if (pms != null && pms.Length > )
{
cmd.Parameters.AddRange((pms));
}
var res = fun(cmd);
dBResEdm.ExeModel = res;
return dBResEdm;
}
}
}
catch (Exception ex)
{
dBResEdm.Module = "SqlCMD方法";
dBResEdm.ExBody = ex;
dBResEdm.ErrCode = ;
return dBResEdm;
}
}
         protected override ExeResEdm SqlCMD(string sql, Func<DbCommand, object> fun, params DbParameter[] pms)
{
ExeResEdm dBResEdm = new ExeResEdm();
try
{
pms = ParameterPrepare(pms);
using (MySqlConnection con = new MySqlConnection(connstr))
{
using (MySqlCommand cmd = new MySqlCommand(sql, con))
{
con.Open();
if (pms != null && pms.Length > )
{
cmd.Parameters.AddRange((pms));
}
var res = fun(cmd);
dBResEdm.ExeModel = res;
return dBResEdm;
}
}
}
catch (Exception ex)
{
dBResEdm.Module = "SqlCMD方法";
dBResEdm.ExBody = ex;
dBResEdm.ErrCode = ;
return dBResEdm;
}
}

  基础类中的其他方法也是类似的套路,在此不再赘述,具体请参见源码。

  在定义了接口和基础方法之后,各个子类就可以在此基础上继承和实现它们了,本代码中的子类是SqlServerHelper,OracleHelperBase,MySqlHelper三个,分别实现对SqlServer,oracle,MySql数据库的访问。这些子类中的方法就是对基类方法的重写,例如SqlServerHelper定义如下:

  

  对oracle数据库,建议使用Oracle.ManagedDataAccess.Client实现的oracle 数据库访问类OracleHelper(无需安装客户端),无32位/64位之分,使用方便,性能好。但该类库仅支持Oracle10g及以上,因此又使用System.Data.OracleClient实现的oracle 数据库访问类OracleHelperMS。这两个类的代码可以是一模一样的,只是引用的类库不同(Oracle.ManagedDataAccess.Client和System.Data.OracleClient)。这两个类可以合并为一个,只需要添加如下代码:

 //#define MS_OracleClient  // 是采用微软oracle类库还是oracle自家的类库

 #if MS_OracleClient
using System.Data.OracleClient;
#else
using Oracle.ManagedDataAccess.Client;
#endif

  若您还需要支持其他数据库类型,请继承和实现 AdoNetBase<T>, IAdoNetBase<T> 即可。

3.2.2 数据访问层Dal

  上面我们定义了ADO.Net访问数据的方法,EF方法只需引用类库即可。工具已备好,我们接下来就可以使用ADO.Net方法或EF方法访问具体的数据库表了。类图如下图所示:

  首先,我们定义一个泛型抽象类DBAccessDal(也可以定义为接口),里面定义了需要实现的获取数据方法和添加数据的方法:

     internal abstract class DBAccessDal<T> where T : class
{
internal abstract ExeResEdm GetAll(PageSerach<T> para); internal abstract ExeResEdm Add(AddDBPara<T> dBPara); }

  然后,分别定义ADO.Net方法访问数据的基类AdoNetBaseDal和EF方法访问数据库的基类EFBaseDal:

                              

  最后,根据上一步中的基类,实现Log_OperateTrace和Log_SystemMonitor的数据访问子类,如下图:

  ADO.Net方式中,基类中已指明了数据库连接对象,Dal中只需要调用相关方法即可。EF方式中,前文写的代码很少,但欠债总是要还的,这里需要额外定义继承自DbContext的Log_OperateTraceContext和Log_SystemMonitorContext来指定数据库上下文。

3.2.3 数据库访问方式工厂

  上文中,介绍了数据库又ADO.Net方式和EF访问方式,我们可以在配置文件中配置使用ADO.Net方式或EF方式,这是通过工厂模式实现的,类图如下:

  例如Log_OperateTraceDBAccessFac定义如下:

     internal class Log_OperateTraceDBAccessFac : DBAccessFac<Log_OperateTrace>
{
protected override DBAccessDal<Log_OperateTrace> GetDalByDBAccessType(DBAccessType dbAccessType)
{
if (dbAccessType == DBAccessType.EF)
{
Log_OperateTraceEFDal log_OperateTraceDal = new Log_OperateTraceEFDal(new Log_OperateTraceContext());
return log_OperateTraceDal;
}
else if (dbAccessType == DBAccessType.NH)
{
throw new Exception("Not define dal methods when DBAccessType = NH");
}
else
{
return new Log_OperateTraceAdoDal();
} }
}

  另外,还有数据库功能公共类ComDBFun和InfluxDB访问类InfluxDBHelper的介绍略。

  至此,数据库访问帮助类介绍完毕,详情请参阅DBUtil部分代码。

四、日志信息获取类

  本部分定义了日志组件使用的基础方法,如客户端服务器信息ClientServerInfo类、在线人数访客人数统计VisitOnlineCount 类、日志组件公共类LogCom.cs。

4.1 ClientServerInfo类

  该类库用于收集客户端和服务器端的信息,包括客户端信息子类ClientInfo和服务器端信息子类ServerInfo。

  ClientInfo类用来获取客户端的ip地址、主机名、Mac地址、浏览器信息等。

  ServerInfo类用来获取服务器端的ip地址、主机名、操作系统、CLR版本、服务器运行时间、可用硬盘空间、CPU使用率、内存使用率等信息。

4.2 访客人数统计类VisitOnlineCount类

  本类中定义了在线人数和访客统计抽象类IVisitCount类,具体的类要实现该类中的抽象方法。

  对.net平台,存在Session_Start和Session_End事件,访客统计的实现思路较为清晰,本组件提供了两种方案:使用Application对象实现、使用缓存实现。具体采用哪种方案由简单工厂决定,默认采用缓存方案。

  对.NetCore平台,不存在Session_Start和Session_End事件事件,需要借助于HttpContext中间件来实现。在HttpContext中,保存了所有的SessionID,若Session过期,则视为该SessionID离线。据此就可以统计出在线人数和历史访客。

4.3 公共类LogCom类

  本类中定义一些本组件内部使用的公共类,主要是写文件的类、日志实体封装类,实现非常简单,类图如下:

五、日志追加器Appender类库  

  日志追加器用于将封装后的日志实体写到媒介中。根据追加方式的不同,实现方案也不同。

  日志追加方式有写到文件、ADO方式写到数据库、通过队列写到数据库、通过消息队列写到数据库四种。相应的有FileAppender、DirectDBAppender、Queue2DBAppender、MQ2DBAppender四种追加器。这四种追加器都实现了公共的追加器BaseAppender类。类图如下:

                     

  公共追加器BaseAppender为抽象类中,定义了两个抽象的WriteLog方法,分别用来写用户操作日志和系统运行日志。

  BaseAppender类中还定义了写日志的WriteLogAndHandFail方法和WriteLogAgain方法,两者的区别在于前者在失败时要写备份日志,参数为集合类型,在初次将日志写到媒介中使用;后者在失败时不进行其他处理,参数为单一实体,在读备份日志到媒介中使用。

  FileAppender、DirectDBAppender、Queue2DBAppender、MQ2DBAppender这四种追加器实现自己的WriteLog方法。Queue2DBAppender是通过线程安全的ConcurrentQueue队列将数据写到数据,MQ2DBAppender是通过消息队列写数据到数据库,这两者都是通过一个缓冲Buffer写数据到数据库,继承自Buffer2DBAppender抽象类,需要实现各自的数据生产和数据消费的方法。而Buffer2DBAppender类由继承DirectDBAppender的写数据到数据库的方法,还开启了数据消费线程。Queue2DBAppender子类中,通过Enqueue 和TryDequeue方法即可实现数据的生产和消费,而MQ2DBAppender类中的数据生产消费任务较为复杂:需要调用RabbitMQManager的Send和Receive方法来生产和消费数据。

  在写数据库中时,一方面写到SQL数据库中,便于读写分离的实现,另一方面写到时序数据库InfluxDB中,便于以后使用Grafana、ELK等工具进行更加灵活优雅的监控。

  用户可以通过配置来决定使用哪一种追加器,代码中通过追加器工厂类AppenderFac,得到相应的追加器工厂实例,调用该追加器的方法进行日志的记录。

六、.NetCore中间件DNCMiddleware类库

  日志追加器用于将封装后的日志实体写到媒介中。根据追加方式的不同,实现方案也不同。

  .NetCore中没有Application_Error事件来捕捉全局异常,没有HttpContext.Current来保存当前请求的信息,需要我们自定义中间件来实现。

61 异常处理中间件ErrorHandlingMiddleware

  在这里定义了异常处理中间件,在捕捉到异常时,将异常日志进行记录。

     internal class ErrorHandlingMiddleware
{
private readonly RequestDelegate next; public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
} public async Task Invoke(Microsoft.AspNetCore.Http.HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
var statusCode = context.Response.StatusCode;
if (ex is ArgumentException)
{
statusCode = ;
}
await HandleExceptionAsync(context, statusCode, ex.Message);
}
finally
{
var statusCode = context.Response.StatusCode;
var msg = "";
if (statusCode == )
{
msg = "未授权";
}
else if (statusCode == )
{
msg = "未找到服务";
}
else if (statusCode == )
{
msg = "请求错误";
}
else if (statusCode != && statusCode != )
{
msg = "未知错误" + statusCode;
}
if (!string.IsNullOrWhiteSpace(msg))
{
await HandleExceptionAsync(context, statusCode, msg);
}
}
} private static Task HandleExceptionAsync(Microsoft.AspNetCore.Http.HttpContext context, int statusCode, string msg)
{
var data = new { code = statusCode.ToString(), is_success = false, msg = msg };
var result = JsonConvert.SerializeObject(new { data = data }); Log_OperateTraceBllEdm exLog = new Log_OperateTraceBllEdm()
{
Detail = result,
LogType = LogType.异常,
Remark = "异常时间" + DateTime.Now,
TabOrModu = "异常模块",
};
LogApi.WriteLog( LogLevel.Error,exLog); context.Response.ContentType = "application/json;charset=utf-8";
return context.Response.WriteAsync(result);
}
} public static class ErrorHandlingExtensions
{
public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ErrorHandlingMiddleware>();
}
}

6.2 请求上下文中间件HttpContext

  在这里,定义了请求上下文的中间件,记录了当前求请求的上下文,模拟了当前上下文的Session信息,将所有的SessionId保存起来,将过期的SessionId移除,来实现在线人数统计和历史访客统计。

     internal static class HttpContext
{
public class SessionEdm
{
public string Key { get; set; }
public string Val { get; set; }
public DateTime ExpiresAtTime { get; set; }
} public static Microsoft.AspNetCore.Http.HttpContext Current => _accessor.HttpContext; static ConcurrentDictionary<string, SessionEdm> sessionMaps = new ConcurrentDictionary<string, SessionEdm>(); static double dncSessionMins = AppConfig.GetDncSessionTimeoutMins(); private static IHttpContextAccessor _accessor;
internal static void Configure(IHttpContextAccessor accessor)
{
_accessor = accessor;
} public static VOEdm GetOnlineVisitNum(int preVisitNum)
{
if (_accessor.HttpContext != null)
{
var curSession = _accessor.HttpContext.Session;
SessionEdm sessionEdm = new SessionEdm() { Key = curSession.Id, Val = "", ExpiresAtTime = DateTime.Now.AddMinutes(dncSessionMins) };
sessionMaps.TryAdd(curSession.Id, sessionEdm);
}
int visitorsNum = sessionMaps.Count;
VOEdm vOEdm = new VOEdm() { VisitNum = preVisitNum + visitorsNum };
//将过期session的值变为0,未过期的session的数量为在线人数
var keys = sessionMaps.Keys.ToArray();
for (int i = ; i < sessionMaps.Count; i++)
{
var cur = sessionMaps[keys[i]];
if (cur.Val == "" && cur.ExpiresAtTime <= DateTime.Now) //已过期
{
cur.Val = "";
}
}
var onlineNums = sessionMaps.Where(a => a.Value.Val == "").Count();
vOEdm.OnlineNum = onlineNums;
return vOEdm;
} } public static class StaticHttpContextExtensions
{
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
} public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
{
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
HttpContext.Configure(httpContextAccessor);
return app;
}
}

七、外部接口类LogApi类

  在本类中,调用其他类的方法,形成供其他业务系统调用的方法。包含以下内容:日志组件注册、写日志等。各业务系统不需要关心这些方法的具体实现,只需要封装业务实体,调用写日志的方法即可。LogApi类会调用其他的类,如等来实现日志记录的功能。类图如下:

  • RegisterLogInitMsg:注册日志组件到本系统,为日志组件准备基础信息:服务器IP、服务器主机名,系统名称等;使用EF自动创建数据;并调用WriteServerStartupLog方法写启动日志,调用WriteMonitorLogThread方法写定时监控日志。
  • GetLogWebApplicationsName:从配置文件中获取用户自定义的系统名称。
  • 这里还包含网站生命周期事件中的日志记录,如下:
  • WriteServerStartupLog:服务器启动时,获取操作系统,.NET CLR版本;
  • WriteFirstVisitLog:网站被初次访问,记录记录IIS版本;
  • WriteServerStopLog:服务器停止时,获取已运行时间;
  • WriteServerStartupLog:系统异常时,记录异常日志;
  • IncreaseOnlineVisitNum:Session Start时,在线人数和访客人数加1;
  • ReduceOnlineNum:Session end时,在线人数减1。

  以上的生命周期事件中,有些仅在.net中可以使用,.netCore中不存在,要实现类似的功能,就需要使用netCore中间件来实现。AddLog2netService和AddLog2netConfigure分别用来注册Log2net服务和Log2net配置。

  本类中定义了4个写日志的方法:

  • WriteLog方法重载(2个):封装日志实体,调用日志追加器的方法将日志写到媒介中,分别对业务操作和监控数据进行写。
  • WriteMsgToDebugFile:写调试日志写到文件中,可通过bWriteInfoToDebugFile配置是否开启。
  • WriteInfoToFile:将将日记写到本地文件中,记录一些重要但又不必写入log日志媒介的信息。

  LogTraceEdm为操作轨迹类业务实体,LogMonitorEdm为监控信息实体,各业务系统将信息封装进这两个实体,然后调用WriteLog方法,就能将日志数据写到相应媒介中。若写日志出现异常,将则该消息以Json格式备份到本地.log文件中,并在以后自动将备份写到相应媒介中。

八、多平台的设计和实现

  日志组件作为基础的组件,供不特定的系统使用,所以需要支持.net4.5/.net4.5.1/.net4.5.2/.net4.6/.net4.6.1/.net4.6.2.net4.7/.net4.7.1/.net4.7.2等平台,支持 .netCore2.0/.netCore2.1/.netCore2.2/.netCore3.0平台,其他的平台由于版本较旧,功能性能不太完善,使用较少,故不予支持。

  实现多平台建议使用VS2017,将项目配置 .csporj 中的代码<TargetFramework>net45</TargetFramework> 改为 <TargetFrameworks>net45;net451;net452;net46;net461;net462;net47;net471;net472;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0</TargetFrameworks> ,即可将单目标框架变为多目标框架。然后在项目配置中的ItemGroup 节点中添加 Condition条件,来指明这些引用所适用的框架平台,具体情况请参见项目配置.csporj 中文件。最后在项目代码中使用 #if #else #endif条件编译指明各个平台下适用的编码。
  本组件中主要涉及生命周期事件的多平台实现、缓存的多平台实现、在线人数的多平台实现等。
  对生命周期事件,.net平台中有 Application Started、Application Stop、Application Error、Session_Start、Session_End、Application_BeginRequest等事件,而在.netCore平台中仅有Application Started、Application Stop事件,其他事件需要通过Middleware中间件来实现。
  对缓存,本系统使用http缓存和CacheManager缓存。http缓存中,分别使用HttpRuntime.Cache缓存和Microsoft.Extensions.Caching.Memory缓存;对CacheManager缓存,.net平台中支持内存缓存、Memcached缓存、redis缓存三种,.netCore平台中仅支持内存缓存、redis缓存两种。
  对在线人数,.net平台中可以通过Application/缓存结合Session_Start、Session_End事件来实现,但在.netCore平台中,该实现较为麻烦,需要开启Session、自定义HttpContext中间件等,利用SessionId列表来标记历史访客,利用Session过期时间来移除过期的SessionId来标记某人的离线。

Log2Net组件代码详解(附开源代码)的更多相关文章

  1. SILC超像素分割算法详解(附Python代码)

    SILC算法详解 一.原理介绍 SLIC算法是simple linear iterative cluster的简称,该算法用来生成超像素(superpixel) 算法步骤: 已知一副图像大小M*N,可 ...

  2. TextCNN 代码详解(附测试数据集以及GitHub 地址)

    前言:本篇是TextCNN系列的第三篇,分享TextCNN的优化经验 前两篇可见: 文本分类算法TextCNN原理详解(一) 一.textCNN 整体框架 1. 模型架构 图一:textCNN 模型结 ...

  3. 斐波那契堆(Fibonacci heap)原理详解(附java代码实现)

    前言 斐波那契堆(Fibonacci heap)是计算机科学中最小堆有序树的集合.它和二项式堆有类似的性质,但比二项式堆有更好的均摊时间.堆的名字来源于斐波那契数,它常用于分析运行时间. 堆结构介绍 ...

  4. BM算法  Boyer-Moore高质量实现代码详解与算法详解

    Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...

  5. ASP.NET MVC 5 学习教程:生成的代码详解

    原文 ASP.NET MVC 5 学习教程:生成的代码详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...

  6. Github-karpathy/char-rnn代码详解

    Github-karpathy/char-rnn代码详解 zoerywzhou@gmail.com http://www.cnblogs.com/swje/ 作者:Zhouwan  2016-1-10 ...

  7. SQL Server 表的管理_关于事务的处理的详解(案例代码)

    SQL Server 表的管理_关于事务的处理的详解(案例代码) 一.SQL 事务 1.1SQL 事务 ●事务是在数据库上按照一定的逻辑顺序执行的任务序列,既可以由用户手动执行,也可以由某种数据库程序 ...

  8. SQL Server 表的管理_关于数据增删查改的操作的详解(案例代码)

    SQL Server 表的管理_关于数据增删查改的操作的详解(案例代码)-DML 1.SQL INSERT INTO 语句(在表中插入) INSERT INTO 语句用于向表中插入新记录. SQL I ...

  9. SQL Server 表的管理_关于表的操作增删查改的操作的详解(案例代码)

    SQL Server 表的管理_关于表的操作增删查改的操作的详解(案例代码) 概述: 表由行和列组成,每个表都必须有个表名. SQL CREATE TABLE 语法 CREATE TABLE tabl ...

  10. 代码详解:TensorFlow Core带你探索深度神经网络“黑匣子”

    来源商业新知网,原标题:代码详解:TensorFlow Core带你探索深度神经网络“黑匣子” 想学TensorFlow?先从低阶API开始吧~某种程度而言,它能够帮助我们更好地理解Tensorflo ...

随机推荐

  1. Bean定义并注册到spring

    1.XML配置文件 2.Annotation注解 3.Java Code 配置方式 BeanDefinitionRegistryPostProcessor

  2. spi flash 操作

    W25Q16V 是华邦出的一颗 spi flash. 25系列是比较通用的一个系列. 后面的数字 16 跟容量有关, 16 表示 16Mbits, 相当于 2MB. 与此类似的还有 W25Q128V ...

  3. Linux查看IP 网关 DNS

    ifconfig查看IP: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFC ...

  4. Apache Qpid CPP的编译与安装

    单机Broker部署(windows/linux) 在Windows/Linux上部署QPID Broker的方法. Windows 需要预先准备的文件和程序 qpid-cpp-0.32.tar.gz ...

  5. 多媒体开发之---h264快速运动估计算法

    #include "stdio.h"#include "stdlib.h"#include "malloc.h"#include " ...

  6. vc6.0的一些快捷键

    1.检测程序中的括号是否匹配    把光标移动到需要检测的括号(如大括号{}.方括号[].圆括号()和尖括号<>)前面,键入快捷键“Ctrl+]”.如果括号匹配正确,光标就跳到匹配的括号处 ...

  7. 完美解决android显示gif

    今天是周5啊.纠结了一天.android显示gif,没该控件 网上找开源项目 找到个viewgif.该作者在各大站点都在推荐自己的项目...好吧.用下吧. . . . 结果呢: 图片略微一大就 内存溢 ...

  8. reviews of learn python3 the hard way

    Almost every time,I try my best to write a long review of the book I have read. But this time I want ...

  9. Hadoop_stack_cmd

    HDFS命令基本格式:Hadoop fs -cmd < args > HDFS命令基本格式:Hadoop fs -cmd < args > ls 命令 hadoop fs -l ...

  10. 仿udhcpd配置文件读取的一段代码

    前段时间看udhcpd的源代码,后来因为没直接应用,也就放弃了.网上有文章udhcpd详解(搜索一下有很多),其中在介绍udhcpd的配置相关代码时,有如下一句话,学习了这段代码后,以后用到需要读配置 ...