How Easily to Fan Out HTTP Requests in .NET

“Fan out” refers to distributing or spreading functionality or data flow from a single point to multiple points. In messaging systems, it means to send one message to many recipients. 

“Fan out” can also be used in parallel programming when tasks or computations are distributed across multiple processing units or cores to improve performance and efficiency.

Let’s look at how easily you can fan out HTTP requests in .NET. 

The Task

Assume you have to fetch the GitHub user details via GitHub REST API. You can do it by sending a GET request for a specific username.
 
https://api.github.com/users/okyrylchuk

The API will return the details about the okyrylchuk (me) user. 

Setup

First, we need to create a GitHub client.

using HttpClient gitHubClient = new()
{
    BaseAddress = new Uri("https://api.github.com"),
};

Second, we need the list of user handlers.

var userHandlers = new[]
{
        "users/okyrylchuk",
        "users/jaredpar",
        "users/davidfowl",
        "users/shanselman",
};

And the last thing is to define the response data.

public record GitHubUser(string Name, string Bio);

Classic Foreach

The classic way to fetch all user details is to run HTTP requests in the ForEach loop.

foreach (var userHandler in userHandlers)
{
    var user = await gitHubClient.GetFromJsonAsync<GitHubUser>(userHandler);

    Console.WriteLine($"Name: {user.Name}\nBio: {user.Bio}\n");
}

The output:

All four requests have been sent one by one, and it took 975 milliseconds overall.

Parallel.ForEachAsync

You can use the TPL (Task Parallel Library) to make requests parallel easily.

The TPL has the Parallel.ForEach method since .NET Framework 4. It executes a foreach operation in which iterations may run in parallel. However, it is a synchronous method.

.NET 6 introduced the asynchronous version Parallel.ForEachAsync.  

Both methods take the ParallelOptions parameter, which you can use to specify the maximum degree of parallelism. It takes a maximum number of concurrent tasks.  

ParallelOptions options = new()
{
    MaxDegreeOfParallelism = 3
};

And the usage is simple.

await Parallel.ForEachAsync(userHandlers, options, async (uri, token) =>
{
    var user = await gitHubClient.GetFromJsonAsync<GitHubUser>(uri, token);
    Console.WriteLine($"Name: {user.Name}\nBio: {user.Bio}\n");
});

Now, all requests are sent in parallel. It took 191 milliseconds overall! It is more than five times faster than the classic foreach loop.

Summary

The Parallel.ForEachAsync is simple and fast. 

You can use it for HTTP requests and other tasks you can run concurrently.

Scroll to Top