Introducing resilience with polly

So now we have the services talking to one another, we should think about building in some resilience – essentially a way for the apps to retry a connect in case of a transient fault, e.g. a network blip.

We’ll revert back to using the Typed Http Client Factories at Stage 3 of the previous post. As such the source code should have the following:

Startup.cs

services.AddHttpClient();

StudentController.cs

public class StudentController : Controller
{
    private readonly StudentRecordSystemService _studentRecordService;
    public StudentController(StudentRecordSystemService studentRecordService)
    {
         _studentRecordService = studentRecordService;
    }

    public async Task Index()
    {
        List students = await _studentRecordService.GetStudentsAsync();
        return View(students);
    }
}

Services/StudentRecordSystemService.cs

public class StudentRecordSystemService
{
     public HttpClient Client { get; }
     public StudentRecordSystemService(HttpClient client)
     {
         client.BaseAddress = new Uri("<a>http://localhost:5100/");</a>
         // Optionally add additional header information, e.g. keys etc... not required in this example.
         Client = client;
     }

    public async Task&lt;List&gt; GetStudentsAsync()
    {
         var response = await Client.GetAsync("api/Student");         
         response.EnsureSuccessStatusCode();
         return await response.Content.ReadAsAsync&lt;List&gt;();
     }
}

Once again, start up both services and check that the expected data is displayed.

Introducing Polly

1. In the ViewerWebApplication, right click on the Dependencies and ‘Manage NuGet Packages…’. Then search for Polly.Extensions.Http and click Install.

2. Update StudentRecordSystemService.cs to match the following (note the introduction of the logger via DI):

private readonly ILogger _logger;

public StudentRecordSystemService(HttpClient client, ILogger logger)

{
     _logger = logger;
     client.BaseAddress = new Uri("<a>http://localhost:5100/");</a>
     // Optionally add additional header information, e.g. keys etc... not required in this example.
     Client = client;
}

public async Task&lt;List&gt; GetStudentsAsync()
{
     var response = await Policy
             .Handle()
             .WaitAndRetryAsync(3, retryAttempt =&gt; TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, timeSpan, context) =&gt;
             {
                 _logger.LogError($"Failed. Waiting {timeSpan}. Exception: {exception.Message}.");
             })
             .ExecuteAsync(async () =&gt; await Client.GetAsync("api/Student"));
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsAsync&lt;List&gt;();
}

And the Startup.cs has also updated to include the LoggerFactory:

public Startup(IConfiguration configuration, ILoggerFactory loggerFactory)
{
     Configuration = configuration;
     LoggerFactory = loggerFactory;
}

public IConfiguration Configuration { get; }
public ILoggerFactory LoggerFactory { get; }

Now if you start the DataViewer app, but not the StudentRecordSystemService, the page will now attempt multiple times (with an increasing time delay between calls, and logging between each event) before eventually giving up and throwing an exception.

Ultimately we’d want to declare these kind of Polly policies just once, and thats something we’ll look at next.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s