基於Asp.Net Core 5.0依賴Quartz.Net框架編寫的任務排程web管理平臺

神叉發表於2021-01-20

原始碼地址: https://github.com/246850/Calamus.TaskScheduler

演示地址:http://47.101.47.193:1063/

 

 

 1、Quartz.NET框架核心類

IScheduler:排程者

IJobDetail:任務

ITrigger:觸發器

JobKey:任務/觸發器標識

JobDataMap:資料包

2、郵件通知(FluentEmail類庫)

_fluentEmail.To(to).Subject(subject).Body(body, true).SendAsync();

3、Quartz.NET宿主方式,依靠IHostedService後臺服務執行

internal class QuartzHostedService : IHostedService
{
	private readonly ISchedulerFactory schedulerFactory;
	private readonly IOptions<QuartzHostedServiceOptions> options;
	private IScheduler scheduler = null!;

	public QuartzHostedService(
		ISchedulerFactory schedulerFactory,
		IOptions<QuartzHostedServiceOptions> options)
	{
		this.schedulerFactory = schedulerFactory;
		this.options = options;
	}

	public async Task StartAsync(CancellationToken cancellationToken)
	{
		scheduler = await schedulerFactory.GetScheduler(cancellationToken);
		await scheduler.Start(cancellationToken);
	}

	public Task StopAsync(CancellationToken cancellationToken)
	{
		return scheduler.Shutdown(options.Value.WaitForJobsToComplete, cancellationToken);
	}
}

4.Asp.Net Core 5.0整合

安裝依賴包

Quartz

Quartz.AspNetCore

Quartz.Plugins.TimeZoneConverter

Quartz.Serialization.Json

FluentEmail.Core

FluentEmail.Smtp

Startup

