第一步: 创建.net core mvc 应用
为什么要创建MVC 呢? 因为 可以 采用登录进行控制hangfire 的权限控制,如果 不需要权限的话 api 应用也是可以的
第二步: 下载相关的hangfire dll (采用的是sqlserver 进行管理任务)
Hangfire.AspNetCore 、Hangfire.RecurringJobExtension、Hangfire.Sqlserver
第三步: 配置Hangfire
1.ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
//配置启用cookie
services.Configure
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
//配置权限
services.AddMvc(opt =>
{
opt.Filters.Add
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddScoped
//注入hangfire 服务
services.AddHangfire(x =>
{
x.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection"));
x.UseRecurringJob(new JobProvider());
x.UseConsole();
x.UseFilter(new LogEverythingAttribute());
});
ApiConfig.HangfireLogUrl = Configuration["HangfireLogFileUrl"];
//GlobalJobFilters.Filters.Add(new LogEverythingAttribute());
//GlobalConfiguration.Configuration.UseAutofacActivator(container);
//add dbcontext
services.AddDbContextPool
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = CookieJobsAuthInfo.AdminAuthCookieScheme;
o.DefaultChallengeScheme = CookieJobsAuthInfo.AdminAuthCookieScheme;
o.DefaultSignInScheme = CookieJobsAuthInfo.AdminAuthCookieScheme;
o.DefaultSignOutScheme = CookieJobsAuthInfo.AdminAuthCookieScheme;
}).AddCookie(CookieJobsAuthInfo.AdminAuthCookieScheme, o =>
{
o.LoginPath = "/Login";
});
//泛型注入到di
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
services.AddSingleton
services.AddSession();
services.AddScoped
services.AddScoped
}
其中
JobProvider:
public class JobProvider : IConfigurationProvider
{
public IEnumerable
{
var result = new List
var assembly = Assembly.GetEntryAssembly();
if (assembly == null) return result;
foreach (var type in assembly.GetTypes())
{
foreach (var method in type.GetTypeInfo().DeclaredMethods)
{
//if(!method.IsDefined(typeof(RecurringJobInfo),false))
// continue;
var attribute = method.GetCustomAttribute
if (attribute == null) continue;
if (string.IsNullOrWhiteSpace(attribute.RecurringJobId))
{
attribute.RecurringJobId =method.GetRecurringJobId();
}
result.Add(new RecurringJobInfo()
{
RecurringJobId = attribute.RecurringJobId,
Cron = attribute.Cron,
Queue = attribute.Queue,
Enable = attribute.Enabled,
Method = method,
TimeZone = TimeZoneInfo.Local
});
}
}
return result;
}
}
LogEverythingAttribute:
public class LogEverythingAttribute : JobFilterAttribute, IClientFilter, IServerFilter, IElectStateFilter, IApplyStateFilter
{
private static readonly ILog Logger = LogProvider.GetCurrentClassLogger();
public void OnCreating(CreatingContext filterContext)
{
Logger.InfoFormat("Creating a job based on method `{0}`...", filterContext.Job.Method.Name);
WriteLog(
$"{ApiConfig.HangfireLogUrl}\\OnCreating-{(filterContext.Job.Method.Name)}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt",
$"Creating a job based on method `{filterContext.Job.Method.Name}`... . 时间为:{DateTime.Now:yyyy-MM-dd-HH-mm-ss}");
}
public void OnCreated(CreatedContext filterContext)
{
WriteLog(
$"{ApiConfig.HangfireLogUrl}\\OnCreated-{(filterContext.BackgroundJob?.Id ?? "0")}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt",
$"Job that is based on method `{filterContext.Job.Method.Name}` has been created with id `{filterContext.BackgroundJob?.Id}` . 时间为:{DateTime.Now:yyyy-MM-dd-HH-mm-ss} \r\n");
Logger.InfoFormat(
"Job that is based on method `{0}` has been created with id `{1}`",
filterContext.Job.Method.Name,
filterContext.BackgroundJob?.Id);
}
public void OnPerforming(PerformingContext filterContext)
{
Logger.InfoFormat("Starting to perform job `{0}`", filterContext.BackgroundJob.Id);
WriteLog(
$"{ApiConfig.HangfireLogUrl}\\OnPerforming-{(filterContext.BackgroundJob?.Id ?? "0")}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt",
$"Starting to perform job `{filterContext.BackgroundJob.Id}` . 时间为:{DateTime.Now:yyyy-MM-dd-HH-mm-ss} \r\n");
}
public void OnPerformed(PerformedContext filterContext)
{
Logger.InfoFormat("Job `{0}` has been performed", filterContext.BackgroundJob.Id);
WriteLog(
$"{ApiConfig.HangfireLogUrl}\\OnPerformed-{(filterContext.BackgroundJob?.Id ?? "0")}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt",
$"Job `{ filterContext.BackgroundJob.Id}` has been performed . 时间为:{DateTime.Now:yyyy-MM-dd-HH-mm-ss} \r\n");
}
public void OnStateElection(ElectStateContext context)
{
var failedState = context.CandidateState as FailedState;
if (failedState != null)
{
Logger.WarnFormat(
"Job `{0}` has been failed due to an exception `{1}`",
context.BackgroundJob.Id,
failedState.Exception);
WriteLog(
$"{ApiConfig.HangfireLogUrl}\\OnStateElection-{(context.BackgroundJob?.Id ?? "0")}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt",
$"Job `{context.BackgroundJob.Id}` has been failed due to an exception `{(JsonConvert.SerializeObject(failedState.Exception))}` . 时间为:{DateTime.Now:yyyy-MM-dd-HH-mm-ss} \r\n");
}
}
public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
Logger.InfoFormat(
"Job `{0}` state was changed from `{1}` to `{2}`",
context.BackgroundJob.Id,
context.OldStateName,
context.NewState.Name);
WriteLog(
$"{ApiConfig.HangfireLogUrl}\\OnStateApplied-{(context.BackgroundJob?.Id ?? "0")}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt",
$"Job `{context.BackgroundJob.Id}` state was changed from `{context.OldStateName}` to `{context.NewState.Name}` . 时间为:{DateTime.Now:yyyy-MM-dd-HH-mm-ss} \r\n");
}
public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
Logger.InfoFormat(
"Job `{0}` state `{1}` was unapplied.",
context.BackgroundJob.Id,
context.OldStateName);
WriteLog(
$"{ApiConfig.HangfireLogUrl}\\OnStateUnapplied-{(context.BackgroundJob?.Id ?? "0")}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt",
$"Job `{context.BackgroundJob.Id}` state `{context.OldStateName}` was unapplied . 时间为:{DateTime.Now:yyyy-MM-dd-HH-mm-ss} \r\n");
}
///
/// 写入文本
///
/// 路径
///
public void WriteLog(string path,string message)
{
var fileUrl = path;
if (!File.Exists(fileUrl))
{
File.Create(fileUrl).Close();
}
FileStream fs = new FileStream(fileUrl, FileMode.Append);
byte[] data = System.Text.Encoding.Default.GetBytes(message??"");
fs.Write(data, 0, data.Length);
fs.Close();
}
}
2. Configure 配置
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//app.UseHttpsRedirection();
//app.UseMvc();
app.UseStaticFiles();
//app.UseHangfireServer();//启动hangfire 服务
app.UseAuthentication();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Login}/{action=Index}");
routes.MapRoute(
name: "areas",
template: "{area:exists}/{controller=Login}/{action=Index}/{id?}");
});
//配置任务属性
var jobOptions = new BackgroundJobServerOptions()
{
Queues = new[] { "default", "apis", "job" },//队列名称,只能小写
WorkerCount = Environment.ProcessorCount * 5,//并发任务数
ServerName = "hangfire"//服务器名称
};
app.UseHangfireServer(jobOptions);
//配置hangfire访问权限
var options = new DashboardOptions()
{
Authorization = new[] { new HangfireAuthorizationFilter() },
};
app.UseHangfireDashboard("/hangfire", options);//启动hangfire 面板
//HandfireExtension.Register();
//app.Run(ctx =>
//{
// ctx.Response.Redirect($"{Configuration["WebRootUrl"]}/hangfire"); //可以支持虚拟路径或者index.html这类起始页.
// return Task.FromResult(0);
//});
}
第四步:
具体样例
第五步:
dotnet core 部署在 iis 上,如果不设置 闲置超时的话 iis 会 自动闲置
可根据项目情况 调整 或 转换成 windows service 或计划任务
结尾:
github demo 地址:https://github.com/xiehanbing/JobHangfire