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