mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:42:50 +08:00
Merge pull request #57 from geffzhang/feature/Optimization-HttpClient-instance
Feature/optimization http client instance
This commit is contained in:
commit
9bb86122f8
@ -143,6 +143,7 @@ namespace Ocelot.DependencyInjection
|
|||||||
services.AddSingleton<IAuthenticationHandlerFactory, AuthenticationHandlerFactory>();
|
services.AddSingleton<IAuthenticationHandlerFactory, AuthenticationHandlerFactory>();
|
||||||
services.AddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
|
services.AddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
|
||||||
services.AddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
|
services.AddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
|
||||||
|
services.AddSingleton<IHttpClientCache, MemoryHttpClientCache>();
|
||||||
|
|
||||||
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
|
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
|
||||||
// could maybe use a scoped data repository
|
// could maybe use a scoped data repository
|
||||||
|
@ -1,32 +1,48 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Requester.QoS;
|
using Ocelot.Requester.QoS;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
internal class HttpClientBuilder
|
internal class HttpClientBuilder : IHttpClientBuilder
|
||||||
{
|
{
|
||||||
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
||||||
|
private Dictionary<string, string> _defaultHeaders;
|
||||||
|
|
||||||
public HttpClientBuilder WithQoS(IQoSProvider qoSProvider, IOcelotLogger logger)
|
|
||||||
|
public IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger)
|
||||||
{
|
{
|
||||||
_handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qoSProvider, logger));
|
_handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal HttpClient Build()
|
public IHttpClient Create()
|
||||||
{
|
{
|
||||||
return _handlers.Any() ?
|
var httpclientHandler = new HttpClientHandler();
|
||||||
new HttpClient(CreateHttpMessageHandler()) :
|
|
||||||
new HttpClient();
|
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler));
|
||||||
|
|
||||||
|
if (_defaultHeaders == null)
|
||||||
|
{
|
||||||
|
return new HttpClientWrapper(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpMessageHandler CreateHttpMessageHandler()
|
foreach (var header in _defaultHeaders)
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpClientWrapper(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler)
|
||||||
{
|
{
|
||||||
HttpMessageHandler httpMessageHandler = new HttpClientHandler();
|
|
||||||
|
|
||||||
_handlers
|
_handlers
|
||||||
.OrderByDescending(handler => handler.Key)
|
.OrderByDescending(handler => handler.Key)
|
||||||
@ -39,8 +55,25 @@ namespace Ocelot.Requester
|
|||||||
delegatingHandler.InnerHandler = httpMessageHandler;
|
delegatingHandler.InnerHandler = httpMessageHandler;
|
||||||
httpMessageHandler = delegatingHandler;
|
httpMessageHandler = delegatingHandler;
|
||||||
});
|
});
|
||||||
|
|
||||||
return httpMessageHandler;
|
return httpMessageHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class was made to make unit testing easier when HttpClient is used.
|
||||||
|
/// </summary>
|
||||||
|
internal class HttpClientWrapper : IHttpClient
|
||||||
|
{
|
||||||
|
public HttpClient Client { get; }
|
||||||
|
|
||||||
|
public HttpClientWrapper(HttpClient client)
|
||||||
|
{
|
||||||
|
Client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
|
||||||
|
{
|
||||||
|
return Client.SendAsync(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
@ -10,24 +11,28 @@ namespace Ocelot.Requester
|
|||||||
{
|
{
|
||||||
public class HttpClientHttpRequester : IHttpRequester
|
public class HttpClientHttpRequester : IHttpRequester
|
||||||
{
|
{
|
||||||
|
private readonly IHttpClientCache _cacheHandlers;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
|
|
||||||
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory)
|
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers)
|
||||||
{
|
{
|
||||||
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
||||||
|
_cacheHandlers = cacheHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
||||||
{
|
{
|
||||||
var builder = new HttpClientBuilder();
|
var builder = new HttpClientBuilder();
|
||||||
|
|
||||||
if (request.IsQos)
|
var cacheKey = GetCacheKey(request, builder);
|
||||||
|
|
||||||
|
var httpClient = _cacheHandlers.Get(cacheKey);
|
||||||
|
if (httpClient == null)
|
||||||
{
|
{
|
||||||
builder.WithQoS(request.QosProvider, _logger);
|
httpClient = builder.Create();
|
||||||
|
_cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(6));
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var httpClient = builder.Build())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
||||||
@ -47,7 +52,20 @@ namespace Ocelot.Requester
|
|||||||
{
|
{
|
||||||
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
|
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
|
||||||
|
{
|
||||||
|
string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
||||||
|
|
||||||
|
if (request.IsQos)
|
||||||
|
{
|
||||||
|
builder.WithQos(request.QosProvider, _logger);
|
||||||
|
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
13
src/Ocelot/Requester/IHttpClient.cs
Normal file
13
src/Ocelot/Requester/IHttpClient.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester
|
||||||
|
{
|
||||||
|
public interface IHttpClient
|
||||||
|
{
|
||||||
|
HttpClient Client { get; }
|
||||||
|
|
||||||
|
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
|
||||||
|
}
|
||||||
|
}
|
25
src/Ocelot/Requester/IHttpClientBuilder.cs
Normal file
25
src/Ocelot/Requester/IHttpClientBuilder.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester
|
||||||
|
{
|
||||||
|
public interface IHttpClientBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a PollyCircuitBreakingDelegatingHandler .
|
||||||
|
/// </summary>
|
||||||
|
IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the <see cref="HttpClient"/>
|
||||||
|
/// </summary>
|
||||||
|
IHttpClient Create();
|
||||||
|
}
|
||||||
|
}
|
16
src/Ocelot/Requester/IHttpClientCache.cs
Normal file
16
src/Ocelot/Requester/IHttpClientCache.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester
|
||||||
|
{
|
||||||
|
public interface IHttpClientCache
|
||||||
|
{
|
||||||
|
bool Exists(string id);
|
||||||
|
IHttpClient Get(string id);
|
||||||
|
void Remove(string id);
|
||||||
|
void Set(string id, IHttpClient handler, TimeSpan expirationTime);
|
||||||
|
}
|
||||||
|
}
|
46
src/Ocelot/Requester/MemoryHttpClientCache.cs
Normal file
46
src/Ocelot/Requester/MemoryHttpClientCache.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.Requester
|
||||||
|
{
|
||||||
|
public class MemoryHttpClientCache : IHttpClientCache
|
||||||
|
{
|
||||||
|
private readonly IMemoryCache _memoryCache;
|
||||||
|
|
||||||
|
public MemoryHttpClientCache(IMemoryCache memoryCache)
|
||||||
|
{
|
||||||
|
_memoryCache = memoryCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(string id, IHttpClient client, TimeSpan expirationTime)
|
||||||
|
{
|
||||||
|
_memoryCache.Set(id, client, new MemoryCacheEntryOptions().SetAbsoluteExpiration(expirationTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists(string id)
|
||||||
|
{
|
||||||
|
IHttpClient counter;
|
||||||
|
return _memoryCache.TryGetValue(id, out counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IHttpClient Get(string id)
|
||||||
|
{
|
||||||
|
IHttpClient counter;
|
||||||
|
if (_memoryCache.TryGetValue(id, out counter))
|
||||||
|
{
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(string id)
|
||||||
|
{
|
||||||
|
_memoryCache.Remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user