Browse Source

Fixed consumer re-register transport bug. (#329)

master
Savorboard 5 years ago
parent
commit
81bf77c62f
12 changed files with 131 additions and 27 deletions
  1. +1
    -1
      src/Directory.Build.props
  2. +0
    -1
      src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs
  3. +9
    -2
      src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClientFactory.cs
  4. +2
    -5
      src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs
  5. +8
    -1
      src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs
  6. +8
    -2
      src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs
  7. +13
    -0
      src/DotNetCore.CAP/BrokerConnectionException.cs
  8. +4
    -1
      src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs
  9. +46
    -13
      src/DotNetCore.CAP/IConsumerRegister.Default.cs
  10. +4
    -1
      src/DotNetCore.CAP/IConsumerRegister.cs
  11. +1
    -0
      src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs
  12. +35
    -0
      src/DotNetCore.CAP/Processor/IProcessor.TransportCheck.cs

Directory.Build.props → src/Directory.Build.props View File

@@ -1,6 +1,6 @@
<Project> <Project>


<Import Project="build\version.props" />
<Import Project="..\build\version.props" />


<PropertyGroup Label="Package"> <PropertyGroup Label="Package">
<Product>CAP</Product> <Product>CAP</Product>

+ 0
- 1
src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs View File

@@ -71,7 +71,6 @@ namespace DotNetCore.CAP.AzureServiceBus


public void Listening(TimeSpan timeout, CancellationToken cancellationToken) public void Listening(TimeSpan timeout, CancellationToken cancellationToken)
{ {

_consumerClient.RegisterMessageHandler(OnConsumerReceived, _consumerClient.RegisterMessageHandler(OnConsumerReceived,
new MessageHandlerOptions(OnExceptionReceived) new MessageHandlerOptions(OnExceptionReceived)
{ {


+ 9
- 2
src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClientFactory.cs View File

@@ -20,8 +20,15 @@ namespace DotNetCore.CAP.AzureServiceBus


public IConsumerClient Create(string groupId) public IConsumerClient Create(string groupId)
{ {
var logger = _loggerFactory.CreateLogger(typeof(AzureServiceBusConsumerClient));
return new AzureServiceBusConsumerClient(logger, groupId, _asbOptions);
try
{
var logger = _loggerFactory.CreateLogger(typeof(AzureServiceBusConsumerClient));
return new AzureServiceBusConsumerClient(logger, groupId, _asbOptions);
}
catch (System.Exception e)
{
throw new BrokerConnectionException(e);
}
} }
} }
} }

+ 2
- 5
src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs View File

@@ -21,6 +21,8 @@ namespace DotNetCore.CAP.Kafka
_groupId = groupId; _groupId = groupId;
_kafkaOptions = options ?? throw new ArgumentNullException(nameof(options)); _kafkaOptions = options ?? throw new ArgumentNullException(nameof(options));
StringDeserializer = new StringDeserializer(Encoding.UTF8); StringDeserializer = new StringDeserializer(Encoding.UTF8);

InitKafkaClient();
} }


public IDeserializer<string> StringDeserializer { get; set; } public IDeserializer<string> StringDeserializer { get; set; }
@@ -38,11 +40,6 @@ namespace DotNetCore.CAP.Kafka
throw new ArgumentNullException(nameof(topics)); throw new ArgumentNullException(nameof(topics));
} }


if (_consumerClient == null)
{
InitKafkaClient();
}

_consumerClient.Subscribe(topics); _consumerClient.Subscribe(topics);
} }




+ 8
- 1
src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs View File

@@ -14,7 +14,14 @@ namespace DotNetCore.CAP.Kafka


public IConsumerClient Create(string groupId) public IConsumerClient Create(string groupId)
{ {
return new KafkaConsumerClient(groupId, _kafkaOptions);
try
{
return new KafkaConsumerClient(groupId, _kafkaOptions);
}
catch (System.Exception e)
{
throw new BrokerConnectionException(e);
}
} }
} }
} }

+ 8
- 2
src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs View File

@@ -8,7 +8,6 @@ namespace DotNetCore.CAP.RabbitMQ
private readonly IConnectionChannelPool _connectionChannelPool; private readonly IConnectionChannelPool _connectionChannelPool;
private readonly RabbitMQOptions _rabbitMQOptions; private readonly RabbitMQOptions _rabbitMQOptions;



