c# - Recurring Job异步调用WebApi,然后在回调方法中修改Hangfire Job的状态
问题描述
Hangfire 1.6.19 .net 核心 2.1
你好,我在Recurring Job中调用一个WebApi时,由于调用超时,job会失败,所以我用异步的方法来调用,但是这样,所有的job都执行成功了,无法反映WebApi是否调用成功或失败。
所以我在异步请求的回调方法中判断。如果 WebApi 返回异常,则作业的状态将更改为失败。
但是,我的修改导致 Hangfire 的失败作业列表无法正常工作。Hangfire 是否提供内部方法来修改作业状态或您有更好的解决方案?
我的代码如下:</p>
[AutomaticRetry(Attempts = 0)]
[DisplayName("InvokeApi,apiUrl:{0}")]
public void InvokeApi(string apiUrl, PerformContext context)
{
var invocationData = InvocationData.Serialize(context.BackgroundJob.Job);
int.TryParse(context?.BackgroundJob.Id, out var jobId);
var client = new RestClient(apiUrl)
{
Timeout = -1,
ReadWriteTimeout = -1
};
var request = new RestRequest(Method.GET)
{
Timeout = -1,
ReadWriteTimeout = -1
};
client.ExecuteAsync(request, response =>
{
if (!response.IsSuccessful)
{
using (var dbContext = new HangfireContext())
{
using (var transaction = dbContext.Database.BeginTransaction())
{
var state = dbContext.State.FirstOrDefault(x =>
x.JobId == jobId && x.Name == "Succeeded");
if (state != null)
{
state.Name = "Failed";
state.Reason = $"StatusDescription={response.StatusDescription},ErrorMessage={response.ErrorMessage ?? "null"}";
}
var job = dbContext.Job.FirstOrDefault(x => x.Id == jobId);
if (job != null)
{
job.StateName = "Failed";
job.InvocationData = Serialize(invocationData);
}
var counter =
dbContext.AggregatedCounter.FirstOrDefault(x => x.Key == "stats:succeeded");
if (counter != null) counter.Value = counter.Value - 1;
dbContext.SaveChanges();
transaction.Commit();
}
}
}
});
}
public string Serialize(InvocationData invocationData)
{
var parameterTypes = JobHelper.FromJson<string[]>(invocationData.ParameterTypes);
var arguments = JobHelper.FromJson<string[]>(invocationData.Arguments);
return JobHelper.ToJson(new MyJobPayload
{
TypeName = invocationData.Type,
MethodName = invocationData.Method,
ParameterTypes = parameterTypes != null && parameterTypes.Length > 0 ? parameterTypes : null,
Arguments = arguments != null && arguments.Length > 0 ? arguments : null
});
}
public class MyJobPayload
{
[JsonProperty("type")]
public string TypeName { get; set; }
[JsonProperty("m")]
public string MethodName { get; set; }
[JsonProperty("p", NullValueHandling = NullValueHandling.Ignore)]
public string[] ParameterTypes { get; set; }
[JsonProperty("a", NullValueHandling = NullValueHandling.Ignore)]
public string[] Arguments { get; set; }
}
期待您的回复,谢谢!
解决方案
终于成功了,但是有没有更好的方法呢?
[AutomaticRetry(Attempts = 0)]
[DisplayName("InvokeApi,apiUrl:{0}")]
public void InvokeApi(string apiUrl, PerformContext context)
{
var invocationData = InvocationData.Serialize(context.BackgroundJob.Job);
int.TryParse(context?.BackgroundJob.Id, out var jobId);
var dtBegin = DateTimeOffset.Now;
var client = new RestClient(apiUrl)
{
Timeout = -1,
ReadWriteTimeout = -1
};
var request = new RestRequest(Method.GET)
{
Timeout = -1,
ReadWriteTimeout = -1
};
client.ExecuteAsync(request, response => {
if (!response.IsSuccessful)
{
var responseMessage = $"TotalSeconds={DateTimeOffset.Now.Subtract(dtBegin).TotalSeconds},StatusDescription={response.StatusDescription},ErrorMessage={response.ErrorMessage ?? "null"}";
try
{
var strHangfireDataContext = EngineContext.Instance.ServiceProvider.GetService<ConnectionStringsConfiguration>().HangfireDataContext;
var optionsBuilder = new DbContextOptionsBuilder<HangfireContext>();
optionsBuilder.UseSqlServer(strHangfireDataContext);
using (var dbContext = new HangfireContext(optionsBuilder.Options))
{
using (var transaction = dbContext.Database.BeginTransaction())
{
var state = dbContext.State.FirstOrDefault(x =>
x.JobId == jobId && x.Name == "Succeeded");
if (state != null)
{
var failedState = new FailedState(new Exception(responseMessage));
state.Name = failedState.Name;
state.Reason = failedState.Reason;
state.Data = JobHelper.ToJson(failedState.SerializeData());
}
var job = dbContext.Job.FirstOrDefault(x => x.Id == jobId);
if (job != null)
{
job.StateName = "Failed";
job.InvocationData = JobHelper.ToJson(new
{
Type = invocationData.Type,
Method = invocationData.Method,
ParameterTypes = invocationData.ParameterTypes,
Arguments = invocationData.Arguments
});
}
var counter =
dbContext.AggregatedCounter.FirstOrDefault(x => x.Key == "stats:succeeded");
if (counter != null) counter.Value = counter.Value - 1;
dbContext.SaveChanges();
transaction.Commit();
}
}
}
catch (Exception ex)
{
_logger.LogError("Exception jobId:" + jobId + ";ex:" + ex.ToString());
}
}
});
}
推荐阅读
- python - 将 CSV 转换为字典值列表
- php - PHP Copy() 仅在某些时候有效
- c# - 通过代码启用标志 AllowArbitraryDataSetTypeInstantiation
- swift - Swift - 超类属性更改时子类中的触发方法
- c - 在 C 中将布尔字符串解释为数字的最快方法是什么?
- ios - 模拟 iOS 屏幕键盘上的“Go”、“Next”按钮按下以进行 xcode 测试
- python - 确定两个分量对测量光谱的相对贡献
- scala - 使用 bloop 处理 Scala Metals 语言服务器中的递归依赖项
- vue.js - Vuetify v-select 防止输入
- reactjs - 带有许多 npm 脚本的 Monorepo