Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 39 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,48 @@
# HttpClient.Cache
## HttpClient.Cache

A caching wrapper around HttpClient to cache responses
A lightweight in-memory cache for HttpClient.

### The Purpose

Working with some high load systems or with system where is important to have good response time
cache is a first one citizen.
Working with high-load systems or with a system where it is important to have a good response time - the cache is a first citizen. This package contains lightweight, self-written, in-memory cache implementation to catch and store responses based on their status code. The configuration is pretty flexible and gives opportynity to set pair between cache time and response code.

### Install

//TODO Nuget deployment
### How to install

```shell
foo@bar:~$ Install-Package HttpClient.Cache
dotnet add package Leefrost.HttpClient.Cache
```

### Examples
### Plans and TODOs:

- [x] in-memory caching support
- [ ] distributed caching support

### How to use

The code below caches 3 top-kind responses (OK, BadRequest, and InternalServerError) for a different time - 60/10/5 seconds.
HttpClient will do 5 requests to the `https://randomuser.me/api/` and do cache the responses for us.

The cache report will show us 1 miss (initial request) and 4 hits (so the response time will be 0)

```csharp
const string url = "http://worldclockapi.com/api/json/utc/now";
const string url = "https://randomuser.me/api/";

//Set the cache time for each required status
//Setting the cache time for each required status
var cacheExpiration = new Dictionary<HttpStatusCode, TimeSpan>
{
{HttpStatusCode.OK, TimeSpan.FromSeconds(60)},
{HttpStatusCode.BadRequest, TimeSpan.FromSeconds(10)},
{HttpStatusCode.InternalServerError, TimeSpan.FromSeconds(5)}
};

//Client calls API and caches it
//Report will show 1 Miss (initial) and 4 Hits.
var innerHandler = new HttpClientHandler();
var cacheHandler = new InMemoryCacheHandler(innerHandler, cacheExpiration);
using (var httpClient = new System.Net.Http.HttpClient(cacheHandler))
//Calling the API and cache the responses
var requestHandler = new HttpClientHandler();
var cacheHandler = new InMemoryCacheHandler(requestHandler, cacheExpiration);
using (var httpClient = new HttpClient(cacheHandler))
{
for (int i = 1; i <= 5; ++i)
{
Console.Write($"Try: {i}: {url} ");
Console.Write($"Attempt {i}: {url}");

var stopwatch = Stopwatch.StartNew();
var result = await httpClient.GetAsync(url);
Expand All @@ -46,23 +52,28 @@ using (var httpClient = new System.Net.Http.HttpClient(cacheHandler))
Console.WriteLine($"Done in: {stopwatch.ElapsedMilliseconds} ms");
await Task.Delay(TimeSpan.FromSeconds(1));
}
Console.WriteLine();
}

//Checking cache stats
var stats = cacheHandler.StatsProvider.GetReport();
Console.WriteLine($"Cache stats - total requests: {stats.Total.TotalRequests}");
Console.WriteLine($"--> Hit: {stats.Total.CacheHit} [{stats.Total.TotalHitsPercent}]");
Console.WriteLine($"--> Miss: {stats.Total.CacheMiss} [{stats.Total.TotalMissPercent}]");
Console.WriteLine($"--> Hits: {stats.Total.CacheHit} [{stats.Total.TotalHitsPercent}]");
Console.WriteLine($"--> Misses: {stats.Total.CacheMiss} [{stats.Total.TotalMissPercent}]");
Console.ReadLine();
```
Will generate next output:
```console
Try: 1: http://worldclockapi.com/api/json/utc/now --> OK Done in: 450 ms
Try: 2: http://worldclockapi.com/api/json/utc/now --> OK Done in: 57 ms
Try: 3: http://worldclockapi.com/api/json/utc/now --> OK Done in: 0 ms
Try: 4: http://worldclockapi.com/api/json/utc/now --> OK Done in: 0 ms
Try: 5: http://worldclockapi.com/api/json/utc/now --> OK Done in: 0 ms

Console will show next output:

```shell
Attempt 1: https://randomuser.me/api/ --> OK Done in: 681 ms
Attempt 2: https://randomuser.me/api/ --> OK Done in: 75 ms
Attempt 3: https://randomuser.me/api/ --> OK Done in: 0 ms
Attempt 4: https://randomuser.me/api/ --> OK Done in: 0 ms
Attempt 5: https://randomuser.me/api/ --> OK Done in: 0 ms

