视频课程目录16
第 12 · HHY 1.7.0
Add Bounded Parallelism
Check several local endpoints with two workers and keep failures in the report.
学习进度保存在此浏览器,无需登录。
章节与讲稿
点击时间跳转视频,展开标题查看讲稿与代码。
Three checks, one report
We already know how to call one endpoint. Now we will check three endpoints and produce one report, including a deliberate failure. A sequential loop would wait for each request before starting the next. Parallel lets several independent operations make progress at once, while an explicit worker limit keeps the workload bounded. We will use two workers, keep each result associated with its URL, and treat one failed request as a report entry instead of losing the entire batch.
users/health true
missing false
orders/health trueThe report preserves the three input positions.
Describe the work as values
The input is an ordinary list of three local URLs. Start the included fixture exactly as in the previous lesson. These small values describe the work without opening any connection yet. That separation is useful: you can inspect the batch before sending it, add another URL, or remove a route without changing the worker logic. A more realistic service record could also contain a display name, but the URL alone is enough for this first reproducible example.
let urls = [
"http://127.0.0.1:9311/users/health",
"http://127.0.0.1:9311/missing",
"http://127.0.0.1:9311/orders/health"
]Start mock-api.py in a separate terminal.
Set the concurrency limit
Convert the list to a Stream, then pass it through parallel with a limit of two. Each worker receives one URL and returns one result. The documentation describes isolated workers and bounded buffers, so the concurrency setting is a resource decision, not a promise of a particular speedup. A larger number can increase pressure on your machine or the server. Begin with a modest limit and choose it according to the work and the service you are allowed to call.
urls |> stream |> parallel(2) { url ->
# one independent request per input
}This is the worker outline; use the full downloadable file.
Turn expected failure into a value
Inside the worker, attempt wraps the request and JSON decoding. It returns a result whose okay field tells us whether that operation succeeded. We then return a plain object containing the original URL and this Boolean. This placement matters. The recovery boundary belongs around each independent operation, so the missing route becomes one false value. If we let that error escape instead, the documented fail fast behavior would cancel the remaining work rather than produce a complete service report.
let result = attempt {
http.get(url) |> timeout(2s) |> send
|> response_body |> parse_json
}
return { url: url, ok: result.ok }Inside the worker closure.
Collect in input order
After the worker block, collect consumes the finite batch. JSON encoding produces a report that other programs can read. Parallel preserves input order even if requests finish in a different order. That makes the output predictable, but it also means a slow early request can delay seeing later results. Do not interpret the order of report rows as the order of network completion. Each row still includes its URL, so it remains meaningful even if you later sort or filter the report.
} |> collect |> encode_json |> printThe complete file has the opening worker block.
Avoid a shared counter
A common mistake is trying to accumulate a shared mutable total inside the workers. Run the supplied counter demonstration and inspect its result. On the validated local build, it returns one and two, rather than a running shared total. This demonstrates isolated behavior on the validated build, rather than a shared counter. Capture diagnostics can differ across engines and versions, so we will not rely on this mistake as an API contract. Return a value from each worker, then aggregate those returned values in the parent pipeline where the ownership is clear.
let mut total = 0
[1, 2] |> stream |> parallel(2) { item ->
total = total + item
return total
} |> collect |> printLocal build output: [1, 2], not a shared total of 3.
Run and inspect the failure
Run the health checker while the fixture is available. The first and third routes succeed, and the intentionally missing route is false. Now stop the fixture and run it again. Requests should fail within their configured limits, and the report should still contain entries because attempt handles each request failure. This is a useful distinction between an operational problem and a broken script: the script can complete its reporting job while honestly recording that the services could not be reached.
hhy run health-check.hhyExpected ok fields: true, false, true.
Practice a richer report
Extend each input from a URL to an object with a service name and URL. Update the worker to request the URL field and retain the name in its returned report. Then compare worker limits of one and two using the same input. Focus first on identical results rather than timing claims, because this tiny fixture is not a performance benchmark. You now have a repeatable concurrency pattern: finite input, bounded workers, local recovery, returned values, and one terminal collection.
Input → parallel(2) → result objects → collectExercise: attach a service name and retain it in each result.