public void ConfigureServices(IServiceCollection services)
{
	services.AddControllersWithViews(options =>
		{
			options.Filters.Add<GatewayResultFilterAttribute>();    // 通用執行結果包裝處理過濾器
			options.Filters.Add<GlobalExceptionFilterAttribute>();  // 全域性異常過濾器
		})
		.AddJsonOptions(options =>
		{
			options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());  // 日期格式化
		})
		.AddFluentValidation(config =>  // 請求模型引數驗證
		{
			config.RunDefaultMvcValidationAfterFluentValidationExecutes = true;    // false : 禁止預設模型驗證
			config.ValidatorOptions.CascadeMode = CascadeMode.Stop; // 不級聯驗證,第一個規則錯誤就停止
			config.RegisterValidatorsFromAssemblyContaining<JobCreateOrUpdateValidator>();
		});
	services.AddHostedService<NLogHostService>();   // NLog 關閉服務
	services.AddDistributedMemoryCache();  // 分散式快取介面
	services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));// 解決中文亂碼
	services.AddHttpClient();   // IHttpClientFactory

	IConfigurationSection quartzConfiguration = Configuration.GetSection("Quartz"); // Quartz配置節點

	/***********Quartz.NET*********/
	services.AddTransient<HttpJob>();   // 註冊job至容器,必須步驟
	services.AddQuartz(config =>
	{
		config.UseTimeZoneConverter();
		// 使用MicrosoftDependencyInjectionJobFactory工廠類從 容器 中建立job例項
		config.UseMicrosoftDependencyInjectionJobFactory(options =>
		{
			options.AllowDefaultConstructor = false;    // 禁止使用無參構建函式建立 job
			options.CreateScope = false;
		});
		config.UseDefaultThreadPool(options =>
		{
			options.MaxConcurrency = 10;    // 最大併發執行執行緒數
		});
		config.UsePersistentStore(options =>
		{
			options.UseProperties = false;
			//options.UseBinarySerializer();  // 二進位制序列化
			options.UseJsonSerializer();    // json序列化
			options.UseMySql(ado =>
			{
				ado.ConnectionString = quartzConfiguration["Database"];
				ado.TablePrefix = quartzConfiguration["TablePrefix"];  // 預設值 QRTZ_
				ado.ConnectionStringName = "Quartz.net";
			});
		});

		// 監聽器
		config.AddSchedulerListener<DefaultSchedulerListener>();
		config.AddJobListener<DefaultJobListener>();
		config.AddTriggerListener<DefaultTriggerListener>();

		// 啟動NLog日誌檔案清除job
		config.ScheduleJob<ClearNLogJob>(trigger =>
		{
			trigger.WithIdentity(NLogJobKey.NameKey, NLogJobKey.GroupKey).StartNow()
			.WithCronSchedule("0 0 0 1/3 * ? ", cron => cron.WithMisfireHandlingInstructionFireAndProceed());    // 從每月1日開始,每3天執行一次
		}, job =>
		{
			job.WithIdentity(NLogJobKey.NameKey, NLogJobKey.GroupKey)
			.StoreDurably(false)     // 是否持久化, 無關聯觸發器時是否移除,false:移除
			.RequestRecovery()  // 重啟後是否恢復任務
			.WithDescription("每3天清空NLog日誌檔案");
		});
	});
	// IHostedService宿主啟動 Quartz服務 services.AddSingleton<IHostedService, QuartzHostedService>()
	services.AddQuartzServer(options =>
	{
		// when shutting down we want jobs to complete gracefully
		options.WaitForJobsToComplete = true;   // 等待任務執行完,再退出
	});

	/***********FluentEmail*********/
	// 為了將郵件通知配置在job data上, 不使用自帶的service註冊方式
	//services.AddFluentEmail(quartzConfiguration["Smtp:UserName"], "Quartz.NET任務排程通知")
	//    .AddRazorRenderer()
	//    .AddSmtpSender(quartzConfiguration["Smtp:Host"], Convert.ToInt32(quartzConfiguration["Smtp:Port"]), quartzConfiguration["Smtp:UserName"], quartzConfiguration["Smtp:Password"]);
	services.AddTransient<IFluentEmail>(serviceProvider =>
	{
		IScheduler scheduler = serviceProvider.GetRequiredService<ISchedulerFactory>().GetScheduler().Result;

		JobKey key = new JobKey(EmailJobKeys.NameKey, EmailJobKeys.GroupKey);
		if (!scheduler.CheckExists(key).Result)
		{
			JobDataMap dataMap = new JobDataMap();
			dataMap.Put(EmailJobKeys.Host, "smtp.qq.com");
			dataMap.Put(EmailJobKeys.Port, 587);    // 465埠一直嘗試不通過,奇怪
			dataMap.Put(EmailJobKeys.UserName, "390915549@qq.com"); // 作者qq,歡迎騷擾
			dataMap.Put(EmailJobKeys.Password, "cirxjtemuzxycagf");
			dataMap.Put(EmailJobKeys.To, string.Empty); // 接收者郵件支援多個,以 ; 隔開
			dataMap.Put(EmailJobKeys.NickName, "Quartz.NET任務排程通知");
			dataMap.Put(EmailJobKeys.CacheExpiry, 30);  // 預設30分鐘內只通知一次
			IJobDetail job = JobBuilder.Create<HttpJob>()
				.StoreDurably(true)
				.RequestRecovery()
				.WithDescription("郵件通知配置Job,切勿刪除")
				.WithIdentity(key)
				.UsingJobData(dataMap)
				.Build();
			scheduler.AddJob(job, true);   // 初始化郵件通知配置
		}

		IJobDetail emailJob = scheduler.GetJobDetail(key).Result;
		IFluentEmail fluentEmail = new Email(new ReplaceRenderer(),
			new SmtpSender(new SmtpClient(emailJob.JobDataMap.GetString(EmailJobKeys.Host), emailJob.JobDataMap.GetInt(EmailJobKeys.Port))
			{
				EnableSsl = true,
				Credentials = new NetworkCredential(emailJob.JobDataMap.GetString(EmailJobKeys.UserName),
				emailJob.JobDataMap.GetString(EmailJobKeys.Password))
			}),
			emailJob.JobDataMap.GetString(EmailJobKeys.UserName),
			emailJob.JobDataMap.GetString(EmailJobKeys.NickName));
		return fluentEmail;
	});

	IocEngine.Instance.Init(services);  // 實在沒辦法才弄個靜態容器獲取service, 監聽器裡無法通過建構函式 注入 ISchedulerFactory, IFluentEmail, 猜測應該是迴圈引用了
}

  

 

5、謝謝觀看,拜拜

 

相關文章