使用.NetCore自带的后台作业,出入队简单模拟生产者消费者处理请求响应的数据
环境:Core:3.1的项目
说明:由于该方案为个人测试项目,重启时队列中的部分数据很可能会丢失,
对数据有要求的该方案不适用,不能照搬需要持久化处理,
另外发布到Linux Docker中通常不会自动回收,但是发布到IIS中需要简单设置不回收即可!!! 如下截图:
在IIS中找到这个站点所用的程序池,点击 高级设置。。。
回收——固定时间间隔 修改为 0
回收——虚拟/专用内存限制 修改为 0
进程模型——闲置超时 修改为 0
1:先来看效果
1.1:操作日志数据到表
1.2:操作日志数据到文件
2:过滤器
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Filters; using System.Text; using System.Diagnostics; using QzjcService.Models.Dto.LogModels; using QzjcService.Controllers; using SqlSugar.IOC; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using QzjcService.Helper; using Microsoft.AspNetCore.Mvc; using QzjcService.Models; namespace QzjcService.Filters { public class ActionNewLogFilter : ActionFilterAttribute { private static Stopwatch _watting = new Stopwatch(); public static double? longtime = 0; private ActionLogModel log = new ActionLogModel(); private readonly ILogger<ActionNewLogFilter> _logger; public ActionNewLogFilter(ILogger<ActionNewLogFilter> logger) { _logger = logger; } public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { _watting.Start(); string controllerName = context.ActionDescriptor.RouteValues["controller"]; string actionName = context.ActionDescriptor.RouteValues["action"]; string method = context.HttpContext.Request.Method;//请求方式 string queryString = context.HttpContext.Request.QueryString.Value;//地址参数 string argments = JsonConvert.SerializeObject(context.ActionArguments); string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path;//接口地址 var logStr = string.Format("\r\n【接口地址】:{0} \r\n【请求方式】:{1} \r\n【地址参数】:{2} \r\n【Body参数】:{3}", new object[] { url, method, queryString, argments }); log.ControllerName = controllerName; log.ActionName = actionName; //请求方式 0:get, 1:Post log.Menthod = (method.Equals("get", StringComparison.InvariantCultureIgnoreCase) || method.Equals("httpget", StringComparison.InvariantCultureIgnoreCase)) ? 0 : 1; log.CreateTime = DateTime.Now; log.RequstParms = logStr; var _context = context.HttpContext; if (_context != null) { var tokenStr = context.HttpContext.Request.Headers[ConstData.Authorization].ToString(); log.userId = string.IsNullOrEmpty(tokenStr) ? 0 : TokenHelper.GetUserModel(context.HttpContext)?.UserId; } await base.OnActionExecutionAsync(context, next); } public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { _watting.Stop(); longtime = 0; if (context.Result is ObjectResult result) { if (result != null) log.ResposeParam = JsonConvert.SerializeObject(result.Value); } longtime = _watting.Elapsed.TotalMilliseconds; log.LongTimeMs = longtime.Value; _watting.Reset(); log.RequstParms += "\r\n【响应参数】:" + log.ResposeParam + "\r\n============================================"; _logger.LogCritical(log.RequstParms); // await DbScoped.Sugar.Insertable<ActionLogModel>(log).ExecuteCommandAsync(); await LogQueueHelper.EnQueueLogAsync(log); await base.OnResultExecutionAsync(context, next); } } }
3:后台自带的定时器
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using QzjcService.Helper; using QzjcService.Models.Dto.LogModels; using SqlSugar.IOC; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace QzjcService.Services { public class LogTimeService : BackgroundService { private readonly ILogger<LogTimeService> _logger; public LogTimeService(ILogger<LogTimeService> logger ) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await Task.Factory.StartNew(async () => { while (!stoppingToken.IsCancellationRequested) { try { var model = await LogQueueHelper.DeQueueLogAsync(); if (model != null) { Console.WriteLine($"===={DateTime.Now}=LogTimeService=DeQueueLog Success===="); await DbScoped.Sugar.Insertable<ActionLogModel>(model).ExecuteCommandAsync(); } await Task.Delay(3000); } catch (Exception ex) { _logger.LogError($"====={DateTime.Now}===LogTimeService异常:=={ex.Message}=========="); continue; } } }); } } }
4:Log记录Model
using SqlSugar; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Threading.Tasks; namespace QzjcService.Models.Dto.LogModels { [Table("qzjc_action_loginfos")] [SugarTable("qzjc_action_loginfos")] public class ActionLogModel { /// <summary> /// /// </summary> [Column("id")] [SugarColumn(ColumnName = "id")] public int id { get; set; } /// <summary> /// /// </summary> [Column("controller_name")] [SugarColumn(ColumnName = "controller_name")] public string ControllerName { get; set; } /// <summary> /// /// </summary> [Column("action_name")] [SugarColumn(ColumnName = "action_name")] public string ActionName { get; set; } /// <summary> /// /// </summary> [Column("request_parms")] [SugarColumn(ColumnName = "request_parms")] public string RequstParms { get; set; } /// <summary> /// /// </summary> [Column("long_time")] [SugarColumn(ColumnName = "long_time")] public double LongTimeMs { get; set; } /// <summary> /// /// </summary> [Column("create_time")] [SugarColumn(ColumnName = "create_time")] public DateTime CreateTime { get; set; } /// <summary> /// /// </summary> [Column("create_userid")] [SugarColumn(ColumnName = "create_userid")] public int? userId { get; set; } [Column("menthod")] [SugarColumn(ColumnName ="")] public int? Menthod { get; set; } //[Column("state")] //[SugarColumn(ColumnName = "state")] //public int? State { get; set; } [Column("respose_parms")] [SugarColumn(ColumnName = "respose_parms")] public string ResposeParam { get; set; } } }
5:贴上系统自带的队列Queue
using QzjcService.Models.Dto.LogModels; using System.Collections; using System.Threading.Channels; using System.Threading.Tasks; namespace QzjcService.Helper { public static class LogQueueHelper { public static Queue queue; private static readonly object Lock = new object(); static LogQueueHelper() { queue = new Queue(); } /// <summary> /// /// </summary> /// <returns></returns> private static async Task<Queue> GetQueueAsync() { if (queue == null) { lock (Lock) { if (queue == null) queue = new Queue(); } } await Task.CompletedTask; return queue; } public static async Task<bool> EnQueueLogAsync(ActionLogModel model) { try { queue = await GetQueueAsync(); queue.Enqueue(model); return true; } catch (System.Exception) { return false; } } public static async Task<ActionLogModel> DeQueueLogAsync() { try { queue = await GetQueueAsync(); object obj = queue.Dequeue(); if (obj != null) { ActionLogModel model = obj as ActionLogModel; return model ?? null; } return null; } catch (System.Exception) { return null; } } } }
6:这里也贴出Serilog相关code
using System; using System.IO; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Configuration; using Serilog; using Serilog.Events; namespace QzjcService { public class Program { public static void Main(string[] args) { string dataTime = DateTime.Now.ToString("yyyy-MM-dd");//这里去掉更好,让其自增长 string fileStr = $"Logs/{dataTime}_logs.txt"; Log.Logger = new LoggerConfiguration() .MinimumLevel.Warning() //Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.Async(c => c.File(fileStr, rollOnFileSizeLimit: true, fileSizeLimitBytes: 1024 * 1024 * 10, retainedFileCountLimit: 30)) .CreateLogger(); try { Log.Information("=========Starting web host=========="); CreateHostBuilder(args).Build().Run(); } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly!"); } finally { Log.CloseAndFlush(); } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureLogging((hostingContext, builder) => { builder.ClearProviders(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(c => { c.Limits.MaxRequestBodySize = 1024 * 1024 * 300; }); webBuilder.UseUrls("http://*:4444"); webBuilder.UseStartup<Startup>(); }) .UseSerilog(); } }
<PackageReference Include="Serilog.Extensions.Hosting" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="8.2.0" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
如有疑问或者错误的地方,请跟帖,本人会第一时间答复以及相互学习,谢谢!个人会不断的上传自己的学习心得!
好了今天就先到这里,下次有时间再更新,如果存在不合理的地方,欢迎大家多多指教留言!!!