7.7 KiB
Moving Background Job Execution To A Separate Application
In this article, I will show you how to move the background job execution to a separate application.
Here are some benefits of doing this:
- If your background jobs consume high system resources (CPU, RAM or Disk), then you can deploy that background application to a dedicated server so it won't affect your application's performance.
- You can scale your background job application independently from your web application. For example, you can deploy multiple instances of your background job application to a Kubernetes cluster and scale it easily.
Here are some disadvantages of doing this:
- You need to deploy and maintain at least two applications instead of one.
- You need to implement a mechanism to share the common code between your applications. For example, you can create a shared project and add it to your applications as a project reference.
Source code
You can find the source code of the application at abpframework/abp-samples.
You can check the PR to see the changes step by step: abpframework/abp-samples#250
Creating the Web Application
First, we need to create a new web application using the ABP CLI:
abp new SeparateBackgroundJob -t app
- Create a shared project named
SeparateBackgroundJob.Common.Sharedto share theBackgroundJobandBackgroundJobArgsclasses between the web and job executor applications. - Install the
Volo.Abp.BackgroundJobs.Abstractionspackage to theSeparateBackgroundJob.Common.Sharedproject.
Add the SeparateBackgroundJobCommonSharedModule class to the SeparateBackgroundJob.Common.Shared project:
[DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))]
public class SeparateBackgroundJobCommonSharedModule : AbpModule
{
}
Add the MyReportJob and MyReportJobArgs classes to the SeparateBackgroundJob.Common.Shared project:
public class MyReportJob : AsyncBackgroundJob<MyReportJobArgs>, ITransientDependency
{
public override Task ExecuteAsync(MyReportJobArgs args)
{
Logger.LogInformation("Executing MyReportJob with args: {0}", args.Content);
return Task.CompletedTask;
}
}
public class MyReportJobArgs
{
public string? Content { get; set; }
}
Add the SeparateBackgroundJob.Common.Shared project reference to the SeparateBackgroundJob.Domain project and add SeparateBackgroundJobCommonSharedModule to the DependsOn attribute of the SeparateBackgroundJobDomainModule class:
[DependsOn(
typeof(SeparateBackgroundJobDomainSharedModule),
typeof(AbpAuditLoggingDomainModule),
typeof(AbpBackgroundJobsDomainModule),
typeof(AbpFeatureManagementDomainModule),
typeof(AbpIdentityDomainModule),
typeof(AbpOpenIddictDomainModule),
typeof(AbpPermissionManagementDomainOpenIddictModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(AbpSettingManagementDomainModule),
typeof(AbpTenantManagementDomainModule),
typeof(AbpEmailingModule),
typeof(SeparateBackgroundJobCommonSharedModule) //Add this line
)]
public class SeparateBackgroundJobDomainModule : AbpModule
Open the Index.cshtml and replace the content with the following code:
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using SeparateBackgroundJob.Localization
@using Volo.Abp.Users
@model SeparateBackgroundJob.Web.Pages.IndexModel
@inject IHtmlLocalizer<SeparateBackgroundJobResource> L
@inject ICurrentUser CurrentUser
@section styles {
<abp-style src="/Pages/Index.css"/>
}
@section scripts {
<abp-script src="/Pages/Index.js"/>
}
<div class="container">
<abp-card>
<abp-card-header>
<abp-card-title>
Add NEW BACKGROUND JOB
</abp-card-title>
</abp-card-header>
<abp-card-body>
<form id="NewItemForm" method="post" class="row row-cols-lg-auto g-3 align-items-center">
<div class="col-12">
<div class="input-group">
<input id="ReportContent" required name="ReportContent" type="text" class="form-control" placeholder="enter text...">
</div>
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</form>
</abp-card-body>
</abp-card>
</div>
Open the Index.cshtml.cs and replace the content with the following code:
public class IndexModel : SeparateBackgroundJobPageModel
{
private readonly IBackgroundJobManager _backgroundJobManager;
[BindProperty(SupportsGet = true)]
public string? ReportContent { get; set; }
public IndexModel(IBackgroundJobManager backgroundJobManager)
{
_backgroundJobManager = backgroundJobManager;
}
public void OnGet()
{
}
public async Task OnPostAsync()
{
await _backgroundJobManager.EnqueueAsync(new MyReportJobArgs
{
Content = ReportContent
});
Alerts.Success("Job is queued!");
}
}
Run the application and navigate to the home page. You should see the following page:
When you enter some text and click the Add button, the job will be queued and executed in the web application:
Creating the Console Application
Now we split the background job execution to a separate console application.
Open the SeparateBackgroundJobWebModule class to disable the background job execution in the web application:
public class SeparateBackgroundJobWebModule : AbpModule
{
....
public override void ConfigureServices(ServiceConfigurationContext context)
{
...
//Disable background job execution in the web application
Configure<AbpBackgroundJobOptions>(options =>
{
options.IsJobExecutionEnabled = false;
});
}
...
}
- Create a new console application using the ABP CLI:
abp new BackgroundJobExecutor -t console
- Add the
BackgroundJobExecutorproject to the solution of the web application. - Add the
SeparateBackgroundJob.Common.Sharedproject reference to theBackgroundJobExecutorproject. - Install the
Volo.Abp.BackgroundJobs.EntityFrameworkCoreandVolo.Abp.EntityFrameworkCore.SqlServerpackages to theBackgroundJobExecutorproject.
Update the BackgroundJobExecutorModule class as follows:
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpBackgroundJobsEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(SeparateBackgroundJobCommonSharedModule)
)]
public class BackgroundJobExecutorModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
});
}
....
}
Open the appsettings.json file to configure the connection string:
{
"ConnectionStrings": {
"AbpBackgroundJobs": "Server=(LocalDb)\\MSSQLLocalDB;Database=SeparateBackgroundJob;Trusted_Connection=True"
}
}
You must use the same connection string for the web application,
AbpBackgroundJobsis the default connection string name for the background job module.
The solution structure should look like this:
Now, run the web and console application. When you enter some text and click the Add button, the job will be queued and executed in the console application:


