From fbdb4e7729177345e228787c3b39e86531d6476c Mon Sep 17 00:00:00 2001 From: Yortw Date: Sat, 30 Jan 2016 12:27:58 +1300 Subject: [PATCH] Improve HttpClient usage for better efficiency * Reuse single instance of HttpClient across calls to make connection pooling effective. Implement Dispose pattern on Klout service to allow client code to control connection lifetimes properly. * Enable gzip compression on default HttpClient instance. * Enable constructor injection of HttpClient instance to use. * Apply ConfigureAwait(false) to internal async calls to reduce async overhead. * Changed internal static strings that should never change to either constants or static *readonly* declarations. Should take care of problems in issue #4 --- src/KloutSharp.Lib/Klout.cs | 111 ++++++++++++------ src/KloutSharp.Lib/Properties/AssemblyInfo.cs | 4 +- 2 files changed, 79 insertions(+), 36 deletions(-) diff --git a/src/KloutSharp.Lib/Klout.cs b/src/KloutSharp.Lib/Klout.cs index c048688..4fdd9ab 100644 --- a/src/KloutSharp.Lib/Klout.cs +++ b/src/KloutSharp.Lib/Klout.cs @@ -16,28 +16,50 @@ namespace KloutSharp.Lib { - public class Klout + public class Klout : IDisposable { - private static string kloutUri = "http://api.klout.com/v2/"; - private static string kloutIdentityUri = kloutUri + "identity.json/"; + private const string kloutUri = "http://api.klout.com/v2/"; + private static readonly string kloutIdentityUri = kloutUri + "identity.json/"; private string key; - public Klout(string key) + private HttpClient _HttpClient; + + public Klout(string key) : this(key, CreateDefaultHttpClient()) { - this.key = key; } - private void CheckKey() + public Klout(string key, HttpClient httpClient) + { + if (httpClient == null) throw new ArgumentNullException(nameof(httpClient)); + + this.key = key; + _HttpClient = httpClient; + } + private static HttpClient CreateDefaultHttpClient() + { + var handler = new System.Net.Http.HttpClientHandler(); + if (handler.SupportsAutomaticDecompression) + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + + var retVal = new HttpClient(handler); + return retVal; + } + private void CheckKey() { if (string.IsNullOrEmpty(key)) throw new KloutException("Klout key not set!"); } - private string UriAddKey(string uri) + private void CheckIsDisposed() + { + if (_HttpClient == null) throw new ObjectDisposedException(nameof(Klout)); + } + private string UriAddKey(string uri) { return string.Format("{0}{2}key={1}", uri, key, uri.Contains("?") ? "&" : "?"); } public async Task IdentityAsync(string id, KloutIdentityKind kind = KloutIdentityKind.TwitterScreenName) { CheckKey(); - var parmeters = string.Empty; + CheckIsDisposed(); + var parmeters = string.Empty; switch (kind) { case KloutIdentityKind.TwitterId: @@ -67,11 +89,10 @@ public async Task IdentityAsync(string id, KloutIdentityKind kind } } var uri = UriAddKey(string.Format(kloutIdentityUri + parmeters, id)); - var client = new HttpClient(); - var res = await client.GetAsync(uri); + var res = await _HttpClient.GetAsync(uri).ConfigureAwait(false); if (res.IsSuccessStatusCode) { - var content = await res.Content.ReadAsStringAsync(); + var content = await res.Content.ReadAsStringAsync().ConfigureAwait(false); var identity = JsonConvert.DeserializeObject(content); return identity; } @@ -82,33 +103,33 @@ public async Task IdentityAsync(string id, KloutIdentityKind kind } public async Task IdentityTwitterIdAsync(string id) { - return await IdentityAsync(id, KloutIdentityKind.TwitterId); + return await IdentityAsync(id, KloutIdentityKind.TwitterId).ConfigureAwait(false); } public async Task IdentityGoogle(string googleId) { - return await IdentityAsync(googleId, KloutIdentityKind.Google); + return await IdentityAsync(googleId, KloutIdentityKind.Google).ConfigureAwait(false); } public async Task IdentityInstagram(string instagramId) { - return await IdentityAsync(instagramId, KloutIdentityKind.Instagram); + return await IdentityAsync(instagramId, KloutIdentityKind.Instagram).ConfigureAwait(false); } public async Task IdentityTwitterScreenName(string twitter) { - return await IdentityAsync(twitter, KloutIdentityKind.Instagram); + return await IdentityAsync(twitter, KloutIdentityKind.Instagram).ConfigureAwait(false); } public async Task IdentityKlout(string kloutId) { - return await IdentityAsync(kloutId, KloutIdentityKind.KloutId); + return await IdentityAsync(kloutId, KloutIdentityKind.KloutId).ConfigureAwait(false); } public async Task UserAsync(string kloutId) { CheckKey(); - var uri = UriAddKey(string.Format(kloutUri + "user.json/{0}", kloutId)); - var client = new HttpClient(); - var res = await client.GetAsync(uri); + CheckIsDisposed(); + var uri = UriAddKey(string.Format(kloutUri + "user.json/{0}", kloutId)); + var res = await _HttpClient.GetAsync(uri).ConfigureAwait(false); if (res.IsSuccessStatusCode) { - var content = await res.Content.ReadAsStringAsync(); + var content = await res.Content.ReadAsStringAsync().ConfigureAwait(false); var user = JsonConvert.DeserializeObject(content); return user; } @@ -120,12 +141,12 @@ public async Task UserAsync(string kloutId) public async Task ScoreAsync(string kloutId) { CheckKey(); - var uri = UriAddKey(string.Format(kloutUri + "user.json/{0}/score", kloutId)); - var client = new HttpClient(); - var res = await client.GetAsync(uri); + CheckIsDisposed(); + var uri = UriAddKey(string.Format(kloutUri + "user.json/{0}/score", kloutId)); + var res = await _HttpClient.GetAsync(uri).ConfigureAwait(false); if (res.IsSuccessStatusCode) { - var content = await res.Content.ReadAsStringAsync(); + var content = await res.Content.ReadAsStringAsync().ConfigureAwait(false); var score = JsonConvert.DeserializeObject(content); return score; } @@ -137,12 +158,12 @@ public async Task ScoreAsync(string kloutId) public async Task> TopicsAsync(string kloutId) { CheckKey(); - var uri = UriAddKey(string.Format(kloutUri + "user.json/{0}/topics", kloutId)); - var client = new HttpClient(); - var res = await client.GetAsync(uri); + CheckIsDisposed(); + var uri = UriAddKey(string.Format(kloutUri + "user.json/{0}/topics", kloutId)); + var res = await _HttpClient.GetAsync(uri).ConfigureAwait(false); if (res.IsSuccessStatusCode) { - var content = await res.Content.ReadAsStringAsync(); + var content = await res.Content.ReadAsStringAsync().ConfigureAwait(false); var topics = JsonConvert.DeserializeObject>(content); return topics; } @@ -154,12 +175,12 @@ public async Task> TopicsAsync(string kloutId) public async Task InfluenceAsync(string kloutId) { CheckKey(); - var uri = UriAddKey(string.Format(kloutUri + "user.json/{0}/influence", kloutId)); - var client = new HttpClient(); - var res = await client.GetAsync(uri); + CheckIsDisposed(); + var uri = UriAddKey(string.Format(kloutUri + "user.json/{0}/influence", kloutId)); + var res = await _HttpClient.GetAsync(uri).ConfigureAwait(false); if (res.IsSuccessStatusCode) { - var content = await res.Content.ReadAsStringAsync(); + var content = await res.Content.ReadAsStringAsync().ConfigureAwait(false); var influence = JsonConvert.DeserializeObject(content); return influence; } @@ -168,5 +189,27 @@ public async Task InfluenceAsync(string kloutId) throw new KloutException(res.StatusCode); } } - } -} + public void Dispose() + { + try + { + Dispose(true); + } + finally + { + GC.SuppressFinalize(this); + } + } + protected virtual void Dispose(bool isDisposing) + { + if (isDisposing) + { + if (_HttpClient != null) + { + _HttpClient.Dispose(); + _HttpClient = null; + } + } + } + } +} \ No newline at end of file diff --git a/src/KloutSharp.Lib/Properties/AssemblyInfo.cs b/src/KloutSharp.Lib/Properties/AssemblyInfo.cs index b4493e1..203600b 100644 --- a/src/KloutSharp.Lib/Properties/AssemblyInfo.cs +++ b/src/KloutSharp.Lib/Properties/AssemblyInfo.cs @@ -26,5 +26,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.3.0")] -[assembly: AssemblyFileVersion("1.0.3.0")] +[assembly: AssemblyVersion("1.0.3.1")] +[assembly: AssemblyFileVersion("1.0.3.1")]