IIS的垃圾回收对后台任务及隐形后台任务的影响

IIS的垃圾回收引起的影响

错误排查

现象:在.net core api里创建的BackgroundService定义rabbitmq消费的逻辑,在一段时间运行后经常会出现消费任务中断,在日志里找了很久的原因但是依然没有结论。

通过日志发现异常出现在消费后的消息确认阶段,在执行确认的时候对应的channel和connection都关闭了

_channel.BasicAckAsync(eventArgs.DeliveryTag, false);

报错信息如下,显示rabbitmq连接中断的原因是:由应用程序主动发起关闭的。

RabbitMQ.Client.Exceptions.AlreadyClosedException: Already closed: The AMQP operation was interrupted: AMQP close-reason, initiated by Application, code=200, text='Goodbye', classId=0, methodId=0
   at RabbitMQ.Client.Impl.SessionBase.ThrowAlreadyClosedException()
   at RabbitMQ.Client.Impl.SessionBase.TransmitAsync[T](T& cmd, CancellationToken cancellationToken)
   at ALC.Server.Infrastructur.RabbitMq.RabbitMQClient.<>c__DisplayClass15_0`1.<<ConsumeMqMessage>b__0>d.MoveNext()

这个报错一般是因为channel的作用域有问题,但是一般发生在启动时,很容易发现。我们这是消费很多消息后才出现的,显然不属于这种情况,继续排查。后续发现w3wp.exe的报错,查看事件查看器windows日志-系统发现有连续的信息如下:

为应用程序池“net9”提供服务并且进程 ID 为“45552”的工作进程因不活动而被关闭。应用程序池超时配置被设置为 20 分钟。需要时将启动一个新工作进程。

上述的“net9”是我配置的程序池名称

弹出应用程序: Visual Studio 实时调试器: [45552] w3wp.exe 中发生了未经处理的 win32 异常。 对此异常的实时调试失败,错误为: 进程 ID 无效。

有关详细信息,请参见文档索引中的“实时调试, 错误”。

通过对上述报错发现,应该和资源池的回收机制有关,因为资源池默认的回收时间就是20分钟。于是继续百度找到如下问题对应后台任务会被回收事件打断。受资源池回收的影响,即使不使用backgroundservice,rabbitmq的消费进程也会被打断:
.net core后台任务解决iis自动回收导致任务被终止的问题

BackgroundService引起的影响

应对措施:让后台任务类或者rabbitmq的消费类实现IDiposable或者IAsyncDisposable,因为资源池在回收的时候会执行Diposable或AsyncDisposable方法。在此方法内,发起一次对本系统的http调用(如果没问题继续调用),即可重新唤醒。

 /// <summary>
            /// 释放托管资源,释放时触发
            /// </summary>
            public void Dispose()
            {
                Common.WriteEmailLog("定时任务被释放闭", "...Dispose...");
                _timer?.Dispose();
                //iis会回收这个定时任务,这边在回收的时候触发一个请求,来再次唤醒该服务
                Thread.Sleep(5000);
                HttpHelper.HttpGet(_configuration.GetSection("AwakenUrl").Value);
            }

对于Rabbitmq消费端的影响

public async ValueTask DisposeAsync()
{
    //异步关闭connection及channel
    await _mQClient.DisposeAsync();
    _logger.LogInformation("发起唤醒请求");
    //iis会回收这个rabbitmq的消费任务,这边在回收的时候触发一个请求,来再次唤醒该服务
    try
    {
        _logger.LogInformation("进入唤醒请求");
        //注意:这时HttpSimpleHelper已经不能以注入的方式去生成了,注意如果是多个实例同时部署到IIS,需要配置不同的AwakenUrl
        var result = await HttpSimpleHelper.GetAsync<bool>(_configuration.GetSection("AwakenUrl").Value);
        _logger.LogInformation($"唤醒请求结束:{result}");
    }
    catch (Exception ex)
    {
        _logger.LogError($"调用苏醒请求出错:{ex}");
        throw;
    }
   
}

对应的关闭channel及Connection的方法

 public async ValueTask DisposeAsync()
 {
     if (_channel != null && _channel.IsOpen)
     {
         await _channel.CloseAsync();
     }
     if (_sendConnection != null && _sendConnection.IsOpen)
     {
         await _sendConnection.CloseAsync();
     }
     _channel?.DisposeAsync();
     _sendConnection?.DisposeAsync();
 }

来源链接:https://www.cnblogs.com/summerZoo/p/18829321

© 版权声明
THE END
支持一下吧
点赞6 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容