周期性作业异步调用WebApi,然后在回调方法中修改Hangfire作业的状态

时间:2018-07-05 13:39:21

标签: c# callback hangfire

Hangfire 1.6.19 .net core 2.1

你好,   当我在“周期性作业”中调用WebApi时,该作业将由于调用超时而失败,因此我使用异步方法进行调用,但是通过这种方式,所有作业均成功执行,并且无法反映WebApi调用是否成功或失败。

所以我判断了异步请求的回调方法。如果WebApi返回异常,则作业状态更改为失败。

但是,我的修改导致Hangfire失败的作业列表无法正常工作。 Hangfire是否提供内部方法来修改工作状态,或者您有更好的解决方案?

我的代码如下:

    [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; }
    }

期待您的回复,谢谢!

1 个答案:

答案 0 :(得分:0)

它终于成功了,但是有更好的方法吗?

    [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());
                }
            }
        });
    }