FtpWebRequest a chyba 'The operation has timed out'

V aplikaci kde používám třídu FtpWebRequest pro FTP komunikaci se najednou začlo objevovat několik vyjímek: System.Net.WebException: The operation has timed out

Podle textu vyjímky jsem nejdříve usuzoval, že se jedná o chybu v komunikaci s FTP serverem nebo jiný síťový problém. Analýzou logu na FTP serveru to ale nedávalo smysl, protože tam se daný request vůbec neobjevil. Hledáním na Internetu jsem nakonec zjistil, že text chybového hlášení je typicky nesmyslný. Celý problém spočívá v omezení maximálního počtu současných připojení na FTP server. Toto je známé u HTTP protokolu a je to popsané v dokumentaci u property ServicePointManager.DefaultConnectionLimit, jenže tam není o FTP protokolu ani slovo.

Bohužel je toto omezení uplatňováno i pro FTP spojení a výchozí hodnota je 2, Pokud máte více paralelně běžících FTP úloh, tak při překročení počtu spojení dojde k této nesmyslné chybě. Ve skutečnosti ta operace totiž ani nezačala, jenže se nedozvíte proč přesně došlo k timeoutu. Stačí mít dvě běžící déletrvající FTP operace upload/download a snažit se spustit třetí, která například vypisuje seznam adresářů a dostanete tuto vyjímku. Chyba se samozřejmě projeví jen za určitých podmínek, když se to nejméně hodí.

Řešením je zvýšit maximální počet buď globálně nastavením ServicePointManager.DefaultConnectionLimit nebo poněkud zvláštně přes property instance FtpWebRequest.ServicePoint kde se toto ovšem opět nastaví globálně pro všechna FtpWebRequest Druhá možnost je v konfiguračním souboru, element connectionManagement.

Aktualizace

Bohužel ani výše uvedený postup problém neřeší, pouze snižuje výskyt těchto chyb (je třeba provést několik tisíc volání, až se tato chyba projeví). Kód FtpWebRequest je neskutečně složitý, používá sdílený pool connections, který má navíc vlastní timeout (mimo timeout TCP operací) a zřejmě tam dochází k chybám při vyzvedávání/vracení connection do poolu, takže se časem vyčerpají nad ServicePointManager.DefaultConnectionLimit. Podobný problém někdo popisuje zde, avšak bez odpovědi. Nakonec jsem to "vyřešil" změnou z FTP protokolu na sdílené soubory.

Další poznatek je, že v odchycené WebException property Response obsahuje FtpWebResponse ze které také nelze nic zjistit. Někdy má property Response dokonce hodnotu null, pokud interní pool timeoutuje dříve než se vůbec povedlo vytvořit TCP spojení, typicky pokud byl nastaven krátký Timeout. V žádném případě nevolejte metodu FtpWebResponse.Close v kódu obsluhující tuto vyjímku (podobné rady se najdou v některých odpovědích na Internetu), protože tím zavřete případnému jinému threadu interní poolovaný stream uvnitř této Response, který si už mezitím vyzvedl voláním metody GetResponse. Ten pak dostane náhodnou ObjectDisposedException a netušíte proč. Interní connection pool by mělo jít vypnout nastavením property KeepAlive na false, ale nevypadá to, že to má nějaký vliv na uvedený problém.

Celé je to ukázka toho, jak na první pohled jednoduchý problém (komunikace s FTP serverem) je díky zbytečně složité implementaci doveden až k prakticky nepoužitelnému kódu.

Komentáře jsou uzavřeny