Cache stats - total requests: 5
--> Hit: 4 [0,8]
--> Miss: 1 [0,2]
--> Hits: 4 [0,8]
--> Misses: 1 [0,2]

```
19 changes: 10 additions & 9 deletions samples/ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@
using System.Net;
using HttpClient.Cache.InMemory;

const string url = "http://worldclockapi.com/api/json/utc/now";
const string url = "https://randomuser.me/api/";

//Set the cache time for each required status
//Setting the cache time for each required status
var cacheExpiration = new Dictionary<HttpStatusCode, TimeSpan>
{
{HttpStatusCode.OK, TimeSpan.FromSeconds(60)},
{HttpStatusCode.BadRequest, TimeSpan.FromSeconds(10)},
{HttpStatusCode.InternalServerError, TimeSpan.FromSeconds(5)}
};

//Client calls API and caches it
//Report will show 1 Miss (initial) and 4 Hits.
var innerHandler = new HttpClientHandler();
var cacheHandler = new InMemoryCacheHandler(innerHandler, cacheExpiration);
//Calling the API and cache the responses
var requestHandler = new HttpClientHandler();
var cacheHandler = new InMemoryCacheHandler(requestHandler, cacheExpiration);
using (var httpClient = new System.Net.Http.HttpClient(cacheHandler))
{
for (int i = 1; i <= 5; ++i)
{
Console.Write($"Attempt: {i}: {url} ");
Console.Write($"Attempt {i}: {url}");

var stopwatch = Stopwatch.StartNew();
var result = await httpClient.GetAsync(url);
Expand All @@ -30,10 +29,12 @@
Console.WriteLine($"Done in: {stopwatch.ElapsedMilliseconds} ms");
await Task.Delay(TimeSpan.FromSeconds(1));
}
Console.WriteLine();
}

//Checking cache stats
var stats = cacheHandler.StatsProvider.GetReport();
Console.WriteLine($"Cache stats - total requests: {stats.Total.TotalRequests}");
Console.WriteLine($"--> Hit: {stats.Total.CacheHit} [{stats.Total.TotalHitsPercent}]");
Console.WriteLine($"--> Miss: {stats.Total.CacheMiss} [{stats.Total.TotalMissPercent}]");
Console.WriteLine($"--> Hits: {stats.Total.CacheHit} [{stats.Total.TotalHitsPercent}]");
Console.WriteLine($"--> Misses: {stats.Total.CacheMiss} [{stats.Total.TotalMissPercent}]");
Console.ReadLine();
7 changes: 2 additions & 5 deletions src/HttpClient.Cache/HttpClient.Cache.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@
<PackageId>Leefrost.HttpClient.Cache</PackageId>
<Authors>Sergii Lischuk</Authors>
<Description>A simple and easy cache for HttpClient</Description>

<!-- PackageProjectUrl is different from the repository URL. It can be a documentation
website or a website explaining the project -->
<RepositoryType>Github</RepositoryType>
<RepositoryUrl>https://github.com/Leefrost/HttpClient.Cache</RepositoryUrl>
<PackageProjectUrl>https://codestory.me</PackageProjectUrl>

<!-- A list of tags to help the search engine to understand the content of the package -->
<PackageTags>cache, httpclient, library</PackageTags>
</PropertyGroup>

Expand Down
67 changes: 39 additions & 28 deletions src/HttpClient.Cache/README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,48 @@
# HttpClient.Cache
## HttpClient.Cache

A caching wrapper around HttpClient to cache responses
A lightweight in-memory cache for HttpClient.

### The Purpose

Working with some high load systems or with system where is important to have good response time
cache is a first one citizen.
Working with high-load systems or with a system where it is important to have a good response time - the cache is a first citizen. This package contains lightweight, self-written, in-memory cache implementation to catch and store responses based on their status code. The configuration is pretty flexible and gives opportynity to set pair between cache time and response code.

### Install

//TODO Nuget deployment
### How to install

```shell
foo@bar:~$ Install-Package HttpClient.Cache
dotnet add package Leefrost.HttpClient.Cache
```

### Examples
### Plans and TODOs:

- [x] in-memory caching support
- [ ] distributed caching support

### How to use

The code below caches 3 top-kind responses (OK, BadRequest, and InternalServerError) for a different time - 60/10/5 seconds.
HttpClient will do 5 requests to the `https://randomuser.me/api/` and do cache the responses for us.

