FTP轮询器偶尔挂起文件下载,直到重新启动

时间:2014-04-18 19:07:12

标签: c# .net ftp windows-services webclient

我有一个Windows服务,每三秒轮询一个远程FTP服务器。它检查目录中的文件,下载存在的所有文件,并在下载后删除这些文件。平均文件大小为10 KB,很少会达到100 KB范围。

偶尔(我注意到没有模式),WebClient会抛出以下内容:

System.Net.WebException: The operation has timed out.
at System.Net.WebClient.OpenRead(Uri address)

它将为一个或多个文件执行此操作,通常是当时远程目录中的任何文件。它将继续无限期地这样做,在每个轮询间隔对“卡住”文件进行搅拌。奇怪的是,当我停止/启动Windows服务时,“卡住”文件下载完美,轮询/下载再次工作很长一段时间。这很奇怪,因为我这样下载:

private object _pollingLock = new object();

public void PollingTimerElapsed(object sender, ElapsedEventArgs e)
{
    if(Monitor.TryEnter(_pollingLock);
    {
        //FtpHelper lists content of files in directory
        ...

        foreach(var file in files)
        {
            using(var client = new WebClient())
            {
                client.Proxy = null;
                using(var data = client.OpenRead(file.Uri)
                {
                    //Use data stream to write file locally
                    ...
                }
            }
            //FtpHelper deletes the file
            ...
        } 
    }
    //Release the _pollingLock inside a finally
}

我会假设为每个文件打开和关闭一个新连接(除非.NET在幕后做某事)。如果文件下载有问题,它将在下一个轮询间隔(3秒)内重新进行重试。为什么服务重启会使事情有效?

我开始怀疑这个问题与缓存(文件或连接)有关。最近我尝试进入Internet Explorer并清除缓存。大约30秒左右,所有下载的文件都没有重新启动服务。但是,下一批到达的文件都被重新挂起了。我可能会尝试添加这样一行:

client.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);

或尝试禁用KeepAlives,但我想在开始尝试随机内容之前获得一些意见。

那么:什么导致偶尔的超时?为什么重新启动服务有效?为什么清除缓存有效?

更新

我制定了缓存策略,并在两周前保持上述变化。从那以后我刚刚第一次超时。它出现以提高频率,但唉,它仍在发生。

更新

根据要求,这就是我开始Timer

的方式
_pollingTimer.AutoReset = true; 
_pollingTimer.Elapser += PollingTimerElapsed; 
_pollingTimer.Interval = 10000; 
_pollingTimer.Enabled = true;`

1 个答案:

答案 0 :(得分:2)

您似乎正在使用System.Timers.Timer.Elapsed事件开始处理。

我发现的一个问题是,如果你的Elapsed事件执行的时间比定时器间隔长,那么你的事件可以在执行之前从另一个线程再次调用。

这在文档中有具体提及:

  

如果SynchronizingObject属性为null,则Elapsed事件为   在ThreadPool线程上引发。如果处理了Elapsed事件   持续时间超过Interval,事件可能会再次提升   ThreadPool线程。在这种情况下,事件处理程序应该是   折返。

假设您确实在使用AutoReset = true的默认计时器(默认情况下为on),首先要做的就是解决这个潜在的问题。您可以使用SynchronizingObject,也可以执行以下操作:

//setup code
Timer myTimer = new Timer(30000);
myTimer.AutoReset = false;
....

//Elapsed handler
public void PollingTimerElapsed(object sender, ElapsedEventArgs e)
{
    //do what you currently do
    ...

    //when finished, kick off the timer again
    myTimer.Start();
}

无论哪种方式,最重要的是确保您的代码不会被多个线程意外地同时调用 - 如果发生这种情况,很有可能偶尔您有一个线程尝试从另一个线程同时删除该文件时从该站点下载的东西。

你提到的事情,例如它只是偶尔发生,通常文件大小很小,通过重启等固定它会使我指向这个问题的方向。