、あなたはほぼ普遍option
かChoice<_,_>
タイプを使用したい回復可能エラーを処理します。実際には、Choice
は、エラーのいくつかの情報を返すことを許可しますが、option
はエラーではありません。言い換えれば、option
が問題でないときに最も良いです。howまたはなぜ何かが失敗した(失敗しただけです)。 Choice<_,_>
は、についての情報がある場合に使用されますまたはなぜ何かが失敗したことが重要です。たとえば、エラー情報をログに書き込むことができます。またはに基づいて異なる状況を異なる方法で処理したい場合は、何かが失敗しました。これは、ユーザーが問題を診断するのに役立つ正確なエラーメッセージを提供する大きなケースです。念頭に置いて
は、ここで私はきれいで、機能的なスタイルで障害を処理するようにコードをリファクタリングしたい方法は次のとおりです。
open System
open System.Net
/// Retrieves the content at the given URI.
let retrievePage (client : WebClient) (uri : Uri) =
// Preconditions
checkNonNull "uri" uri
if not <| uri.IsAbsoluteUri then
invalidArg "uri" "The URI must be an absolute URI."
try
// If the data is retrieved successfully, return it.
client.DownloadData uri
|> Choice1Of2
with
| :? System.Net.WebException as webExn ->
// Return the URI and WebException so they can be used to diagnose the problem.
Choice2Of2 (uri, webExn)
| _ ->
// Reraise any other exceptions -- we don't want to handle them here.
reraise()
/// Retrieves the content at the given URI.
/// If a WebException is raised when retrieving the content, the request
/// will be retried up to a specified number of times.
let rec retrievePageRetry (retryWaitTime : TimeSpan) remainingRetries (client : WebClient) (uri : Uri) =
// Preconditions
checkNonNull "uri" uri
if not <| uri.IsAbsoluteUri then
invalidArg "uri" "The URI must be an absolute URI."
elif remainingRetries = 0u then
invalidArg "remainingRetries" "The number of retries must be greater than zero (0)."
// Try to retrieve the page.
match retrievePage client uri with
| Choice1Of2 _ as result ->
// Successfully retrieved the page. Return the result.
result
| Choice2Of2 _ as error ->
// Decrement the number of retries.
let retries = remainingRetries - 1u
// If there are no retries left, return the error along with the URI
// for diagnostic purposes; otherwise, wait a bit and try again.
if retries = 0u then error
else
// NOTE : If this is modified to use 'async', you MUST
// change this to use 'Async.Sleep' here instead!
System.Threading.Thread.Sleep retryWaitTime
// Try retrieving the page again.
retrievePageRetry retryWaitTime retries client uri
[<EntryPoint>]
let main argv =
/// WebClient used for retrieving content.
use wc = new WebClient()
/// The amount of time to wait before re-attempting to fetch a page.
let retryWaitTime = TimeSpan.FromSeconds 2.0
/// The maximum number of times we'll try to fetch each page.
let maxPageRetries = 3u
/// The URI to fetch.
let fullURI = Uri ("http://www.badaddress.xyz", UriKind.Absolute)
// Fetch the page data.
match retrievePageRetry retryWaitTime maxPageRetries wc fullURI with
| Choice1Of2 pageData ->
printfn "Retrieved %u bytes from: %O" (Array.length pageData) fullURI
0 // Success
| Choice2Of2 (uri, error) ->
printfn "Unable to retrieve the content from: %O" uri
printfn "HTTP Status: (%i) %O" (int error.Status) error.Status
printfn "Message: %s" error.Message
1 // Failure
は基本的に、私は2つの機能にコードを分割、プラスオリジナルmain
:
- 指定されたURIからコンテンツを取得しようとする機能の1つ。
- 試行を再試行するロジックを含む1つの関数。これは、実際の要求を実行する最初の関数を「ラップ」します。
- 元のメイン機能では、「設定」(
app.config
またはweb.config
から簡単に取得できます)と最終結果の印刷のみが処理されるようになりました。言い換えれば、再試行のロジックが気になることはありません。match
ステートメントでコードの1行を修正し、必要であればリトライしないリクエスト関数を使用することができます。
あなたが複数のURIからコンテンツをプルAND(例えば、5分)の再試行の間にかなりの時間を待つしたい場合は、Thread.Sleep
を使用するのではなく、プライオリティキューか何かを使用するように再試行ロジックを変更する必要がありますかAsync.Sleep
。
恥知らずのプラグイン:私のExtCoreライブラリには、このようなものをビルドするときに、特にすべての非同期化をしたい場合は、人生を大幅に楽にするためのいくつかのことが含まれています。最も重要なのは、asyncChoice
ワークフローとcollections functions designed to work with itです。
(リトライタイムアウトやリトライ回数などの)パラメータを渡すことについてのあなたの質問については、それらを渡すかハードコーディングするかを決定するための難易度の高いルールはないと思います関数。ほとんどの場合、私はそれらを渡すことを好みますが、いくつかのパラメータを渡す必要がある場合は、それらをすべて保持して代わりに渡すようにレコードを作成する方がよいでしょう。私が使ったもう1つのアプローチは、デフォルトのファイルをコンフィギュレーションファイルから取得する場合の値option
の値を作成することです(ファイルから一度だけから取得し、再解析を避けるためにプライベートフィールドに割り当てます)あなたの関数が呼び出されるたびに設定ファイル)。これにより、コードで使用したデフォルト値を簡単に変更できますが、必要に応じてそれらをオーバーライドする柔軟性も得られます。