The cache report will show us 1 miss (initial request) and 4 hits (so the response time will be 0)

```csharp
const string url = "http://worldclockapi.com/api/json/utc/now";
const string url = "https://randomuser.me/api/";

//Set the cache time for each required status
//Setting the cache time for each required status
var cacheExpiration = new Dictionary<HttpStatusCode, TimeSpan>
{
{HttpStatusCode.OK, TimeSpan.FromSeconds(60)},
{HttpStatusCode.BadRequest, TimeSpan.FromSeconds(10)},
{HttpStatusCode.InternalServerError, TimeSpan.FromSeconds(5)}
};

//Client calls API and caches it
//Report will show 1 Miss (initial) and 4 Hits.
var innerHandler = new HttpClientHandler();
var cacheHandler = new InMemoryCacheHandler(innerHandler, cacheExpiration);
using (var httpClient = new System.Net.Http.HttpClient(cacheHandler))
//Calling the API and cache the responses
var requestHandler = new HttpClientHandler();
var cacheHandler = new InMemoryCacheHandler(requestHandler, cacheExpiration);
using (var httpClient = new HttpClient(cacheHandler))
{
for (int i = 1; i <= 5; ++i)
{
Console.Write($"Try: {i}: {url} ");
Console.Write($"Attempt {i}: {url}");

var stopwatch = Stopwatch.StartNew();
var result = await httpClient.GetAsync(url);
Expand All @@ -46,23 +52,28 @@ using (var httpClient = new System.Net.Http.HttpClient(cacheHandler))
Console.WriteLine($"Done in: {stopwatch.ElapsedMilliseconds} ms");
await Task.Delay(TimeSpan.FromSeconds(1));
}
Console.WriteLine();
}

//Checking cache stats
var stats = cacheHandler.StatsProvider.GetReport();
Console.WriteLine($"Cache stats - total requests: {stats.Total.TotalRequests}");
Console.WriteLine($"--> Hit: {stats.Total.CacheHit} [{stats.Total.TotalHitsPercent}]");
Console.WriteLine($"--> Miss: {stats.Total.CacheMiss} [{stats.Total.TotalMissPercent}]");
Console.WriteLine($"--> Hits: {stats.Total.CacheHit} [{stats.Total.TotalHitsPercent}]");
Console.WriteLine($"--> Misses: {stats.Total.CacheMiss} [{stats.Total.TotalMissPercent}]");
Console.ReadLine();
```
Will generate next output:
```console
Try: 1: http://worldclockapi.com/api/json/utc/now --> OK Done in: 450 ms
Try: 2: http://worldclockapi.com/api/json/utc/now --> OK Done in: 57 ms
Try: 3: http://worldclockapi.com/api/json/utc/now --> OK Done in: 0 ms
Try: 4: http://worldclockapi.com/api/json/utc/now --> OK Done in: 0 ms
Try: 5: http://worldclockapi.com/api/json/utc/now --> OK Done in: 0 ms

Console will show next output:

```shell
Attempt 1: https://randomuser.me/api/ --> OK Done in: 681 ms
Attempt 2: https://randomuser.me/api/ --> OK Done in: 75 ms
Attempt 3: https://randomuser.me/api/ --> OK Done in: 0 ms
Attempt 4: https://randomuser.me/api/ --> OK Done in: 0 ms
Attempt 5: https://randomuser.me/api/ --> OK Done in: 0 ms

Cache stats - total requests: 5
--> Hit: 4 [0,8]
--> Miss: 1 [0,2]
--> Hits: 4 [0,8]
--> Misses: 1 [0,2]

```