Skip to content

Commit

Permalink
Made the file config poller use IHostedService, bit more generic, not… (
Browse files Browse the repository at this point in the history
ThreeMammals#507)

* Made the file config poller use IHostedService, bit more generic, not just need to provide the correct implementations of the repo services and it will poll anything..this means we can open up redis for ThreeMammals#458

* removed comments
  • Loading branch information
TomPallister authored Jul 29, 2018
1 parent 1817564 commit 0f2cf2d
Show file tree
Hide file tree
Showing 8 changed files with 798 additions and 763 deletions.
Original file line number Diff line number Diff line change
@@ -1,84 +1,112 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Setter;
using Ocelot.Logging;

namespace Ocelot.Configuration.Repository
{
public class ConsulFileConfigurationPoller : IDisposable
{
private readonly IOcelotLogger _logger;
private readonly IFileConfigurationRepository _repo;
private readonly IFileConfigurationSetter _setter;
private string _previousAsJson;
private readonly Timer _timer;
private bool _polling;
private readonly IConsulPollerConfiguration _config;

public ConsulFileConfigurationPoller(
IOcelotLoggerFactory factory,
IFileConfigurationRepository repo,
IFileConfigurationSetter setter,
IConsulPollerConfiguration config)
{
_setter = setter;
_config = config;
_logger = factory.CreateLogger<ConsulFileConfigurationPoller>();
_repo = repo;
_previousAsJson = "";
_timer = new Timer(async x =>
{
if(_polling)
{
return;
}

_polling = true;
await Poll();
_polling = false;
}, null, _config.Delay, _config.Delay);
}

private async Task Poll()
{
_logger.LogInformation("Started polling consul");

var fileConfig = await _repo.Get();

if(fileConfig.IsError)
{
_logger.LogWarning($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}");
return;
}

var asJson = ToJson(fileConfig.Data);

if(!fileConfig.IsError && asJson != _previousAsJson)
{
await _setter.Set(fileConfig.Data);
_previousAsJson = asJson;
}

_logger.LogInformation("Finished polling consul");
}

/// <summary>
/// We could do object comparison here but performance isnt really a problem. This might be an issue one day!
/// </summary>
/// <returns>hash of the config</returns>
private string ToJson(FileConfiguration config)
{
var currentHash = JsonConvert.SerializeObject(config);
return currentHash;
}

public void Dispose()
{
_timer.Dispose();
}
}
}
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Setter;
using Ocelot.Logging;

namespace Ocelot.Configuration.Repository
{
public class FileConfigurationPoller : IHostedService, IDisposable
{
private readonly IOcelotLogger _logger;
private readonly IFileConfigurationRepository _repo;
private string _previousAsJson;
private Timer _timer;
private bool _polling;
private readonly IFileConfigurationPollerOptions _options;
private readonly IInternalConfigurationRepository _internalConfigRepo;
private readonly IInternalConfigurationCreator _internalConfigCreator;

public FileConfigurationPoller(
IOcelotLoggerFactory factory,
IFileConfigurationRepository repo,
IFileConfigurationPollerOptions options,
IInternalConfigurationRepository internalConfigRepo,
IInternalConfigurationCreator internalConfigCreator)
{
_internalConfigRepo = internalConfigRepo;
_internalConfigCreator = internalConfigCreator;
_options = options;
_logger = factory.CreateLogger<FileConfigurationPoller>();
_repo = repo;
_previousAsJson = "";
}

public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation($"{nameof(FileConfigurationPoller)} is starting.");

_timer = new Timer(async x =>
{
if(_polling)
{
return;
}

_polling = true;
await Poll();
_polling = false;
}, null, _options.Delay, _options.Delay);

return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation($"{nameof(FileConfigurationPoller)} is stopping.");

_timer?.Change(Timeout.Infinite, 0);

return Task.CompletedTask;
}

private async Task Poll()
{
_logger.LogInformation("Started polling consul");

var fileConfig = await _repo.Get();

if(fileConfig.IsError)
{
_logger.LogWarning($"error geting file config, errors are {string.Join(",", fileConfig.Errors.Select(x => x.Message))}");
return;
}

var asJson = ToJson(fileConfig.Data);

if(!fileConfig.IsError && asJson != _previousAsJson)
{
var config = await _internalConfigCreator.Create(fileConfig.Data);

if(!config.IsError)
{
_internalConfigRepo.AddOrReplace(config.Data);
}

_previousAsJson = asJson;
}

_logger.LogInformation("Finished polling consul");
}

/// <summary>
/// We could do object comparison here but performance isnt really a problem. This might be an issue one day!
/// </summary>
/// <returns>hash of the config</returns>
private string ToJson(FileConfiguration config)
{
var currentHash = JsonConvert.SerializeObject(config);
return currentHash;
}

public void Dispose()
{
_timer.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Ocelot.Configuration.Repository
{
public interface IConsulPollerConfiguration
public interface IFileConfigurationPollerOptions
{
int Delay { get; }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Ocelot.Configuration.Repository
{
public class InMemoryConsulPollerConfiguration : IConsulPollerConfiguration
public class InMemoryFileConfigurationPollerOptions : IFileConfigurationPollerOptions
{
public int Delay => 1000;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Ocelot.Configuration.Setter
{
public class FileAndInternalConfigurationSetter : IFileConfigurationSetter
{
private readonly IInternalConfigurationRepository _configRepo;
private readonly IInternalConfigurationRepository internalConfigRepo;
private readonly IInternalConfigurationCreator _configCreator;
private readonly IFileConfigurationRepository _repo;

Expand All @@ -17,7 +17,7 @@ public FileAndInternalConfigurationSetter(
IInternalConfigurationCreator configCreator,
IFileConfigurationRepository repo)
{
_configRepo = configRepo;
internalConfigRepo = configRepo;
_configCreator = configCreator;
_repo = repo;
}
Expand All @@ -35,7 +35,7 @@ public async Task<Response> Set(FileConfiguration fileConfig)

if(!config.IsError)
{
_configRepo.AddOrReplace(config.Data);
internalConfigRepo.AddOrReplace(config.Data);
}

return new ErrorResponse(config.Errors);
Expand Down
4 changes: 2 additions & 2 deletions src/Ocelot/DependencyInjection/OcelotBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo

// We add this here so that we can always inject something into the factory for IoC..
_services.AddSingleton<IServiceTracer, FakeServiceTracer>();
_services.TryAddSingleton<IConsulPollerConfiguration, InMemoryConsulPollerConfiguration>();
_services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
_services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
_services.TryAddSingleton<IPlaceholders, Placeholders>();
_services.TryAddSingleton<IConsulClientFactory, ConsulClientFactory>();
Expand Down Expand Up @@ -245,7 +245,7 @@ public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)

public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
{
_services.AddSingleton<ConsulFileConfigurationPoller>();
_services.AddHostedService<FileConfigurationPoller>();
_services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
return this;
}
Expand Down
Loading

0 comments on commit 0f2cf2d

Please sign in to comment.