mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 17:28:16 +08:00
implemented a send to self pattern for sticky session timeouts rather than a normal timer
This commit is contained in:
15
src/Ocelot/Infrastructure/DelayedMessage.cs
Normal file
15
src/Ocelot/Infrastructure/DelayedMessage.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace Ocelot.Infrastructure
|
||||
{
|
||||
internal class DelayedMessage<T>
|
||||
{
|
||||
public DelayedMessage(T message, int delay)
|
||||
{
|
||||
Delay = delay;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public T Message { get; set; }
|
||||
|
||||
public int Delay { get; set; }
|
||||
}
|
||||
}
|
@ -1,14 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Infrastructure
|
||||
{
|
||||
public interface IBus<T>
|
||||
{
|
||||
void Subscribe(Action<T> action);
|
||||
Task Publish(T message, int delay);
|
||||
void Publish(T message, int delay);
|
||||
}
|
||||
}
|
||||
|
@ -8,15 +8,15 @@ namespace Ocelot.Infrastructure
|
||||
{
|
||||
public class InMemoryBus<T> : IBus<T>
|
||||
{
|
||||
private readonly BlockingCollection<T> _queue;
|
||||
private readonly BlockingCollection<DelayedMessage<T>> _queue;
|
||||
private readonly List<Action<T>> _subscriptions;
|
||||
private Thread _processing;
|
||||
|
||||
public InMemoryBus()
|
||||
{
|
||||
_queue = new BlockingCollection<T>();
|
||||
_queue = new BlockingCollection<DelayedMessage<T>>();
|
||||
_subscriptions = new List<Action<T>>();
|
||||
_processing = new Thread(Process);
|
||||
_processing = new Thread(async () => await Process());
|
||||
_processing.Start();
|
||||
}
|
||||
|
||||
@ -25,19 +25,21 @@ namespace Ocelot.Infrastructure
|
||||
_subscriptions.Add(action);
|
||||
}
|
||||
|
||||
public async Task Publish(T message, int delay)
|
||||
public void Publish(T message, int delay)
|
||||
{
|
||||
await Task.Delay(delay);
|
||||
_queue.Add(message);
|
||||
var delayed = new DelayedMessage<T>(message, delay);
|
||||
_queue.Add(delayed);
|
||||
}
|
||||
|
||||
private void Process()
|
||||
private async Task Process()
|
||||
{
|
||||
foreach(var message in _queue.GetConsumingEnumerable())
|
||||
foreach(var delayedMessage in _queue.GetConsumingEnumerable())
|
||||
{
|
||||
foreach(var subscription in _subscriptions)
|
||||
await Task.Delay(delayedMessage.Delay);
|
||||
|
||||
foreach (var subscription in _subscriptions)
|
||||
{
|
||||
subscription(message);
|
||||
subscription(delayedMessage.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Infrastructure;
|
||||
@ -11,13 +10,13 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
using Responses;
|
||||
using Values;
|
||||
|
||||
public class CookieStickySessions : ILoadBalancer, IDisposable
|
||||
public class CookieStickySessions : ILoadBalancer
|
||||
{
|
||||
private readonly int _keyExpiryInMs;
|
||||
private readonly string _key;
|
||||
private readonly ILoadBalancer _loadBalancer;
|
||||
private readonly ConcurrentDictionary<string, StickySession> _stored;
|
||||
private IBus<StickySession> _bus;
|
||||
private readonly IBus<StickySession> _bus;
|
||||
private readonly object _lock = new object();
|
||||
|
||||
public CookieStickySessions(ILoadBalancer loadBalancer, string key, int keyExpiryInMs, IBus<StickySession> bus)
|
||||
@ -27,12 +26,14 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
_keyExpiryInMs = keyExpiryInMs;
|
||||
_loadBalancer = loadBalancer;
|
||||
_stored = new ConcurrentDictionary<string, StickySession>();
|
||||
_bus.Subscribe(ss => {
|
||||
if(_stored.TryGetValue(ss.Key, out var stickySession))
|
||||
_bus.Subscribe(ss =>
|
||||
{
|
||||
//todo - get test coverage for this.
|
||||
if (_stored.TryGetValue(ss.Key, out var stickySession))
|
||||
{
|
||||
lock(_lock)
|
||||
lock (_lock)
|
||||
{
|
||||
if(stickySession.Expiry < DateTime.Now)
|
||||
if (stickySession.Expiry < DateTime.UtcNow)
|
||||
{
|
||||
_stored.Remove(stickySession.Key, out _);
|
||||
_loadBalancer.Release(stickySession.HostAndPort);
|
||||
@ -42,26 +43,24 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_timer?.Dispose();
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context)
|
||||
{
|
||||
var key = context.HttpContext.Request.Cookies[_key];
|
||||
|
||||
if (!string.IsNullOrEmpty(key) && _stored.ContainsKey(key))
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var cached = _stored[key];
|
||||
if (!string.IsNullOrEmpty(key) && _stored.ContainsKey(key))
|
||||
{
|
||||
var cached = _stored[key];
|
||||
|
||||
var updated = new StickySession(cached.HostAndPort, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs), key);
|
||||
var updated = new StickySession(cached.HostAndPort, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs), key);
|
||||
|
||||
_stored[key] = updated;
|
||||
_stored[key] = updated;
|
||||
|
||||
await _bus.Publish(updated, _keyExpiryInMs);
|
||||
_bus.Publish(updated, _keyExpiryInMs);
|
||||
|
||||
return new OkResponse<ServiceHostAndPort>(updated.HostAndPort);
|
||||
return new OkResponse<ServiceHostAndPort>(updated.HostAndPort);
|
||||
}
|
||||
}
|
||||
|
||||
var next = await _loadBalancer.Lease(context);
|
||||
@ -71,11 +70,14 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
return new ErrorResponse<ServiceHostAndPort>(next.Errors);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(key) && !_stored.ContainsKey(key))
|
||||
lock (_lock)
|
||||
{
|
||||
var ss = new StickySession(next.Data, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs), key);
|
||||
_stored[key] = ss;
|
||||
await _bus.Publish(ss, _keyExpiryInMs);
|
||||
if (!string.IsNullOrEmpty(key) && !_stored.ContainsKey(key))
|
||||
{
|
||||
var ss = new StickySession(next.Data, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs), key);
|
||||
_stored[key] = ss;
|
||||
_bus.Publish(ss, _keyExpiryInMs);
|
||||
}
|
||||
}
|
||||
|
||||
return new OkResponse<ServiceHostAndPort>(next.Data);
|
||||
|
@ -99,7 +99,7 @@ namespace Ocelot.Raft
|
||||
var response = _httpClient.PostAsync($"{_hostAndPort}/administration/raft/command", content).GetAwaiter().GetResult();
|
||||
if(response.IsSuccessStatusCode)
|
||||
{
|
||||
var okResponse = JsonConvert.DeserializeObject<OkResponse<ICommand>>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult(), _jsonSerializerSettings);
|
||||
var okResponse = JsonConvert.DeserializeObject<OkResponse<ICommand>>(response.Content.ReadAsStringAsync().GetAwaiter().GetResult(), _jsonSerializerSettings);
|
||||
return new OkResponse<T>((T)okResponse.Command);
|
||||
}
|
||||
else
|
||||
|
Reference in New Issue
Block a user