Browse Source

Merge pull request #475 from colinin/scope-job

tasks: use scope services to perform jobs
pull/489/head
yx lin 4 years ago
committed by GitHub
parent
commit
2415c4041b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoModal.vue
  2. 75
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs
  3. 207
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJobBase.cs
  4. 18
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs
  5. 14
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs
  6. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobAdapter.cs
  7. 4
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundWorkerAdapter.cs
  8. 10
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundWorkerManager.cs
  9. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs
  10. 6
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs
  11. 1
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json
  12. 1
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json
  13. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs

15
apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoModal.vue

@ -152,6 +152,13 @@
labelAlign: 'left',
labelWidth: 120,
schemas: [
{
field: 'oldKey',
component: 'Input',
label: 'oldKey',
show: false,
colProps: { span: 24 },
},
{
field: 'key',
component: 'Input',
@ -319,8 +326,12 @@
function handleSaveParam() {
validate().then((input) => {
console.log(input);
const model = unref(modelRef);
model.args ??= {};
if (input.oldKey && input.key !== input.oldKey) {
delete model.args[input.oldKey];
}
model.args[input.key] = input.value;
resetParamFields();
closeParamModal();
@ -330,13 +341,13 @@
function handleEditParam(record) {
openParamModal(true);
nextTick(() => {
setParamFields(record);
setParamFields(Object.assign({ oldKey: record.key }, record));
});
}
function handleDeleteParam(record) {
const model = unref(modelRef);
model.args ??= {};
delete model.args[record.key]
delete model.args[record.key];
}
</script>

75
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJob.cs

@ -1,15 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Http;
using Volo.Abp.Json;
namespace LINGYUN.Abp.BackgroundTasks.Jobs;
public class HttpRequestJob : IJobRunnable
public class HttpRequestJob : HttpRequestJobBase, IJobRunnable
{
public const string PropertyUrl = "url";
public const string PropertyMethod = "method";
@ -20,67 +17,41 @@ public class HttpRequestJob : IJobRunnable
public virtual async Task ExecuteAsync(JobRunnableContext context)
{
var clientFactory = context.GetRequiredService<IHttpClientFactory>();
InitJob(context);
var client = clientFactory.CreateClient();
var requestMessage = BuildRequestMessage(context);
var response = await client.SendAsync(
requestMessage,
HttpCompletionOption.ResponseHeadersRead);
var stringContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode && stringContent.IsNullOrWhiteSpace())
{
context.SetResult($"HttpStatusCode: {(int)response.StatusCode}, Reason: {response.ReasonPhrase}");
return;
}
context.SetResult(stringContent);
}
protected virtual HttpRequestMessage BuildRequestMessage(JobRunnableContext context)
{
var url = context.GetString(PropertyUrl);
var method = context.GetString(PropertyMethod);
context.TryGetString(PropertyData, out var data);
context.TryGetJobData(PropertyData, out var data);
context.TryGetString(PropertyContentType, out var contentType);
var jsonSerializer = context.GetRequiredService<IJsonSerializer>();
var httpRequestMesasge = new HttpRequestMessage(new HttpMethod(method), url);
if (!data.IsNullOrWhiteSpace())
var headers = new Dictionary<string, string>();
if (context.TryGetJobData(PropertyHeaders, out var headerInput))
{
// TODO: 需要支持表单类型
// application/json 支持
httpRequestMesasge.Content = new StringContent(
data,
Encoding.UTF8,
contentType ?? MimeTypes.Application.Json);
}
if (context.TryGetJobData(PropertyHeaders, out var headers))
{
var headersDic = new Dictionary<string, object>();
if (headers is string headerString)
if (headerInput is string headerString)
{
try
{
headersDic = jsonSerializer.Deserialize<Dictionary<string, object>>(headerString);
headers = Deserialize<Dictionary<string, string>>(headerString);
}
catch { }
}
foreach (var header in headersDic)
{
httpRequestMesasge.Headers.Add(header.Key, header.Value.ToString());
}
}
// TODO: 和 headers 一起?
if (context.TryGetString(PropertyToken, out var accessToken))
var response = await RequestAsync(
context,
new HttpMethod(method),
url,
data,
contentType,
headers.ToImmutableDictionary());
var stringContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode && stringContent.IsNullOrWhiteSpace())
{
httpRequestMesasge.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
context.SetResult($"HttpStatusCode: {(int)response.StatusCode}, Reason: {response.ReasonPhrase}");
return;
}
return httpRequestMesasge;
context.SetResult(stringContent);
}
}

207
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/HttpRequestJobBase.cs

@ -0,0 +1,207 @@
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Content;
using Volo.Abp.Http;
using Volo.Abp.Http.Client;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.BackgroundTasks.Jobs;
public abstract class HttpRequestJobBase
{
protected ICurrentTenant CurrentTenant { get; set; }
protected virtual void InitJob(JobRunnableContext context)
{
CurrentTenant = context.GetRequiredService<ICurrentTenant>();
}
protected virtual async Task<T> RequestAsync<T>(
JobRunnableContext context,
HttpMethod httpMethod,
string requestUrl,
object data = null,
string contentType = MimeTypes.Application.Json,
IReadOnlyDictionary<string, string> headers = null,
string clientName = null)
{
var response = await RequestAsync(
context,
httpMethod,
requestUrl,
data,
contentType,
headers,
clientName);
var responseContent = response.Content;
if (typeof(T) == typeof(IRemoteStreamContent) ||
typeof(T) == typeof(RemoteStreamContent))
{
return (T)(object)new RemoteStreamContent(
await responseContent.ReadAsStreamAsync(),
responseContent.Headers?.ContentDisposition?.FileNameStar ??
RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString(),
responseContent.Headers?.ContentType?.ToString(),
responseContent.Headers?.ContentLength);
}
var stringContent = await responseContent.ReadAsStringAsync();
if (string.IsNullOrWhiteSpace(stringContent))
{
return default;
}
if (typeof(T) == typeof(string))
{
return (T)(object)stringContent;
}
return Deserialize<T>(stringContent);
}
protected virtual async Task<HttpResponseMessage> RequestAsync(
JobRunnableContext context,
HttpMethod httpMethod,
string requestUrl,
object data = null,
string contentType = MimeTypes.Application.Json,
IReadOnlyDictionary<string, string> headers = null,
string clientName = null)
{
var request = BuildRequestMessage(httpMethod, requestUrl, data, contentType, headers);
var clientFactory = context.GetRequiredService<IHttpClientFactory>();
var client = clientName.IsNullOrWhiteSpace()
? clientFactory.CreateClient()
: clientFactory.CreateClient(clientName);
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
await ThrowExceptionForResponseAsync(response);
}
return response;
}
protected virtual HttpRequestMessage BuildRequestMessage(
HttpMethod httpMethod,
string requestUrl,
object data = null,
string contentType = MimeTypes.Application.Json,
IReadOnlyDictionary<string, string> headers = null)
{
var httpRequestMesasge = new HttpRequestMessage(httpMethod, requestUrl);
if (data != null)
{
// TODO: 需要支持表单类型
// application/json 支持
httpRequestMesasge.Content = new StringContent(
Serialize(data),
Encoding.UTF8,
contentType ?? MimeTypes.Application.Json);
}
AddHeaders(httpRequestMesasge, headers);
return httpRequestMesasge;
}
protected virtual void AddHeaders(
HttpRequestMessage requestMessage,
IReadOnlyDictionary<string, string> headers = null)
{
if (CurrentTenant?.Id.HasValue == true)
{
requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.ToString());
}
if (headers != null)
{
foreach (var header in headers)
{
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
// 不包装请求结果
requestMessage.Headers.TryAddWithoutValidation("_AbpDontWrapResult", "true");
requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(CultureInfo.CurrentCulture.Name));
}
protected virtual async Task ThrowExceptionForResponseAsync(HttpResponseMessage response)
{
if (response.Headers.Contains(AbpHttpConsts.AbpErrorFormat))
{
RemoteServiceErrorResponse errorResponse;
try
{
errorResponse = Deserialize<RemoteServiceErrorResponse>(
await response.Content.ReadAsStringAsync()
);
}
catch (Exception ex)
{
throw new AbpRemoteCallException(
new RemoteServiceErrorInfo
{
Message = response.ReasonPhrase,
Code = response.StatusCode.ToString()
},
ex
)
{
HttpStatusCode = (int)response.StatusCode
};
}
throw new AbpRemoteCallException(errorResponse.Error)
{
HttpStatusCode = (int)response.StatusCode
};
}
else
{
throw new AbpRemoteCallException(
new RemoteServiceErrorInfo
{
Message = response.ReasonPhrase,
Code = response.StatusCode.ToString()
}
)
{
HttpStatusCode = (int)response.StatusCode
};
}
}
protected virtual T Deserialize<T>(string value)
{
return JsonConvert.DeserializeObject<T>(value);
}
protected virtual string Serialize(object value)
{
return JsonConvert.SerializeObject(value);
}
protected virtual StringSegment RemoveQuotes(StringSegment input)
{
if (!StringSegment.IsNullOrEmpty(input) && input.Length >= 2 && input[0] == '"' && input[input.Length - 1] == '"')
{
input = input.Subsegment(1, input.Length - 2);
}
return input;
}
}

18
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs

@ -21,16 +21,16 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
protected IClock Clock { get; }
protected IJobEventProvider EventProvider { get; }
protected IServiceProvider ServiceProvider { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public QuartzJobListener(
IClock clock,
IServiceProvider serviceProvider,
IJobEventProvider eventProvider)
IJobEventProvider eventProvider,
IServiceScopeFactory serviceScopeFactory)
{
Clock = clock;
ServiceProvider = serviceProvider;
EventProvider = eventProvider;
ServiceScopeFactory = serviceScopeFactory;
Logger = NullLogger<QuartzJobListener>.Instance;
}
@ -62,7 +62,7 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
return;
}
using var scope = ServiceProvider.CreateScope();
using var scope = ServiceScopeFactory.CreateScope();
var jobEventData = new JobEventData(
jobId,
context.JobDetail.JobType,
@ -108,7 +108,7 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
return;
}
using var scope = ServiceProvider.CreateScope();
using var scope = ServiceScopeFactory.CreateScope();
var jobType = context.JobDetail.JobType;
if (jobType.IsGenericType)
{
@ -132,8 +132,10 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
}
jobEventData.Description = context.JobDetail.Description;
jobEventData.RunTime = Clock.Now;
jobEventData.LastRunTime = context.PreviousFireTimeUtc?.LocalDateTime;
jobEventData.NextRunTime = context.NextFireTimeUtc?.LocalDateTime;
jobEventData.LastRunTime = context.PreviousFireTimeUtc?.LocalDateTime
?? context.Trigger.GetPreviousFireTimeUtc()?.LocalDateTime;
jobEventData.NextRunTime = context.NextFireTimeUtc?.LocalDateTime
?? context.Trigger.GetNextFireTimeUtc()?.LocalDateTime;
if (context.Result != null)
{
jobEventData.Result = context.Result?.ToString();

14
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobSimpleAdapter.cs

@ -1,6 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
@ -9,23 +8,22 @@ namespace LINGYUN.Abp.BackgroundTasks.Quartz;
public class QuartzJobSimpleAdapter<TJobRunnable> : IJob
where TJobRunnable : IJobRunnable
{
protected IServiceProvider ServiceProvider { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public QuartzJobSimpleAdapter(
IServiceProvider serviceProvider)
IServiceScopeFactory serviceScopeFactory)
{
ServiceProvider = serviceProvider;
ServiceScopeFactory = serviceScopeFactory;
}
public async virtual Task Execute(IJobExecutionContext context)
{
// 任务已经在一个作用域中
// using var scope = ServiceProvider.CreateScope();
var jobExecuter = ServiceProvider.GetRequiredService<IJobRunnableExecuter>();
using var scope = ServiceScopeFactory.CreateScope();
var jobExecuter = scope.ServiceProvider.GetRequiredService<IJobRunnableExecuter>();
var jobContext = new JobRunnableContext(
typeof(TJobRunnable),
ServiceProvider,
scope.ServiceProvider,
context.MergedJobDataMap.ToImmutableDictionary());
await jobExecuter.ExecuteAsync(jobContext);

2
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobAdapter.cs

@ -14,8 +14,8 @@ public class BackgroundJobAdapter<TArgs> : IJobRunnable
public ILogger<BackgroundJobAdapter<TArgs>> Logger { protected get; set; }
protected AbpBackgroundJobOptions Options { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IBackgroundJobExecuter JobExecuter { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public BackgroundJobAdapter(
IOptions<AbpBackgroundJobOptions> options,

4
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundWorkerAdapter.cs

@ -14,8 +14,6 @@ public class BackgroundWorkerAdapter<TWorker> : BackgroundWorkerBase, IBackgroun
where TWorker : IBackgroundWorker
{
protected IClock Clock => LazyServiceProvider.LazyGetRequiredService<IClock>();
protected IJobStore JobStore => LazyServiceProvider.LazyGetRequiredService<IJobStore>();
protected IJobScheduler JobScheduler => LazyServiceProvider.LazyGetRequiredService<IJobScheduler>();
protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
private readonly MethodInfo _doWorkAsyncMethod;
@ -94,7 +92,7 @@ public class BackgroundWorkerAdapter<TWorker> : BackgroundWorkerBase, IBackgroun
public async Task ExecuteAsync(JobRunnableContext context)
{
var worker = (IBackgroundWorker)ServiceProvider.GetService(typeof(TWorker));
var worker = (IBackgroundWorker)context.GetService(typeof(TWorker));
var workerContext = new PeriodicBackgroundWorkerContext(ServiceProvider);
switch (worker)

10
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundWorkerManager.cs

@ -12,23 +12,21 @@ public class BackgroundWorkerManager : IBackgroundWorkerManager, ISingletonDepen
{
protected IJobStore JobStore { get; }
protected IJobScheduler JobScheduler { get; }
protected IServiceProvider ServiceProvider { get; }
public BackgroundWorkerManager(
IJobStore jobStore,
IJobScheduler jobScheduler,
IServiceProvider serviceProvider)
IJobScheduler jobScheduler)
{
JobStore = jobStore;
JobScheduler = jobScheduler;
ServiceProvider = serviceProvider;
}
public async Task AddAsync(IBackgroundWorker worker)
{
var adapterType = typeof(BackgroundWorkerAdapter<>).MakeGenericType(ProxyHelper.GetUnProxiedType(worker));
var adapterType = typeof(BackgroundWorkerAdapter<>)
.MakeGenericType(ProxyHelper.GetUnProxiedType(worker));
var workerAdapter = ServiceProvider.GetService(adapterType) as IBackgroundWorkerRunnable;
var workerAdapter = Activator.CreateInstance(adapterType) as IBackgroundWorkerRunnable;
var jobInfo = workerAdapter?.BuildWorker(worker);
if (jobInfo == null)

2
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs

@ -113,7 +113,7 @@ public class JobExecutedEvent : JobEventBase<JobExecutedEvent>, ITransientDepend
{
if (exception.InnerException != null)
{
return exception.Message + " => " + GetExceptionMessage(exception.InnerException);
return GetExceptionMessage(exception.InnerException);
}
return exception.Message;

6
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs

@ -246,11 +246,7 @@ public class BackgroundJobInfoAppService : TaskManagementApplicationService, IBa
backgroundJobInfo.Description = input.Description;
backgroundJobInfo.MaxCount = input.MaxCount;
backgroundJobInfo.MaxTryCount = input.MaxTryCount;
foreach (var arg in input.Args)
{
backgroundJobInfo.Args[arg.Key] = arg.Value;
}
backgroundJobInfo.Args = input.Args;
backgroundJobInfo.SetPriority(input.Priority);
switch (input.JobType)

1
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json

@ -70,6 +70,7 @@
"BackgroundJobs:AddNew": "New Job",
"BackgroundJobs:Edit": "Edit Job",
"BackgroundJobs:AddNewArg": "New Args",
"BackgroundJobs:Paramter": "Job Paramter",
"BackgroundJobs:Pause": "Pause",
"BackgroundJobs:Resume": "Resume",
"BackgroundJobs:Trigger": "Trigger",

1
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json

@ -70,6 +70,7 @@
"BackgroundJobs:AddNew": "新建作业",
"BackgroundJobs:Edit": "编辑作业",
"BackgroundJobs:AddNewArg": "添加参数",
"BackgroundJobs:Paramter": "作业参数",
"BackgroundJobs:Pause": "暂停",
"BackgroundJobs:Resume": "恢复",
"BackgroundJobs:Trigger": "触发",

2
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs

@ -30,7 +30,7 @@ public class BackgroundJobInfo : AuditedAggregateRoot<string>, IMultiTenant
/// <summary>
/// 任务参数
/// </summary>
public virtual ExtraPropertyDictionary Args { get; protected set; }
public virtual ExtraPropertyDictionary Args { get; set; }
/// <summary>
/// 任务状态
/// </summary>

Loading…
Cancel
Save