Updates project to target .NET 6-9, adds ErrorOr package, standardizes naming and copyright info, simplifies command executor interfaces, removes legacy ExecutorResult in favor of ApiResult patterndevelop
parent
1e7dd0f239
commit
517af98597
45 changed files with 7848 additions and 276 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,123 @@ |
|||||||
|
@startuml XxlJob.Core Class Diagram |
||||||
|
title XxlJob.Core Class Diagram |
||||||
|
|
||||||
|
class XxlRestfulServiceHandler { |
||||||
|
- JobDispatcher _jobDispatcher |
||||||
|
- IJobLogger _jobLogger |
||||||
|
+ HandleAync(HttpContext context) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
interface IJobLogger { |
||||||
|
+ SetLogFile(long logTime, long logId) |
||||||
|
+ Log(string pattern, params object[] format) |
||||||
|
+ LogError(Exception ex) |
||||||
|
+ ReadLog(long logTime, long logId, int fromLine) |
||||||
|
+ LogSpecialFile() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class JobDispatcher { |
||||||
|
- TaskExecutorFactory _executorFactory |
||||||
|
- CallbackQueue _callbackTaskQueue |
||||||
|
- ConcurrentDictionary<int, JobTaskQueue> RUNNING_QUEUE |
||||||
|
|
||||||
|
+ TryRemoveJobTask(int jobId) |
||||||
|
+ Execute(TriggerParam triggerParam) |
||||||
|
+ IdleBeat(int jobId) |
||||||
|
} |
||||||
|
|
||||||
|
class TaskExecutorFactory { |
||||||
|
- Dictionary<string, ITaskExecutor> _cache |
||||||
|
+ GetTaskExecutor(string glueType) |
||||||
|
} |
||||||
|
|
||||||
|
class CallbackQueue { |
||||||
|
- AdminClient _adminClient |
||||||
|
- IJobLogger _jobLogger |
||||||
|
- RetryCallbackTaskQueue _retryQueue |
||||||
|
- ConcurrentQueue<HandleCallbackParam> taskQueue |
||||||
|
|
||||||
|
+ Push(HandleCallbackParam callbackParam) |
||||||
|
+ Dispose() |
||||||
|
} |
||||||
|
|
||||||
|
class RetryCallbackTaskQueue {} |
||||||
|
|
||||||
|
class AdminClient { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class JobTaskQueue { |
||||||
|
- ConcurrentQueue<TriggerParam> TASK_QUEUE |
||||||
|
- ConcurrentDictionary<long, byte> ID_IN_QUEUE |
||||||
|
+ EventHandler<HandleCallbackParam> CallBack |
||||||
|
|
||||||
|
+ IsRunning() |
||||||
|
+ Replace(TriggerParam triggerParam) |
||||||
|
+ Push(TriggerParam triggerParam) |
||||||
|
+ Stop() |
||||||
|
+ Dispose() |
||||||
|
} |
||||||
|
|
||||||
|
interface ITaskExecutor { |
||||||
|
+ Execute(TriggerParam triggerParam) |
||||||
|
} |
||||||
|
|
||||||
|
class BeanTaskExecutor { |
||||||
|
- IJobHandlerFactory _handlerFactory |
||||||
|
- IJobLogger _jobLogger |
||||||
|
} |
||||||
|
|
||||||
|
class TriggerParam {} |
||||||
|
|
||||||
|
class HandleCallbackParam {} |
||||||
|
|
||||||
|
interface IJobHandlerFactory {} |
||||||
|
|
||||||
|
class JobHandlerFactory { |
||||||
|
+ GetJobHandler(string handlerName) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
interface IJobHandler { |
||||||
|
+ Execute(JobExecuteContext context) |
||||||
|
} |
||||||
|
|
||||||
|
abstract class AbstractJobHandler {} |
||||||
|
|
||||||
|
|
||||||
|
class JobExecuteContext { |
||||||
|
+ string JobParameter |
||||||
|
+ IJobLogger JobLogger |
||||||
|
+ CancellationToken cancellationToken |
||||||
|
} |
||||||
|
|
||||||
|
class JobHandlerAttribute {} |
||||||
|
|
||||||
|
note "用于标记JobHandler的名字" as N1 |
||||||
|
|
||||||
|
XxlRestfulServiceHandler -right-> JobDispatcher |
||||||
|
XxlRestfulServiceHandler --> IJobLogger |
||||||
|
|
||||||
|
JobDispatcher --> TaskExecutorFactory |
||||||
|
JobDispatcher --> CallbackQueue |
||||||
|
JobDispatcher --> JobTaskQueue |
||||||
|
|
||||||
|
TaskExecutorFactory *-- ITaskExecutor |
||||||
|
BeanTaskExecutor -up-|> ITaskExecutor |
||||||
|
|
||||||
|
CallbackQueue --> IJobLogger |
||||||
|
CallbackQueue --> RetryCallbackTaskQueue |
||||||
|
CallbackQueue --> AdminClient |
||||||
|
CallbackQueue --> HandleCallbackParam |
||||||
|
|
||||||
|
BeanTaskExecutor --> IJobHandlerFactory |
||||||
|
JobHandlerFactory -right-|> IJobHandlerFactory |
||||||
|
JobHandlerFactory *-left- IJobHandler |
||||||
|
|
||||||
|
AbstractJobHandler -right-|> IJobHandler |
||||||
|
|
||||||
|
N1 .. JobHandlerAttribute |
||||||
|
@enduml |
||||||
|
After Width: | Height: | Size: 41 KiB |
@ -0,0 +1,127 @@ |
|||||||
|
@startuml XxlJob.Core - Class Diagram |
||||||
|
|
||||||
|
title XxlJob.Core - Class Diagram |
||||||
|
|
||||||
|
|
||||||
|
class XxlJobHttpHandler{ |
||||||
|
+ HandleAsync(HttpContext context) |
||||||
|
} |
||||||
|
|
||||||
|
interface ICommandExecutorFactory{ |
||||||
|
+ GetCommandExecutor(string commandName) |
||||||
|
} |
||||||
|
|
||||||
|
class CommandExecutorFactory{} |
||||||
|
|
||||||
|
interface ICommandExecutor{ |
||||||
|
+ string CommandName |
||||||
|
+ ExecuteAsync(byte[] payload) |
||||||
|
} |
||||||
|
class BeatCommandExecutor{} |
||||||
|
class IdleBeatCommandExecutor{} |
||||||
|
class TiggerCommandExecutor{} |
||||||
|
class AbortCommandExecutor{} |
||||||
|
|
||||||
|
|
||||||
|
interface IJobDispatcher{ |
||||||
|
+ StartAsync() |
||||||
|
+ StopAsync() |
||||||
|
+ DisposeAsync() |
||||||
|
|
||||||
|
+ TiggerJobAsync(Job job) |
||||||
|
+ ReplaceJobAsync(Job job) |
||||||
|
+ AbortJobAsync(int jobId) |
||||||
|
} |
||||||
|
class JobDispatcher{ |
||||||
|
- JobQueue _jobQueue |
||||||
|
- CallQueue _callbackQueue |
||||||
|
} |
||||||
|
|
||||||
|
interface IJobExecutorFactory{ |
||||||
|
+ GetTaskExecutor(string glueType) |
||||||
|
} |
||||||
|
class JobExecutorFactory{ |
||||||
|
} |
||||||
|
interface IJobExecutor{ |
||||||
|
+ string GlueType |
||||||
|
+ ExecuteAsync(Job job) |
||||||
|
} |
||||||
|
|
||||||
|
class BeanJobExecutor{ |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
interface IJobHandlerFactory{ |
||||||
|
+ GetJobHandler(string handlerName) |
||||||
|
} |
||||||
|
|
||||||
|
class JobHandlerFactory{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
interface IJobHandler{ |
||||||
|
+ string Name |
||||||
|
+ HandleAsync(JobContext context) |
||||||
|
} |
||||||
|
|
||||||
|
class JobWorker{ |
||||||
|
- Queue _jobQueue |
||||||
|
|
||||||
|
+ DisposeAsync() |
||||||
|
|
||||||
|
+ Event OnJobCompleted |
||||||
|
|
||||||
|
+ EnqueueTaskAsync(JobTask task) |
||||||
|
+ ReplaceJobAsync(Job job) |
||||||
|
+ AbortJobAsync(JobId jobId) |
||||||
|
|
||||||
|
} |
||||||
|
class JobExecuteResult{} |
||||||
|
class CallBackWorker{ |
||||||
|
- Queue _callbackQueue |
||||||
|
|
||||||
|
+ DisposeAsync() |
||||||
|
|
||||||
|
+ EnqueueAsync(JobExecuteResult result) |
||||||
|
} |
||||||
|
|
||||||
|
interface IAdminClient{} |
||||||
|
|
||||||
|
XxlJobHttpHandler ..> ICommandExecutorFactory |
||||||
|
|
||||||
|
CommandExecutorFactory -up-|> ICommandExecutorFactory |
||||||
|
|
||||||
|
BeatCommandExecutor -up-|> ICommandExecutor |
||||||
|
IdleBeatCommandExecutor -up-|> ICommandExecutor |
||||||
|
TiggerCommandExecutor -up-|> ICommandExecutor |
||||||
|
AbortCommandExecutor -up-|> ICommandExecutor |
||||||
|
|
||||||
|
ICommandExecutor .up.* CommandExecutorFactory |
||||||
|
|
||||||
|
BeanJobExecutor -right-|> IJobExecutor |
||||||
|
|
||||||
|
IJobExecutor .right.* JobExecutorFactory |
||||||
|
|
||||||
|
JobDispatcher -up-|> IJobDispatcher |
||||||
|
|
||||||
|
JobDispatcher ..> JobWorker |
||||||
|
JobDispatcher ..> CallBackWorker |
||||||
|
|
||||||
|
|
||||||
|
JobWorker ..> IJobExecutorFactory |
||||||
|
CallBackWorker ..> IAdminClient |
||||||
|
|
||||||
|
JobExecutorFactory -up-|> IJobExecutorFactory |
||||||
|
|
||||||
|
|
||||||
|
BeatCommandExecutor ..> IJobDispatcher |
||||||
|
IdleBeatCommandExecutor ..> IJobDispatcher |
||||||
|
TiggerCommandExecutor ..> IJobDispatcher |
||||||
|
AbortCommandExecutor ..> IJobDispatcher |
||||||
|
|
||||||
|
BeanJobExecutor ..> IJobHandlerFactory |
||||||
|
JobHandlerFactory -up-|> IJobHandlerFactory |
||||||
|
IJobHandler ..* JobHandlerFactory |
||||||
|
|
||||||
|
|
||||||
|
@enduml |
||||||
|
After Width: | Height: | Size: 45 KiB |
@ -0,0 +1,12 @@ |
|||||||
|
{ |
||||||
|
"profiles": { |
||||||
|
"DotXxlJob.Core": { |
||||||
|
"commandName": "Project", |
||||||
|
"launchBrowser": true, |
||||||
|
"environmentVariables": { |
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||||
|
}, |
||||||
|
"applicationUrl": "https://localhost:51910;http://localhost:51911" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,111 @@ |
|||||||
|
// Copyright (c) Xuanye Wang. All rights reserved. |
||||||
|
// Licensed under MIT license |
||||||
|
|
||||||
|
using DotXxlJob.Core.Models; |
||||||
|
using Microsoft.AspNetCore.Http; |
||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using System; |
||||||
|
using System.IO; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
|
||||||
|
namespace DotXxlJob.Core |
||||||
|
{ |
||||||
|
public class XxlJobHttpHandler |
||||||
|
{ |
||||||
|
private readonly ICommandExecutorFactory _commandExecutorFactory; |
||||||
|
private readonly ISerializer _serializer; |
||||||
|
private readonly XxlJobExecutorOptions _options; |
||||||
|
public XxlJobHttpHandler(ICommandExecutorFactory commandExecutorFactory, ISerializer serializer, IOptions<XxlJobExecutorOptions> optionsAccessor) |
||||||
|
{ |
||||||
|
if (optionsAccessor?.Value == null) |
||||||
|
{ |
||||||
|
throw new ArgumentNullException(nameof(optionsAccessor)); |
||||||
|
} |
||||||
|
_options = optionsAccessor.Value; |
||||||
|
_commandExecutorFactory = commandExecutorFactory; |
||||||
|
_serializer = serializer; |
||||||
|
} |
||||||
|
public async Task HandleAsync(HttpContext context) |
||||||
|
{ |
||||||
|
var request = context.Request; |
||||||
|
var response = context.Response; |
||||||
|
var command = GetMethodName(request.Path.Value); |
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(command)) |
||||||
|
{ |
||||||
|
//no need to do anything |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!ValidateAccessToken(request)) |
||||||
|
{ |
||||||
|
await SendErrorResponse(response, StatusCodes.Status401Unauthorized, "Unauthorized"); |
||||||
|
return; |
||||||
|
} |
||||||
|
var executor = _commandExecutorFactory.GetCommandExecutor(command); |
||||||
|
|
||||||
|
if (executor == null) |
||||||
|
{ |
||||||
|
await SendErrorResponse(response, StatusCodes.Status400BadRequest, "The method have not been implemented"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
byte[] payload; |
||||||
|
using (var memoryStream = new MemoryStream()) |
||||||
|
{ |
||||||
|
await request.Body.CopyToAsync(memoryStream); |
||||||
|
payload = memoryStream.ToArray(); |
||||||
|
} |
||||||
|
var result = await executor.ExecuteAsync(payload); |
||||||
|
await SendResponse(response, result.Code, result); |
||||||
|
} |
||||||
|
|
||||||
|
private bool ValidateAccessToken(HttpRequest request) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(_options.AccessToken)) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (request.Headers.TryGetValue("XXL-JOB-ACCESS-TOKEN", out var accessToken) && _options.AccessToken.Equals(accessToken)) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private static string GetMethodName(string? path) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(path)) |
||||||
|
{ |
||||||
|
return string.Empty; |
||||||
|
} |
||||||
|
|
||||||
|
var arrParts = path.Split('/'); |
||||||
|
if (arrParts.Length < 1) |
||||||
|
{ |
||||||
|
return string.Empty; |
||||||
|
} |
||||||
|
return arrParts[arrParts.Length - 1].ToLower(); |
||||||
|
} |
||||||
|
|
||||||
|
private Task SendResponse(HttpResponse response, int statusCode, object? data = null) |
||||||
|
{ |
||||||
|
response.StatusCode = statusCode; |
||||||
|
response.ContentType = "application/json"; |
||||||
|
if (data != null) |
||||||
|
{ |
||||||
|
var bytes = _serializer.Serialize(data, data.GetType()); |
||||||
|
return response.Body.WriteAsync(bytes, 0, bytes.Length); |
||||||
|
} |
||||||
|
return Task.CompletedTask; |
||||||
|
} |
||||||
|
|
||||||
|
private Task SendErrorResponse(HttpResponse response, int statusCode, string message) |
||||||
|
{ |
||||||
|
return SendResponse(response, statusCode, ApiResult.Failure(message)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
Loading…
Reference in new issue