public RabbitMQConsumerClientFactory(RabbitMQOptions rabbitMQOptions, IConnectionChannelPool channelPool) public RabbitMQConsumerClientFactory(RabbitMQOptions rabbitMQOptions, IConnectionChannelPool channelPool)
{ {
_rabbitMQOptions = rabbitMQOptions; _rabbitMQOptions = rabbitMQOptions;
@@ -17,7 +16,14 @@ namespace DotNetCore.CAP.RabbitMQ


public IConsumerClient Create(string groupId) public IConsumerClient Create(string groupId)
{ {
return new RabbitMQConsumerClient(groupId, _connectionChannelPool, _rabbitMQOptions);
try
{
return new RabbitMQConsumerClient(groupId, _connectionChannelPool, _rabbitMQOptions);
}
catch (System.Exception e)
{
throw new BrokerConnectionException(e);
}
} }
} }
} }

+ 13
- 0
src/DotNetCore.CAP/BrokerConnectionException.cs View File

@@ -0,0 +1,13 @@
using System;

namespace DotNetCore.CAP
{
public class BrokerConnectionException : Exception
{
public BrokerConnectionException(Exception innerException)
: base("Broker Unreachable", innerException)
{

}
}
}

+ 4
- 1
src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs View File

@@ -50,12 +50,15 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddSingleton<MethodMatcherCache>(); services.TryAddSingleton<MethodMatcherCache>();


//Processors //Processors
services.TryAddEnumerable(ServiceDescriptor.Singleton<IProcessingServer, ConsumerHandler>());
services.TryAddSingleton<IConsumerRegister, ConsumerRegister>();

services.TryAddEnumerable(ServiceDescriptor.Singleton<IProcessingServer, CapProcessingServer>()); services.TryAddEnumerable(ServiceDescriptor.Singleton<IProcessingServer, CapProcessingServer>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IProcessingServer, ConsumerRegister>());
services.TryAddSingleton<IStateChanger, StateChanger>(); services.TryAddSingleton<IStateChanger, StateChanger>();


//Queue's message processor //Queue's message processor
services.TryAddSingleton<NeedRetryMessageProcessor>(); services.TryAddSingleton<NeedRetryMessageProcessor>();
services.TryAddSingleton<TransportCheckProcessor>();


//Sender and Executors //Sender and Executors
services.TryAddSingleton<IDispatcher, Dispatcher>(); services.TryAddSingleton<IDispatcher, Dispatcher>();


src/DotNetCore.CAP/IConsumerHandler.Default.cs → src/DotNetCore.CAP/IConsumerRegister.Default.cs View File

@@ -14,29 +14,30 @@ using Microsoft.Extensions.Logging;


namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
internal class ConsumerHandler : IConsumerHandler
internal class ConsumerRegister : IConsumerRegister
{ {
private readonly IStorageConnection _connection; private readonly IStorageConnection _connection;
private readonly IConsumerClientFactory _consumerClientFactory; private readonly IConsumerClientFactory _consumerClientFactory;
private readonly CancellationTokenSource _cts;
private readonly IDispatcher _dispatcher; private readonly IDispatcher _dispatcher;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly TimeSpan _pollingDelay = TimeSpan.FromSeconds(1); private readonly TimeSpan _pollingDelay = TimeSpan.FromSeconds(1);
private readonly MethodMatcherCache _selector; private readonly MethodMatcherCache _selector;


private CancellationTokenSource _cts;
private string _serverAddress; private string _serverAddress;
private Task _compositeTask; private Task _compositeTask;
private bool _disposed; private bool _disposed;
private static bool _isHealthy = true;


// diagnostics listener // diagnostics listener
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
private static readonly DiagnosticListener s_diagnosticListener = private static readonly DiagnosticListener s_diagnosticListener =
new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName);


public ConsumerHandler(IConsumerClientFactory consumerClientFactory,
public ConsumerRegister(IConsumerClientFactory consumerClientFactory,
IDispatcher dispatcher, IDispatcher dispatcher,
IStorageConnection connection, IStorageConnection connection,
ILogger<ConsumerHandler> logger,
ILogger<ConsumerRegister> logger,
MethodMatcherCache selector) MethodMatcherCache selector)
{ {
_selector = selector; _selector = selector;
@@ -44,26 +45,44 @@ namespace DotNetCore.CAP
_consumerClientFactory = consumerClientFactory; _consumerClientFactory = consumerClientFactory;
_dispatcher = dispatcher; _dispatcher = dispatcher;
_connection = connection; _connection = connection;
_cts = new CancellationTokenSource();
}

public bool IsHealthy()
{
return _isHealthy;
} }


public void Start() public void Start()
{ {
_cts = new CancellationTokenSource();

var groupingMatches = _selector.GetCandidatesMethodsOfGroupNameGrouped(); var groupingMatches = _selector.GetCandidatesMethodsOfGroupNameGrouped();


foreach (var matchGroup in groupingMatches) foreach (var matchGroup in groupingMatches)
{ {
Task.Factory.StartNew(() => Task.Factory.StartNew(() =>
{ {
using (var client = _consumerClientFactory.Create(matchGroup.Key))
try
{ {
_serverAddress = client.ServersAddress;
using (var client = _consumerClientFactory.Create(matchGroup.Key))
{
_serverAddress = client.ServersAddress;


RegisterMessageProcessor(client);
RegisterMessageProcessor(client);


client.Subscribe(matchGroup.Value.Select(x => x.Attribute.Name));
client.Subscribe(matchGroup.Value.Select(x => x.Attribute.Name));


client.Listening(_pollingDelay, _cts.Token);
client.Listening(_pollingDelay, _cts.Token);
}
}
catch (OperationCanceledException)
{
//ignore
}
catch (BrokerConnectionException e)
{
_isHealthy = false;
_logger.LogError(e, e.Message);
} }
}, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
} }
@@ -71,6 +90,18 @@ namespace DotNetCore.CAP
_compositeTask = Task.CompletedTask; _compositeTask = Task.CompletedTask;
} }


public void ReStart(bool force = false)
{
if (!IsHealthy() || force)
{
Pulse();

_isHealthy = true;

Start();
}
}

public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)
@@ -79,9 +110,11 @@ namespace DotNetCore.CAP
} }


_disposed = true; _disposed = true;
_cts.Cancel();
try try
{ {
Pulse();

_compositeTask?.Wait(TimeSpan.FromSeconds(2)); _compositeTask?.Wait(TimeSpan.FromSeconds(2));
} }
catch (AggregateException ex) catch (AggregateException ex)
@@ -96,7 +129,7 @@ namespace DotNetCore.CAP


public void Pulse() public void Pulse()
{ {
//ignore
_cts?.Cancel();
} }


private void RegisterMessageProcessor(IConsumerClient client) private void RegisterMessageProcessor(IConsumerClient client)
@@ -174,7 +207,7 @@ namespace DotNetCore.CAP


private void StoreMessage(CapReceivedMessage receivedMessage) private void StoreMessage(CapReceivedMessage receivedMessage)
{ {
_connection.StoreReceivedMessage(receivedMessage);
_connection.StoreReceivedMessage(receivedMessage);
} }


private (Guid, string) TracingBefore(string topic, string values) private (Guid, string) TracingBefore(string topic, string values)

src/DotNetCore.CAP/IConsumerHandler.cs → src/DotNetCore.CAP/IConsumerRegister.cs View File

@@ -6,7 +6,10 @@ namespace DotNetCore.CAP
/// <summary> /// <summary>
/// Handler received message of subscribed. /// Handler received message of subscribed.
/// </summary> /// </summary>
public interface IConsumerHandler : IProcessingServer
public interface IConsumerRegister : IProcessingServer
{ {
bool IsHealthy();

void ReStart(bool force = false);
} }
} }

+ 1
- 0
src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs View File

@@ -93,6 +93,7 @@ namespace DotNetCore.CAP.Processor
{ {
var returnedProcessors = new List<IProcessor> var returnedProcessors = new List<IProcessor>
{ {
_provider.GetRequiredService<TransportCheckProcessor>(),
_provider.GetRequiredService<NeedRetryMessageProcessor>(), _provider.GetRequiredService<NeedRetryMessageProcessor>(),
_provider.GetRequiredService<ICollectProcessor>() _provider.GetRequiredService<ICollectProcessor>()
}; };


+ 35
- 0
src/DotNetCore.CAP/Processor/IProcessor.TransportCheck.cs View File

@@ -0,0 +1,35 @@
// Copyright (c) .NET Core Community. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Threading.Tasks;

namespace DotNetCore.CAP.Processor
{
public class TransportCheckProcessor : IProcessor
{
private readonly IConsumerRegister _register;
private readonly TimeSpan _waitingInterval;

public TransportCheckProcessor(IConsumerRegister register)
{
_register = register;
_waitingInterval = TimeSpan.FromSeconds(30);
}

public async Task ProcessAsync(ProcessingContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}

if (!_register.IsHealthy())
{
_register.ReStart();
}

await context.WaitAsync(_waitingInterval);
}
}
}

Loading…
Cancel
Save