mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 14:42:51 +08:00
more messing with send ot self
This commit is contained in:
parent
17a515c4c0
commit
c041d90e38
14
src/Ocelot/Infrastructure/IBus.cs
Normal file
14
src/Ocelot/Infrastructure/IBus.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.Infrastructure
|
||||||
|
{
|
||||||
|
public class InMemoryBus<T> : IBus<T>
|
||||||
|
{
|
||||||
|
private readonly BlockingCollection<T> _queue;
|
||||||
|
private readonly List<Action<T>> _subscriptions;
|
||||||
|
private Thread _processing;
|
||||||
|
|
||||||
|
public InMemoryBus()
|
||||||
|
{
|
||||||
|
_queue = new BlockingCollection<T>();
|
||||||
|
_subscriptions = new List<Action<T>>();
|
||||||
|
_processing = new Thread(Process);
|
||||||
|
_processing.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Subscribe(Action<T> action)
|
||||||
|
{
|
||||||
|
_subscriptions.Add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Publish(T message, int delay)
|
||||||
|
{
|
||||||
|
await Task.Delay(delay);
|
||||||
|
_queue.Add(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Process()
|
||||||
|
{
|
||||||
|
foreach(var message in _queue.GetConsumingEnumerable())
|
||||||
|
{
|
||||||
|
foreach(var subscription in _subscriptions)
|
||||||
|
{
|
||||||
|
subscription(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Responses;
|
using Responses;
|
||||||
using Values;
|
using Values;
|
||||||
@ -16,28 +17,29 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
private readonly string _key;
|
private readonly string _key;
|
||||||
private readonly ILoadBalancer _loadBalancer;
|
private readonly ILoadBalancer _loadBalancer;
|
||||||
private readonly ConcurrentDictionary<string, StickySession> _stored;
|
private readonly ConcurrentDictionary<string, StickySession> _stored;
|
||||||
private readonly Timer _timer;
|
private IBus<StickySession> _bus;
|
||||||
private bool _expiring;
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
public CookieStickySessions(ILoadBalancer loadBalancer, string key, int keyExpiryInMs)
|
public CookieStickySessions(ILoadBalancer loadBalancer, string key, int keyExpiryInMs, IBus<StickySession> bus)
|
||||||
{
|
{
|
||||||
|
_bus = bus;
|
||||||
_key = key;
|
_key = key;
|
||||||
_keyExpiryInMs = keyExpiryInMs;
|
_keyExpiryInMs = keyExpiryInMs;
|
||||||
_loadBalancer = loadBalancer;
|
_loadBalancer = loadBalancer;
|
||||||
_stored = new ConcurrentDictionary<string, StickySession>();
|
_stored = new ConcurrentDictionary<string, StickySession>();
|
||||||
_timer = new Timer(x =>
|
_bus.Subscribe(ss => {
|
||||||
|
if(_stored.TryGetValue(ss.Key, out var stickySession))
|
||||||
{
|
{
|
||||||
if (_expiring)
|
lock(_lock)
|
||||||
{
|
{
|
||||||
return;
|
if(stickySession.Expiry < DateTime.Now)
|
||||||
|
{
|
||||||
|
_stored.Remove(stickySession.Key, out _);
|
||||||
|
_loadBalancer.Release(stickySession.HostAndPort);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_expiring = true;
|
}
|
||||||
|
});
|
||||||
Expire();
|
|
||||||
|
|
||||||
_expiring = false;
|
|
||||||
}, null, 0, 50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -47,15 +49,17 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
|
|
||||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context)
|
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var value = context.HttpContext.Request.Cookies[_key];
|
var key = context.HttpContext.Request.Cookies[_key];
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(value) && _stored.ContainsKey(value))
|
if (!string.IsNullOrEmpty(key) && _stored.ContainsKey(key))
|
||||||
{
|
{
|
||||||
var cached = _stored[value];
|
var cached = _stored[key];
|
||||||
|
|
||||||
var updated = new StickySession(cached.HostAndPort, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs));
|
var updated = new StickySession(cached.HostAndPort, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs), key);
|
||||||
|
|
||||||
_stored[value] = updated;
|
_stored[key] = updated;
|
||||||
|
|
||||||
|
await _bus.Publish(updated, _keyExpiryInMs);
|
||||||
|
|
||||||
return new OkResponse<ServiceHostAndPort>(updated.HostAndPort);
|
return new OkResponse<ServiceHostAndPort>(updated.HostAndPort);
|
||||||
}
|
}
|
||||||
@ -67,9 +71,11 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
return new ErrorResponse<ServiceHostAndPort>(next.Errors);
|
return new ErrorResponse<ServiceHostAndPort>(next.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(value) && !_stored.ContainsKey(value))
|
if (!string.IsNullOrEmpty(key) && !_stored.ContainsKey(key))
|
||||||
{
|
{
|
||||||
_stored[value] = new StickySession(next.Data, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs));
|
var ss = new StickySession(next.Data, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs), key);
|
||||||
|
_stored[key] = ss;
|
||||||
|
await _bus.Publish(ss, _keyExpiryInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OkResponse<ServiceHostAndPort>(next.Data);
|
return new OkResponse<ServiceHostAndPort>(next.Data);
|
||||||
@ -78,16 +84,5 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
public void Release(ServiceHostAndPort hostAndPort)
|
public void Release(ServiceHostAndPort hostAndPort)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Expire()
|
|
||||||
{
|
|
||||||
var expired = _stored.Where(x => x.Value.Expiry < DateTime.UtcNow);
|
|
||||||
|
|
||||||
foreach (var expire in expired)
|
|
||||||
{
|
|
||||||
_stored.Remove(expire.Key, out _);
|
|
||||||
_loadBalancer.Release(expire.Value.HostAndPort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
|
|
||||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||||
@ -25,7 +26,8 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
return new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName);
|
return new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName);
|
||||||
case nameof(CookieStickySessions):
|
case nameof(CookieStickySessions):
|
||||||
var loadBalancer = new RoundRobin(async () => await serviceProvider.Get());
|
var loadBalancer = new RoundRobin(async () => await serviceProvider.Get());
|
||||||
return new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, reRoute.LoadBalancerOptions.ExpiryInMs);
|
var bus = new InMemoryBus<StickySession>();
|
||||||
|
return new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, reRoute.LoadBalancerOptions.ExpiryInMs, bus);
|
||||||
default:
|
default:
|
||||||
return new NoLoadBalancer(await serviceProvider.Get());
|
return new NoLoadBalancer(await serviceProvider.Get());
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,17 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
|||||||
{
|
{
|
||||||
public class StickySession
|
public class StickySession
|
||||||
{
|
{
|
||||||
public StickySession(ServiceHostAndPort hostAndPort, DateTime expiry)
|
public StickySession(ServiceHostAndPort hostAndPort, DateTime expiry, string key)
|
||||||
{
|
{
|
||||||
HostAndPort = hostAndPort;
|
HostAndPort = hostAndPort;
|
||||||
Expiry = expiry;
|
Expiry = expiry;
|
||||||
|
Key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceHostAndPort HostAndPort { get; }
|
public ServiceHostAndPort HostAndPort { get; }
|
||||||
|
|
||||||
public DateTime Expiry { get; }
|
public DateTime Expiry { get; }
|
||||||
|
|
||||||
|
public string Key {get;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.UnitTests.Responder;
|
using Ocelot.UnitTests.Responder;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
|
||||||
public class CookieStickySessionsTests
|
public class CookieStickySessionsTests
|
||||||
{
|
{
|
||||||
@ -24,12 +25,14 @@ namespace Ocelot.UnitTests.LoadBalancer
|
|||||||
private Response<ServiceHostAndPort> _result;
|
private Response<ServiceHostAndPort> _result;
|
||||||
private Response<ServiceHostAndPort> _firstHostAndPort;
|
private Response<ServiceHostAndPort> _firstHostAndPort;
|
||||||
private Response<ServiceHostAndPort> _secondHostAndPort;
|
private Response<ServiceHostAndPort> _secondHostAndPort;
|
||||||
|
private IBus<StickySession> _bus;
|
||||||
|
|
||||||
public CookieStickySessionsTests()
|
public CookieStickySessionsTests()
|
||||||
{
|
{
|
||||||
|
_bus = new InMemoryBus<StickySession>();
|
||||||
_loadBalancer = new Mock<ILoadBalancer>();
|
_loadBalancer = new Mock<ILoadBalancer>();
|
||||||
_defaultExpiryInMs = 100;
|
_defaultExpiryInMs = 100;
|
||||||
_stickySessions = new CookieStickySessions(_loadBalancer.Object, "sessionid", _defaultExpiryInMs);
|
_stickySessions = new CookieStickySessions(_loadBalancer.Object, "sessionid", _defaultExpiryInMs, _bus);
|
||||||
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user