parent
92f054db57
commit
2d2dac9cb3
41 changed files with 982 additions and 304 deletions
@ -0,0 +1,72 @@ |
||||
------------ 0x43---------------- |
||||
ReadClassDefinition |
||||
Hessian.ClassDef={"Name":"com.xxl.rpc.remoting.net.params.XxlRpcRequest","Fields":["requestId","createMillisTime","accessToken","className","methodName","version","parameterTypes","parameters"]} |
||||
------------------------------------------------------------ |
||||
------------ 0x60---------------- |
||||
ReadObjectCompact |
||||
------------ 0x00---------------- |
||||
ReadShortString requestId |
||||
------------ 0x4c---------------- |
||||
ReadLongFull createMillisTime |
||||
------------ 0x00---------------- |
||||
ReadShortString accessToken |
||||
------------ 0x00---------------- |
||||
ReadShortString className |
||||
------------ 0x08---------------- |
||||
ReadShortString methodName |
||||
------------ 0x4e---------------- |
||||
ReadNull version |
||||
------------ 0x71---------------- |
||||
ReadCompactFixList parameterTypes |
||||
------------ 0x43---------------- |
||||
ReadClassDefinition |
||||
------------ 0x61---------------- |
||||
ReadObjectCompact |
||||
------------ 0x0e---------------- |
||||
ReadShortString |
||||
Hessian.HessianObject=[{"Item1":"requestId","Item2":""},{"Item1":"createMillisTime","Item2":1547809331509},{"Item1":"accessToken","Item2":""},{"Item1":"className","Item2":""},{"Item1":"methodName","Item2":"callback"} |
||||
,{"Item1":"version","Item2":null},{"Item1":"parameterTypes","Item2":[{"Name":"java.lang.Clas |
||||
s","Fields":["name"]}]},{"Item1":"parameters","Item2":[{"Item1":"name","Item2":"java.util.List"}]}] |
||||
------------------------------------------------------------ |
||||
------------ 0x72---------------- |
||||
ReadCompactFixList List |
||||
------------ 0x43---------------- |
||||
ReadClassDefinition HandleCallbackParam |
||||
------------ 0x62---------------- |
||||
ReadObjectCompact HandleCallbackParam |
||||
------------ 0xcc---------------- |
||||
ReadIntegerTwoBytes logId |
||||
------------ 0x4c---------------- |
||||
ReadLongFull logDateTim |
||||
------------ 0x43---------------- |
||||
ReadClassDefinition executeResult |
||||
System.Collections.Generic.List`1[System.Object]=[{"Name":"com.xxl.job.core.biz.model.HandleCallbackParam","Fields":["logId","logDateTim","executeResult"]},[{"Item1":"logId","Item2":1111},{"Item1":"logDateTim","Item2":1547809331507},{"Item1":"executeResult","Item2":{"Name":"com.xxl.job.core.biz.model.Return |
||||
T","Fields":["code","msg","content"]}}]] |
||||
------------------------------------------------------------ |
||||
------------ 0x63---------------- |
||||
ReadObjectCompact ReturnT |
||||
------------ 0xc8---------------- |
||||
ReadIntegerTwoBytes code |
||||
------------ 0x4e---------------- |
||||
ReadNull msg |
||||
------------ 0x07---------------- |
||||
ReadShortString aaaaaaa |
||||
Hessian.HessianObject=[{"Item1":"code","Item2":200},{"Item1":"msg","Item2":null},{"Item1":"content","Item2":"aaaaaaa"}] |
||||
------------------------------------------------------------ |
||||
------------ 0x62---------------- |
||||
ReadObjectCompact HandleCallbackParam |
||||
------------ 0xd7---------------- |
||||
ReadIntegerThreeBytes logId |
||||
------------ 0x4c---------------- |
||||
ReadLongFull logDateTim |
||||
------------ 0x63---------------- |
||||
ReadObjectCompact executeResult |
||||
------------ 0xc8---------------- |
||||
ReadIntegerTwoBytes code |
||||
------------ 0x4e---------------- |
||||
ReadNull msg |
||||
------------ 0x06---------------- |
||||
ReadShortString content |
||||
Hessian.HessianObject=[{"Item1":"logId","Item2":222222},{"Item1":"logDateTim","Item2":1547809331507},{"Item1":"executeResult","Item2":[{"Item1":"code","Item2":200},{"Item1":"msg","Item2":null},{"Item1":"content","Item2":"bbbbbb"}]}] |
||||
------------------------------------------------------------ |
||||
|
||||
Binary file not shown.
@ -1,13 +0,0 @@ |
||||
using DotXxlJob.Core.Model; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public class CallbackTaskQueue |
||||
{ |
||||
public void Push(CallbackParam callbackParam) |
||||
{ |
||||
|
||||
//throw new System.NotImplementedException(); |
||||
} |
||||
} |
||||
} |
||||
@ -1,35 +0,0 @@ |
||||
using System; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public static class DateTimeExtensions |
||||
{ |
||||
private const long UnixEpochTicks = 621355968000000000; |
||||
private const long UnixEpochSeconds = 62135596800; |
||||
private const long UnixEpochMilliseconds = 62135596800000; |
||||
|
||||
public static DateTimeOffset FromUnixTimeSeconds(this long seconds) |
||||
{ |
||||
long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; |
||||
return new DateTime(ticks, DateTimeKind.Utc); |
||||
} |
||||
|
||||
public static DateTime FromUnixTimeMilliseconds(this long milliseconds) |
||||
{ |
||||
long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; |
||||
return new DateTime(ticks, DateTimeKind.Utc); |
||||
} |
||||
|
||||
public static long ToUnixTimeSeconds(this DateTime dateTime) |
||||
{ |
||||
long seconds = dateTime.ToUniversalTime().Ticks / TimeSpan.TicksPerSecond; |
||||
return seconds - UnixEpochSeconds; |
||||
} |
||||
|
||||
public static long ToUnixTimeMilliseconds(this DateTime dateTime) |
||||
{ |
||||
long milliseconds = dateTime.ToUniversalTime().Ticks / TimeSpan.TicksPerMillisecond; |
||||
return milliseconds - UnixEpochMilliseconds; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
using System.Threading.Tasks; |
||||
using DotXxlJob.Core.Model; |
||||
|
||||
namespace DotXxlJob.Core.DefaultHandlers |
||||
{ |
||||
public abstract class AbsJobHandler:IJobHandler |
||||
{ |
||||
public virtual void Dispose() |
||||
{ |
||||
|
||||
} |
||||
|
||||
public abstract Task<ReturnT> Execute(JobExecuteContext context); |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
using System.Threading.Tasks; |
||||
using DotXxlJob.Core.Model; |
||||
|
||||
namespace DotXxlJob.Core.DefaultHandlers |
||||
{ |
||||
[JobHandler("httpJobHandler")] |
||||
public class HttpJobHandler:AbsJobHandler |
||||
{ |
||||
public override Task<ReturnT> Execute(JobExecuteContext context) |
||||
{ |
||||
context.JobLogger.Log("Get Request Data:{0}",context.JobParameter); |
||||
return Task.FromResult(ReturnT.SUCCESS); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,44 @@ |
||||
using System; |
||||
using DotXxlJob.Core.Config; |
||||
using Microsoft.Extensions.Configuration; |
||||
using Microsoft.Extensions.DependencyInjection; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public static class ServiceCollectionExtensions |
||||
{ |
||||
public static IServiceCollection AddXxlJobExecutor(this IServiceCollection services,IConfiguration configuration) |
||||
{ |
||||
services.AddLogging(); |
||||
services.AddOptions(); |
||||
services.Configure<XxlJobExecutorOptions>(configuration.GetSection("xxlJob")) |
||||
.AddXxlJobExecutorServiceDependency(); |
||||
|
||||
return services; |
||||
} |
||||
public static IServiceCollection AddXxlJobExecutor(this IServiceCollection services,Action<XxlJobExecutorOptions> configAction) |
||||
{ |
||||
services.AddLogging(); |
||||
services.AddOptions(); |
||||
services.Configure(configAction).AddXxlJobExecutorServiceDependency(); |
||||
return services; |
||||
} |
||||
private static IServiceCollection AddXxlJobExecutorServiceDependency(this IServiceCollection services) |
||||
{ |
||||
|
||||
services.AddHttpClient("DotXxlJobClient"); |
||||
services.AddSingleton<IJobLogger, JobLogger>(); |
||||
services.AddSingleton<ITaskExecutor, TaskExecutors.BeanTaskExecutor>(); |
||||
services.AddSingleton<IJobHandlerFactory,DefaultJobHandlerFactory >(); |
||||
services.AddSingleton<JobDispatcher>(); |
||||
services.AddSingleton<TaskExecutorFactory>(); |
||||
services.AddSingleton<XxlRpcServiceHandler>(); |
||||
services.AddSingleton<CallbackTaskQueue>(); |
||||
services.AddSingleton<AdminClient>(); |
||||
services.AddSingleton<IExecutorRegistry, ExecutorRegistry>(); |
||||
|
||||
return services; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -1,18 +0,0 @@ |
||||
using System.Threading.Tasks; |
||||
using DotXxlJob.Core.Model; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public class HttpJobHandler:IJobHandler |
||||
{ |
||||
public void Dispose() |
||||
{ |
||||
|
||||
} |
||||
|
||||
public Task<ReturnT> Execute(JobExecuteContext context) |
||||
{ |
||||
return Task.FromResult(ReturnT.SUCCESS); |
||||
} |
||||
} |
||||
} |
||||
@ -1,20 +0,0 @@ |
||||
using DotXxlJob.Core.Model; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public interface IXxlJobExecutor |
||||
{ |
||||
ReturnT Beat(); |
||||
|
||||
|
||||
ReturnT IdleBeat(int jobId); |
||||
|
||||
|
||||
ReturnT Kill(int jobId); |
||||
|
||||
ReturnT Log(long logDateTim, int logId, int fromLineNum); |
||||
|
||||
|
||||
ReturnT Run(TriggerParam triggerParam); |
||||
} |
||||
} |
||||
@ -1,7 +0,0 @@ |
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public class JobLogger |
||||
{ |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,64 @@ |
||||
using System.Reflection; |
||||
using Utf8Json; |
||||
using Utf8Json.Formatters; |
||||
using Utf8Json.Resolvers; |
||||
|
||||
namespace DotXxlJob.Core.Json |
||||
{ |
||||
public class ProjectDefaultResolver : IJsonFormatterResolver |
||||
{ |
||||
public static IJsonFormatterResolver Instance = new ProjectDefaultResolver(); |
||||
|
||||
// configure your resolver and formatters. |
||||
static readonly IJsonFormatter[] formatters = { |
||||
new DateTimeFormatter("yyyy-MM-dd HH:mm:ss"), |
||||
new NullableDateTimeFormatter("yyyy-MM-dd HH:mm:ss") |
||||
}; |
||||
|
||||
static readonly IJsonFormatterResolver[] resolvers = new[] |
||||
{ |
||||
EnumResolver.UnderlyingValue, |
||||
StandardResolver.AllowPrivateExcludeNullSnakeCase |
||||
}; |
||||
|
||||
ProjectDefaultResolver() |
||||
{ |
||||
} |
||||
|
||||
public IJsonFormatter<T> GetFormatter<T>() |
||||
{ |
||||
return FormatterCache<T>.formatter; |
||||
} |
||||
|
||||
static class FormatterCache<T> |
||||
{ |
||||
public static readonly IJsonFormatter<T> formatter; |
||||
|
||||
static FormatterCache() |
||||
{ |
||||
foreach (var item in formatters) |
||||
{ |
||||
foreach (var implInterface in item.GetType().GetTypeInfo().ImplementedInterfaces) |
||||
{ |
||||
var ti = implInterface.GetTypeInfo(); |
||||
if (ti.IsGenericType && ti.GenericTypeArguments[0] == typeof(T)) |
||||
{ |
||||
formatter = (IJsonFormatter<T>)item; |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
foreach (var item in resolvers) |
||||
{ |
||||
var f = item.GetFormatter<T>(); |
||||
if (f != null) |
||||
{ |
||||
formatter = f; |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
using System; |
||||
using DotXxlJob.Core.Model; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public interface IJobLogger |
||||
{ |
||||
|
||||
void SetLogFile(long logTime, int logId); |
||||
|
||||
void Log(string pattern, params object[] format); |
||||
|
||||
|
||||
void LogError(Exception ex); |
||||
|
||||
|
||||
LogResult ReadLog(long logTime, int logId, int fromLine); |
||||
|
||||
|
||||
void LogSpecialFile(long logTime, int logId, string pattern, params object[] format); |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,185 @@ |
||||
using System; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Text; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using DotXxlJob.Core.Config; |
||||
using DotXxlJob.Core.Model; |
||||
using Hessian; |
||||
using Microsoft.Extensions.Logging; |
||||
using Microsoft.Extensions.Options; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public class JobLogger:IJobLogger |
||||
{ |
||||
private readonly ILogger<JobLogger> _logger; |
||||
|
||||
private readonly AsyncLocal<string> LogFileName = new AsyncLocal<string>(); |
||||
|
||||
private readonly XxlJobExecutorOptions _options; |
||||
public JobLogger(IOptions<XxlJobExecutorOptions> optionsAccessor,ILogger<JobLogger> logger) |
||||
{ |
||||
this._logger = logger; |
||||
this._options = optionsAccessor.Value; |
||||
} |
||||
|
||||
public void SetLogFile(long logTime, int logId) |
||||
{ |
||||
try |
||||
{ |
||||
var filePath = MakeLogFileName(logTime, logId); |
||||
var dir = Path.GetDirectoryName(filePath); |
||||
if (!Directory.Exists(dir)) |
||||
{ |
||||
Directory.CreateDirectory(dir); |
||||
CleanOldLogs(); |
||||
} |
||||
LogFileName.Value = filePath; |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
_logger.LogError(ex, "SetLogFileName error."); |
||||
} |
||||
} |
||||
|
||||
|
||||
public void Log(string pattern, params object[] format) |
||||
{ |
||||
var appendLog = string.Format(pattern, format); |
||||
var callInfo = new StackTrace(true).GetFrame(1); |
||||
LogDetail(GetLogFileName(), callInfo, appendLog); |
||||
} |
||||
|
||||
public void LogError(Exception ex) |
||||
{ |
||||
var callInfo = new StackTrace(true).GetFrame(1); |
||||
LogDetail(GetLogFileName(), callInfo, ex.Message + ex.StackTrace); |
||||
} |
||||
|
||||
public LogResult ReadLog(long logTime, int logId, int fromLine) |
||||
{ |
||||
var filePath = MakeLogFileName(logTime, logId); |
||||
if (string.IsNullOrEmpty(filePath)) |
||||
{ |
||||
return new LogResult(fromLine, 0, "readLog fail, logFile not found", true); |
||||
} |
||||
if (!File.Exists(filePath)) |
||||
{ |
||||
return new LogResult(fromLine, 0, "readLog fail, logFile not exists", true); |
||||
} |
||||
|
||||
// read file |
||||
var logContentBuffer = new StringBuilder(); |
||||
int toLineNum = 0; |
||||
try |
||||
{ |
||||
using (var reader = new StreamReader(filePath, Encoding.UTF8)) |
||||
{ |
||||
string line; |
||||
while ((line = reader.ReadLine()) != null) |
||||
{ |
||||
toLineNum++; |
||||
if (toLineNum >= fromLine) |
||||
{ |
||||
logContentBuffer.AppendLine(line); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
_logger.LogError(ex, "ReadLog error."); |
||||
} |
||||
|
||||
// result |
||||
var logResult = new LogResult(fromLine, toLineNum, logContentBuffer.ToString(), false); |
||||
return logResult; |
||||
} |
||||
|
||||
public void LogSpecialFile(long logTime, int logId, string pattern, params object[] format) |
||||
{ |
||||
var filePath = MakeLogFileName(logTime, logId); |
||||
var callInfo = new StackTrace(true).GetFrame(1); |
||||
var content = string.Format(pattern, format); |
||||
LogDetail(filePath, callInfo, content); |
||||
} |
||||
|
||||
|
||||
private string GetLogFileName() |
||||
{ |
||||
return LogFileName.Value; |
||||
} |
||||
private string MakeLogFileName(long logDateTime, int logId) |
||||
{ |
||||
//log fileName like: logPath/HandlerLogs/yyyy-MM-dd/9999.log |
||||
return Path.Combine(_options.LogPath, Constants.HandleLogsDirectory, |
||||
logDateTime.FromMilliseconds().ToString("yyyy-MM-dd"), $"{logId}.log"); |
||||
} |
||||
private void LogDetail(string logFileName, StackFrame callInfo, string appendLog) |
||||
{ |
||||
if (string.IsNullOrEmpty(logFileName)) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
var stringBuffer = new StringBuilder(); |
||||
stringBuffer |
||||
.Append(DateTime.Now.ToString("s")).Append(" ") |
||||
.Append("[" + callInfo.GetMethod().DeclaringType.FullName + "#" + callInfo.GetMethod().Name + "]").Append("-") |
||||
.Append("[line " + callInfo.GetFileLineNumber() + "]").Append("-") |
||||
.Append("[thread " + Thread.CurrentThread.ManagedThreadId + "]").Append(" ") |
||||
.Append(appendLog ?? string.Empty) |
||||
.AppendLine(); |
||||
|
||||
var formatAppendLog = stringBuffer.ToString(); |
||||
|
||||
try |
||||
{ |
||||
File.AppendAllText(logFileName, formatAppendLog, Encoding.UTF8); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
this._logger.LogError(ex, "LogDetail error"); |
||||
} |
||||
} |
||||
|
||||
private void CleanOldLogs() |
||||
{ |
||||
if (_options.LogRetentionDays <= 0) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
Task.Run(() => |
||||
{ |
||||
try |
||||
{ |
||||
var handlerLogsDir = new DirectoryInfo(Path.Combine(_options.LogPath, Constants.HandleLogsDirectory)); |
||||
if (!handlerLogsDir.Exists) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
var today = DateTime.UtcNow.Date; |
||||
foreach (var dir in handlerLogsDir.GetDirectories()) |
||||
{ |
||||
if (DateTime.TryParse(dir.Name, out var dirDate)) |
||||
{ |
||||
if (today.Subtract(dirDate.Date).Days > _options.LogRetentionDays) |
||||
{ |
||||
dir.Delete(true); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
_logger.LogError(ex, "CleanOldLogs error."); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -1,10 +0,0 @@ |
||||
namespace DotXxlJob.Core.Model |
||||
{ |
||||
public class CallbackParam |
||||
{ |
||||
public CallbackParam(TriggerParam triggerParam, ReturnT result) |
||||
{ |
||||
throw new System.NotImplementedException(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,113 @@ |
||||
using System; |
||||
using System.Collections.Concurrent; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using DotXxlJob.Core.Config; |
||||
using DotXxlJob.Core.Model; |
||||
using Microsoft.Extensions.Logging; |
||||
using Microsoft.Extensions.Options; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public class CallbackTaskQueue:IDisposable |
||||
{ |
||||
private readonly AdminClient _adminClient; |
||||
private readonly IJobLogger _jobLogger; |
||||
private readonly RetryCallbackTaskQueue _retryQueue; |
||||
private readonly ILogger<CallbackTaskQueue> _logger; |
||||
private readonly ConcurrentQueue<HandleCallbackParam> TASK_QUEUE = new ConcurrentQueue<HandleCallbackParam>(); |
||||
|
||||
private bool _stop; |
||||
|
||||
private bool _isRunning; |
||||
|
||||
private Task _runTask; |
||||
public CallbackTaskQueue(AdminClient adminClient,IJobLogger jobLogger,IOptions<XxlJobExecutorOptions> optionsAccessor |
||||
, ILoggerFactory loggerFactory) |
||||
{ |
||||
this._adminClient = adminClient; |
||||
this._jobLogger = jobLogger; |
||||
|
||||
this._retryQueue = new RetryCallbackTaskQueue(optionsAccessor.Value.LogPath, |
||||
Push, |
||||
loggerFactory.CreateLogger<RetryCallbackTaskQueue>()); |
||||
|
||||
this._logger = loggerFactory.CreateLogger<CallbackTaskQueue>(); |
||||
} |
||||
|
||||
public void Push(HandleCallbackParam callbackParam) |
||||
{ |
||||
TASK_QUEUE.Enqueue(callbackParam); |
||||
StartCallBack(); |
||||
} |
||||
|
||||
|
||||
public void Dispose() |
||||
{ |
||||
this._stop = true; |
||||
this._retryQueue.Dispose(); |
||||
this._runTask?.GetAwaiter().GetResult(); |
||||
} |
||||
|
||||
|
||||
private void StartCallBack() |
||||
{ |
||||
if ( this._isRunning) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
this._runTask = Task.Run(async () => |
||||
{ |
||||
this._logger.LogDebug("start to callback"); |
||||
this._isRunning = true; |
||||
while (!this._stop) |
||||
{ |
||||
await DoCallBack(); |
||||
} |
||||
this._logger.LogDebug("end to callback"); |
||||
this._isRunning = false; |
||||
}); |
||||
|
||||
} |
||||
|
||||
private async Task DoCallBack() |
||||
{ |
||||
List<HandleCallbackParam> list = new List<HandleCallbackParam>(); |
||||
|
||||
while (list.Count < Constants.MaxCallbackRecordsPerRequest && this.TASK_QUEUE.TryDequeue(out var item)) |
||||
{ |
||||
list.Add(item); |
||||
} |
||||
|
||||
if (!list.Any()) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
ReturnT result; |
||||
try |
||||
{ |
||||
result = await this._adminClient.Callback(list); |
||||
} |
||||
catch (Exception ex){ |
||||
this._logger.LogError(ex,"trigger callback error:{error}",ex.Message); |
||||
result = ReturnT.Failed(ex.Message); |
||||
this._retryQueue.Push(list); |
||||
} |
||||
|
||||
LogCallBackResult(result, list); |
||||
} |
||||
|
||||
private void LogCallBackResult(ReturnT result,List<HandleCallbackParam> list) |
||||
{ |
||||
foreach (var param in list) |
||||
{ |
||||
this._jobLogger.LogSpecialFile(param.LogDateTime, param.LogId, result.Msg??"Empty"); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,125 @@ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using DotXxlJob.Core.Json; |
||||
using DotXxlJob.Core.Model; |
||||
using Microsoft.Extensions.Logging; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public class RetryCallbackTaskQueue:IDisposable |
||||
{ |
||||
|
||||
private readonly Action<HandleCallbackParam> _actionDoCallback; |
||||
private readonly ILogger<RetryCallbackTaskQueue> _logger; |
||||
|
||||
private bool _stop; |
||||
private Task _runTask; |
||||
private readonly string _backupFile; |
||||
public RetryCallbackTaskQueue(string backupPath,Action<HandleCallbackParam> actionDoCallback,ILogger<RetryCallbackTaskQueue> logger) |
||||
{ |
||||
|
||||
this._actionDoCallback = actionDoCallback; |
||||
this._logger = logger; |
||||
this._backupFile = Path.Combine(backupPath, "xxl-job-callback.log"); |
||||
var dir = Path.GetDirectoryName(backupPath); |
||||
if (!Directory.Exists(dir)) |
||||
{ |
||||
Directory.CreateDirectory(dir); |
||||
} |
||||
|
||||
StartQueue(); |
||||
} |
||||
|
||||
private void StartQueue() |
||||
{ |
||||
this._runTask = Task.Factory.StartNew(async () => |
||||
{ |
||||
while (!this._stop) |
||||
{ |
||||
await LoadFromFile(); |
||||
await Task.Delay(Constants.CallbackRetryInterval); |
||||
} |
||||
|
||||
}, TaskCreationOptions.LongRunning); |
||||
} |
||||
|
||||
private async Task LoadFromFile() |
||||
{ |
||||
var list = new List<HandleCallbackParam>(); |
||||
|
||||
if (!File.Exists(_backupFile)) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
var nextLine = string.Empty; |
||||
using (StreamReader reader = new StreamReader(this._backupFile)) |
||||
{ |
||||
while ((nextLine = await reader.ReadLineAsync()) != null) |
||||
{ |
||||
try |
||||
{ |
||||
list.Add(Utf8Json.JsonSerializer.Deserialize<HandleCallbackParam>(nextLine, ProjectDefaultResolver.Instance)); |
||||
} |
||||
catch(Exception ex) |
||||
{ |
||||
this._logger.LogError(ex,"de error:{error}",ex.Message); |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
if (list.Any()) |
||||
{ |
||||
foreach (var item in list) |
||||
{ |
||||
this._actionDoCallback(item); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
public void Push(List<HandleCallbackParam> list) |
||||
{ |
||||
if (!list.Any()) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
try |
||||
{ |
||||
|
||||
using (var writer = new StreamWriter(this._backupFile, true, Encoding.UTF8)) |
||||
{ |
||||
foreach (var item in list) |
||||
{ |
||||
if (item.CallbackRetryTimes >= Constants.MaxCallbackRetryTimes) |
||||
{ |
||||
_logger.LogInformation("callback too many times and will be abandon,logId {logId}", item.LogId); |
||||
} |
||||
else |
||||
{ |
||||
item.CallbackRetryTimes++; |
||||
byte[] buffer = Utf8Json.JsonSerializer.Serialize(item,ProjectDefaultResolver.Instance); |
||||
writer.WriteLine(Encoding.UTF8.GetString(buffer)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
_logger.LogError(ex, "SaveCallbackParams error."); |
||||
} |
||||
} |
||||
|
||||
public void Dispose() |
||||
{ |
||||
this._stop = true; |
||||
this._runTask?.GetAwaiter().GetResult(); |
||||
} |
||||
} |
||||
} |
||||
@ -1,21 +0,0 @@ |
||||
using Microsoft.Extensions.DependencyInjection; |
||||
|
||||
namespace DotXxlJob.Core |
||||
{ |
||||
public static class ServiceCollectionExtensions |
||||
{ |
||||
public static IServiceCollection AddXxlJobExecutor(this IServiceCollection services) |
||||
{ |
||||
services.AddSingleton<ITaskExecutor, TaskExecutors.BeanTaskExecutor>(); |
||||
services.AddSingleton<IJobHandlerFactory,DefaultJobHandlerFactory >(); |
||||
services.AddSingleton<JobDispatcher>(); |
||||
services.AddSingleton<TaskExecutorFactory>(); |
||||
services.AddSingleton<XxlRpcServiceHandler>(); |
||||
services.AddSingleton<CallbackTaskQueue>(); |
||||
services.AddSingleton<AdminClient>(); |
||||
services.AddSingleton<IExecutorRegistry, ExecutorRegistry>(); |
||||
|
||||
return services; |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue