Browse Source

support pattern matching for consumer. (#132)

master
Savorboard 6 years ago
parent
commit
68243b8866
2 changed files with 88 additions and 63 deletions
  1. +83
    -11
      src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs
  2. +5
    -52
      src/DotNetCore.CAP/Internal/MethodMatcherCache.cs

+ 83
- 11
src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs View File

@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
@@ -19,6 +20,8 @@ namespace DotNetCore.CAP.Internal
{
private readonly CapOptions _capOptions;
private readonly IServiceProvider _serviceProvider;
private List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>> _asteriskList;
private List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>> _poundList;

/// <summary>
/// Creates a new <see cref="DefaultConsumerServiceSelector" />.
@@ -29,17 +32,6 @@ namespace DotNetCore.CAP.Internal
_capOptions = capOptions;
}

/// <summary>
/// Selects the best <see cref="ConsumerExecutorDescriptor" /> candidate from <paramref name="executeDescriptor" /> for
/// the
/// current message associated.
/// </summary>
public ConsumerExecutorDescriptor SelectBestCandidate(string key,
IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key);
}

public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates()
{
var executorDescriptorList = new List<ConsumerExecutorDescriptor>();
@@ -51,6 +43,26 @@ namespace DotNetCore.CAP.Internal
return executorDescriptorList;
}

public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
var result = MatchUsingName(key, executeDescriptor);
if (result != null)
{
return result;
}

//[*] match with regex, i.e. foo.*.abc
result = MatchAsteriskUsingRegex(key, executeDescriptor);
if (result != null)
{
return result;
}

//[#] match regex, i.e. foo.#
result = MatchPoundUsingRegex(key, executeDescriptor);
return result;
}

private IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(
IServiceProvider provider)
{
@@ -130,5 +142,65 @@ namespace DotNetCore.CAP.Internal

return descriptor;
}

private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key);
}

private ConsumerExecutorDescriptor MatchAsteriskUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
if (_asteriskList == null)
{
_asteriskList = executeDescriptor
.Where(x => x.Attribute.Name.IndexOf('*') >= 0)
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor>
{
Name = ("^" + x.Attribute.Name + "$").Replace("*", "[a-zA-Z]+").Replace(".", "\\."),
Descriptor = x
}).ToList();
}
foreach (var red in _asteriskList)
{
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline))
{
return red.Descriptor;
}
}

return null;
}

private ConsumerExecutorDescriptor MatchPoundUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
if (_poundList == null)
{
_poundList = executeDescriptor
.Where(x => x.Attribute.Name.IndexOf('#') >= 0)
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor>
{
Name = ("^" + x.Attribute.Name + "$").Replace("#", "[a-zA-Z\\.]+"),
Descriptor = x
}).ToList();
}

foreach (var red in _poundList)
{
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline))
{
return red.Descriptor;
}
}

return null;
}


private class RegexExecuteDescriptor<T>
{
public string Name { get; set; }

public T Descriptor { get; set; }
}
}
}

+ 5
- 52
src/DotNetCore.CAP/Internal/MethodMatcherCache.cs View File

@@ -11,21 +11,20 @@ namespace DotNetCore.CAP.Internal
internal class MethodMatcherCache
{
private readonly IConsumerServiceSelector _selector;
private List<string> _allTopics;

public MethodMatcherCache(IConsumerServiceSelector selector)
{
_selector = selector;
Entries = new ConcurrentDictionary<string, IList<ConsumerExecutorDescriptor>>();
Entries = new ConcurrentDictionary<string, IReadOnlyList<ConsumerExecutorDescriptor>>();
}

private ConcurrentDictionary<string, IList<ConsumerExecutorDescriptor>> Entries { get; }
private ConcurrentDictionary<string, IReadOnlyList<ConsumerExecutorDescriptor>> Entries { get; }

/// <summary>
/// Get a dictionary of candidates.In the dictionary,
/// the Key is the CAPSubscribeAttribute Group, the Value for the current Group of candidates
/// </summary>
public ConcurrentDictionary<string, IList<ConsumerExecutorDescriptor>> GetCandidatesMethodsOfGroupNameGrouped()
public ConcurrentDictionary<string, IReadOnlyList<ConsumerExecutorDescriptor>> GetCandidatesMethodsOfGroupNameGrouped()
{
if (Entries.Count != 0)
{
@@ -44,28 +43,6 @@ namespace DotNetCore.CAP.Internal
return Entries;
}

/// <summary>
/// Get a dictionary of specify topic candidates.
/// The Key is Group name, the value is specify topic candidates.
/// </summary>
/// <param name="topicName">message topic name</param>
public IDictionary<string, IList<ConsumerExecutorDescriptor>> GetTopicExector(string topicName)
{
if (Entries == null)
{
throw new ArgumentNullException(nameof(Entries));
}

var dic = new Dictionary<string, IList<ConsumerExecutorDescriptor>>();
foreach (var item in Entries)
{
var topicCandidates = item.Value.Where(x => x.Attribute.Name == topicName);
dic.Add(item.Key, topicCandidates.ToList());
}

return dic;
}

/// <summary>
/// Attempts to get the topic exector associated with the specified topic name and group name from the
/// <see cref="Entries" />.
@@ -86,36 +63,12 @@ namespace DotNetCore.CAP.Internal

if (Entries.TryGetValue(groupName, out var groupMatchTopics))
{
matchTopic = groupMatchTopics.FirstOrDefault(x => x.Attribute.Name == topicName);
matchTopic = _selector.SelectBestCandidate(topicName, groupMatchTopics);

return matchTopic != null;
}

return false;
}

/// <summary>
/// Get all subscribe topics name.
/// </summary>
public IEnumerable<string> GetSubscribeTopics()
{
if (_allTopics != null)
{
return _allTopics;
}

if (Entries == null)
{
throw new ArgumentNullException(nameof(Entries));
}

_allTopics = new List<string>();

foreach (var descriptors in Entries.Values)
{
_allTopics.AddRange(descriptors.Select(x => x.Attribute.Name));
}

return _allTopics;
}
}
}

Loading…
Cancel
Save