From 68243b88663c55d1417f1d3d06bee7e0ce36a90a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 24 May 2018 15:20:45 +0800 Subject: [PATCH] support pattern matching for consumer. (#132) --- .../IConsumerServiceSelector.Default.cs | 94 ++++++++++++++++--- .../Internal/MethodMatcherCache.cs | 57 +---------- 2 files changed, 88 insertions(+), 63 deletions(-) diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs index 7bf92f3..f751499 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs @@ -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> _asteriskList; + private List> _poundList; /// /// Creates a new . @@ -29,17 +32,6 @@ namespace DotNetCore.CAP.Internal _capOptions = capOptions; } - /// - /// Selects the best candidate from for - /// the - /// current message associated. - /// - public ConsumerExecutorDescriptor SelectBestCandidate(string key, - IReadOnlyList executeDescriptor) - { - return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); - } - public IReadOnlyList SelectCandidates() { var executorDescriptorList = new List(); @@ -51,6 +43,26 @@ namespace DotNetCore.CAP.Internal return executorDescriptorList; } + public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList 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 FindConsumersFromInterfaceTypes( IServiceProvider provider) { @@ -130,5 +142,65 @@ namespace DotNetCore.CAP.Internal return descriptor; } + + private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList executeDescriptor) + { + return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); + } + + private ConsumerExecutorDescriptor MatchAsteriskUsingRegex(string key, IReadOnlyList executeDescriptor) + { + if (_asteriskList == null) + { + _asteriskList = executeDescriptor + .Where(x => x.Attribute.Name.IndexOf('*') >= 0) + .Select(x => new RegexExecuteDescriptor + { + 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 executeDescriptor) + { + if (_poundList == null) + { + _poundList = executeDescriptor + .Where(x => x.Attribute.Name.IndexOf('#') >= 0) + .Select(x => new RegexExecuteDescriptor + { + 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 + { + public string Name { get; set; } + + public T Descriptor { get; set; } + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs index d6b40ba..8ff25c7 100644 --- a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs +++ b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs @@ -11,21 +11,20 @@ namespace DotNetCore.CAP.Internal internal class MethodMatcherCache { private readonly IConsumerServiceSelector _selector; - private List _allTopics; public MethodMatcherCache(IConsumerServiceSelector selector) { _selector = selector; - Entries = new ConcurrentDictionary>(); + Entries = new ConcurrentDictionary>(); } - private ConcurrentDictionary> Entries { get; } + private ConcurrentDictionary> Entries { get; } /// /// Get a dictionary of candidates.In the dictionary, /// the Key is the CAPSubscribeAttribute Group, the Value for the current Group of candidates /// - public ConcurrentDictionary> GetCandidatesMethodsOfGroupNameGrouped() + public ConcurrentDictionary> GetCandidatesMethodsOfGroupNameGrouped() { if (Entries.Count != 0) { @@ -44,28 +43,6 @@ namespace DotNetCore.CAP.Internal return Entries; } - /// - /// Get a dictionary of specify topic candidates. - /// The Key is Group name, the value is specify topic candidates. - /// - /// message topic name - public IDictionary> GetTopicExector(string topicName) - { - if (Entries == null) - { - throw new ArgumentNullException(nameof(Entries)); - } - - var dic = new Dictionary>(); - foreach (var item in Entries) - { - var topicCandidates = item.Value.Where(x => x.Attribute.Name == topicName); - dic.Add(item.Key, topicCandidates.ToList()); - } - - return dic; - } - /// /// Attempts to get the topic exector associated with the specified topic name and group name from the /// . @@ -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; } - - /// - /// Get all subscribe topics name. - /// - public IEnumerable GetSubscribeTopics() - { - if (_allTopics != null) - { - return _allTopics; - } - - if (Entries == null) - { - throw new ArgumentNullException(nameof(Entries)); - } - - _allTopics = new List(); - - foreach (var descriptors in Entries.Values) - { - _allTopics.AddRange(descriptors.Select(x => x.Attribute.Name)); - } - - return _allTopics; - } } } \ No newline at end of file