使用.NetCore自帶的後臺作業,出入隊簡單模擬生產者消費者處理請求響應的資料

2022-06-28 18:00:11

環境: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" />