diff --git a/CAP.sln b/CAP.sln index cee4bf6..174a366 100644 --- a/CAP.sln +++ b/CAP.sln @@ -54,15 +54,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.MySql.Test", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.RabbitMQ.MySql", "samples\Sample.RabbitMQ.MySql\Sample.RabbitMQ.MySql.csproj", "{9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.RabbitMQ.SqlServer", "samples\Sample.RabbitMQ.SqlServer\Sample.RabbitMQ.SqlServer.csproj", "{AF17B956-B79E-48B7-9B5B-EB15A386B112}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql", "src\DotNetCore.CAP.PostgreSql\DotNetCore.CAP.PostgreSql.csproj", "{82C403AB-ED68-4084-9A1D-11334F9F08F9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.RabbitMQ.PostgreSql", "samples\Sample.RabbitMQ.PostgreSql\Sample.RabbitMQ.PostgreSql.csproj", "{A17E8E72-DFFC-4822-BB38-73D59A8B264E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql.Test", "test\DotNetCore.CAP.PostgreSql.Test\DotNetCore.CAP.PostgreSql.Test.csproj", "{7CA3625D-1817-4695-881D-7E79A1E1DED2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.SqlServer", "samples\Sample.Kafka.SqlServer\Sample.Kafka.SqlServer.csproj", "{573B4D39-5489-48B3-9B6C-5234249CB980}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.MySql", "samples\Sample.Kafka.SqlServer\Sample.Kafka.MySql.csproj", "{573B4D39-5489-48B3-9B6C-5234249CB980}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -105,18 +101,10 @@ Global {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873}.Debug|Any CPU.Build.0 = Debug|Any CPU {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873}.Release|Any CPU.Build.0 = Release|Any CPU - {AF17B956-B79E-48B7-9B5B-EB15A386B112}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AF17B956-B79E-48B7-9B5B-EB15A386B112}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF17B956-B79E-48B7-9B5B-EB15A386B112}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AF17B956-B79E-48B7-9B5B-EB15A386B112}.Release|Any CPU.Build.0 = Release|Any CPU {82C403AB-ED68-4084-9A1D-11334F9F08F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {82C403AB-ED68-4084-9A1D-11334F9F08F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {82C403AB-ED68-4084-9A1D-11334F9F08F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {82C403AB-ED68-4084-9A1D-11334F9F08F9}.Release|Any CPU.Build.0 = Release|Any CPU - {A17E8E72-DFFC-4822-BB38-73D59A8B264E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A17E8E72-DFFC-4822-BB38-73D59A8B264E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A17E8E72-DFFC-4822-BB38-73D59A8B264E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A17E8E72-DFFC-4822-BB38-73D59A8B264E}.Release|Any CPU.Build.0 = Release|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Debug|Any CPU.Build.0 = Debug|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -139,9 +127,7 @@ Global {FA15685A-778A-4D2A-A2FE-27FAD2FFA65B} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {80A84F62-1558-427B-BA74-B47AA8A665B5} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873} = {3A6B6931-A123-477A-9469-8B468B5385AF} - {AF17B956-B79E-48B7-9B5B-EB15A386B112} = {3A6B6931-A123-477A-9469-8B468B5385AF} {82C403AB-ED68-4084-9A1D-11334F9F08F9} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} - {A17E8E72-DFFC-4822-BB38-73D59A8B264E} = {3A6B6931-A123-477A-9469-8B468B5385AF} {7CA3625D-1817-4695-881D-7E79A1E1DED2} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} {573B4D39-5489-48B3-9B6C-5234249CB980} = {3A6B6931-A123-477A-9469-8B468B5385AF} EndGlobalSection diff --git a/ConfigureMSDTC.ps1 b/ConfigureMSDTC.ps1 deleted file mode 100644 index d574b34..0000000 --- a/ConfigureMSDTC.ps1 +++ /dev/null @@ -1,40 +0,0 @@ -# -# Enable MSDTC -# - -Write-Host "Enabling MSDTC..." -ForegroundColor Yellow -$DTCSecurity = "Incoming" -$RegPath = "HKLM:\SOFTWARE\Microsoft\MSDTC\" - -#Set Security and MSDTC path - -$RegSecurityPath = "$RegPath\Security" - -Set-ItemProperty path $RegSecurityPath name "NetworkDtcAccess" value 1 -Set-ItemProperty path $RegSecurityPath name "NetworkDtcAccessClients" value 1 -Set-ItemProperty path $RegSecurityPath name "NetworkDtcAccessTransactions" value 1 -Set-ItemProperty path $RegSecurityPath name "NetworkDtcAccessInbound" value 1 -Set-ItemProperty path $RegSecurityPath name "NetworkDtcAccessOutbound" value 1 -Set-ItemProperty path $RegSecurityPath name "LuTransactions" value 1 - -if ($DTCSecurity eq "None") -{ - Set-ItemProperty path $RegPath name "TurnOffRpcSecurity" value 1 - Set-ItemProperty path $RegPath name "AllowOnlySecureRpcCalls" value 0 - Set-ItemProperty path $RegPath name "FallbackToUnsecureRPCIfNecessary" value 0 -} -elseif ($DTCSecurity eq "Incoming") -{ - Set-ItemProperty path $RegPath name "TurnOffRpcSecurity" value 0 - Set-ItemProperty path $RegPath name "AllowOnlySecureRpcCalls" value 0 - Set-ItemProperty path $RegPath name "FallbackToUnsecureRPCIfNecessary" value 1 -} -else -{ - Set-ItemProperty path $RegPath name "TurnOffRpcSecurity" value 0 - Set-ItemProperty path $RegPath name "AllowOnlySecureRpcCalls" value 1 - Set-ItemProperty path $RegPath name "FallbackToUnsecureRPCIfNecessary" value 0 -} - -Restart-Service MSDTC -Write-Host "MSDTC has been configured" foregroundcolor green diff --git a/appveyor.yml b/appveyor.yml index 05802e7..2bdfaba 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,6 @@ services: - mysql - postgresql build_script: -- ps: ./ConfigureMSDTC.ps1 - ps: ./build.ps1 test: off artifacts: diff --git a/build/version.props b/build/version.props index fae43bc..d705cb4 100644 --- a/build/version.props +++ b/build/version.props @@ -1,8 +1,8 @@ 2 - 1 - 4 + 2 + 0 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/samples/Sample.Kafka.SqlServer/AppDbContext.cs b/samples/Sample.Kafka.SqlServer/AppDbContext.cs deleted file mode 100644 index 78d6492..0000000 --- a/samples/Sample.Kafka.SqlServer/AppDbContext.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace Sample.Kafka.SqlServer -{ - public class AppDbContext : DbContext - { - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - //optionsBuilder.UseSqlServer("Server=192.168.2.206;Initial Catalog=Sample.Kafka.SqlServer;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"); - optionsBuilder.UseSqlServer("Server=192.168.2.206;Initial Catalog=TestCap;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"); - } - } -} diff --git a/samples/Sample.Kafka.SqlServer/CmsContentSerializer.cs b/samples/Sample.Kafka.SqlServer/CmsContentSerializer.cs deleted file mode 100644 index dbbd736..0000000 --- a/samples/Sample.Kafka.SqlServer/CmsContentSerializer.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Models; -using Newtonsoft.Json; - -namespace Sample.RabbitMQ.SqlServer -{ - public class MessageContent : CapMessage - { - [JsonProperty("id")] - public override string Id { get; set; } - - [JsonProperty("createdTime")] - public override DateTime Timestamp { get; set; } - - [JsonProperty("msgBody")] - public override string Content { get; set; } - - [JsonProperty("callbackTopicName")] - public override string CallbackName { get; set; } - } - - public class MyMessagePacker : IMessagePacker - { - private readonly IContentSerializer _serializer; - - public MyMessagePacker(IContentSerializer serializer) - { - _serializer = serializer; - } - - public string Pack(CapMessage obj) - { - var content = new MessageContent - { - Id = obj.Id, - Content = obj.Content, - CallbackName = obj.CallbackName, - Timestamp = obj.Timestamp - }; - return _serializer.Serialize(content); - } - - public CapMessage UnPack(string packingMessage) - { - return _serializer.DeSerialize(packingMessage); - } - } -} - diff --git a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs index 77a28b3..afcec67 100644 --- a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs @@ -1,137 +1,43 @@ using System; -using System.Diagnostics; using System.Threading.Tasks; using DotNetCore.CAP; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; +using MySql.Data.MySqlClient; -namespace Sample.Kafka.SqlServer.Controllers +namespace Sample.Kafka.MySql.Controllers { - public class Person - { - [JsonProperty("id")] - public string Id { get; set; } - - [JsonProperty("uname")] - public string Name { get; set; } - - public HAHA Haha { get; set; } - - public override string ToString() - { - return "Name:" + Name + ";Id:" + Id + "Haha:" + Haha?.ToString(); - } - } - - public class HAHA - { - [JsonProperty("id")] - public string Id { get; set; } - - [JsonProperty("uname")] - public string Name { get; set; } - public override string ToString() - { - return "Name:" + Name + ";Id:" + Id; - } - } - - [Route("api/[controller]")] public class ValuesController : Controller, ICapSubscribe { private readonly ICapPublisher _capBus; - private readonly AppDbContext _dbContext; - public ValuesController(ICapPublisher producer, AppDbContext dbContext) + public ValuesController(ICapPublisher producer) { _capBus = producer; - _dbContext = dbContext; } - [Route("~/publish")] - public IActionResult PublishMessage() + public async Task PublishMessage() { - var p = new Person + using (var connection = new MySqlConnection("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;")) { - Id = Guid.NewGuid().ToString(), - Name = "杨晓东", - Haha = new HAHA - { - Id = Guid.NewGuid().ToString(), - Name = "1-1杨晓东", - } - }; - - _capBus.Publish("wl.yxd.test", p, "wl.yxd.test.callback"); - - - //_capBus.Publish("wl.cj.test", p); - return Ok(); - } - - [CapSubscribe("wl.yxd.test.callback")] - public void KafkaTestCallback(Person p) - { - Console.WriteLine("回调内容:" + p); - } + connection.Open(); + var transaction = connection.BeginTransaction(); + //your business code here - [CapSubscribe("wl.cj.test")] - public string KafkaTestReceived(Person person) - { - Console.WriteLine(person); - Debug.WriteLine(person); - return "this is callback message"; - } - - [Route("~/publishWithTrans")] - public async Task PublishMessageWithTransaction() - { - using (var trans = await _dbContext.Database.BeginTransactionAsync()) - { - await _capBus.PublishAsync("sample.rabbitmq.mysql", ""); + await _capBus.PublishAsync("xxx.xxx.test2", 123456, transaction); - trans.Commit(); + transaction.Commit(); } - return Ok(); - } - - [CapSubscribe("sample.rabbitmq.mysql33333", Group = "Test.Group")] - public void KafkaTest22(Person person) - { - var aa = _dbContext.Database; - - _dbContext.Dispose(); - Console.WriteLine("[sample.kafka.sqlserver] message received " + person.ToString()); - Debug.WriteLine("[sample.kafka.sqlserver] message received " + person.ToString()); - } - - //[CapSubscribe("sample.rabbitmq.mysql22222")] - //public void KafkaTest22(DateTime time) - //{ - // Console.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - // Debug.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - //} - - [CapSubscribe("sample.rabbitmq.mysql22222")] - public async Task KafkaTest33(DateTime time) - { - Console.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - Debug.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - return await Task.FromResult(time); + return Ok("publish successful!"); } - [NonAction] - [CapSubscribe("sample.kafka.sqlserver3")] - [CapSubscribe("sample.kafka.sqlserver4")] - public void KafkaTest() + [CapSubscribe("xxx.xxx.test2")] + public void Test2(int value) { - Console.WriteLine("[sample.kafka.sqlserver] message received"); - Debug.WriteLine("[sample.kafka.sqlserver] message received"); + Console.WriteLine(value); } } } \ No newline at end of file diff --git a/samples/Sample.Kafka.SqlServer/Program.cs b/samples/Sample.Kafka.SqlServer/Program.cs index c8cd00a..7d62e55 100644 --- a/samples/Sample.Kafka.SqlServer/Program.cs +++ b/samples/Sample.Kafka.SqlServer/Program.cs @@ -1,10 +1,7 @@ -using System.IO; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -namespace Sample.Kafka.SqlServer +namespace Sample.Kafka.MySql { public class Program { diff --git a/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json b/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json new file mode 100644 index 0000000..60815cf --- /dev/null +++ b/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57172/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "cap", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Sample.Kafka.SqlServer": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "cap", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:57174/" + } + } +} \ No newline at end of file diff --git a/samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj b/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj similarity index 74% rename from samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj rename to samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj index 1441491..2123eaa 100644 --- a/samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj +++ b/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj @@ -2,20 +2,21 @@ netcoreapp2.0 - Sample.Kafka.SqlServer + Sample.Kafka.MySql NU1701 NU1701 - + + - + diff --git a/samples/Sample.Kafka.SqlServer/Startup.cs b/samples/Sample.Kafka.SqlServer/Startup.cs index bc15d25..cdea862 100644 --- a/samples/Sample.Kafka.SqlServer/Startup.cs +++ b/samples/Sample.Kafka.SqlServer/Startup.cs @@ -1,23 +1,18 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Sample.RabbitMQ.SqlServer; -namespace Sample.Kafka.SqlServer +namespace Sample.Kafka.MySql { public class Startup { public void ConfigureServices(IServiceCollection services) { - services.AddDbContext(); - services.AddCap(x => { - x.UseEntityFramework(); - x.UseKafka("192.168.2.215:9092"); + x.UseMySql("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"); + x.UseKafka("192.168.10.110:9092"); x.UseDashboard(); + //x.UseDiscovery(d => //{ // d.DiscoveryServerHostName = "localhost"; @@ -26,11 +21,12 @@ namespace Sample.Kafka.SqlServer // d.CurrentNodePort = 5820; // d.NodeName = "CAP 2号节点"; //}); - }).AddMessagePacker(); + }); + services.AddMvc(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app) { app.UseMvc(); diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 0b394cb..7b30772 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Threading.Tasks; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; diff --git a/samples/Sample.RabbitMQ.MySql/Properties/launchSettings.json b/samples/Sample.RabbitMQ.MySql/Properties/launchSettings.json new file mode 100644 index 0000000..369b41c --- /dev/null +++ b/samples/Sample.RabbitMQ.MySql/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57171/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "cap", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Sample.RabbitMQ.MySql": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "cap", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:57173/" + } + } +} \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.MySql/Startup.cs b/samples/Sample.RabbitMQ.MySql/Startup.cs index 12a2986..6525770 100644 --- a/samples/Sample.RabbitMQ.MySql/Startup.cs +++ b/samples/Sample.RabbitMQ.MySql/Startup.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/samples/Sample.RabbitMQ.PostgreSql/AppDbContext.cs b/samples/Sample.RabbitMQ.PostgreSql/AppDbContext.cs deleted file mode 100644 index d4bde28..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/AppDbContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; - -namespace Sample.RabbitMQ.PostgreSql -{ - public class AppDbContext : DbContext - { - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseNpgsql("Server=localhost;Database=Sample.RabbitMQ.PostgreSql;UserId=postgres;Password=123123;"); - } - } -} diff --git a/samples/Sample.RabbitMQ.PostgreSql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.PostgreSql/Controllers/ValuesController.cs deleted file mode 100644 index 10ea6d1..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/Controllers/ValuesController.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks; -using DotNetCore.CAP; -using Microsoft.AspNetCore.Mvc; - -namespace Sample.RabbitMQ.PostgreSql.Controllers -{ - [Route("api/[controller]")] - public class ValuesController : Controller - { - private readonly AppDbContext _dbContext; - private readonly ICapPublisher _capBus; - - public ValuesController(AppDbContext dbContext, ICapPublisher capPublisher) - { - _dbContext = dbContext; - _capBus = capPublisher; - } - - [Route("~/publish")] - public IActionResult PublishMessage() - { - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); - - return Ok(); - } - - - [Route("~/publish2")] - public IActionResult PublishMessage2() - { - _capBus.Publish("sample.kafka.sqlserver4", DateTime.Now); - - return Ok(); - } - - [Route("~/publishWithTrans")] - public async Task PublishMessageWithTransaction() - { - using (var trans = await _dbContext.Database.BeginTransactionAsync()) - { - await _capBus.PublishAsync("sample.kafka.sqlserver", ""); - - trans.Commit(); - } - return Ok(); - } - - [NonAction] - [CapSubscribe("sample.rabbitmq.mysql")] - public void ReceiveMessage() - { - Console.WriteLine("[sample.rabbitmq.mysql] message received"); - Debug.WriteLine("[sample.rabbitmq.mysql] message received"); - } - } -} diff --git a/samples/Sample.RabbitMQ.PostgreSql/Program.cs b/samples/Sample.RabbitMQ.PostgreSql/Program.cs deleted file mode 100644 index 3cb0755..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/Program.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Sample.RabbitMQ.PostgreSql -{ - public class Program - { - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup() - .Build(); - } -} diff --git a/samples/Sample.RabbitMQ.PostgreSql/Sample.RabbitMQ.PostgreSql.csproj b/samples/Sample.RabbitMQ.PostgreSql/Sample.RabbitMQ.PostgreSql.csproj deleted file mode 100644 index f2fe0c5..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/Sample.RabbitMQ.PostgreSql.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netcoreapp2.0 - - - - - - - - - - - - - - diff --git a/samples/Sample.RabbitMQ.PostgreSql/Startup.cs b/samples/Sample.RabbitMQ.PostgreSql/Startup.cs deleted file mode 100644 index b0477c0..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/Startup.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; - -namespace Sample.RabbitMQ.PostgreSql -{ - public class Startup - { - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(); - services.AddCap(x => - { - x.UseEntityFramework(); - x.UseRabbitMQ("localhost"); - x.UseDashboard(); - x.UseDiscovery(d => - { - d.DiscoveryServerHostName = "localhost"; - d.DiscoveryServerPort = 8500; - d.CurrentNodeHostName = "localhost"; - d.CurrentNodePort = 5800; - d.NodeName = "CAP 一号节点"; - }); - }); - services.AddMvc(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - app.UseMvc(); - - app.UseCap(); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs b/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs deleted file mode 100644 index 1ba1fdb..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Sample.RabbitMQ.SqlServer.Controllers; - -namespace Sample.RabbitMQ.SqlServer -{ - public class AppDbContext : DbContext - { - - public DbSet Persons { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - //optionsBuilder.UseSqlServer("Server=192.168.2.206;Initial Catalog=TestCap;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"); - optionsBuilder.UseSqlServer("Server=DESKTOP-M9R8T31;Initial Catalog=Sample.Kafka.SqlServer;User Id=sa;Password=P@ssw0rd;MultipleActiveResultSets=True"); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs deleted file mode 100644 index 6c0ccef..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using DotNetCore.CAP; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Sample.RabbitMQ.SqlServer.Controllers -{ - public class Person - { - public int Id { get; set; } - public string Name { get; set; } - public int Age { get; set; } - - public override string ToString() - { - return "Name:" + Name + ";Age:" + Age; - } - } - - - [Route("api/[controller]")] - public class ValuesController : Controller, ICapSubscribe - { - private readonly ICapPublisher _capBus; - private readonly AppDbContext _dbContext; - - public ValuesController(ICapPublisher producer, AppDbContext dbContext) - { - _capBus = producer; - _dbContext = dbContext; - } - - [Route("~/publish")] - public IActionResult PublishMessage() - { - - _capBus.Publish("sample.rabbitmq.sqlserver.order.check", DateTime.Now); - - //var person = new Person - //{ - // Name = "杨晓东", - // Age = 11, - // Id = 23 - //}; - //_capBus.Publish("sample.rabbitmq.mysql33333", person); - - return Ok(); - } - - [Route("~/publishWithTrans")] - public async Task PublishMessageWithTransaction() - { - using (var trans = await _dbContext.Database.BeginTransactionAsync()) - { - await _capBus.PublishAsync("sample.rabbitmq.mysql", ""); - - trans.Commit(); - } - return Ok(); - } - - [CapSubscribe("sample.rabbitmq.mysql33333",Group ="Test.Group")] - public void KafkaTest22(Person person) - { - var aa = _dbContext.Database; - - _dbContext.Dispose(); - - Console.WriteLine("[sample.kafka.sqlserver] message received " + person.ToString()); - Debug.WriteLine("[sample.kafka.sqlserver] message received " + person.ToString()); - } - - //[CapSubscribe("sample.rabbitmq.mysql22222")] - //public void KafkaTest22(DateTime time) - //{ - // Console.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - // Debug.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - //} - - [CapSubscribe("sample.rabbitmq.mysql22222")] - public async Task KafkaTest33(DateTime time) - { - Console.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - Debug.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - return await Task.FromResult(time); - } - - [NonAction] - [CapSubscribe("sample.kafka.sqlserver3")] - [CapSubscribe("sample.kafka.sqlserver4")] - public void KafkaTest() - { - Console.WriteLine("[sample.kafka.sqlserver] message received"); - Debug.WriteLine("[sample.kafka.sqlserver] message received"); - } - } -} \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.Designer.cs b/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.Designer.cs deleted file mode 100644 index 3d8122b..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using Sample.RabbitMQ.SqlServer; -using System; - -namespace Sample.RabbitMQ.SqlServer.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20170824130007_AddPersons")] - partial class AddPersons - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Sample.RabbitMQ.SqlServer.Controllers.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Age"); - - b.Property("Name"); - - b.HasKey("Id"); - - b.ToTable("Persons"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.cs b/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.cs deleted file mode 100644 index 2fc048b..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; - -namespace Sample.RabbitMQ.SqlServer.Migrations -{ - public partial class AddPersons : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Persons", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), - Age = table.Column(type: "int", nullable: false), - Name = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Persons", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Persons"); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Migrations/AppDbContextModelSnapshot.cs b/samples/Sample.RabbitMQ.SqlServer/Migrations/AppDbContextModelSnapshot.cs deleted file mode 100644 index 0a20ac3..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Migrations/AppDbContextModelSnapshot.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using Sample.RabbitMQ.SqlServer; -using System; - -namespace Sample.RabbitMQ.SqlServer.Migrations -{ - [DbContext(typeof(AppDbContext))] - partial class AppDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Sample.RabbitMQ.SqlServer.Controllers.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Age"); - - b.Property("Name"); - - b.HasKey("Id"); - - b.ToTable("Persons"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Program.cs b/samples/Sample.RabbitMQ.SqlServer/Program.cs deleted file mode 100644 index ebbd50e..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Program.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.IO; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; - -namespace Sample.RabbitMQ.SqlServer -{ - public class Program - { - - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseUrls("http://*:5800") - .UseStartup() - .Build(); - - } -} \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj b/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj deleted file mode 100644 index a32da56..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp2.0 - Sample.RabbitMQ.SqlServer - - - - - - - - - - - - - - - diff --git a/samples/Sample.RabbitMQ.SqlServer/Services/ICmsService.cs b/samples/Sample.RabbitMQ.SqlServer/Services/ICmsService.cs deleted file mode 100644 index 5119cce..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Services/ICmsService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DotNetCore.CAP; - -namespace Sample.RabbitMQ.SqlServer.Services -{ - public interface ICmsService - { - void Add(); - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Services/IOrderService.cs b/samples/Sample.RabbitMQ.SqlServer/Services/IOrderService.cs deleted file mode 100644 index 9f8e3ad..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Services/IOrderService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Sample.RabbitMQ.SqlServer.Services -{ - public interface IOrderService - { - void Check(); - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Services/Impl/CmsService.cs b/samples/Sample.RabbitMQ.SqlServer/Services/Impl/CmsService.cs deleted file mode 100644 index b8c7800..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Services/Impl/CmsService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DotNetCore.CAP; - -namespace Sample.RabbitMQ.SqlServer.Services.Impl -{ - public class CmsService : ICmsService, ICapSubscribe - { - public void Add() - { - throw new NotImplementedException(); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Services/Impl/OrderService.cs b/samples/Sample.RabbitMQ.SqlServer/Services/Impl/OrderService.cs deleted file mode 100644 index 81d0eb7..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Services/Impl/OrderService.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DotNetCore.CAP; - -namespace Sample.RabbitMQ.SqlServer.Services.Impl -{ - public class OrderService : IOrderService, ICapSubscribe - { - [CapSubscribe("sample.rabbitmq.sqlserver.order.check")] - public void Check() - { - Console.WriteLine("out"); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Startup.cs b/samples/Sample.RabbitMQ.SqlServer/Startup.cs deleted file mode 100644 index 4821b97..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Startup.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Sample.RabbitMQ.SqlServer.Services; -using Sample.RabbitMQ.SqlServer.Services.Impl; - -namespace Sample.RabbitMQ.SqlServer -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(); - - services.AddScoped(); - services.AddTransient(); - - services.AddCap(x => - { - x.UseEntityFramework(); - x.UseRabbitMQ("localhost"); - x.UseDashboard(); - x.UseDiscovery(d => - { - d.DiscoveryServerHostName = "localhost"; - d.DiscoveryServerPort = 8500; - - d.CurrentNodeHostName = "192.168.1.11"; - d.CurrentNodePort = 5800; - d.NodeName = "CAP Node Windows"; - d.NodeId = 1; - }); - }); - - services.AddMvc(); - } - - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) - { - loggerFactory.AddConsole(); - loggerFactory.AddDebug(); - - app.UseMvc(); - - app.UseCap(); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs b/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs index b2e2129..04493f5 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Kafka; using Microsoft.Extensions.DependencyInjection; @@ -23,8 +26,8 @@ namespace DotNetCore.CAP services.AddSingleton(kafkaOptions); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); } } diff --git a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs index 1b1f687..629963b 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -45,16 +48,19 @@ namespace DotNetCore.CAP if (_kafkaConfig == null) { if (string.IsNullOrWhiteSpace(Servers)) + { throw new ArgumentNullException(nameof(Servers)); + } MainConfig["bootstrap.servers"] = Servers; MainConfig["queue.buffering.max.ms"] = "10"; MainConfig["socket.blocking.max.ms"] = "10"; MainConfig["enable.auto.commit"] = "false"; MainConfig["log.connection.close"] = "false"; - + _kafkaConfig = MainConfig.AsEnumerable(); } + return _kafkaConfig; } } diff --git a/src/DotNetCore.CAP.Kafka/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.Kafka/CAP.Options.Extensions.cs index be980d6..e32278e 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP; // ReSharper disable once CheckNamespace @@ -24,7 +27,10 @@ namespace Microsoft.Extensions.DependencyInjection /// public static CapOptions UseKafka(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new KafkaCapOptionsExtension(configure)); diff --git a/src/DotNetCore.CAP.Kafka/CAP.SubscribeAttribute.cs b/src/DotNetCore.CAP.Kafka/CAP.SubscribeAttribute.cs index 161b073..ad4021e 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.SubscribeAttribute.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.SubscribeAttribute.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Abstractions; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Abstractions; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.Kafka/ConnectionPool.cs b/src/DotNetCore.CAP.Kafka/ConnectionPool.cs index 3746439..6879942 100644 --- a/src/DotNetCore.CAP.Kafka/ConnectionPool.cs +++ b/src/DotNetCore.CAP.Kafka/ConnectionPool.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Concurrent; using System.Diagnostics; using System.Threading; @@ -35,7 +38,9 @@ namespace DotNetCore.CAP.Kafka _maxSize = 0; while (_pool.TryDequeue(out var context)) + { context.Dispose(); + } } private static Func CreateActivator(KafkaOptions options) diff --git a/src/DotNetCore.CAP.Kafka/IConnectionPool.cs b/src/DotNetCore.CAP.Kafka/IConnectionPool.cs index 36d5912..7fb01a5 100644 --- a/src/DotNetCore.CAP.Kafka/IConnectionPool.cs +++ b/src/DotNetCore.CAP.Kafka/IConnectionPool.cs @@ -1,4 +1,6 @@ - +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using Confluent.Kafka; namespace DotNetCore.CAP.Kafka diff --git a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs new file mode 100644 index 0000000..cda5755 --- /dev/null +++ b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs @@ -0,0 +1,65 @@ +// 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.Diagnostics; +using System.Text; +using System.Threading.Tasks; +using DotNetCore.CAP.Internal; +using DotNetCore.CAP.Processor.States; +using Microsoft.Extensions.Logging; + +namespace DotNetCore.CAP.Kafka +{ + internal class KafkaPublishMessageSender : BasePublishMessageSender + { + private readonly ConnectionPool _connectionPool; + private readonly ILogger _logger; + + public KafkaPublishMessageSender( + CapOptions options, IStateChanger stateChanger, IStorageConnection connection, + ConnectionPool connectionPool, ILogger logger) + : base(logger, options, connection, stateChanger) + { + _logger = logger; + _connectionPool = connectionPool; + } + + public override async Task PublishAsync(string keyName, string content) + { + var producer = _connectionPool.Rent(); + + try + { + var contentBytes = Encoding.UTF8.GetBytes(content); + var message = await producer.ProduceAsync(keyName, null, contentBytes); + + if (message.Error.HasError) + { + return OperateResult.Failed(new OperateError + { + Code = message.Error.Code.ToString(), + Description = message.Error.Reason + }); + } + + _logger.LogDebug($"kafka topic message [{keyName}] has been published."); + + return OperateResult.Success; + } + catch (Exception ex) + { + var wapperEx = new PublisherSentFailedException(ex.Message, ex); + return OperateResult.Failed(wapperEx); + } + finally + { + var returned = _connectionPool.Return(producer); + if (!returned) + { + producer.Dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs index 241870e..ccce448 100644 --- a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs +++ b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Text; using System.Threading; @@ -29,10 +32,14 @@ namespace DotNetCore.CAP.Kafka public void Subscribe(IEnumerable topics) { if (topics == null) + { throw new ArgumentNullException(nameof(topics)); + } if (_consumerClient == null) + { InitKafkaClient(); + } _consumerClient.Subscribe(topics); } @@ -44,6 +51,7 @@ namespace DotNetCore.CAP.Kafka cancellationToken.ThrowIfCancellationRequested(); _consumerClient.Poll(timeout); } + // ReSharper disable once FunctionNeverReturns } diff --git a/src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs b/src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs index 8bda50a..54ddc5b 100644 --- a/src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs +++ b/src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Kafka +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Kafka { internal sealed class KafkaConsumerClientFactory : IConsumerClientFactory { diff --git a/src/DotNetCore.CAP.Kafka/PublishQueueExecutor.cs b/src/DotNetCore.CAP.Kafka/PublishQueueExecutor.cs deleted file mode 100644 index 6bbda6b..0000000 --- a/src/DotNetCore.CAP.Kafka/PublishQueueExecutor.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP.Kafka -{ - internal class PublishQueueExecutor : BasePublishQueueExecutor - { - private readonly ConnectionPool _connectionPool; - private readonly ILogger _logger; - - public PublishQueueExecutor( - CapOptions options, - IStateChanger stateChanger, - ConnectionPool connectionPool, - ILogger logger) - : base(options, stateChanger, logger) - { - _logger = logger; - _connectionPool = connectionPool; - } - - public override async Task PublishAsync(string keyName, string content) - { - var producer = _connectionPool.Rent(); - try - { - var contentBytes = Encoding.UTF8.GetBytes(content); - - var message = await producer.ProduceAsync(keyName, null, contentBytes); - - if (!message.Error.HasError) - { - _logger.LogDebug($"kafka topic message [{keyName}] has been published."); - - return OperateResult.Success; - } - return OperateResult.Failed(new OperateError - { - Code = message.Error.Code.ToString(), - Description = message.Error.Reason - }); - - } - catch (Exception ex) - { - _logger.LogError(ex, - $"An error occurred during sending the topic message to kafka. Topic:[{keyName}], Exception: {ex.Message}"); - - return OperateResult.Failed(ex); - } - finally - { - var returned = _connectionPool.Return(producer); - if (!returned) - producer.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs b/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs index 390365b..c2b9983 100644 --- a/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs @@ -1,4 +1,7 @@ -using System; +// 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; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs index a0bf29d..6e757ab 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.MySql; using DotNetCore.CAP.Processor; using Microsoft.EntityFrameworkCore; @@ -41,7 +44,7 @@ namespace DotNetCore.CAP using (var scope = x.CreateScope()) { var provider = scope.ServiceProvider; - var dbContext = (DbContext)provider.GetService(mysqlOptions.DbContextType); + var dbContext = (DbContext) provider.GetService(mysqlOptions.DbContextType); mysqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; return mysqlOptions; } diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs index c75ae6a..bcac400 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs @@ -1,4 +1,6 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + namespace DotNetCore.CAP { public class MySqlOptions : EFOptions diff --git a/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs index 1949c82..f59b75d 100644 --- a/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP; using Microsoft.EntityFrameworkCore; @@ -14,7 +17,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseMySql(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new MySqlCapOptionsExtension(configure)); @@ -31,7 +37,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options, Action configure) where TContext : DbContext { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new MySqlCapOptionsExtension(x => { diff --git a/src/DotNetCore.CAP.MySql/CapPublisher.cs b/src/DotNetCore.CAP.MySql/CapPublisher.cs index 25f5127..662e257 100644 --- a/src/DotNetCore.CAP.MySql/CapPublisher.cs +++ b/src/DotNetCore.CAP.MySql/CapPublisher.cs @@ -1,4 +1,7 @@ -using System; +// 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.Data; using System.Threading.Tasks; using Dapper; @@ -14,27 +17,31 @@ namespace DotNetCore.CAP.MySql public class CapPublisher : CapPublisherBase, ICallbackPublisher { private readonly DbContext _dbContext; - private readonly ILogger _logger; private readonly MySqlOptions _options; - public CapPublisher(IServiceProvider provider, - ILogger logger, + public CapPublisher(ILogger logger, IDispatcher dispatcher, IServiceProvider provider, MySqlOptions options) + : base(logger, dispatcher) { ServiceProvider = provider; _options = options; - _logger = logger; - if (_options.DbContextType == null) return; + if (_options.DbContextType == null) + { + return; + } + IsUsingEF = true; _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); } - public async Task PublishAsync(CapPublishedMessage message) + public async Task PublishCallbackAsync(CapPublishedMessage message) { using (var conn = new MySqlConnection(_options.ConnectionString)) { - await conn.ExecuteAsync(PrepareSql(), message); + var id = await conn.ExecuteScalarAsync(PrepareSql(), message); + message.Id = id; + Enqueue(message); } } @@ -51,23 +58,20 @@ namespace DotNetCore.CAP.MySql dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbTrans = dbContextTransaction.GetDbTransaction(); } + DbTransaction = dbTrans; } - protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - dbConnection.Execute(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("Published Message has been persisted in the database. name:" + message); + return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); } - protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("Published Message has been persisted in the database. name:" + message); + return await dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); } #region private methods @@ -75,7 +79,7 @@ namespace DotNetCore.CAP.MySql private string PrepareSql() { return - $"INSERT INTO `{_options.TableNamePrefix}.published` (`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; + $"INSERT INTO `{_options.TableNamePrefix}.published` (`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST_INSERT_ID()"; } #endregion private methods diff --git a/src/DotNetCore.CAP.MySql/FetchedMessage.cs b/src/DotNetCore.CAP.MySql/FetchedMessage.cs deleted file mode 100644 index a0f3b8e..0000000 --- a/src/DotNetCore.CAP.MySql/FetchedMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.MySql -{ - internal class FetchedMessage - { - public int MessageId { get; set; } - - public MessageType MessageType { get; set; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs index a59d9e2..fd892cb 100644 --- a/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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; using Dapper; using DotNetCore.CAP.Processor; diff --git a/src/DotNetCore.CAP.MySql/MySqlFetchedMessage.cs b/src/DotNetCore.CAP.MySql/MySqlFetchedMessage.cs deleted file mode 100644 index c4ab741..0000000 --- a/src/DotNetCore.CAP.MySql/MySqlFetchedMessage.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Dapper; -using DotNetCore.CAP.Models; -using MySql.Data.MySqlClient; - -namespace DotNetCore.CAP.MySql -{ - public class MySqlFetchedMessage : IFetchedMessage - { - private readonly MySqlOptions _options; - private readonly string _processId; - - public MySqlFetchedMessage(int messageId, MessageType type, string processId, MySqlOptions options) - { - MessageId = messageId; - MessageType = type; - - _processId = processId; - _options = options; - } - - public int MessageId { get; } - - public MessageType MessageType { get; } - - public void RemoveFromQueue() - { - using (var connection = new MySqlConnection(_options.ConnectionString)) - { - connection.Execute($"DELETE FROM `{_options.TableNamePrefix}.queue` WHERE `ProcessId`=@ProcessId" - , new { ProcessId = _processId }); - } - } - - public void Requeue() - { - using (var connection = new MySqlConnection(_options.ConnectionString)) - { - connection.Execute($"UPDATE `{_options.TableNamePrefix}.queue` SET `ProcessId`=NULL WHERE `ProcessId`=@ProcessId" - , new { ProcessId = _processId }); - } - } - - public void Dispose() - { - // ignored - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs b/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs index 49fd270..6fd0888 100644 --- a/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs +++ b/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Data; using System.Linq; @@ -28,9 +31,7 @@ set transaction isolation level read committed; select count(Id) from `{0}.published` where StatusName = N'Succeeded'; select count(Id) from `{0}.received` where StatusName = N'Succeeded'; select count(Id) from `{0}.published` where StatusName = N'Failed'; -select count(Id) from `{0}.received` where StatusName = N'Failed'; -select count(Id) from `{0}.published` where StatusName in (N'Processing',N'Scheduled',N'Enqueued'); -select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Scheduled',N'Enqueued');", _prefix); +select count(Id) from `{0}.received` where StatusName = N'Failed';", _prefix); var statistics = UseConnection(connection => { @@ -42,10 +43,8 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched stats.PublishedFailed = multi.ReadSingle(); stats.ReceivedFailed = multi.ReadSingle(); - - stats.PublishedProcessing = multi.ReadSingle(); - stats.ReceivedProcessing = multi.ReadSingle(); } + return stats; }); return statistics; @@ -70,17 +69,24 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched var tableName = queryDto.MessageType == MessageType.Publish ? "published" : "received"; var where = string.Empty; if (!string.IsNullOrEmpty(queryDto.StatusName)) - if (string.Equals(queryDto.StatusName, StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - where += " and StatusName in (N'Processing',N'Scheduled',N'Enqueued')"; - else - where += " and StatusName=@StatusName"; + { + where += " and StatusName=@StatusName"; + } + if (!string.IsNullOrEmpty(queryDto.Name)) + { where += " and Name=@Name"; + } + if (!string.IsNullOrEmpty(queryDto.Group)) + { where += " and Group=@Group"; + } + if (!string.IsNullOrEmpty(queryDto.Content)) + { where += " and Content like '%@Content%'"; + } var sqlQuery = $"select * from `{_prefix}.{tableName}` where 1=1 {where} order by Added desc limit @Limit offset @Offset"; @@ -101,11 +107,6 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Failed)); } - public int PublishedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Processing)); - } - public int PublishedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Succeeded)); @@ -116,11 +117,6 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Failed)); } - public int ReceivedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Processing)); - } - public int ReceivedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Succeeded)); @@ -128,9 +124,7 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched private int GetNumberOfMessage(IDbConnection connection, string tableName, string statusName) { - var sqlQuery = statusName == StatusName.Processing - ? $"select count(Id) from `{_prefix}.{tableName}` where StatusName in (N'Processing',N'Scheduled',N'Enqueued')" - : $"select count(Id) from `{_prefix}.{tableName}` where StatusName = @state"; + var sqlQuery = $"select count(Id) from `{_prefix}.{tableName}` where StatusName = @state"; var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; @@ -179,7 +173,12 @@ select aggr.* from ( .ToDictionary(x => (string) x.Key, x => (int) x.Count); foreach (var key in keyMaps.Keys) - if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0); + { + if (!valuesMap.ContainsKey(key)) + { + valuesMap.Add(key, 0); + } + } var result = new Dictionary(); for (var i = 0; i < keyMaps.Count; i++) diff --git a/src/DotNetCore.CAP.MySql/MySqlStorage.cs b/src/DotNetCore.CAP.MySql/MySqlStorage.cs index 3755b0d..98fa0de 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorage.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorage.cs @@ -1,3 +1,6 @@ +// 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.Data; using System.Threading; @@ -11,10 +14,10 @@ namespace DotNetCore.CAP.MySql { public class MySqlStorage : IStorage { + private readonly CapOptions _capOptions; private readonly IDbConnection _existingConnection = null; private readonly ILogger _logger; private readonly MySqlOptions _options; - private readonly CapOptions _capOptions; public MySqlStorage(ILogger logger, MySqlOptions options, @@ -37,7 +40,11 @@ namespace DotNetCore.CAP.MySql public async Task InitializeAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) return; + if (cancellationToken.IsCancellationRequested) + { + return; + } + var sql = CreateDbTablesScript(_options.TableNamePrefix); using (var connection = new MySqlConnection(_options.ConnectionString)) { @@ -51,11 +58,7 @@ namespace DotNetCore.CAP.MySql { var batchSql = $@" -CREATE TABLE IF NOT EXISTS `{prefix}.queue` ( - `MessageId` int(11) NOT NULL, - `MessageType` tinyint(4) NOT NULL, - `ProcessId` varchar(50) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +DROP TABLE IF EXISTS `{prefix}.queue`; CREATE TABLE IF NOT EXISTS `{prefix}.received` ( `Id` int(127) NOT NULL AUTO_INCREMENT, @@ -102,7 +105,9 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( var connection = _existingConnection ?? new MySqlConnection(_options.ConnectionString); if (connection.State == ConnectionState.Closed) + { connection.Open(); + } return connection; } @@ -115,7 +120,9 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( internal void ReleaseConnection(IDbConnection connection) { if (connection != null && !IsExistingConnection(connection)) + { connection.Dispose(); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index 7a59bde..691717d 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -1,3 +1,6 @@ +// 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.Collections.Generic; using System.Threading.Tasks; @@ -13,8 +16,6 @@ namespace DotNetCore.CAP.MySql private readonly CapOptions _capOptions; private readonly string _prefix; - private const string DateTimeMaxValue = "9999-12-31 23:59:59"; - public MySqlStorageConnection(MySqlOptions options, CapOptions capOptions) { _capOptions = capOptions; @@ -39,51 +40,32 @@ namespace DotNetCore.CAP.MySql } } - public Task FetchNextMessageAsync() + public async Task> GetPublishedMessagesOfNeedRetry() { - var processId = ObjectId.GenerateNewStringId(); - var sql = $@" -UPDATE `{_prefix}.queue` SET `ProcessId`=@ProcessId WHERE `ProcessId` IS NULL LIMIT 1; -SELECT `MessageId`,`MessageType` FROM `{_prefix}.queue` WHERE `ProcessId`=@ProcessId;"; - - return FetchNextMessageCoreAsync(sql, processId); - } - - public async Task GetNextPublishedMessageToBeEnqueuedAsync() - { - var sql = $@" -UPDATE `{_prefix}.published` SET Id=LAST_INSERT_ID(Id),ExpiresAt='{DateTimeMaxValue}' WHERE ExpiresAt IS NULL AND `StatusName` = '{StatusName.Scheduled}' LIMIT 1; -SELECT * FROM `{_prefix}.published` WHERE Id=LAST_INSERT_ID();"; + var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var sql = + $"SELECT * FROM `{_prefix}.published` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `Added`<'{fourMinsAgo}' AND (`StatusName` = '{StatusName.Failed}' OR `StatusName` = '{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new MySqlConnection(Options.ConnectionString)) { - connection.Open(); - connection.Execute("SELECT LAST_INSERT_ID(0)"); - return await connection.QueryFirstOrDefaultAsync(sql); + return await connection.QueryAsync(sql); } } - public async Task> GetFailedPublishedMessages() + public async Task StoreReceivedMessageAsync(CapReceivedMessage message) { - var sql = $"SELECT * FROM `{_prefix}.published` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `StatusName` = '{StatusName.Failed}' LIMIT 200;"; - - using (var connection = new MySqlConnection(Options.ConnectionString)) + if (message == null) { - return await connection.QueryAsync(sql); + throw new ArgumentNullException(nameof(message)); } - } - - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); var sql = $@" INSERT INTO `{_prefix}.received`(`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) -VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; +VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST_INSERT_ID();"; using (var connection = new MySqlConnection(Options.ConnectionString)) { - await connection.ExecuteAsync(sql, message); + return await connection.ExecuteScalarAsync(sql, message); } } @@ -96,23 +78,11 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } } - public async Task GetNextReceivedMessageToBeEnqueuedAsync() + public async Task> GetReceivedMessagesOfNeedRetry() { - var sql = $@" -UPDATE `{_prefix}.received` SET Id=LAST_INSERT_ID(Id),ExpiresAt='{DateTimeMaxValue}' WHERE ExpiresAt IS NULL AND `StatusName` = '{StatusName.Scheduled}' LIMIT 1; -SELECT * FROM `{_prefix}.received` WHERE Id=LAST_INSERT_ID();"; - - using (var connection = new MySqlConnection(Options.ConnectionString)) - { - connection.Open(); - connection.Execute("SELECT LAST_INSERT_ID(0)"); - return await connection.QueryFirstOrDefaultAsync(sql); - } - } - - public async Task> GetFailedReceivedMessages() - { - var sql = $"SELECT * FROM `{_prefix}.received` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `StatusName` = '{StatusName.Failed}' LIMIT 200;"; + var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var sql = + $"SELECT * FROM `{_prefix}.received` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `Added`<'{fourMinsAgo}' AND (`StatusName` = '{StatusName.Failed}' OR `StatusName` = '{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new MySqlConnection(Options.ConnectionString)) { return await connection.QueryAsync(sql); @@ -141,20 +111,6 @@ SELECT * FROM `{_prefix}.received` WHERE Id=LAST_INSERT_ID();"; } } - private async Task FetchNextMessageCoreAsync(string sql, string processId) - { - FetchedMessage fetchedMessage; - using (var connection = new MySqlConnection(Options.ConnectionString)) - { - fetchedMessage = await connection.QuerySingleOrDefaultAsync(sql, new { ProcessId = processId }); - } - - if (fetchedMessage == null) - return null; - - return new MySqlFetchedMessage(fetchedMessage.MessageId, fetchedMessage.MessageType, processId, Options); - } - public void Dispose() { } diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs b/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs index 0dbb424..7121b09 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs @@ -1,4 +1,7 @@ -using System; +// 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.Data; using System.Threading.Tasks; using Dapper; @@ -11,7 +14,7 @@ namespace DotNetCore.CAP.MySql { private readonly IDbConnection _dbConnection; - private readonly IDbTransaction _dbTransaction; + //private readonly IDbTransaction _dbTransaction; private readonly string _prefix; public MySqlStorageTransaction(MySqlStorageConnection connection) @@ -20,55 +23,45 @@ namespace DotNetCore.CAP.MySql _prefix = options.TableNamePrefix; _dbConnection = new MySqlConnection(options.ConnectionString); - _dbConnection.Open(); - _dbTransaction = _dbConnection.BeginTransaction(IsolationLevel.ReadCommitted); + // _dbConnection.Open(); for performance + // _dbTransaction = _dbConnection.BeginTransaction(IsolationLevel.ReadCommitted); } public void UpdateMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"UPDATE `{_prefix}.published` SET `Retries` = @Retries,`Content`= @Content,`ExpiresAt` = @ExpiresAt,`StatusName`=@StatusName WHERE `Id`=@Id;"; - _dbConnection.Execute(sql, message, _dbTransaction); + _dbConnection.Execute(sql, message); } public void UpdateMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"UPDATE `{_prefix}.received` SET `Retries` = @Retries,`Content`= @Content,`ExpiresAt` = @ExpiresAt,`StatusName`=@StatusName WHERE `Id`=@Id;"; - _dbConnection.Execute(sql, message, _dbTransaction); - } - - public void EnqueueMessage(CapPublishedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); - - var sql = $"INSERT INTO `{_prefix}.queue`(`MessageId`,`MessageType`) values(@MessageId,@MessageType);"; - _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish}, - _dbTransaction); - } - - public void EnqueueMessage(CapReceivedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); - - var sql = $"INSERT INTO `{_prefix}.queue`(`MessageId`,`MessageType`) values(@MessageId,@MessageType);"; - _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe}, - _dbTransaction); + _dbConnection.Execute(sql, message); } public Task CommitAsync() { - _dbTransaction.Commit(); + _dbConnection.Close(); + _dbConnection.Dispose(); + //_dbTransaction.Commit(); return Task.CompletedTask; } public void Dispose() { - _dbTransaction.Dispose(); + //_dbTransaction.Dispose(); _dbConnection.Dispose(); } } diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs index 294ed6a..883d297 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs @@ -1,4 +1,7 @@ -using System; +// 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; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs index e001c05..08ffb90 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP; using Microsoft.EntityFrameworkCore; @@ -14,7 +17,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UsePostgreSql(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new PostgreSqlCapOptionsExtension(configure)); @@ -30,7 +36,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options, Action configure) where TContext : DbContext { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new PostgreSqlCapOptionsExtension(x => { diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs index f1e94f8..62af675 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.PostgreSql; using DotNetCore.CAP.Processor; using Microsoft.EntityFrameworkCore; @@ -40,7 +43,7 @@ namespace DotNetCore.CAP using (var scope = x.CreateScope()) { var provider = scope.ServiceProvider; - var dbContext = (DbContext)provider.GetService(postgreSqlOptions.DbContextType); + var dbContext = (DbContext) provider.GetService(postgreSqlOptions.DbContextType); postgreSqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; return postgreSqlOptions; } diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs index 7368b94..9f52154 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs @@ -1,4 +1,5 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs b/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs index bbaa86e..e299dc4 100644 --- a/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs +++ b/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs @@ -1,4 +1,7 @@ -using System; +// 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.Data; using System.Threading.Tasks; using Dapper; @@ -14,16 +17,14 @@ namespace DotNetCore.CAP.PostgreSql public class CapPublisher : CapPublisherBase, ICallbackPublisher { private readonly DbContext _dbContext; - private readonly ILogger _logger; private readonly PostgreSqlOptions _options; - public CapPublisher(IServiceProvider provider, - ILogger logger, - PostgreSqlOptions options) + public CapPublisher(ILogger logger, IDispatcher dispatcher, + IServiceProvider provider, PostgreSqlOptions options) + : base(logger, dispatcher) { ServiceProvider = provider; _options = options; - _logger = logger; if (_options.DbContextType != null) { @@ -31,12 +32,14 @@ namespace DotNetCore.CAP.PostgreSql _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); } } - - public async Task PublishAsync(CapPublishedMessage message) + + public async Task PublishCallbackAsync(CapPublishedMessage message) { using (var conn = new NpgsqlConnection(_options.ConnectionString)) { - await conn.ExecuteAsync(PrepareSql(), message); + var id = await conn.ExecuteScalarAsync(PrepareSql(), message); + message.Id = id; + Enqueue(message); } } @@ -53,23 +56,20 @@ namespace DotNetCore.CAP.PostgreSql dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbTrans = dbContextTransaction.GetDbTransaction(); } + DbTransaction = dbTrans; } - protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - dbConnection.Execute(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("Published Message has been persisted in the database. name:" + message); + return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); } - protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("Published Message has been persisted in the database. name:" + message); + return dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); } #region private methods @@ -77,7 +77,7 @@ namespace DotNetCore.CAP.PostgreSql private string PrepareSql() { return - $"INSERT INTO \"{_options.Schema}\".\"published\" (\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; + $"INSERT INTO \"{_options.Schema}\".\"published\" (\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING \"Id\";"; } #endregion private methods diff --git a/src/DotNetCore.CAP.PostgreSql/FetchedMessage.cs b/src/DotNetCore.CAP.PostgreSql/FetchedMessage.cs deleted file mode 100644 index 4abd0f3..0000000 --- a/src/DotNetCore.CAP.PostgreSql/FetchedMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.PostgreSql -{ - internal class FetchedMessage - { - public int MessageId { get; set; } - - public MessageType MessageType { get; set; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs index 256e14a..ef606eb 100644 --- a/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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; using Dapper; using DotNetCore.CAP.Processor; diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlFetchedMessage.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlFetchedMessage.cs deleted file mode 100644 index 943db78..0000000 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlFetchedMessage.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Data; -using System.Threading; -using Dapper; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.PostgreSql -{ - public class PostgreSqlFetchedMessage : IFetchedMessage - { - private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); - private readonly IDbConnection _connection; - private readonly object _lockObject = new object(); - private readonly Timer _timer; - private readonly IDbTransaction _transaction; - - public PostgreSqlFetchedMessage(int messageId, - MessageType type, - IDbConnection connection, - IDbTransaction transaction) - { - MessageId = messageId; - MessageType = type; - _connection = connection; - _transaction = transaction; - _timer = new Timer(ExecuteKeepAliveQuery, null, KeepAliveInterval, KeepAliveInterval); - } - - public int MessageId { get; } - - public MessageType MessageType { get; } - - public void RemoveFromQueue() - { - lock (_lockObject) - { - _transaction.Commit(); - } - } - - public void Requeue() - { - lock (_lockObject) - { - _transaction.Rollback(); - } - } - - public void Dispose() - { - lock (_lockObject) - { - _timer?.Dispose(); - _transaction.Dispose(); - _connection.Dispose(); - } - } - - private void ExecuteKeepAliveQuery(object obj) - { - lock (_lockObject) - { - try - { - _connection?.Execute("SELECT 1", _transaction); - } - catch - { - // ignored - } - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs index df6020a..8fcf9f8 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Data; using System.Linq; @@ -27,9 +30,7 @@ namespace DotNetCore.CAP.PostgreSql select count(""Id"") from ""{0}"".""published"" where ""StatusName"" = N'Succeeded'; select count(""Id"") from ""{0}"".""received"" where ""StatusName"" = N'Succeeded'; select count(""Id"") from ""{0}"".""published"" where ""StatusName"" = N'Failed'; -select count(""Id"") from ""{0}"".""received"" where ""StatusName"" = N'Failed'; -select count(""Id"") from ""{0}"".""published"" where ""StatusName"" in (N'Processing',N'Scheduled',N'Enqueued'); -select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Processing',N'Scheduled',N'Enqueued');", +select count(""Id"") from ""{0}"".""received"" where ""StatusName"" = N'Failed';", _options.Schema); var statistics = UseConnection(connection => @@ -42,10 +43,8 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce stats.PublishedFailed = multi.ReadSingle(); stats.ReceivedFailed = multi.ReadSingle(); - - stats.PublishedProcessing = multi.ReadSingle(); - stats.ReceivedProcessing = multi.ReadSingle(); } + return stats; }); return statistics; @@ -57,17 +56,24 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce var where = string.Empty; if (!string.IsNullOrEmpty(queryDto.StatusName)) - if (string.Equals(queryDto.StatusName, StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - where += " and \"StatusName\" in (N'Processing',N'Scheduled',N'Enqueued')"; - else - where += " and Lower(\"StatusName\") = Lower(@StatusName)"; + { + where += " and Lower(\"StatusName\") = Lower(@StatusName)"; + } + if (!string.IsNullOrEmpty(queryDto.Name)) + { where += " and Lower(\"Name\") = Lower(@Name)"; + } + if (!string.IsNullOrEmpty(queryDto.Group)) + { where += " and Lower(\"Group\") = Lower(@Group)"; + } + if (!string.IsNullOrEmpty(queryDto.Content)) + { where += " and \"Content\" ILike '%@Content%'"; + } var sqlQuery = $"select * from \"{_options.Schema}\".\"{tableName}\" where 1=1 {where} order by \"Added\" desc offset @Offset limit @Limit"; @@ -88,11 +94,6 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Failed)); } - public int PublishedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Processing)); - } - public int PublishedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Succeeded)); @@ -103,11 +104,6 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Failed)); } - public int ReceivedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Processing)); - } - public int ReceivedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Succeeded)); @@ -129,11 +125,10 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce private int GetNumberOfMessage(IDbConnection connection, string tableName, string statusName) { - var sqlQuery = statusName == StatusName.Processing - ? $"select count(\"Id\") from \"{_options.Schema}\".\"{tableName}\" where \"StatusName\" in (N'Processing',N'Scheduled',N'Enqueued')" - : $"select count(\"Id\") from \"{_options.Schema}\".\"{tableName}\" where Lower(\"StatusName\") = Lower(@state)"; + var sqlQuery = + $"select count(\"Id\") from \"{_options.Schema}\".\"{tableName}\" where Lower(\"StatusName\") = Lower(@state)"; - var count = connection.ExecuteScalar(sqlQuery, new { state = statusName }); + var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; } @@ -175,12 +170,17 @@ with aggr as ( ) select ""Key"",""Count"" from aggr where ""Key""= Any(@keys);"; - var valuesMap = connection.Query(sqlQuery,new { keys = keyMaps.Keys.ToList(), statusName }) - .ToList() - .ToDictionary(x => (string)x.Key, x => (int)x.Count); + var valuesMap = connection.Query(sqlQuery, new {keys = keyMaps.Keys.ToList(), statusName}) + .ToList() + .ToDictionary(x => (string) x.Key, x => (int) x.Count); foreach (var key in keyMaps.Keys) - if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0); + { + if (!valuesMap.ContainsKey(key)) + { + valuesMap.Add(key, 0); + } + } var result = new Dictionary(); for (var i = 0; i < keyMaps.Count; i++) diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs index e1fd4f0..34c8038 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs @@ -1,3 +1,6 @@ +// 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.Data; using System.Threading; @@ -11,9 +14,9 @@ namespace DotNetCore.CAP.PostgreSql { public class PostgreSqlStorage : IStorage { + private readonly CapOptions _capOptions; private readonly IDbConnection _existingConnection = null; private readonly ILogger _logger; - private readonly CapOptions _capOptions; private readonly PostgreSqlOptions _options; public PostgreSqlStorage(ILogger logger, @@ -37,7 +40,10 @@ namespace DotNetCore.CAP.PostgreSql public async Task InitializeAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) return; + if (cancellationToken.IsCancellationRequested) + { + return; + } var sql = CreateDbTablesScript(_options.Schema); @@ -45,6 +51,7 @@ namespace DotNetCore.CAP.PostgreSql { await connection.ExecuteAsync(sql); } + _logger.LogDebug("Ensuring all create database tables script are applied."); } @@ -68,7 +75,9 @@ namespace DotNetCore.CAP.PostgreSql var connection = _existingConnection ?? new NpgsqlConnection(_options.ConnectionString); if (connection.State == ConnectionState.Closed) + { connection.Open(); + } return connection; } @@ -81,7 +90,9 @@ namespace DotNetCore.CAP.PostgreSql internal void ReleaseConnection(IDbConnection connection) { if (connection != null && !IsExistingConnection(connection)) + { connection.Dispose(); + } } protected virtual string CreateDbTablesScript(string schema) @@ -89,10 +100,7 @@ namespace DotNetCore.CAP.PostgreSql var batchSql = $@" CREATE SCHEMA IF NOT EXISTS ""{schema}""; -CREATE TABLE IF NOT EXISTS ""{schema}"".""queue""( - ""MessageId"" int NOT NULL , - ""MessageType"" int NOT NULL -); +DROP TABLE IF EXISTS ""{schema}"".""queue""; CREATE TABLE IF NOT EXISTS ""{schema}"".""received""( ""Id"" SERIAL PRIMARY KEY NOT NULL, diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs index 4874f8a..763407c 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs @@ -1,6 +1,8 @@ +// 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.Collections.Generic; -using System.Data; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Infrastructure; @@ -36,44 +38,31 @@ namespace DotNetCore.CAP.PostgreSql } } - public Task FetchNextMessageAsync() - { - var sql = $@"DELETE FROM ""{Options.Schema}"".""queue"" WHERE ""MessageId"" = (SELECT ""MessageId"" FROM ""{Options.Schema}"".""queue"" FOR UPDATE SKIP LOCKED LIMIT 1) RETURNING *;"; - return FetchNextMessageCoreAsync(sql); - } - - public async Task GetNextPublishedMessageToBeEnqueuedAsync() + public async Task> GetPublishedMessagesOfNeedRetry() { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); var sql = - $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"StatusName\" = '{StatusName.Scheduled}' FOR UPDATE SKIP LOCKED LIMIT 1;"; + $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) { - return await connection.QueryFirstOrDefaultAsync(sql); + return await connection.QueryAsync(sql); } } - public async Task> GetFailedPublishedMessages() + public async Task StoreReceivedMessageAsync(CapReceivedMessage message) { - var sql = - $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"StatusName\"='{StatusName.Failed}' LIMIT 200;"; - - using (var connection = new NpgsqlConnection(Options.ConnectionString)) + if (message == null) { - return await connection.QueryAsync(sql); + throw new ArgumentNullException(nameof(message)); } - } - - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); var sql = - $"INSERT INTO \"{Options.Schema}\".\"received\"(\"Name\",\"Group\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + $"INSERT INTO \"{Options.Schema}\".\"received\"(\"Name\",\"Group\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING \"Id\";"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) { - await connection.ExecuteAsync(sql, message); + return await connection.ExecuteScalarAsync(sql, message); } } @@ -86,20 +75,11 @@ namespace DotNetCore.CAP.PostgreSql } } - public async Task GetNextReceivedMessageToBeEnqueuedAsync() - { - var sql = - $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"StatusName\" = '{StatusName.Scheduled}' FOR UPDATE SKIP LOCKED LIMIT 1;"; - using (var connection = new NpgsqlConnection(Options.ConnectionString)) - { - return await connection.QueryFirstOrDefaultAsync(sql); - } - } - - public async Task> GetFailedReceivedMessages() + public async Task> GetReceivedMessagesOfNeedRetry() { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); var sql = - $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"StatusName\"='{StatusName.Failed}' LIMIT 200;"; + $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) { return await connection.QueryAsync(sql); @@ -131,35 +111,5 @@ namespace DotNetCore.CAP.PostgreSql return connection.Execute(sql) > 0; } } - - private async Task FetchNextMessageCoreAsync(string sql, object args = null) - { - //here don't use `using` to dispose - var connection = new NpgsqlConnection(Options.ConnectionString); - await connection.OpenAsync(); - var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); - FetchedMessage fetchedMessage; - try - { - fetchedMessage = await connection.QueryFirstOrDefaultAsync(sql, args, transaction); - } - catch (NpgsqlException) - { - transaction.Dispose(); - connection.Dispose(); - throw; - } - - if (fetchedMessage == null) - { - transaction.Rollback(); - transaction.Dispose(); - connection.Dispose(); - return null; - } - - return new PostgreSqlFetchedMessage(fetchedMessage.MessageId, fetchedMessage.MessageType, connection, - transaction); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs index 3cb4931..d64c46a 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs @@ -1,4 +1,7 @@ -using System; +// 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.Data; using System.Threading.Tasks; using Dapper; @@ -26,7 +29,10 @@ namespace DotNetCore.CAP.PostgreSql public void UpdateMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $@"UPDATE ""{ @@ -37,7 +43,10 @@ namespace DotNetCore.CAP.PostgreSql public void UpdateMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $@"UPDATE ""{ @@ -46,9 +55,24 @@ namespace DotNetCore.CAP.PostgreSql _dbConnection.Execute(sql, message, _dbTransaction); } + public Task CommitAsync() + { + _dbTransaction.Commit(); + return Task.CompletedTask; + } + + public void Dispose() + { + _dbTransaction.Dispose(); + _dbConnection.Dispose(); + } + public void EnqueueMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);"; _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish}, @@ -57,23 +81,14 @@ namespace DotNetCore.CAP.PostgreSql public void EnqueueMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);"; _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe}, _dbTransaction); } - - public Task CommitAsync() - { - _dbTransaction.Commit(); - return Task.CompletedTask; - } - - public void Dispose() - { - _dbTransaction.Dispose(); - _dbConnection.Dispose(); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.Options.Extensions.cs index bdf0487..b8bd74f 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP; // ReSharper disable once CheckNamespace @@ -13,7 +16,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseRabbitMQ(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new RabbitMQCapOptionsExtension(configure)); diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs index d76ab91..c600a4c 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs @@ -1,5 +1,7 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ReSharper disable once CheckNamespace namespace DotNetCore.CAP { public class RabbitMQOptions diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbitMQCapOptionsExtension.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbitMQCapOptionsExtension.cs index c2eb13e..a952313 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbitMQCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbitMQCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.RabbitMQ; using Microsoft.Extensions.DependencyInjection; @@ -24,8 +27,8 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.SubscribeAttribute.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.SubscribeAttribute.cs index 811a281..a09a8d6 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.SubscribeAttribute.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.SubscribeAttribute.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Abstractions; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Abstractions; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs b/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs index 3ddedb1..4e028d1 100644 --- a/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs +++ b/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Concurrent; using System.Diagnostics; using System.Threading; @@ -40,7 +43,10 @@ namespace DotNetCore.CAP.RabbitMQ public IConnection GetConnection() { if (_connection != null && _connection.IsOpen) + { return _connection; + } + _connection = _connectionActivator(); _connection.ConnectionShutdown += RabbitMQ_ConnectionShutdown; return _connection; @@ -51,7 +57,9 @@ namespace DotNetCore.CAP.RabbitMQ _maxSize = 0; while (_pool.TryDequeue(out var context)) + { context.Dispose(); + } } private static Func CreateConnection(RabbitMQOptions options) diff --git a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs index c39985b..d7a6b08 100644 --- a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs @@ -1,4 +1,7 @@ -using RabbitMQ.Client; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using RabbitMQ.Client; namespace DotNetCore.CAP.RabbitMQ { diff --git a/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs b/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs index 3f0045e..9afedc7 100644 --- a/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs +++ b/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs @@ -1,21 +1,25 @@ -using System; +// 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.Text; using System.Threading.Tasks; +using DotNetCore.CAP.Internal; using DotNetCore.CAP.Processor.States; using Microsoft.Extensions.Logging; using RabbitMQ.Client; namespace DotNetCore.CAP.RabbitMQ { - internal sealed class PublishQueueExecutor : BasePublishQueueExecutor + internal sealed class RabbitMQPublishMessageSender : BasePublishMessageSender { private readonly IConnectionChannelPool _connectionChannelPool; private readonly ILogger _logger; private readonly RabbitMQOptions _rabbitMQOptions; - public PublishQueueExecutor(ILogger logger, CapOptions options, - RabbitMQOptions rabbitMQOptions, IConnectionChannelPool connectionChannelPool, IStateChanger stateChanger) - : base(options, stateChanger, logger) + public RabbitMQPublishMessageSender(ILogger logger, CapOptions options, RabbitMQOptions rabbitMQOptions, + IStorageConnection connection, IConnectionChannelPool connectionChannelPool, IStateChanger stateChanger) + : base(logger, options, connection, stateChanger) { _logger = logger; _connectionChannelPool = connectionChannelPool; @@ -30,10 +34,7 @@ namespace DotNetCore.CAP.RabbitMQ var body = Encoding.UTF8.GetBytes(content); channel.ExchangeDeclare(_rabbitMQOptions.TopicExchangeName, RabbitMQOptions.ExchangeType, true); - channel.BasicPublish(_rabbitMQOptions.TopicExchangeName, - keyName, - null, - body); + channel.BasicPublish(_rabbitMQOptions.TopicExchangeName, keyName, null, body); _logger.LogDebug($"RabbitMQ topic message [{keyName}] has been published."); @@ -41,21 +42,22 @@ namespace DotNetCore.CAP.RabbitMQ } catch (Exception ex) { - _logger.LogError( - $"RabbitMQ topic message [{keyName}] has been raised an exception of sending. the exception is: {ex.Message}"); - - return Task.FromResult(OperateResult.Failed(ex, - new OperateError - { - Code = ex.HResult.ToString(), - Description = ex.Message - })); + var wapperEx = new PublisherSentFailedException(ex.Message, ex); + var errors = new OperateError + { + Code = ex.HResult.ToString(), + Description = ex.Message + }; + + return Task.FromResult(OperateResult.Failed(wapperEx, errors)); } finally { var returned = _connectionChannelPool.Return(channel); if (!returned) + { channel.Dispose(); + } } } } diff --git a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs index da483f0..acbff96 100644 --- a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs +++ b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Text; using System.Threading; @@ -13,9 +16,9 @@ namespace DotNetCore.CAP.RabbitMQ private readonly string _exchageName; private readonly string _queueName; private readonly RabbitMQOptions _rabbitMQOptions; + private IModel _channel; private IConnection _connection; - private IModel _channel; private ulong _deliveryTag; public RabbitMQConsumerClient(string queueName, @@ -36,10 +39,15 @@ namespace DotNetCore.CAP.RabbitMQ public void Subscribe(IEnumerable topics) { - if (topics == null) throw new ArgumentNullException(nameof(topics)); + if (topics == null) + { + throw new ArgumentNullException(nameof(topics)); + } foreach (var topic in topics) + { _channel.QueueBind(_queueName, _exchageName, topic); + } } public void Listening(TimeSpan timeout, CancellationToken cancellationToken) @@ -58,6 +66,7 @@ namespace DotNetCore.CAP.RabbitMQ cancellationToken.ThrowIfCancellationRequested(); cancellationToken.WaitHandle.WaitOne(timeout); } + // ReSharper disable once FunctionNeverReturns } @@ -88,8 +97,9 @@ namespace DotNetCore.CAP.RabbitMQ RabbitMQOptions.ExchangeType, true); - var arguments = new Dictionary { - { "x-message-ttl", _rabbitMQOptions.QueueMessageExpires } + var arguments = new Dictionary + { + {"x-message-ttl", _rabbitMQOptions.QueueMessageExpires} }; _channel.QueueDeclare(_queueName, true, false, false, arguments); } @@ -100,7 +110,7 @@ namespace DotNetCore.CAP.RabbitMQ { var args = new LogMessageEventArgs { - LogType = MqLogType.ConsumerCancelled, + LogType = MqLogType.ConsumerCancelled, Reason = e.ConsumerTag }; OnLog?.Invoke(sender, args); diff --git a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs index 0a03c58..52c3dd1 100644 --- a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs +++ b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.RabbitMQ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.RabbitMQ { internal sealed class RabbitMQConsumerClientFactory : IConsumerClientFactory { diff --git a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs index 0c62833..243b1c8 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs @@ -1,4 +1,7 @@ -using System; +// 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; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs index c882165..6aca9cc 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP; using Microsoft.EntityFrameworkCore; @@ -14,7 +17,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseSqlServer(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new SqlServerCapOptionsExtension(configure)); @@ -30,7 +36,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options, Action configure) where TContext : DbContext { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new SqlServerCapOptionsExtension(x => { diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs index 55408bd..a176bb7 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Processor; using DotNetCore.CAP.SqlServer; using Microsoft.EntityFrameworkCore; @@ -41,7 +44,7 @@ namespace DotNetCore.CAP using (var scope = x.CreateScope()) { var provider = scope.ServiceProvider; - var dbContext = (DbContext)provider.GetService(sqlServerOptions.DbContextType); + var dbContext = (DbContext) provider.GetService(sqlServerOptions.DbContextType); sqlServerOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; return sqlServerOptions; } diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs index f2d44a4..b5c1e5f 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs @@ -1,4 +1,5 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP.SqlServer/CapPublisher.cs b/src/DotNetCore.CAP.SqlServer/CapPublisher.cs index f362d12..b93ca68 100644 --- a/src/DotNetCore.CAP.SqlServer/CapPublisher.cs +++ b/src/DotNetCore.CAP.SqlServer/CapPublisher.cs @@ -1,4 +1,7 @@ -using System; +// 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.Data; using System.Data.SqlClient; using System.Threading.Tasks; @@ -14,28 +17,31 @@ namespace DotNetCore.CAP.SqlServer public class CapPublisher : CapPublisherBase, ICallbackPublisher { private readonly DbContext _dbContext; - private readonly ILogger _logger; private readonly SqlServerOptions _options; - public CapPublisher(IServiceProvider provider, - ILogger logger, - SqlServerOptions options) + public CapPublisher(ILogger logger, IDispatcher dispatcher, + IServiceProvider provider, SqlServerOptions options) + : base(logger, dispatcher) { ServiceProvider = provider; - _logger = logger; _options = options; - if (_options.DbContextType == null) return; + if (_options.DbContextType == null) + { + return; + } IsUsingEF = true; - _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); + _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); } - public async Task PublishAsync(CapPublishedMessage message) + public async Task PublishCallbackAsync(CapPublishedMessage message) { using (var conn = new SqlConnection(_options.ConnectionString)) { - await conn.ExecuteAsync(PrepareSql(), message); + var id = await conn.ExecuteScalarAsync(PrepareSql(), message); + message.Id = id; + Enqueue(message); } } @@ -52,23 +58,20 @@ namespace DotNetCore.CAP.SqlServer dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbTrans = dbContextTransaction.GetDbTransaction(); } + DbTransaction = dbTrans; } - protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - dbConnection.Execute(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("published message has been persisted to the database. name:" + message); + return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); } - protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("published message has been persisted to the database. name:" + message); + return dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); } #region private methods @@ -76,7 +79,7 @@ namespace DotNetCore.CAP.SqlServer private string PrepareSql() { return - $"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; + $"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOPE_IDENTITY();"; } #endregion private methods diff --git a/src/DotNetCore.CAP.SqlServer/FetchedMessage.cs b/src/DotNetCore.CAP.SqlServer/FetchedMessage.cs deleted file mode 100644 index 990b61e..0000000 --- a/src/DotNetCore.CAP.SqlServer/FetchedMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.SqlServer -{ - internal class FetchedMessage - { - public int MessageId { get; set; } - - public MessageType MessageType { get; set; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs index e87f87a..8e344d6 100644 --- a/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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.Data.SqlClient; using System.Threading.Tasks; using Dapper; diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerFetchedMessage.cs b/src/DotNetCore.CAP.SqlServer/SqlServerFetchedMessage.cs deleted file mode 100644 index e423258..0000000 --- a/src/DotNetCore.CAP.SqlServer/SqlServerFetchedMessage.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Data; -using System.Threading; -using Dapper; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.SqlServer -{ - public class SqlServerFetchedMessage : IFetchedMessage - { - private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); - private readonly IDbConnection _connection; - private readonly object _lockObject = new object(); - private readonly Timer _timer; - private readonly IDbTransaction _transaction; - - public SqlServerFetchedMessage(int messageId, - MessageType type, - IDbConnection connection, - IDbTransaction transaction) - { - MessageId = messageId; - MessageType = type; - _connection = connection; - _transaction = transaction; - _timer = new Timer(ExecuteKeepAliveQuery, null, KeepAliveInterval, KeepAliveInterval); - } - - public int MessageId { get; } - - public MessageType MessageType { get; } - - public void RemoveFromQueue() - { - lock (_lockObject) - { - _transaction.Commit(); - } - } - - public void Requeue() - { - lock (_lockObject) - { - _transaction.Rollback(); - } - } - - public void Dispose() - { - lock (_lockObject) - { - _timer?.Dispose(); - _transaction.Dispose(); - _connection.Dispose(); - } - } - - private void ExecuteKeepAliveQuery(object obj) - { - lock (_lockObject) - { - try - { - _connection?.Execute("SELECT 1", _transaction); - } - catch - { - // ignored - } - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs index 1d0d1c0..732143e 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Data; using System.Linq; @@ -28,9 +31,7 @@ set transaction isolation level read committed; select count(Id) from [{0}].Published with (nolock) where StatusName = N'Succeeded'; select count(Id) from [{0}].Received with (nolock) where StatusName = N'Succeeded'; select count(Id) from [{0}].Published with (nolock) where StatusName = N'Failed'; -select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; -select count(Id) from [{0}].Published with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued'); -select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued');", +select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed';", _options.Schema); var statistics = UseConnection(connection => @@ -43,10 +44,8 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces stats.PublishedFailed = multi.ReadSingle(); stats.ReceivedFailed = multi.ReadSingle(); - - stats.PublishedProcessing = multi.ReadSingle(); - stats.ReceivedProcessing = multi.ReadSingle(); } + return stats; }); return statistics; @@ -71,17 +70,24 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces var tableName = queryDto.MessageType == MessageType.Publish ? "Published" : "Received"; var where = string.Empty; if (!string.IsNullOrEmpty(queryDto.StatusName)) - if (string.Equals(queryDto.StatusName, StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - where += " and statusname in (N'Processing',N'Scheduled',N'Enqueued')"; - else - where += " and statusname=@StatusName"; + { + where += " and statusname=@StatusName"; + } + if (!string.IsNullOrEmpty(queryDto.Name)) + { where += " and name=@Name"; + } + if (!string.IsNullOrEmpty(queryDto.Group)) + { where += " and group=@Group"; + } + if (!string.IsNullOrEmpty(queryDto.Content)) + { where += " and content like '%@Content%'"; + } var sqlQuery = $"select * from [{_options.Schema}].{tableName} where 1=1 {where} order by Added desc offset @Offset rows fetch next @Limit rows only"; @@ -102,11 +108,6 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces return UseConnection(conn => GetNumberOfMessage(conn, "Published", StatusName.Failed)); } - public int PublishedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "Published", StatusName.Processing)); - } - public int PublishedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "Published", StatusName.Succeeded)); @@ -117,11 +118,6 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces return UseConnection(conn => GetNumberOfMessage(conn, "Received", StatusName.Failed)); } - public int ReceivedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "Received", StatusName.Processing)); - } - public int ReceivedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "Received", StatusName.Succeeded)); @@ -129,9 +125,8 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces private int GetNumberOfMessage(IDbConnection connection, string tableName, string statusName) { - var sqlQuery = statusName == StatusName.Processing - ? $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued')" - : $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName = @state"; + var sqlQuery = + $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName = @state"; var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; @@ -182,7 +177,12 @@ select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; .ToDictionary(x => (string) x.Key, x => (int) x.Count); foreach (var key in keyMaps.Keys) - if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0); + { + if (!valuesMap.ContainsKey(key)) + { + valuesMap.Add(key, 0); + } + } var result = new Dictionary(); for (var i = 0; i < keyMaps.Count; i++) diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs index 75d8398..4a0f164 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs @@ -1,3 +1,6 @@ +// 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.Data; using System.Data.SqlClient; @@ -11,9 +14,9 @@ namespace DotNetCore.CAP.SqlServer { public class SqlServerStorage : IStorage { + private readonly CapOptions _capOptions; private readonly IDbConnection _existingConnection = null; private readonly ILogger _logger; - private readonly CapOptions _capOptions; private readonly SqlServerOptions _options; public SqlServerStorage(ILogger logger, @@ -37,7 +40,10 @@ namespace DotNetCore.CAP.SqlServer public async Task InitializeAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) return; + if (cancellationToken.IsCancellationRequested) + { + return; + } var sql = CreateDbTablesScript(_options.Schema); @@ -45,6 +51,7 @@ namespace DotNetCore.CAP.SqlServer { await connection.ExecuteAsync(sql); } + _logger.LogDebug("Ensuring all create database tables script are applied."); } @@ -57,12 +64,9 @@ BEGIN EXEC('CREATE SCHEMA [{schema}]') END; -IF OBJECT_ID(N'[{schema}].[Queue]',N'U') IS NULL +IF OBJECT_ID(N'[{schema}].[Queue]',N'U') IS NOT NULL BEGIN - CREATE TABLE [{schema}].[Queue]( - [MessageId] [int] NOT NULL, - [MessageType] [tinyint] NOT NULL - ) ON [PRIMARY] + DROP TABLE [{schema}].[Queue]; END; IF OBJECT_ID(N'[{schema}].[Received]',N'U') IS NULL @@ -122,7 +126,9 @@ END;"; var connection = _existingConnection ?? new SqlConnection(_options.ConnectionString); if (connection.State == ConnectionState.Closed) + { connection.Open(); + } return connection; } @@ -135,7 +141,9 @@ END;"; internal void ReleaseConnection(IDbConnection connection) { if (connection != null && !IsExistingConnection(connection)) + { connection.Dispose(); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs index 424c43d..9b931c1 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs @@ -1,6 +1,8 @@ +// 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.Collections.Generic; -using System.Data; using System.Data.SqlClient; using System.Threading.Tasks; using Dapper; @@ -36,49 +38,32 @@ namespace DotNetCore.CAP.SqlServer } } - public Task FetchNextMessageAsync() - { - var sql = $@" -DELETE TOP (1) -FROM [{Options.Schema}].[Queue] WITH (readpast, updlock, rowlock) -OUTPUT DELETED.MessageId,DELETED.[MessageType];"; - - return FetchNextMessageCoreAsync(sql); - } - - public async Task GetNextPublishedMessageToBeEnqueuedAsync() + public async Task> GetPublishedMessagesOfNeedRetry() { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); var sql = - $"SELECT TOP (1) * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE StatusName = '{StatusName.Scheduled}'"; + $"SELECT TOP (200) * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND Added<'{fourMinsAgo}' AND (StatusName = '{StatusName.Failed}' OR StatusName = '{StatusName.Scheduled}')"; using (var connection = new SqlConnection(Options.ConnectionString)) { - return await connection.QueryFirstOrDefaultAsync(sql); + return await connection.QueryAsync(sql); } } - public async Task> GetFailedPublishedMessages() + public async Task StoreReceivedMessageAsync(CapReceivedMessage message) { - var sql = - $"SELECT TOP (200) * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND StatusName = '{StatusName.Failed}'"; - - using (var connection = new SqlConnection(Options.ConnectionString)) + if (message == null) { - return await connection.QueryAsync(sql); + throw new ArgumentNullException(nameof(message)); } - } - - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); var sql = $@" INSERT INTO [{Options.Schema}].[Received]([Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) -VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; +VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOPE_IDENTITY();"; using (var connection = new SqlConnection(Options.ConnectionString)) { - await connection.ExecuteAsync(sql, message); + return await connection.ExecuteScalarAsync(sql, message); } } @@ -91,20 +76,11 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } } - public async Task GetNextReceivedMessageToBeEnqueuedAsync() - { - var sql = - $"SELECT TOP (1) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE StatusName = '{StatusName.Scheduled}'"; - using (var connection = new SqlConnection(Options.ConnectionString)) - { - return await connection.QueryFirstOrDefaultAsync(sql); - } - } - - public async Task> GetFailedReceivedMessages() + public async Task> GetReceivedMessagesOfNeedRetry() { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); var sql = - $"SELECT TOP (200) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND StatusName = '{StatusName.Failed}'"; + $"SELECT TOP (200) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND Added<'{fourMinsAgo}' AND (StatusName = '{StatusName.Failed}' OR StatusName = '{StatusName.Scheduled}')"; using (var connection = new SqlConnection(Options.ConnectionString)) { return await connection.QueryAsync(sql); @@ -136,35 +112,5 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; public void Dispose() { } - - private async Task FetchNextMessageCoreAsync(string sql, object args = null) - { - //here don't use `using` to dispose - var connection = new SqlConnection(Options.ConnectionString); - await connection.OpenAsync(); - var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); - FetchedMessage fetchedMessage; - try - { - fetchedMessage = await connection.QueryFirstOrDefaultAsync(sql, args, transaction); - } - catch (SqlException) - { - transaction.Dispose(); - connection.Dispose(); - throw; - } - - if (fetchedMessage == null) - { - transaction.Rollback(); - transaction.Dispose(); - connection.Dispose(); - return null; - } - - return new SqlServerFetchedMessage(fetchedMessage.MessageId, fetchedMessage.MessageType, connection, - transaction); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs index 3cf8ae6..8889e4a 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs @@ -1,4 +1,7 @@ -using System; +// 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.Data; using System.Data.SqlClient; using System.Threading.Tasks; @@ -26,7 +29,10 @@ namespace DotNetCore.CAP.SqlServer public void UpdateMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"UPDATE [{_schema}].[Published] SET [Retries] = @Retries,[Content] = @Content,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; @@ -35,16 +41,34 @@ namespace DotNetCore.CAP.SqlServer public void UpdateMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"UPDATE [{_schema}].[Received] SET [Retries] = @Retries,[Content] = @Content,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; _dbConnection.Execute(sql, message, _dbTransaction); } + public Task CommitAsync() + { + _dbTransaction.Commit(); + return Task.CompletedTask; + } + + public void Dispose() + { + _dbTransaction.Dispose(); + _dbConnection.Dispose(); + } + public void EnqueueMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);"; _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish}, @@ -53,23 +77,14 @@ namespace DotNetCore.CAP.SqlServer public void EnqueueMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);"; _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe}, _dbTransaction); } - - public Task CommitAsync() - { - _dbTransaction.Commit(); - return Task.CompletedTask; - } - - public void Dispose() - { - _dbTransaction.Dispose(); - _dbConnection.Dispose(); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 37e9808..b7231dc 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -1,14 +1,26 @@ -using System; +// 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.Data; using System.Threading.Tasks; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; +using Microsoft.Extensions.Logging; namespace DotNetCore.CAP.Abstractions { public abstract class CapPublisherBase : ICapPublisher, IDisposable { + private readonly IDispatcher _dispatcher; + private readonly ILogger _logger; + + protected CapPublisherBase(ILogger logger, IDispatcher dispatcher) + { + _logger = logger; + _dispatcher = dispatcher; + } + protected IDbConnection DbConnection { get; set; } protected IDbTransaction DbTransaction { get; set; } protected bool IsCapOpenedTrans { get; set; } @@ -21,9 +33,7 @@ namespace DotNetCore.CAP.Abstractions CheckIsUsingEF(name); PrepareConnectionForEF(); - var content = Serialize(contentObj, callbackName); - - PublishWithTrans(name, content); + PublishWithTrans(name, contentObj, callbackName); } public Task PublishAsync(string name, T contentObj, string callbackName = null) @@ -31,9 +41,7 @@ namespace DotNetCore.CAP.Abstractions CheckIsUsingEF(name); PrepareConnectionForEF(); - var content = Serialize(contentObj, callbackName); - - return PublishWithTransAsync(name, content); + return PublishWithTransAsync(name, contentObj, callbackName); } public void Publish(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null) @@ -41,9 +49,7 @@ namespace DotNetCore.CAP.Abstractions CheckIsAdoNet(name); PrepareConnectionForAdo(dbTransaction); - var content = Serialize(contentObj, callbackName); - - PublishWithTrans(name, content); + PublishWithTrans(name, contentObj, callbackName); } public Task PublishAsync(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null) @@ -51,28 +57,31 @@ namespace DotNetCore.CAP.Abstractions CheckIsAdoNet(name); PrepareConnectionForAdo(dbTransaction); - var content = Serialize(contentObj, callbackName); + return PublishWithTransAsync(name, contentObj, callbackName); + } - return PublishWithTransAsync(name, content); + protected void Enqueue(CapPublishedMessage message) + { + _dispatcher.EnqueueToPublish(message); } protected abstract void PrepareConnectionForEF(); - protected abstract void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected abstract int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message); - protected abstract Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected abstract Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message); protected virtual string Serialize(T obj, string callbackName = null) { - var packer = (IMessagePacker)ServiceProvider.GetService(typeof(IMessagePacker)); + var packer = (IMessagePacker) ServiceProvider.GetService(typeof(IMessagePacker)); string content; if (obj != null) { if (Helper.IsComplexType(obj.GetType())) { - var serializer = (IContentSerializer)ServiceProvider.GetService(typeof(IContentSerializer)); + var serializer = (IContentSerializer) ServiceProvider.GetService(typeof(IContentSerializer)); content = serializer.Serialize(obj); } else @@ -89,7 +98,6 @@ namespace DotNetCore.CAP.Abstractions { CallbackName = callbackName }; - return packer.Pack(message); } @@ -108,51 +116,99 @@ namespace DotNetCore.CAP.Abstractions private void CheckIsUsingEF(string name) { - if (name == null) throw new ArgumentNullException(nameof(name)); + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + if (!IsUsingEF) + { throw new InvalidOperationException( "If you are using the EntityFramework, you need to configure the DbContextType first." + " otherwise you need to use overloaded method with IDbConnection and IDbTransaction."); + } } private void CheckIsAdoNet(string name) { - if (name == null) throw new ArgumentNullException(nameof(name)); + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + if (IsUsingEF) + { throw new InvalidOperationException( "If you are using the EntityFramework, you do not need to use this overloaded."); + } } - private async Task PublishWithTransAsync(string name, string content) + private async Task PublishWithTransAsync(string name, T contentObj, string callbackName = null) { - var message = new CapPublishedMessage + try { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; + var content = Serialize(contentObj, callbackName); - await ExecuteAsync(DbConnection, DbTransaction, message); + var message = new CapPublishedMessage + { + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; + + var id = await ExecuteAsync(DbConnection, DbTransaction, message); + + ClosedCap(); - ClosedCap(); + if (id > 0) + { + _logger.LogInformation($"message [{message}] has been persisted in the database."); + + message.Id = id; - PublishQueuer.PulseEvent.Set(); + Enqueue(message); + } + } + catch (Exception e) + { + _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + Console.WriteLine(e); + throw; + } } - private void PublishWithTrans(string name, string content) + private void PublishWithTrans(string name, T contentObj, string callbackName = null) { - var message = new CapPublishedMessage + try { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; + var content = Serialize(contentObj, callbackName); + + var message = new CapPublishedMessage + { + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; + + var id = Execute(DbConnection, DbTransaction, message); + + ClosedCap(); - Execute(DbConnection, DbTransaction, message); + if (id > 0) + { + _logger.LogInformation($"message [{message}] has been persisted in the database."); - ClosedCap(); + message.Id = id; - PublishQueuer.PulseEvent.Set(); + Enqueue(message); + } + } + catch (Exception e) + { + _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + Console.WriteLine(e); + throw; + } } private void ClosedCap() @@ -162,8 +218,11 @@ namespace DotNetCore.CAP.Abstractions DbTransaction.Commit(); DbTransaction.Dispose(); } + if (IsCapOpenedConn) + { DbConnection.Dispose(); + } } public void Dispose() diff --git a/src/DotNetCore.CAP/Abstractions/IContentSerializer.cs b/src/DotNetCore.CAP/Abstractions/IContentSerializer.cs index 402843a..108f975 100644 --- a/src/DotNetCore.CAP/Abstractions/IContentSerializer.cs +++ b/src/DotNetCore.CAP/Abstractions/IContentSerializer.cs @@ -1,11 +1,17 @@ -using System; +// 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 DotNetCore.CAP.Models; namespace DotNetCore.CAP.Abstractions { /// /// Message content serializer. - /// By default, CAP will use Json as a serializer, and you can customize this interface to achieve serialization of other methods. + /// + /// By default, CAP will use Json as a serializer, and you can customize this interface to achieve serialization of + /// other methods. + /// /// public interface IContentSerializer { @@ -40,7 +46,7 @@ namespace DotNetCore.CAP.Abstractions /// /// /// We use the wrapper to provide some additional information for the message content,which is important for CAP。 - /// Typically, we may need to customize the field display name of the message, + /// Typically, we may need to customize the field display name of the message, /// which includes interacting with other message components, which can be adapted in this manner /// public interface IMessagePacker @@ -52,9 +58,9 @@ namespace DotNetCore.CAP.Abstractions string Pack(CapMessage obj); /// - /// Unpack a message strings to object. + /// Unpack a message strings to object. /// /// The string of packed message. CapMessage UnPack(string packingMessage); - } + } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/IModelBinderFactory.cs b/src/DotNetCore.CAP/Abstractions/IModelBinderFactory.cs index df3423f..ce5de92 100644 --- a/src/DotNetCore.CAP/Abstractions/IModelBinderFactory.cs +++ b/src/DotNetCore.CAP/Abstractions/IModelBinderFactory.cs @@ -1,4 +1,7 @@ -using System.Reflection; +// 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.Reflection; using DotNetCore.CAP.Abstractions.ModelBinding; namespace DotNetCore.CAP.Abstractions diff --git a/src/DotNetCore.CAP/Abstractions/ISubscriberExecutor.cs b/src/DotNetCore.CAP/Abstractions/ISubscriberExecutor.cs deleted file mode 100644 index 18c9f5d..0000000 --- a/src/DotNetCore.CAP/Abstractions/ISubscriberExecutor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading.Tasks; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.Abstractions -{ - /// - /// Consumer method executor. - /// - public interface ISubscriberExecutor - { - /// - /// Execute the consumer method. - /// - /// The received message. - Task ExecuteAsync(CapReceivedMessage receivedMessage); - } -} diff --git a/src/DotNetCore.CAP/Abstractions/ModelBinding/IModelBinder.cs b/src/DotNetCore.CAP/Abstractions/ModelBinding/IModelBinder.cs index ad276b7..6914c47 100644 --- a/src/DotNetCore.CAP/Abstractions/ModelBinding/IModelBinder.cs +++ b/src/DotNetCore.CAP/Abstractions/ModelBinding/IModelBinder.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; namespace DotNetCore.CAP.Abstractions.ModelBinding { diff --git a/src/DotNetCore.CAP/Abstractions/ModelBinding/ModelBindingResult.cs b/src/DotNetCore.CAP/Abstractions/ModelBinding/ModelBindingResult.cs index 0a23d2b..62a9337 100644 --- a/src/DotNetCore.CAP/Abstractions/ModelBinding/ModelBindingResult.cs +++ b/src/DotNetCore.CAP/Abstractions/ModelBinding/ModelBindingResult.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Internal; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Internal; namespace DotNetCore.CAP.Abstractions.ModelBinding { @@ -42,7 +45,10 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding public override string ToString() { if (IsSuccess) + { return $"Success '{Model}'"; + } + return "Failed"; } @@ -50,7 +56,10 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding { var other = obj as ModelBindingResult?; if (other == null) + { return false; + } + return Equals(other.Value); } diff --git a/src/DotNetCore.CAP/Abstractions/TopicAttribute.cs b/src/DotNetCore.CAP/Abstractions/TopicAttribute.cs index 59dd897..6b83de4 100644 --- a/src/DotNetCore.CAP/Abstractions/TopicAttribute.cs +++ b/src/DotNetCore.CAP/Abstractions/TopicAttribute.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Abstractions { diff --git a/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs b/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs index 51ccbff..2a8a586 100644 --- a/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs +++ b/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP; using DotNetCore.CAP.Dashboard.GatewayProxy; using Microsoft.Extensions.DependencyInjection; @@ -19,7 +22,9 @@ namespace Microsoft.AspNetCore.Builder public static IApplicationBuilder UseCap(this IApplicationBuilder app) { if (app == null) + { throw new ArgumentNullException(nameof(app)); + } CheckRequirement(app); @@ -31,7 +36,10 @@ namespace Microsoft.AspNetCore.Builder if (provider.GetService() != null) { if (provider.GetService() != null) + { app.UseMiddleware(); + } + app.UseMiddleware(); } @@ -42,18 +50,24 @@ namespace Microsoft.AspNetCore.Builder { var marker = app.ApplicationServices.GetService(); if (marker == null) + { throw new InvalidOperationException( "AddCap() must be called on the service collection. eg: services.AddCap(...)"); + } var messageQueueMarker = app.ApplicationServices.GetService(); if (messageQueueMarker == null) + { throw new InvalidOperationException( "You must be config used message queue provider at AddCap() options! eg: services.AddCap(options=>{ options.UseKafka(...) })"); + } var databaseMarker = app.ApplicationServices.GetService(); if (databaseMarker == null) + { throw new InvalidOperationException( "You must be config used database provider at AddCap() options! eg: services.AddCap(options=>{ options.UseSqlServer(...) })"); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/CAP.Builder.cs b/src/DotNetCore.CAP/CAP.Builder.cs index e59270d..9722461 100644 --- a/src/DotNetCore.CAP/CAP.Builder.cs +++ b/src/DotNetCore.CAP/CAP.Builder.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Abstractions; using Microsoft.Extensions.DependencyInjection; diff --git a/src/DotNetCore.CAP/CAP.Options.cs b/src/DotNetCore.CAP/CAP.Options.cs index a7f6198..267a61f 100644 --- a/src/DotNetCore.CAP/CAP.Options.cs +++ b/src/DotNetCore.CAP/CAP.Options.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Reflection; using DotNetCore.CAP.Models; @@ -10,16 +13,6 @@ namespace DotNetCore.CAP /// public class CapOptions { - /// - /// Default value for polling delay timeout, in seconds. - /// - public const int DefaultPollingDelay = 15; - - /// - /// Default processor count to process messages of cap.queue. - /// - public const int DefaultQueueProcessorCount = 2; - /// /// Default succeeded message expiration time span, in seconds. /// @@ -28,18 +21,16 @@ namespace DotNetCore.CAP /// /// Failed message retry waiting interval. /// - public const int DefaultFailedMessageWaitingInterval = 600; + public const int DefaultFailedMessageWaitingInterval = 60; /// /// Failed message retry count. /// - public const int DefaultFailedRetryCount = 100; + public const int DefaultFailedRetryCount = 50; public CapOptions() { - PollingDelay = DefaultPollingDelay; - QueueProcessorCount = DefaultQueueProcessorCount; SucceedMessageExpiredAfter = DefaultSucceedMessageExpirationAfter; FailedRetryInterval = DefaultFailedMessageWaitingInterval; FailedRetryCount = DefaultFailedRetryCount; @@ -54,18 +45,6 @@ namespace DotNetCore.CAP /// public string DefaultGroup { get; set; } - /// - /// Producer job polling delay time. - /// Default is 15 sec. - /// - public int PollingDelay { get; set; } - - /// - /// Gets or sets the messages queue (Cap.Queue table) processor count. - /// Default is 2 processor. - /// - public int QueueProcessorCount { get; set; } - /// /// Sent or received succeed message after time span of due, then the message will be deleted at due time. /// Default is 24*3600 seconds. @@ -74,7 +53,7 @@ namespace DotNetCore.CAP /// /// Failed messages polling delay time. - /// Default is 600 seconds. + /// Default is 60 seconds. /// public int FailedRetryInterval { get; set; } @@ -85,7 +64,7 @@ namespace DotNetCore.CAP /// /// The number of message retries, the retry will stop when the threshold is reached. - /// Default is 100 times. + /// Default is 50 times. /// public int FailedRetryCount { get; set; } @@ -96,7 +75,9 @@ namespace DotNetCore.CAP public void RegisterExtension(ICapOptionsExtension extension) { if (extension == null) + { throw new ArgumentNullException(nameof(extension)); + } Extensions.Add(extension); } diff --git a/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs b/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs index cfb1fad..23b585b 100644 --- a/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs +++ b/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using DotNetCore.CAP; using DotNetCore.CAP.Abstractions; @@ -25,7 +28,10 @@ namespace Microsoft.Extensions.DependencyInjection this IServiceCollection services, Action setupAction) { - if (setupAction == null) throw new ArgumentNullException(nameof(setupAction)); + if (setupAction == null) + { + throw new ArgumentNullException(nameof(setupAction)); + } services.TryAddSingleton(); services.Configure(setupAction); @@ -49,21 +55,21 @@ namespace Microsoft.Extensions.DependencyInjection services.AddSingleton(); //Queue's message processor - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); - //Executors - services.AddSingleton(); - services.AddSingleton(); - services.TryAddSingleton(); + //Sender and Executors + services.AddSingleton(); + // Warning: IPublishMessageSender need to inject at extension project. + services.AddSingleton(); //Options and extension service var options = new CapOptions(); setupAction(options); foreach (var serviceExtension in options.Extensions) + { serviceExtension.AddServices(services); + } + services.AddSingleton(options); return new CapBuilder(services); @@ -73,13 +79,19 @@ namespace Microsoft.Extensions.DependencyInjection { var consumerListenerServices = new List>(); foreach (var rejectedServices in services) + { if (rejectedServices.ImplementationType != null && typeof(ICapSubscribe).IsAssignableFrom(rejectedServices.ImplementationType)) + { consumerListenerServices.Add(new KeyValuePair(typeof(ICapSubscribe), rejectedServices.ImplementationType)); + } + } foreach (var service in consumerListenerServices) + { services.AddTransient(service.Key, service.Value); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs b/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs index ca707ee..d9df0bd 100644 --- a/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// 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.Net; using System.Threading.Tasks; diff --git a/src/DotNetCore.CAP/Dashboard/CAP.DashboardMiddleware.cs b/src/DotNetCore.CAP/Dashboard/CAP.DashboardMiddleware.cs index 9997280..70d85be 100644 --- a/src/DotNetCore.CAP/Dashboard/CAP.DashboardMiddleware.cs +++ b/src/DotNetCore.CAP/Dashboard/CAP.DashboardMiddleware.cs @@ -1,4 +1,7 @@ -using System; +// 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.Linq; using System.Net; using System.Threading.Tasks; @@ -27,7 +30,10 @@ namespace DotNetCore.CAP public Task Invoke(HttpContext context) { if (!context.Request.Path.StartsWithSegments(_options.PathMatch, - out var matchedPath, out var remainingPath)) return _next(context); + out var matchedPath, out var remainingPath)) + { + return _next(context); + } // Update the path var path = context.Request.Path; @@ -41,7 +47,9 @@ namespace DotNetCore.CAP var findResult = _routes.FindDispatcher(context.Request.Path.Value); if (findResult == null) + { return _next.Invoke(context); + } if (_options.Authorization.Any(filter => !filter.Authorize(dashboardContext))) { diff --git a/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptions.cs b/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptions.cs index b144996..e04ab24 100644 --- a/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptions.cs +++ b/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptions.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// 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.Collections.Generic; using DotNetCore.CAP.Dashboard; // ReSharper disable once CheckNamespace diff --git a/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptionsExtensions.cs b/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptionsExtensions.cs index c2ef243..cfc1bf5 100644 --- a/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptionsExtensions.cs +++ b/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptionsExtensions.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP; using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard.GatewayProxy; @@ -40,7 +43,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseDashboard(this CapOptions capOptions, Action options) { - if (options == null) throw new ArgumentNullException(nameof(options)); + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } capOptions.RegisterExtension(new DashboardOptionsExtension(options)); diff --git a/src/DotNetCore.CAP/Dashboard/CombinedResourceDispatcher.cs b/src/DotNetCore.CAP/Dashboard/CombinedResourceDispatcher.cs index 94c0d99..65875c4 100644 --- a/src/DotNetCore.CAP/Dashboard/CombinedResourceDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/CombinedResourceDispatcher.cs @@ -1,4 +1,7 @@ -using System.Reflection; +// 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.Reflection; namespace DotNetCore.CAP.Dashboard { @@ -22,10 +25,12 @@ namespace DotNetCore.CAP.Dashboard protected override void WriteResponse(DashboardResponse response) { foreach (var resourceName in _resourceNames) + { WriteResource( response, _assembly, $"{_baseNamespace}.{resourceName}"); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/CommandDispatcher.cs b/src/DotNetCore.CAP/Dashboard/CommandDispatcher.cs index 75ac2b4..2959673 100644 --- a/src/DotNetCore.CAP/Dashboard/CommandDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/CommandDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// 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.Net; using System.Threading.Tasks; @@ -25,9 +28,13 @@ namespace DotNetCore.CAP.Dashboard } if (_command(context)) + { response.StatusCode = (int) HttpStatusCode.NoContent; + } else + { response.StatusCode = 422; + } return Task.FromResult(true); } diff --git a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.Designer.cs b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.Designer.cs index a9be4fa..c08a9ab 100644 --- a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.Designer.cs +++ b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.Designer.cs @@ -879,15 +879,6 @@ namespace DotNetCore.CAP.Dashboard.Resources { } } - /// - /// Looks up a localized string similar to Processing. - /// - public static string SidebarMenu_Processing { - get { - return ResourceManager.GetString("SidebarMenu_Processing", resourceCulture); - } - } - /// /// Looks up a localized string similar to Succeeded. /// diff --git a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.resx b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.resx index b8aa259..57ed89d 100644 --- a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.resx +++ b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.resx @@ -240,9 +240,6 @@ Failed - - Processing - Succeeded diff --git a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.zh.resx b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.zh.resx index a75494d..90ed01e 100644 --- a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.zh.resx +++ b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.zh.resx @@ -234,9 +234,6 @@ 失败 - - 执行中 - 完成 diff --git a/src/DotNetCore.CAP/Dashboard/DashboardContext.cs b/src/DotNetCore.CAP/Dashboard/DashboardContext.cs index de6dd43..c97a308 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardContext.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardContext.cs @@ -1,4 +1,7 @@ -using System; +// 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.Text.RegularExpressions; using Microsoft.AspNetCore.Http; @@ -8,8 +11,15 @@ namespace DotNetCore.CAP.Dashboard { protected DashboardContext(IStorage storage, DashboardOptions options) { - if (storage == null) throw new ArgumentNullException(nameof(storage)); - if (options == null) throw new ArgumentNullException(nameof(options)); + if (storage == null) + { + throw new ArgumentNullException(nameof(storage)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } Storage = storage; Options = options; @@ -36,7 +46,10 @@ namespace DotNetCore.CAP.Dashboard HttpContext httpContext) : base(storage, options) { - if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); + if (httpContext == null) + { + throw new ArgumentNullException(nameof(httpContext)); + } HttpContext = httpContext; Request = new CapDashboardRequest(httpContext); diff --git a/src/DotNetCore.CAP/Dashboard/DashboardMetric.cs b/src/DotNetCore.CAP/Dashboard/DashboardMetric.cs index 8bda1b1..6d0209b 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardMetric.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardMetric.cs @@ -1,3 +1,6 @@ +// 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; namespace DotNetCore.CAP.Dashboard diff --git a/src/DotNetCore.CAP/Dashboard/DashboardMetrics.cs b/src/DotNetCore.CAP/Dashboard/DashboardMetrics.cs index c6c4ba9..2fcd370 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardMetrics.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardMetrics.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Linq; using DotNetCore.CAP.Dashboard.Resources; @@ -64,24 +67,6 @@ namespace DotNetCore.CAP.Dashboard } : null); - //---------------------------------------------------- - - public static readonly DashboardMetric PublishedProcessingCount = new DashboardMetric( - "published_processing:count", - "Metrics_ProcessingJobs", - page => new Metric(page.Statistics.PublishedProcessing.ToString("N0")) - { - Style = page.Statistics.PublishedProcessing > 0 ? MetricStyle.Warning : MetricStyle.Default - }); - - public static readonly DashboardMetric ReceivedProcessingCount = new DashboardMetric( - "received_processing:count", - "Metrics_ProcessingJobs", - page => new Metric(page.Statistics.ReceivedProcessing.ToString("N0")) - { - Style = page.Statistics.ReceivedProcessing > 0 ? MetricStyle.Warning : MetricStyle.Default - }); - //---------------------------------------------------- public static readonly DashboardMetric PublishedSucceededCount = new DashboardMetric( "published_succeeded:count", @@ -129,9 +114,6 @@ namespace DotNetCore.CAP.Dashboard AddMetric(PublishedFailedCountOrNull); AddMetric(ReceivedFailedCountOrNull); - AddMetric(PublishedProcessingCount); - AddMetric(ReceivedProcessingCount); - AddMetric(PublishedSucceededCount); AddMetric(ReceivedSucceededCount); @@ -141,7 +123,10 @@ namespace DotNetCore.CAP.Dashboard public static void AddMetric(DashboardMetric metric) { - if (metric == null) throw new ArgumentNullException(nameof(metric)); + if (metric == null) + { + throw new ArgumentNullException(nameof(metric)); + } lock (Metrics) { diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs b/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs index 2499206..5fdb054 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -25,7 +28,11 @@ namespace DotNetCore.CAP.Dashboard public CapDashboardRequest(HttpContext context) { - if (context == null) throw new ArgumentNullException(nameof(context)); + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + _context = context; } diff --git a/src/DotNetCore.CAP/Dashboard/DashboardResponse.cs b/src/DotNetCore.CAP/Dashboard/DashboardResponse.cs index d435f2a..ceb0d52 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardResponse.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardResponse.cs @@ -1,4 +1,7 @@ -using System; +// 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.Globalization; using System.IO; using System.Threading.Tasks; diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs index 30e2689..312da57 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs @@ -1,4 +1,7 @@ -using System.Reflection; +// 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.Reflection; using DotNetCore.CAP.Dashboard.Pages; using DotNetCore.CAP.Infrastructure; diff --git a/src/DotNetCore.CAP/Dashboard/EmbeddedResourceDispatcher.cs b/src/DotNetCore.CAP/Dashboard/EmbeddedResourceDispatcher.cs index ce19de3..c48257c 100644 --- a/src/DotNetCore.CAP/Dashboard/EmbeddedResourceDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/EmbeddedResourceDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// 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.Reflection; using System.Threading.Tasks; @@ -47,8 +50,10 @@ namespace DotNetCore.CAP.Dashboard using (var inputStream = assembly.GetManifestResourceStream(resourceName)) { if (inputStream == null) + { throw new ArgumentException( $@"Resource with name {resourceName} not found in assembly {assembly}."); + } inputStream.CopyTo(response.Body); } diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/DownstreamUrl.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/DownstreamUrl.cs index b4ba407..61ca4b1 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/DownstreamUrl.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/DownstreamUrl.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.GatewayProxy +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.GatewayProxy { public class DownstreamUrl { diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/GatewayProxyMiddleware.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/GatewayProxyMiddleware.cs index b166acd..596b32b 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/GatewayProxyMiddleware.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/GatewayProxyMiddleware.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.IO; using System.Linq; @@ -94,7 +97,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy public async Task SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response) { foreach (var httpResponseHeader in response.Content.Headers) + { AddHeaderIfDoesntExist(context, httpResponseHeader); + } var content = await response.Content.ReadAsByteArrayAsync(); @@ -113,7 +118,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy using (Stream stream = new MemoryStream(content)) { if (response.StatusCode != HttpStatusCode.NotModified) + { await stream.CopyToAsync(context.Response.Body); + } } } @@ -134,8 +141,10 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy KeyValuePair> httpResponseHeader) { if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key)) + { context.Response.Headers.Add(httpResponseHeader.Key, new StringValues(httpResponseHeader.Value.ToArray())); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.Default.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.Default.cs index 90d80a9..4548ce5 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.Default.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.IO; using System.Linq; @@ -45,7 +48,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy FragmentString fragment = new FragmentString()) { if (scheme == null) + { throw new ArgumentNullException(nameof(scheme)); + } var combinedPath = pathBase.HasValue || path.HasValue ? (pathBase + path).ToString() : "/"; @@ -75,7 +80,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy private async Task MapContent(HttpRequest request) { if (request.Body == null) + { return null; + } var content = new ByteArrayContent(await ToByteArray(request.Body)); @@ -97,8 +104,12 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage) { foreach (var header in request.Headers) + { if (IsSupportedHeader(header)) + { requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + } + } } private async Task ToByteArray(Stream stream) diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.cs index ee1f994..e4f3d52 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.cs @@ -1,4 +1,7 @@ -using System.Net.Http; +// 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.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientBuilder.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientBuilder.cs index df913b6..843f9df 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientBuilder.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientBuilder.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Linq; using System.Net.Http; diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientHttpRequester.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientHttpRequester.cs index 807e452..a608690 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientHttpRequester.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientHttpRequester.cs @@ -1,4 +1,7 @@ -using System; +// 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.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -44,7 +47,10 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester var httpClient = _cacheHandlers.Get(cacheKey); if (httpClient == null) + { httpClient = builder.Create(); + } + return httpClient; } diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClient.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClient.cs index 8a8d12f..6fb0bb8 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClient.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClient.cs @@ -1,4 +1,7 @@ -using System.Net.Http; +// 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.Net.Http; using System.Threading.Tasks; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientBuilder.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientBuilder.cs index 3c24f6e..ed7f46e 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientBuilder.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientBuilder.cs @@ -1,4 +1,7 @@ -using System.Net.Http; +// 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.Net.Http; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester { diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientCache.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientCache.cs index d1b5ce3..088a64d 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientCache.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientCache.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester { diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpRequester.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpRequester.cs index 3b87f07..b1c7fec 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpRequester.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpRequester.cs @@ -1,4 +1,7 @@ -using System.Net.Http; +// 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.Net.Http; using System.Threading.Tasks; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/MemoryHttpClientCache.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/MemoryHttpClientCache.cs index 20498ec..21aa033 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/MemoryHttpClientCache.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/MemoryHttpClientCache.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Concurrent; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester @@ -31,7 +34,10 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester { IHttpClient client = null; if (_httpClientsCache.TryGetValue(id, out var connectionQueue)) + { connectionQueue.TryDequeue(out client); + } + return client; } diff --git a/src/DotNetCore.CAP/Dashboard/HtmlHelper.cs b/src/DotNetCore.CAP/Dashboard/HtmlHelper.cs index 75d09c1..a685fa7 100644 --- a/src/DotNetCore.CAP/Dashboard/HtmlHelper.cs +++ b/src/DotNetCore.CAP/Dashboard/HtmlHelper.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Linq; using System.Net; @@ -24,44 +27,71 @@ namespace DotNetCore.CAP.Dashboard public NonEscapedString Breadcrumbs(string title, IDictionary items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + return RenderPartial(new Breadcrumbs(title, items)); } public NonEscapedString MessagesSidebar(MessageType type) { if (type == MessageType.Publish) + { return SidebarMenu(MessagesSidebarMenu.PublishedItems); + } + return SidebarMenu(MessagesSidebarMenu.ReceivedItems); } public NonEscapedString SidebarMenu(IEnumerable> items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + return RenderPartial(new SidebarMenu(items)); } public NonEscapedString BlockMetric(DashboardMetric metric) { - if (metric == null) throw new ArgumentNullException(nameof(metric)); + if (metric == null) + { + throw new ArgumentNullException(nameof(metric)); + } + return RenderPartial(new BlockMetric(metric)); } public NonEscapedString InlineMetric(DashboardMetric metric) { - if (metric == null) throw new ArgumentNullException(nameof(metric)); + if (metric == null) + { + throw new ArgumentNullException(nameof(metric)); + } + return RenderPartial(new InlineMetric(metric)); } public NonEscapedString Paginator(Pager pager) { - if (pager == null) throw new ArgumentNullException(nameof(pager)); + if (pager == null) + { + throw new ArgumentNullException(nameof(pager)); + } + return RenderPartial(new Paginator(pager)); } public NonEscapedString PerPageSelector(Pager pager) { - if (pager == null) throw new ArgumentNullException(nameof(pager)); + if (pager == null) + { + throw new ArgumentNullException(nameof(pager)); + } + return RenderPartial(new PerPageSelector(pager)); } @@ -79,7 +109,9 @@ namespace DotNetCore.CAP.Dashboard public NonEscapedString StateLabel(string stateName) { if (string.IsNullOrWhiteSpace(stateName)) + { return Raw($"{Strings.Common_NoState}"); + } return Raw( $"{stateName}"); @@ -102,40 +134,59 @@ namespace DotNetCore.CAP.Dashboard public string ToHumanDuration(TimeSpan? duration, bool displaySign = true) { - if (duration == null) return null; + if (duration == null) + { + return null; + } var builder = new StringBuilder(); if (displaySign) + { builder.Append(duration.Value.TotalMilliseconds < 0 ? "-" : "+"); + } duration = duration.Value.Duration(); if (duration.Value.Days > 0) + { builder.Append($"{duration.Value.Days}d "); + } if (duration.Value.Hours > 0) + { builder.Append($"{duration.Value.Hours}h "); + } if (duration.Value.Minutes > 0) + { builder.Append($"{duration.Value.Minutes}m "); + } if (duration.Value.TotalHours < 1) + { if (duration.Value.Seconds > 0) { builder.Append(duration.Value.Seconds); if (duration.Value.Milliseconds > 0) + { builder.Append($".{duration.Value.Milliseconds.ToString().PadLeft(3, '0')}"); + } builder.Append("s "); } else { if (duration.Value.Milliseconds > 0) + { builder.Append($"{duration.Value.Milliseconds}ms "); + } } + } if (builder.Length <= 1) + { builder.Append(" <1ms "); + } builder.Remove(builder.Length - 1, 1); @@ -237,14 +288,25 @@ namespace DotNetCore.CAP.Dashboard private string WrapType(Type type) { if (type == null) + { return string.Empty; + } if (type.Name == "Void") + { return WrapKeyword(type.Name.ToLower()); + } + if (Helper.IsComplexType(type)) + { return WrapType(type.Name); + } + if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal)) + { return WrapKeyword(type.Name.ToLower()); + } + return WrapType(type.Name); } diff --git a/src/DotNetCore.CAP/Dashboard/IDashboardAuthorizationFilter.cs b/src/DotNetCore.CAP/Dashboard/IDashboardAuthorizationFilter.cs index f3833a8..0e984ba 100644 --- a/src/DotNetCore.CAP/Dashboard/IDashboardAuthorizationFilter.cs +++ b/src/DotNetCore.CAP/Dashboard/IDashboardAuthorizationFilter.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard { public interface IDashboardAuthorizationFilter { diff --git a/src/DotNetCore.CAP/Dashboard/IDashboardDispatcher.cs b/src/DotNetCore.CAP/Dashboard/IDashboardDispatcher.cs index ce9cf3e..9204b6e 100644 --- a/src/DotNetCore.CAP/Dashboard/IDashboardDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/IDashboardDispatcher.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; namespace DotNetCore.CAP.Dashboard { diff --git a/src/DotNetCore.CAP/Dashboard/IMonitoringApi.cs b/src/DotNetCore.CAP/Dashboard/IMonitoringApi.cs index 4e9f60b..e5ecd71 100644 --- a/src/DotNetCore.CAP/Dashboard/IMonitoringApi.cs +++ b/src/DotNetCore.CAP/Dashboard/IMonitoringApi.cs @@ -1,3 +1,6 @@ +// 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.Collections.Generic; using DotNetCore.CAP.Dashboard.Monitoring; @@ -13,14 +16,10 @@ namespace DotNetCore.CAP.Dashboard int PublishedFailedCount(); - int PublishedProcessingCount(); - int PublishedSucceededCount(); int ReceivedFailedCount(); - int ReceivedProcessingCount(); - int ReceivedSucceededCount(); IDictionary HourlySucceededJobs(MessageType type); diff --git a/src/DotNetCore.CAP/Dashboard/JsonDispatcher.cs b/src/DotNetCore.CAP/Dashboard/JsonDispatcher.cs index c50109f..89eadea 100644 --- a/src/DotNetCore.CAP/Dashboard/JsonDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/JsonDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// 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; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -37,7 +40,9 @@ namespace DotNetCore.CAP.Dashboard } if (_jsonCommand != null) + { serialized = _jsonCommand(context); + } context.Response.ContentType = "application/json"; await context.Response.WriteAsync(serialized ?? string.Empty); diff --git a/src/DotNetCore.CAP/Dashboard/JsonStats.cs b/src/DotNetCore.CAP/Dashboard/JsonStats.cs index fbc9f2c..4b3db7b 100644 --- a/src/DotNetCore.CAP/Dashboard/JsonStats.cs +++ b/src/DotNetCore.CAP/Dashboard/JsonStats.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// 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.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; diff --git a/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs b/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs index 145cdff..42746d7 100644 --- a/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs +++ b/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Infrastructure; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Infrastructure; namespace DotNetCore.CAP.Dashboard { @@ -8,19 +11,27 @@ namespace DotNetCore.CAP.Dashboard { // if unknown, assume not local if (string.IsNullOrEmpty(context.Request.RemoteIpAddress)) + { return false; + } // check if localhost if (context.Request.RemoteIpAddress == "127.0.0.1" || context.Request.RemoteIpAddress == "::1") + { return true; + } // compare with local address if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress) + { return true; + } // check if private ip if (Helper.IsInnerIP(context.Request.RemoteIpAddress)) + { return true; + } return false; } diff --git a/src/DotNetCore.CAP/Dashboard/MenuItem.cs b/src/DotNetCore.CAP/Dashboard/MenuItem.cs index 3f0ea1c..d6aac72 100644 --- a/src/DotNetCore.CAP/Dashboard/MenuItem.cs +++ b/src/DotNetCore.CAP/Dashboard/MenuItem.cs @@ -1,3 +1,6 @@ +// 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.Collections.Generic; using System.Linq; @@ -23,7 +26,9 @@ namespace DotNetCore.CAP.Dashboard var metrics = new List {Metric}; if (Metrics != null) + { metrics.AddRange(Metrics); + } return metrics.Where(x => x != null).ToList(); } diff --git a/src/DotNetCore.CAP/Dashboard/MessageHistoryRenderer.cs b/src/DotNetCore.CAP/Dashboard/MessageHistoryRenderer.cs index 990aee7..2df3b98 100644 --- a/src/DotNetCore.CAP/Dashboard/MessageHistoryRenderer.cs +++ b/src/DotNetCore.CAP/Dashboard/MessageHistoryRenderer.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Net; @@ -23,18 +26,13 @@ namespace DotNetCore.CAP.Dashboard { Register(StatusName.Succeeded, SucceededRenderer); Register(StatusName.Failed, FailedRenderer); - Register(StatusName.Processing, ProcessingRenderer); - BackgroundStateColors.Add(StatusName.Enqueued, "#F5F5F5"); BackgroundStateColors.Add(StatusName.Succeeded, "#EDF7ED"); BackgroundStateColors.Add(StatusName.Failed, "#FAEBEA"); - BackgroundStateColors.Add(StatusName.Processing, "#FCEFDC"); BackgroundStateColors.Add(StatusName.Scheduled, "#E0F3F8"); - ForegroundStateColors.Add(StatusName.Enqueued, "#999"); ForegroundStateColors.Add(StatusName.Succeeded, "#5cb85c"); ForegroundStateColors.Add(StatusName.Failed, "#d9534f"); - ForegroundStateColors.Add(StatusName.Processing, "#f0ad4e"); ForegroundStateColors.Add(StatusName.Scheduled, "#5bc0de"); } @@ -46,7 +44,9 @@ namespace DotNetCore.CAP.Dashboard public static string GetBackgroundStateColor(string stateName) { if (stateName == null || !BackgroundStateColors.ContainsKey(stateName)) + { return "inherit"; + } return BackgroundStateColors[stateName]; } @@ -59,7 +59,9 @@ namespace DotNetCore.CAP.Dashboard public static string GetForegroundStateColor(string stateName) { if (stateName == null || !ForegroundStateColors.ContainsKey(stateName)) + { return "inherit"; + } return ForegroundStateColors[stateName]; } @@ -68,9 +70,13 @@ namespace DotNetCore.CAP.Dashboard Func, NonEscapedString> renderer) { if (!Renderers.ContainsKey(state)) + { Renderers.Add(state, renderer); + } else + { Renderers[state] = renderer; + } } public static bool Exists(string state) @@ -96,7 +102,10 @@ namespace DotNetCore.CAP.Dashboard public static NonEscapedString DefaultRenderer(HtmlHelper helper, IDictionary stateData) { - if (stateData == null || stateData.Count == 0) return null; + if (stateData == null || stateData.Count == 0) + { + return null; + } var builder = new StringBuilder(); builder.Append("
"); @@ -146,7 +155,10 @@ namespace DotNetCore.CAP.Dashboard builder.Append("
"); - if (!itemsAdded) return null; + if (!itemsAdded) + { + return null; + } return new NonEscapedString(builder.ToString()); } @@ -166,9 +178,13 @@ namespace DotNetCore.CAP.Dashboard string serverId = null; if (stateData.ContainsKey("ServerId")) + { serverId = stateData["ServerId"]; + } else if (stateData.ContainsKey("ServerName")) + { serverId = stateData["ServerName"]; + } if (serverId != null) { diff --git a/src/DotNetCore.CAP/Dashboard/MessagesSidebarMenu.cs b/src/DotNetCore.CAP/Dashboard/MessagesSidebarMenu.cs index 0d68f5d..35a4dfd 100644 --- a/src/DotNetCore.CAP/Dashboard/MessagesSidebarMenu.cs +++ b/src/DotNetCore.CAP/Dashboard/MessagesSidebarMenu.cs @@ -1,3 +1,6 @@ +// 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.Collections.Generic; using DotNetCore.CAP.Dashboard.Resources; @@ -20,13 +23,6 @@ namespace DotNetCore.CAP.Dashboard Metric = DashboardMetrics.PublishedSucceededCount }); - PublishedItems.Add(page => - new MenuItem(Strings.SidebarMenu_Processing, page.Url.To("/published/processing")) - { - Active = page.RequestPath.StartsWith("/published/processing"), - Metric = DashboardMetrics.PublishedProcessingCount - }); - PublishedItems.Add(page => new MenuItem(Strings.SidebarMenu_Failed, page.Url.To("/published/failed")) { Active = page.RequestPath.StartsWith("/published/failed"), @@ -41,12 +37,6 @@ namespace DotNetCore.CAP.Dashboard Metric = DashboardMetrics.ReceivedSucceededCount }); - ReceivedItems.Add(page => new MenuItem(Strings.SidebarMenu_Processing, page.Url.To("/received/processing")) - { - Active = page.RequestPath.StartsWith("/received/processing"), - Metric = DashboardMetrics.ReceivedProcessingCount - }); - ReceivedItems.Add(page => new MenuItem(Strings.SidebarMenu_Failed, page.Url.To("/received/failed")) { Active = page.RequestPath.StartsWith("/received/failed"), diff --git a/src/DotNetCore.CAP/Dashboard/Metric.cs b/src/DotNetCore.CAP/Dashboard/Metric.cs index fafa537..661511e 100644 --- a/src/DotNetCore.CAP/Dashboard/Metric.cs +++ b/src/DotNetCore.CAP/Dashboard/Metric.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard { public class Metric { diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs index 5c2b1f0..1a256ba 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Dashboard.Monitoring { diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageQueryDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageQueryDto.cs index 98f3e97..02da29b 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageQueryDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageQueryDto.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Models; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Dashboard.Monitoring { diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/ServerDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/ServerDto.cs index 8564205..f332b5e 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/ServerDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/ServerDto.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Monitoring +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Monitoring { public class SubscriberDto { diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/StatisticsDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/StatisticsDto.cs index 38a8355..7c7a529 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/StatisticsDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/StatisticsDto.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Monitoring +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Monitoring { public class StatisticsDto { @@ -9,8 +12,5 @@ public int PublishedFailed { get; set; } public int ReceivedFailed { get; set; } - - public int PublishedProcessing { get; set; } - public int ReceivedProcessing { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/NavigationMenu.cs b/src/DotNetCore.CAP/Dashboard/NavigationMenu.cs index a0f94e7..4d8babd 100644 --- a/src/DotNetCore.CAP/Dashboard/NavigationMenu.cs +++ b/src/DotNetCore.CAP/Dashboard/NavigationMenu.cs @@ -1,3 +1,6 @@ +// 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.Collections.Generic; using DotNetCore.CAP.Dashboard.Resources; diff --git a/src/DotNetCore.CAP/Dashboard/NonEscapedString.cs b/src/DotNetCore.CAP/Dashboard/NonEscapedString.cs index 60b60b9..dcc235c 100644 --- a/src/DotNetCore.CAP/Dashboard/NonEscapedString.cs +++ b/src/DotNetCore.CAP/Dashboard/NonEscapedString.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard { public class NonEscapedString { diff --git a/src/DotNetCore.CAP/Dashboard/Pager.cs b/src/DotNetCore.CAP/Dashboard/Pager.cs index 8962ff0..55b7267 100644 --- a/src/DotNetCore.CAP/Dashboard/Pager.cs +++ b/src/DotNetCore.CAP/Dashboard/Pager.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; namespace DotNetCore.CAP.Dashboard @@ -35,14 +38,21 @@ namespace DotNetCore.CAP.Dashboard public string PageUrl(int page) { - if (page < 1 || page > TotalPageCount) return "#"; + if (page < 1 || page > TotalPageCount) + { + return "#"; + } return BasePageUrl + "?from=" + (page - 1) * RecordsPerPage + "&count=" + RecordsPerPage; } public string RecordsPerPageUrl(int perPage) { - if (perPage <= 0) return "#"; + if (perPage <= 0) + { + return "#"; + } + return BasePageUrl + "?from=0&count=" + perPage; } @@ -51,23 +61,35 @@ namespace DotNetCore.CAP.Dashboard // start page index _startPageIndex = CurrentPage - PageItemsCount / 2; if (_startPageIndex + PageItemsCount > TotalPageCount) + { _startPageIndex = TotalPageCount + 1 - PageItemsCount; + } + if (_startPageIndex < 1) + { _startPageIndex = 1; + } // end page index _endPageIndex = _startPageIndex + PageItemsCount - 1; if (_endPageIndex > TotalPageCount) + { _endPageIndex = TotalPageCount; + } var pagerItems = new List(); - if (TotalPageCount == 0) return pagerItems; + if (TotalPageCount == 0) + { + return pagerItems; + } AddPrevious(pagerItems); // first page if (_startPageIndex > 1) + { pagerItems.Add(new Item(1, false, ItemType.Page)); + } // more page before numeric page buttons AddMoreBefore(pagerItems); @@ -80,7 +102,9 @@ namespace DotNetCore.CAP.Dashboard // last page if (_endPageIndex < TotalPageCount) + { pagerItems.Add(new Item(TotalPageCount, false, ItemType.Page)); + } // Next page AddNext(pagerItems); @@ -99,7 +123,11 @@ namespace DotNetCore.CAP.Dashboard if (_startPageIndex > 2) { var index = _startPageIndex - 1; - if (index < 1) index = 1; + if (index < 1) + { + index = 1; + } + var item = new Item(index, false, ItemType.MorePage); results.Add(item); } @@ -110,7 +138,11 @@ namespace DotNetCore.CAP.Dashboard if (_endPageIndex < TotalPageCount - 1) { var index = _startPageIndex + PageItemsCount; - if (index > TotalPageCount) index = TotalPageCount; + if (index > TotalPageCount) + { + index = TotalPageCount; + } + var item = new Item(index, false, ItemType.MorePage); results.Add(item); } diff --git a/src/DotNetCore.CAP/Dashboard/Pages/BlockMetric.cs b/src/DotNetCore.CAP/Dashboard/Pages/BlockMetric.cs index 4687d2d..faa0c84 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/BlockMetric.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/BlockMetric.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { internal partial class BlockMetric { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/Breadcrumbs.cs b/src/DotNetCore.CAP/Dashboard/Pages/Breadcrumbs.cs index 20c2339..2295890 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/Breadcrumbs.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/Breadcrumbs.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// 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.Collections.Generic; namespace DotNetCore.CAP.Dashboard.Pages { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/HomePage.cs b/src/DotNetCore.CAP/Dashboard/Pages/HomePage.cs index 2e1b0ce..8773831 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/HomePage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/HomePage.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// 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.Collections.Generic; namespace DotNetCore.CAP.Dashboard.Pages { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/InlineMetric.cs b/src/DotNetCore.CAP/Dashboard/Pages/InlineMetric.cs index f24c19a..26b9ba1 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/InlineMetric.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/InlineMetric.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { internal partial class InlineMetric { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cs b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cs index 820402b..720e552 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { partial class LayoutPage { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml index 28ef474..e73ee90 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml +++ b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml @@ -16,68 +16,68 @@ - -
+ +
- - - - -
- @RenderBody() -
-
- - -
+ +
+ @RenderBody()
+
+ + + +
+
- + \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/Pages/NodePage.cs b/src/DotNetCore.CAP/Dashboard/Pages/NodePage.cs index 2728dc4..9f677b8 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/NodePage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/NodePage.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// 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.Collections.Generic; using DotNetCore.CAP.NodeDiscovery; using Microsoft.Extensions.DependencyInjection; @@ -31,8 +34,10 @@ namespace DotNetCore.CAP.Dashboard.Pages { return new List(); } + _nodes = _discoveryProvider.GetNodes().GetAwaiter().GetResult(); } + return _nodes; } } diff --git a/src/DotNetCore.CAP/Dashboard/Pages/PublishedPage.cs b/src/DotNetCore.CAP/Dashboard/Pages/PublishedPage.cs index 469ef0a..2ccece2 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/PublishedPage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/PublishedPage.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Dashboard.Pages { @@ -15,10 +18,10 @@ namespace DotNetCore.CAP.Dashboard.Pages { if (string.Equals(StatusName, Infrastructure.StatusName.Succeeded, StringComparison.CurrentCultureIgnoreCase)) + { return api.PublishedSucceededCount(); - if (string.Equals(StatusName, Infrastructure.StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - return api.PublishedProcessingCount(); + } + return api.PublishedFailedCount(); } } diff --git a/src/DotNetCore.CAP/Dashboard/Pages/ReceivedPage.cs b/src/DotNetCore.CAP/Dashboard/Pages/ReceivedPage.cs index ddf2caa..9090876 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/ReceivedPage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/ReceivedPage.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Dashboard.Pages { @@ -15,10 +18,10 @@ namespace DotNetCore.CAP.Dashboard.Pages { if (string.Equals(StatusName, Infrastructure.StatusName.Succeeded, StringComparison.CurrentCultureIgnoreCase)) + { return api.ReceivedSucceededCount(); - if (string.Equals(StatusName, Infrastructure.StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - return api.ReceivedProcessingCount(); + } + return api.ReceivedFailedCount(); } } diff --git a/src/DotNetCore.CAP/Dashboard/Pages/SidebarMenu.cs b/src/DotNetCore.CAP/Dashboard/Pages/SidebarMenu.cs index 8f79ae3..a2fcd41 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/SidebarMenu.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/SidebarMenu.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; namespace DotNetCore.CAP.Dashboard.Pages diff --git a/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cs b/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cs index e20da01..0794cf7 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { internal partial class Paginator { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cshtml b/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cshtml index 4c2b640..b377a57 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cshtml +++ b/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cshtml @@ -1,5 +1,4 @@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@ - @using DotNetCore.CAP.Dashboard @using DotNetCore.CAP.Dashboard.Resources @inherits DotNetCore.CAP.Dashboard.RazorPage diff --git a/src/DotNetCore.CAP/Dashboard/Pages/_PerPageSelector.cs b/src/DotNetCore.CAP/Dashboard/Pages/_PerPageSelector.cs index cbe86fb..6f88462 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/_PerPageSelector.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/_PerPageSelector.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { internal partial class PerPageSelector { diff --git a/src/DotNetCore.CAP/Dashboard/RazorPage.cs b/src/DotNetCore.CAP/Dashboard/RazorPage.cs index 93b3425..616441d 100644 --- a/src/DotNetCore.CAP/Dashboard/RazorPage.cs +++ b/src/DotNetCore.CAP/Dashboard/RazorPage.cs @@ -1,4 +1,7 @@ -using System; +// 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.Diagnostics; using System.Net; using System.Text; @@ -36,7 +39,11 @@ namespace DotNetCore.CAP.Dashboard { get { - if (_statisticsLazy == null) throw new InvalidOperationException("Page is not initialized."); + if (_statisticsLazy == null) + { + throw new InvalidOperationException("Page is not initialized."); + } + return _statisticsLazy.Value; } } @@ -104,6 +111,7 @@ namespace DotNetCore.CAP.Dashboard { return $"{discoveryOptions.NodeName}({discoveryOptions.NodeId})"; } + return null; } @@ -111,7 +119,7 @@ namespace DotNetCore.CAP.Dashboard { if (CapCache.Global.TryGet("cap.nodes.count", out var count)) { - dto.Servers = (int)count; + dto.Servers = (int) count; } else { @@ -128,7 +136,10 @@ namespace DotNetCore.CAP.Dashboard protected void WriteLiteral(string textToAppend) { if (string.IsNullOrEmpty(textToAppend)) + { return; + } + _content.Append(textToAppend); } @@ -136,7 +147,10 @@ namespace DotNetCore.CAP.Dashboard protected virtual void Write(object value) { if (value == null) + { return; + } + var html = value as NonEscapedString; WriteLiteral(html?.ToString() ?? Encode(value.ToString())); } diff --git a/src/DotNetCore.CAP/Dashboard/RazorPageDispatcher.cs b/src/DotNetCore.CAP/Dashboard/RazorPageDispatcher.cs index ade0988..f707a8d 100644 --- a/src/DotNetCore.CAP/Dashboard/RazorPageDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/RazorPageDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// 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.Text.RegularExpressions; using System.Threading.Tasks; diff --git a/src/DotNetCore.CAP/Dashboard/RouteCollection.cs b/src/DotNetCore.CAP/Dashboard/RouteCollection.cs index 3831ad6..b093e9f 100644 --- a/src/DotNetCore.CAP/Dashboard/RouteCollection.cs +++ b/src/DotNetCore.CAP/Dashboard/RouteCollection.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Text.RegularExpressions; @@ -11,24 +14,39 @@ namespace DotNetCore.CAP.Dashboard public void Add(string pathTemplate, IDashboardDispatcher dispatcher) { - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (dispatcher == null) throw new ArgumentNullException(nameof(dispatcher)); + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (dispatcher == null) + { + throw new ArgumentNullException(nameof(dispatcher)); + } _dispatchers.Add(new Tuple(pathTemplate, dispatcher)); } public Tuple FindDispatcher(string path) { - if (path.Length == 0) path = "/"; + if (path.Length == 0) + { + path = "/"; + } foreach (var dispatcher in _dispatchers) { var pattern = dispatcher.Item1; if (!pattern.StartsWith("^", StringComparison.OrdinalIgnoreCase)) + { pattern = "^" + pattern; + } + if (!pattern.EndsWith("$", StringComparison.OrdinalIgnoreCase)) + { pattern += "$"; + } var match = Regex.Match( path, @@ -36,7 +54,9 @@ namespace DotNetCore.CAP.Dashboard RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); if (match.Success) + { return new Tuple(dispatcher.Item2, match); + } } return null; diff --git a/src/DotNetCore.CAP/Dashboard/RouteCollectionExtensions.cs b/src/DotNetCore.CAP/Dashboard/RouteCollectionExtensions.cs index 393335c..dec40eb 100644 --- a/src/DotNetCore.CAP/Dashboard/RouteCollectionExtensions.cs +++ b/src/DotNetCore.CAP/Dashboard/RouteCollectionExtensions.cs @@ -1,4 +1,7 @@ -using System; +// 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.Text.RegularExpressions; namespace DotNetCore.CAP.Dashboard @@ -10,9 +13,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Func pageFunc) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (pageFunc == null) throw new ArgumentNullException(nameof(pageFunc)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (pageFunc == null) + { + throw new ArgumentNullException(nameof(pageFunc)); + } routes.Add(pathTemplate, new RazorPageDispatcher(pageFunc)); } @@ -22,9 +36,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Func command) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (command == null) throw new ArgumentNullException(nameof(command)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } routes.Add(pathTemplate, new CommandDispatcher(command)); } @@ -34,9 +59,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Func func) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (func == null) throw new ArgumentNullException(nameof(func)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (func == null) + { + throw new ArgumentNullException(nameof(func)); + } routes.Add(pathTemplate, new JsonDispatcher(func)); } @@ -46,9 +82,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Func jsonfunc) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (jsonfunc == null) throw new ArgumentNullException(nameof(jsonfunc)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (jsonfunc == null) + { + throw new ArgumentNullException(nameof(jsonfunc)); + } routes.Add(pathTemplate, new JsonDispatcher(jsonfunc)); } @@ -58,9 +105,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Action command) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (command == null) throw new ArgumentNullException(nameof(command)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } routes.Add(pathTemplate, new BatchCommandDispatcher(command)); } diff --git a/src/DotNetCore.CAP/Dashboard/UrlHelper.cs b/src/DotNetCore.CAP/Dashboard/UrlHelper.cs index 9eb89d1..b3acbfa 100644 --- a/src/DotNetCore.CAP/Dashboard/UrlHelper.cs +++ b/src/DotNetCore.CAP/Dashboard/UrlHelper.cs @@ -1,4 +1,7 @@ -using System; +// 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.Net; namespace DotNetCore.CAP.Dashboard diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 697d6db..816da2f 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -142,4 +142,9 @@ Strings.Designer.cs + + + RazorGenerator + + \ No newline at end of file diff --git a/src/DotNetCore.CAP/IBootstrapper.Default.cs b/src/DotNetCore.CAP/IBootstrapper.Default.cs index 64f298c..d89986a 100644 --- a/src/DotNetCore.CAP/IBootstrapper.Default.cs +++ b/src/DotNetCore.CAP/IBootstrapper.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -19,10 +22,6 @@ namespace DotNetCore.CAP private readonly ILogger _logger; private Task _bootstrappingTask; - private IStorage Storage { get; } - - private IEnumerable Processors { get; } - public DefaultBootstrapper( ILogger logger, IStorage storage, @@ -49,6 +48,10 @@ namespace DotNetCore.CAP }); } + private IStorage Storage { get; } + + private IEnumerable Processors { get; } + public Task BootstrapAsync() { return _bootstrappingTask = BootstrapTaskAsync(); @@ -58,15 +61,23 @@ namespace DotNetCore.CAP { await Storage.InitializeAsync(_cts.Token); - if (_cts.IsCancellationRequested) return; + if (_cts.IsCancellationRequested) + { + return; + } _appLifetime.ApplicationStopping.Register(() => { foreach (var item in Processors) + { item.Dispose(); + } }); - if (_cts.IsCancellationRequested) return; + if (_cts.IsCancellationRequested) + { + return; + } await BootstrapCoreAsync(); @@ -77,6 +88,7 @@ namespace DotNetCore.CAP protected virtual Task BootstrapCoreAsync() { foreach (var item in Processors) + { try { item.Start(); @@ -85,6 +97,8 @@ namespace DotNetCore.CAP { _logger.ProcessorsStartedError(ex); } + } + return Task.CompletedTask; } } diff --git a/src/DotNetCore.CAP/IBootstrapper.cs b/src/DotNetCore.CAP/IBootstrapper.cs index aacd1dc..9da1627 100644 --- a/src/DotNetCore.CAP/IBootstrapper.cs +++ b/src/DotNetCore.CAP/IBootstrapper.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP/ICallbackPublisher.cs b/src/DotNetCore.CAP/ICallbackPublisher.cs index 37170b9..0046e94 100644 --- a/src/DotNetCore.CAP/ICallbackPublisher.cs +++ b/src/DotNetCore.CAP/ICallbackPublisher.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; using DotNetCore.CAP.Models; namespace DotNetCore.CAP @@ -11,6 +14,6 @@ namespace DotNetCore.CAP /// /// Publish a callback message /// - Task PublishAsync(CapPublishedMessage obj); + Task PublishCallbackAsync(CapPublishedMessage obj); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/ICapOptionsExtension.cs b/src/DotNetCore.CAP/ICapOptionsExtension.cs index cc467b8..f6c2fc4 100644 --- a/src/DotNetCore.CAP/ICapOptionsExtension.cs +++ b/src/DotNetCore.CAP/ICapOptionsExtension.cs @@ -1,4 +1,7 @@ -using Microsoft.Extensions.DependencyInjection; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Extensions.DependencyInjection; namespace DotNetCore.CAP { @@ -10,7 +13,7 @@ namespace DotNetCore.CAP /// /// Registered child service. /// - /// add service to the + /// add service to the void AddServices(IServiceCollection services); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 9d0fd2f..8ba45e1 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -1,4 +1,7 @@ -using System.Data; +// 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.Data; using System.Threading.Tasks; namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP/ICapSubscribe.cs b/src/DotNetCore.CAP/ICapSubscribe.cs index b358609..9b536ad 100644 --- a/src/DotNetCore.CAP/ICapSubscribe.cs +++ b/src/DotNetCore.CAP/ICapSubscribe.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP { /// /// An empty interface, which is used to mark the current class have a CAP methods. diff --git a/src/DotNetCore.CAP/IConsumerClient.cs b/src/DotNetCore.CAP/IConsumerClient.cs index 4dd3143..8b46e71 100644 --- a/src/DotNetCore.CAP/IConsumerClient.cs +++ b/src/DotNetCore.CAP/IConsumerClient.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Threading; diff --git a/src/DotNetCore.CAP/IConsumerClientFactory.cs b/src/DotNetCore.CAP/IConsumerClientFactory.cs index 94b9cae..a5ce33f 100644 --- a/src/DotNetCore.CAP/IConsumerClientFactory.cs +++ b/src/DotNetCore.CAP/IConsumerClientFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP { /// /// Consumer client factory to create consumer client instance. diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index 645a121..86baecc 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -1,41 +1,41 @@ -using System; +// 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.Linq; using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace DotNetCore.CAP { internal class ConsumerHandler : IConsumerHandler { + private readonly IStorageConnection _connection; private readonly IConsumerClientFactory _consumerClientFactory; - private readonly CancellationTokenSource _cts; + private readonly IDispatcher _dispatcher; private readonly ILogger _logger; - private readonly TimeSpan _pollingDelay = TimeSpan.FromSeconds(1); private readonly MethodMatcherCache _selector; - private readonly IServiceProvider _serviceProvider; private Task _compositeTask; - private bool _disposed; - public ConsumerHandler( - IServiceProvider serviceProvider, - IConsumerClientFactory consumerClientFactory, + public ConsumerHandler(IConsumerClientFactory consumerClientFactory, + IDispatcher dispatcher, + IStorageConnection connection, ILogger logger, MethodMatcherCache selector) { _selector = selector; _logger = logger; - _serviceProvider = serviceProvider; _consumerClientFactory = consumerClientFactory; + _dispatcher = dispatcher; + _connection = connection; _cts = new CancellationTokenSource(); } @@ -44,25 +44,30 @@ namespace DotNetCore.CAP var groupingMatches = _selector.GetCandidatesMethodsOfGroupNameGrouped(); foreach (var matchGroup in groupingMatches) + { Task.Factory.StartNew(() => - { - using (var client = _consumerClientFactory.Create(matchGroup.Key)) - { - RegisterMessageProcessor(client); + { + using (var client = _consumerClientFactory.Create(matchGroup.Key)) + { + 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); - } - }, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + client.Listening(_pollingDelay, _cts.Token); + } + }, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + } - _compositeTask = Task.CompletedTask; + _compositeTask = Task.CompletedTask; } public void Dispose() { if (_disposed) + { return; + } + _disposed = true; _cts.Cancel(); try @@ -73,35 +78,35 @@ namespace DotNetCore.CAP { var innerEx = ex.InnerExceptions[0]; if (!(innerEx is OperationCanceledException)) + { _logger.ExpectedOperationCanceledException(innerEx); + } } } public void Pulse() { - SubscribeQueuer.PulseEvent.Set(); + //ignore } private void RegisterMessageProcessor(IConsumerClient client) { client.OnMessageReceived += (sender, message) => { - _logger.EnqueuingReceivedMessage(message.Name, message.Content); + try + { + var storedMessage = StoreMessage(message); + + client.Commit(); - using (var scope = _serviceProvider.CreateScope()) + _dispatcher.EnqueueToExecute(storedMessage); + } + catch (Exception e) { - try - { - StoreMessage(scope, message); - client.Commit(); - } - catch (Exception e) - { - _logger.LogError(e, "An exception occurred when storage received message. Message:'{0}'.", message); - client.Reject(); - } + _logger.LogError(e, "An exception occurred when storage received message. Message:'{0}'.", + message); + client.Reject(); } - Pulse(); }; client.OnLog += WriteLog; @@ -134,15 +139,15 @@ namespace DotNetCore.CAP } } - private static void StoreMessage(IServiceScope serviceScope, MessageContext messageContext) + private CapReceivedMessage StoreMessage(MessageContext messageContext) { - var provider = serviceScope.ServiceProvider; - var messageStore = provider.GetRequiredService(); var receivedMessage = new CapReceivedMessage(messageContext) { StatusName = StatusName.Scheduled }; - messageStore.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult(); + var id = _connection.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult(); + receivedMessage.Id = id; + return receivedMessage; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/IConsumerHandler.cs b/src/DotNetCore.CAP/IConsumerHandler.cs index f9e7c6c..86588e2 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP { /// /// Handler received message of subscribed. diff --git a/src/DotNetCore.CAP/IDispatcher.cs b/src/DotNetCore.CAP/IDispatcher.cs new file mode 100644 index 0000000..e8553ea --- /dev/null +++ b/src/DotNetCore.CAP/IDispatcher.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP +{ + public interface IDispatcher + { + void EnqueueToPublish(CapPublishedMessage message); + + void EnqueueToExecute(CapReceivedMessage message); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IFetchedMessage.cs b/src/DotNetCore.CAP/IFetchedMessage.cs index ca7acee..34a78ca 100644 --- a/src/DotNetCore.CAP/IFetchedMessage.cs +++ b/src/DotNetCore.CAP/IFetchedMessage.cs @@ -1,3 +1,6 @@ +// 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 DotNetCore.CAP.Models; diff --git a/src/DotNetCore.CAP/IProcessingServer.cs b/src/DotNetCore.CAP/IProcessingServer.cs index 5369800..b7baa9a 100644 --- a/src/DotNetCore.CAP/IProcessingServer.cs +++ b/src/DotNetCore.CAP/IProcessingServer.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP/IPublishExecutor.cs b/src/DotNetCore.CAP/IPublishExecutor.cs index 08245de..3997273 100644 --- a/src/DotNetCore.CAP/IPublishExecutor.cs +++ b/src/DotNetCore.CAP/IPublishExecutor.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; namespace DotNetCore.CAP { @@ -15,4 +18,4 @@ namespace DotNetCore.CAP /// Task PublishAsync(string keyName, string content); } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs new file mode 100644 index 0000000..d57fc8c --- /dev/null +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -0,0 +1,118 @@ +// 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.Diagnostics; +using System.Threading.Tasks; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Internal; +using DotNetCore.CAP.Models; +using DotNetCore.CAP.Processor; +using DotNetCore.CAP.Processor.States; +using Microsoft.Extensions.Logging; + +namespace DotNetCore.CAP +{ + public abstract class BasePublishMessageSender : IPublishMessageSender, IPublishExecutor + { + private readonly IStorageConnection _connection; + private readonly ILogger _logger; + private readonly CapOptions _options; + private readonly IStateChanger _stateChanger; + + protected BasePublishMessageSender( + ILogger logger, + CapOptions options, + IStorageConnection connection, + IStateChanger stateChanger) + { + _options = options; + _connection = connection; + _stateChanger = stateChanger; + _logger = logger; + } + + public abstract Task PublishAsync(string keyName, string content); + + public async Task SendAsync(CapPublishedMessage message) + { + var sp = Stopwatch.StartNew(); + + var result = await PublishAsync(message.Name, message.Content); + + sp.Stop(); + + if (result.Succeeded) + { + await SetSuccessfulState(message); + _logger.MessageHasBeenSent(sp.Elapsed.TotalSeconds); + + return OperateResult.Success; + } + + _logger.MessagePublishException(message.Id, result.Exception); + + await SetFailedState(message, result.Exception, out bool stillRetry); + + if (stillRetry) + { + _logger.SenderRetrying(3); + + await SendAsync(message); + } + return OperateResult.Failed(result.Exception); + } + + private static bool UpdateMessageForRetryAsync(CapPublishedMessage message) + { + var retryBehavior = RetryBehavior.DefaultRetry; + + var retries = ++message.Retries; + if (retries >= retryBehavior.RetryCount) + { + return false; + } + + var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); + message.ExpiresAt = due; + + return true; + } + + private Task SetSuccessfulState(CapPublishedMessage message) + { + var succeededState = new SucceededState(_options.SucceedMessageExpiredAfter); + + return _stateChanger.ChangeStateAsync(message, succeededState, _connection); + } + + private Task SetFailedState(CapPublishedMessage message, Exception ex, out bool stillRetry) + { + IState newState = new FailedState(); + + if (ex is PublisherSentFailedException) + { + stillRetry = false; + message.Retries = _options.FailedRetryCount; // not retry if PublisherSentFailedException + } + else + { + stillRetry = UpdateMessageForRetryAsync(message); + if (stillRetry) + { + _logger.ConsumerExecutionFailedWillRetry(ex); + return Task.CompletedTask; + } + } + + AddErrorReasonToContent(message, ex); + + return _stateChanger.ChangeStateAsync(message, newState, _connection); + } + + private static void AddErrorReasonToContent(CapPublishedMessage message, Exception exception) + { + message.Content = Helper.AddExceptionProperty(message.Content, exception); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IPublishMessageSender.cs b/src/DotNetCore.CAP/IPublishMessageSender.cs new file mode 100644 index 0000000..c99afd8 --- /dev/null +++ b/src/DotNetCore.CAP/IPublishMessageSender.cs @@ -0,0 +1,13 @@ +// 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.Threading.Tasks; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP +{ + public interface IPublishMessageSender + { + Task SendAsync(CapPublishedMessage message); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IQueueExecutor.Publish.Base.cs b/src/DotNetCore.CAP/IQueueExecutor.Publish.Base.cs deleted file mode 100644 index 194e595..0000000 --- a/src/DotNetCore.CAP/IQueueExecutor.Publish.Base.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP -{ - public abstract class BasePublishQueueExecutor : IQueueExecutor, IPublishExecutor - { - private readonly ILogger _logger; - private readonly CapOptions _options; - private readonly IStateChanger _stateChanger; - - protected BasePublishQueueExecutor( - CapOptions options, - IStateChanger stateChanger, - ILogger logger) - { - _options = options; - _stateChanger = stateChanger; - _logger = logger; - } - - public async Task ExecuteAsync(IStorageConnection connection, IFetchedMessage fetched) - { - var message = await connection.GetPublishedMessageAsync(fetched.MessageId); - try - { - var sp = Stopwatch.StartNew(); - await _stateChanger.ChangeStateAsync(message, new ProcessingState(), connection); - - if (message.Retries > 0) - _logger.JobRetrying(message.Retries); - var result = await PublishAsync(message.Name, message.Content); - sp.Stop(); - - IState newState; - if (!result.Succeeded) - { - var shouldRetry = UpdateMessageForRetryAsync(message); - if (shouldRetry) - { - newState = new ScheduledState(); - _logger.JobFailedWillRetry(result.Exception); - } - else - { - newState = new FailedState(); - _logger.JobFailed(result.Exception); - } - message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); - } - else - { - newState = new SucceededState(_options.SucceedMessageExpiredAfter); - } - await _stateChanger.ChangeStateAsync(message, newState, connection); - - fetched.RemoveFromQueue(); - - if (result.Succeeded) - _logger.JobExecuted(sp.Elapsed.TotalSeconds); - - return OperateResult.Success; - } - catch (Exception ex) - { - fetched.Requeue(); - _logger.ExceptionOccuredWhileExecuting(message?.Name, ex); - return OperateResult.Failed(ex); - } - } - - public abstract Task PublishAsync(string keyName, string content); - - private static bool UpdateMessageForRetryAsync(CapPublishedMessage message) - { - var retryBehavior = RetryBehavior.DefaultRetry; - - var retries = ++message.Retries; - if (retries >= retryBehavior.RetryCount) - return false; - - var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); - message.ExpiresAt = due; - - return true; - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs b/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs deleted file mode 100644 index d1ad6ea..0000000 --- a/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Internal; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP -{ - public class SubscribeQueueExecutor : IQueueExecutor - { - private readonly ILogger _logger; - private readonly CapOptions _options; - private readonly IStateChanger _stateChanger; - private readonly ISubscriberExecutor _subscriberExecutor; - - public SubscribeQueueExecutor( - CapOptions options, - IStateChanger stateChanger, - ISubscriberExecutor subscriberExecutor, - ILogger logger) - { - _options = options; - _subscriberExecutor = subscriberExecutor; - _stateChanger = stateChanger; - _logger = logger; - } - - public async Task ExecuteAsync(IStorageConnection connection, IFetchedMessage fetched) - { - var message = await connection.GetReceivedMessageAsync(fetched.MessageId); - - if (message == null) - { - _logger.LogError($"Can not found the `message` at cap received message table, message id:{fetched.MessageId} !!!"); - return OperateResult.Failed(); - } - - try - { - var sp = Stopwatch.StartNew(); - await _stateChanger.ChangeStateAsync(message, new ProcessingState(), connection); - - if (message.Retries > 0) - _logger.JobRetrying(message.Retries); - - var result = await _subscriberExecutor.ExecuteAsync(message); - sp.Stop(); - - var state = GetNewState(result, message); - - await _stateChanger.ChangeStateAsync(message, state, connection); - - fetched.RemoveFromQueue(); - - if (result.Succeeded) - _logger.JobExecuted(sp.Elapsed.TotalSeconds); - - return OperateResult.Success; - } - catch (SubscriberNotFoundException ex) - { - _logger.LogError(ex.Message); - - AddErrorReasonToContent(message, ex); - - ++message.Retries; //issue: https://github.com/dotnetcore/CAP/issues/90 - - await _stateChanger.ChangeStateAsync(message, new FailedState(), connection); - - fetched.RemoveFromQueue(); - - return OperateResult.Failed(ex); - } - catch (Exception ex) - { - _logger.ExceptionOccuredWhileExecuting(message.Name, ex); - - fetched.Requeue(); - - return OperateResult.Failed(ex); - } - } - - private IState GetNewState(OperateResult result, CapReceivedMessage message) - { - IState newState; - if (!result.Succeeded) - { - var shouldRetry = UpdateMessageForRetry(message); - if (shouldRetry) - { - newState = new ScheduledState(); - _logger.JobFailedWillRetry(result.Exception); - } - else - { - newState = new FailedState(); - _logger.JobFailed(result.Exception); - } - AddErrorReasonToContent(message, result.Exception); - } - else - { - newState = new SucceededState(_options.SucceedMessageExpiredAfter); - } - return newState; - } - - private static bool UpdateMessageForRetry(CapReceivedMessage message) - { - var retryBehavior = RetryBehavior.DefaultRetry; - - var retries = ++message.Retries; - if (retries >= retryBehavior.RetryCount) - return false; - - var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); - message.ExpiresAt = due; - - return true; - } - - private static void AddErrorReasonToContent(CapReceivedMessage message, Exception exception) - { - message.Content = Helper.AddExceptionProperty(message.Content, exception); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IQueueExecutor.cs b/src/DotNetCore.CAP/IQueueExecutor.cs deleted file mode 100644 index ad4a6b1..0000000 --- a/src/DotNetCore.CAP/IQueueExecutor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace DotNetCore.CAP -{ - public interface IQueueExecutor - { - Task ExecuteAsync(IStorageConnection connection, IFetchedMessage message); - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IQueueExecutorFactory.cs b/src/DotNetCore.CAP/IQueueExecutorFactory.cs deleted file mode 100644 index 5b46ac6..0000000 --- a/src/DotNetCore.CAP/IQueueExecutorFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP -{ - public interface IQueueExecutorFactory - { - IQueueExecutor GetInstance(MessageType messageType); - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IStorage.cs b/src/DotNetCore.CAP/IStorage.cs index 1f857ac..a31b351 100644 --- a/src/DotNetCore.CAP/IStorage.cs +++ b/src/DotNetCore.CAP/IStorage.cs @@ -1,3 +1,6 @@ +// 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.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Dashboard; diff --git a/src/DotNetCore.CAP/IStorageConnection.cs b/src/DotNetCore.CAP/IStorageConnection.cs index a4f7b0b..18d5ff8 100644 --- a/src/DotNetCore.CAP/IStorageConnection.cs +++ b/src/DotNetCore.CAP/IStorageConnection.cs @@ -1,3 +1,6 @@ +// 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.Collections.Generic; using System.Threading.Tasks; @@ -18,20 +21,10 @@ namespace DotNetCore.CAP /// The message's id. Task GetPublishedMessageAsync(int id); - /// - /// Fetches the next message to be executed. - /// - Task FetchNextMessageAsync(); - - /// - /// Returns the next message to be enqueued. - /// - Task GetNextPublishedMessageToBeEnqueuedAsync(); - /// /// Returns executed failed messages. /// - Task> GetFailedPublishedMessages(); + Task> GetPublishedMessagesOfNeedRetry(); // Received messages @@ -39,7 +32,7 @@ namespace DotNetCore.CAP /// Stores the message. /// /// The message to store. - Task StoreReceivedMessageAsync(CapReceivedMessage message); + Task StoreReceivedMessageAsync(CapReceivedMessage message); /// /// Returns the message with the given id. @@ -47,15 +40,10 @@ namespace DotNetCore.CAP /// The message's id. Task GetReceivedMessageAsync(int id); - /// - /// Returns the next message to be enqueued. - /// - Task GetNextReceivedMessageToBeEnqueuedAsync(); - /// /// Returns executed failed message. /// - Task> GetFailedReceivedMessages(); + Task> GetReceivedMessagesOfNeedRetry(); /// /// Creates and returns an . diff --git a/src/DotNetCore.CAP/IStorageTransaction.cs b/src/DotNetCore.CAP/IStorageTransaction.cs index 05fea1b..7ee5185 100644 --- a/src/DotNetCore.CAP/IStorageTransaction.cs +++ b/src/DotNetCore.CAP/IStorageTransaction.cs @@ -1,4 +1,7 @@ -using System; +// 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; using DotNetCore.CAP.Models; @@ -14,10 +17,6 @@ namespace DotNetCore.CAP void UpdateMessage(CapReceivedMessage message); - void EnqueueMessage(CapPublishedMessage message); - - void EnqueueMessage(CapReceivedMessage message); - Task CommitAsync(); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs new file mode 100644 index 0000000..0ea2b13 --- /dev/null +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -0,0 +1,159 @@ +// 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.Diagnostics; +using System.Threading.Tasks; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Internal; +using DotNetCore.CAP.Models; +using DotNetCore.CAP.Processor; +using DotNetCore.CAP.Processor.States; +using Microsoft.Extensions.Logging; + +namespace DotNetCore.CAP +{ + internal class DefaultSubscriberExecutor : ISubscriberExecutor + { + private readonly ICallbackMessageSender _callbackMessageSender; + private readonly IStorageConnection _connection; + private readonly ILogger _logger; + private readonly CapOptions _options; + + private readonly MethodMatcherCache _selector; + private readonly IStateChanger _stateChanger; + + public DefaultSubscriberExecutor( + ILogger logger, + CapOptions options, + IConsumerInvokerFactory consumerInvokerFactory, + ICallbackMessageSender callbackMessageSender, + IStateChanger stateChanger, + IStorageConnection connection, + MethodMatcherCache selector) + { + _selector = selector; + _callbackMessageSender = callbackMessageSender; + _options = options; + _stateChanger = stateChanger; + _connection = connection; + _logger = logger; + + Invoker = consumerInvokerFactory.CreateInvoker(); + } + + private IConsumerInvoker Invoker { get; } + + public async Task ExecuteAsync(CapReceivedMessage message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + try + { + var sp = Stopwatch.StartNew(); + + await InvokeConsumerMethodAsync(message); + + sp.Stop(); + + await SetSuccessfulState(message); + + _logger.ConsumerExecuted(sp.Elapsed.TotalSeconds); + + return OperateResult.Success; + } + catch (Exception ex) + { + _logger.ExceptionOccuredWhileExecuting(message.Name, ex); + + await SetFailedState(message, ex, out bool stillRetry); + if (stillRetry) + { + await ExecuteAsync(message); + } + + return OperateResult.Failed(ex); + } + } + + private Task SetSuccessfulState(CapReceivedMessage message) + { + var succeededState = new SucceededState(_options.SucceedMessageExpiredAfter); + + return _stateChanger.ChangeStateAsync(message, succeededState, _connection); + } + + private Task SetFailedState(CapReceivedMessage message, Exception ex, out bool stillRetry) + { + IState newState = new FailedState(); + + if (ex is SubscriberNotFoundException) + { + stillRetry = false; + message.Retries = _options.FailedRetryCount; // not retry if SubscriberNotFoundException + } + else + { + stillRetry = UpdateMessageForRetry(message); + if (stillRetry) + { + _logger.ConsumerExecutionFailedWillRetry(ex); + return Task.CompletedTask; + } + } + + AddErrorReasonToContent(message, ex); + + return _stateChanger.ChangeStateAsync(message, newState, _connection); + } + + private static bool UpdateMessageForRetry(CapReceivedMessage message) + { + var retryBehavior = RetryBehavior.DefaultRetry; + + var retries = ++message.Retries; + if (retries >= retryBehavior.RetryCount) + { + return false; + } + + var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); + message.ExpiresAt = due; + + return true; + } + + private static void AddErrorReasonToContent(CapReceivedMessage message, Exception exception) + { + message.Content = Helper.AddExceptionProperty(message.Content, exception); + } + + private async Task InvokeConsumerMethodAsync(CapReceivedMessage receivedMessage) + { + if (!_selector.TryGetTopicExector(receivedMessage.Name, receivedMessage.Group, + out var executor)) + { + var error = $"message can not be found subscriber, Message:{receivedMessage},\r\n see: https://github.com/dotnetcore/CAP/issues/63"; + throw new SubscriberNotFoundException(error); + } + try + { + var consumerContext = new ConsumerContext(executor, receivedMessage.ToMessageContext()); + + var ret = await Invoker.InvokeAsync(consumerContext); + + if (!string.IsNullOrEmpty(ret.CallbackName)) + { + await _callbackMessageSender.SendAsync(ret.MessageId, ret.CallbackName, ret.Result); + } + } + catch (Exception ex) + { + throw new SubscriberExecutionFailedException(ex.Message, ex); + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/ISubscriberExecutor.cs b/src/DotNetCore.CAP/ISubscriberExecutor.cs new file mode 100644 index 0000000..a099b60 --- /dev/null +++ b/src/DotNetCore.CAP/ISubscriberExecutor.cs @@ -0,0 +1,13 @@ +// 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.Threading.Tasks; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP +{ + public interface ISubscriberExecutor + { + Task ExecuteAsync(CapReceivedMessage message); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Infrastructure/Helper.cs b/src/DotNetCore.CAP/Infrastructure/Helper.cs index b04592e..92ae6fb 100644 --- a/src/DotNetCore.CAP/Infrastructure/Helper.cs +++ b/src/DotNetCore.CAP/Infrastructure/Helper.cs @@ -1,4 +1,7 @@ -using System; +// 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.ComponentModel; using System.Reflection; using Newtonsoft.Json; @@ -32,7 +35,10 @@ namespace DotNetCore.CAP.Infrastructure public static object FromJson(string value, Type type) { - if (type == null) throw new ArgumentNullException(nameof(type)); + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } return value != null ? JsonConvert.DeserializeObject(value, type, _serializerSettings) @@ -54,13 +60,19 @@ namespace DotNetCore.CAP.Infrastructure public static bool IsController(TypeInfo typeInfo) { if (!typeInfo.IsClass) + { return false; + } if (typeInfo.IsAbstract) + { return false; + } if (!typeInfo.IsPublic) + { return false; + } return !typeInfo.ContainsGenericParameters && typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase); @@ -149,9 +161,13 @@ namespace DotNetCore.CAP.Infrastructure var jObj = JObject.Parse(json); if (jObj.TryGetValue(propertyName, out var _)) + { jObj[propertyName] = propertyValue; + } else + { jObj.Add(new JProperty(propertyName, propertyValue)); + } return jObj.ToString(Formatting.None); } diff --git a/src/DotNetCore.CAP/Infrastructure/ObjectId.cs b/src/DotNetCore.CAP/Infrastructure/ObjectId.cs index 4a51166..4b93ce6 100644 --- a/src/DotNetCore.CAP/Infrastructure/ObjectId.cs +++ b/src/DotNetCore.CAP/Infrastructure/ObjectId.cs @@ -1,4 +1,7 @@ -using System; +// 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.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -57,7 +60,10 @@ namespace DotNetCore.CAP.Infrastructure public ObjectId(byte[] bytes) { if (bytes == null) + { throw new ArgumentNullException("bytes"); + } + Unpack(bytes, out _timestamp, out _machine, out _pid, out _increment); } @@ -83,11 +89,16 @@ namespace DotNetCore.CAP.Infrastructure public ObjectId(int timestamp, int machine, short pid, int increment) { if ((machine & 0xff000000) != 0) + { throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); + } + if ((increment & 0xff000000) != 0) + { throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); + } _timestamp = timestamp; _machine = machine; @@ -102,7 +113,10 @@ namespace DotNetCore.CAP.Infrastructure public ObjectId(string value) { if (value == null) + { throw new ArgumentNullException("value"); + } + Unpack(ParseHexString(value), out _timestamp, out _machine, out _pid, out _increment); } @@ -256,11 +270,16 @@ namespace DotNetCore.CAP.Infrastructure public static byte[] Pack(int timestamp, int machine, short pid, int increment) { if ((machine & 0xff000000) != 0) + { throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); + } + if ((increment & 0xff000000) != 0) + { throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); + } var bytes = new byte[12]; bytes[0] = (byte) (timestamp >> 24); @@ -286,9 +305,15 @@ namespace DotNetCore.CAP.Infrastructure public static ObjectId Parse(string s) { if (s == null) + { throw new ArgumentNullException("s"); + } + if (s.Length != 24) + { throw new ArgumentOutOfRangeException("s", "ObjectId string value must be 24 characters."); + } + return new ObjectId(ParseHexString(s)); } @@ -303,9 +328,15 @@ namespace DotNetCore.CAP.Infrastructure public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment) { if (bytes == null) + { throw new ArgumentNullException("bytes"); + } + if (bytes.Length != 12) + { throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long."); + } + timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3]; machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6]; pid = (short) ((bytes[7] << 8) + bytes[8]); @@ -349,11 +380,23 @@ namespace DotNetCore.CAP.Infrastructure public int CompareTo(ObjectId other) { var r = _timestamp.CompareTo(other._timestamp); - if (r != 0) return r; + if (r != 0) + { + return r; + } + r = _machine.CompareTo(other._machine); - if (r != 0) return r; + if (r != 0) + { + return r; + } + r = _pid.CompareTo(other._pid); - if (r != 0) return r; + if (r != 0) + { + return r; + } + return _increment.CompareTo(other._increment); } @@ -379,7 +422,10 @@ namespace DotNetCore.CAP.Infrastructure public override bool Equals(object obj) { if (obj is ObjectId) + { return Equals((ObjectId) obj); + } + return false; } @@ -423,15 +469,21 @@ namespace DotNetCore.CAP.Infrastructure public static byte[] ParseHexString(string s) { if (s == null) + { throw new ArgumentNullException("s"); + } if (s.Length % 2 == 1) + { throw new Exception("The binary key cannot have an odd number of digits"); + } var arr = new byte[s.Length >> 1]; for (var i = 0; i < s.Length >> 1; ++i) + { arr[i] = (byte) ((GetHexVal(s[i << 1]) << 4) + GetHexVal(s[(i << 1) + 1])); + } return arr; } @@ -444,7 +496,10 @@ namespace DotNetCore.CAP.Infrastructure public static string ToHexString(byte[] bytes) { if (bytes == null) + { throw new ArgumentNullException("bytes"); + } + var result = new char[bytes.Length * 2]; for (var i = 0; i < bytes.Length; i++) { @@ -452,6 +507,7 @@ namespace DotNetCore.CAP.Infrastructure result[2 * i] = (char) val; result[2 * i + 1] = (char) (val >> 16); } + return new string(result); } @@ -474,9 +530,15 @@ namespace DotNetCore.CAP.Infrastructure public static DateTime ToUniversalTime(DateTime dateTime) { if (dateTime == DateTime.MinValue) + { return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + } + if (dateTime == DateTime.MaxValue) + { return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc); + } + return dateTime.ToUniversalTime(); } diff --git a/src/DotNetCore.CAP/Infrastructure/StatusName.cs b/src/DotNetCore.CAP/Infrastructure/StatusName.cs index 62ee0fd..65d9427 100644 --- a/src/DotNetCore.CAP/Infrastructure/StatusName.cs +++ b/src/DotNetCore.CAP/Infrastructure/StatusName.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Infrastructure +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Infrastructure { /// /// The message status name. @@ -6,8 +9,6 @@ public struct StatusName { public const string Scheduled = nameof(Scheduled); - public const string Enqueued = nameof(Enqueued); - public const string Processing = nameof(Processing); public const string Succeeded = nameof(Succeeded); public const string Failed = nameof(Failed); } diff --git a/src/DotNetCore.CAP/Infrastructure/WaitHandleEx.cs b/src/DotNetCore.CAP/Infrastructure/WaitHandleEx.cs index 5a277d2..14ed410 100644 --- a/src/DotNetCore.CAP/Infrastructure/WaitHandleEx.cs +++ b/src/DotNetCore.CAP/Infrastructure/WaitHandleEx.cs @@ -1,4 +1,7 @@ -using System; +// 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; using System.Threading.Tasks; diff --git a/src/DotNetCore.CAP/Internal/CapCache.cs b/src/DotNetCore.CAP/Internal/CapCache.cs index d9826db..243f935 100644 --- a/src/DotNetCore.CAP/Internal/CapCache.cs +++ b/src/DotNetCore.CAP/Internal/CapCache.cs @@ -1,3 +1,6 @@ +// 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.Collections.Generic; using System.Linq; @@ -56,6 +59,7 @@ namespace DotNetCore.CAP.Internal Clear(); _locker.Dispose(); } + // Dispose unmanaged resources } } @@ -71,7 +75,9 @@ namespace DotNetCore.CAP.Internal try { foreach (var t in _timers.Values) + { t.Dispose(); + } } catch { @@ -98,9 +104,11 @@ namespace DotNetCore.CAP.Internal if (_timers.TryGetValue(key, out timer)) { if (restartTimerIfExists) + { timer.Change( cacheTimeout ?? Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); + } } else { @@ -139,7 +147,10 @@ namespace DotNetCore.CAP.Internal /// public void AddOrUpdate(K key, T cacheObject, TimeSpan? cacheTimeout, bool restartTimerIfExists = false) { - if (disposed) return; + if (disposed) + { + return; + } _locker.EnterWriteLock(); try @@ -147,9 +158,13 @@ namespace DotNetCore.CAP.Internal CheckTimer(key, cacheTimeout, restartTimerIfExists); if (!_cache.ContainsKey(key)) + { _cache.Add(key, cacheObject); + } else + { _cache[key] = cacheObject; + } } finally { @@ -182,7 +197,10 @@ namespace DotNetCore.CAP.Internal /// The object from the cache or default(T), if not found. public T Get(K key) { - if (disposed) return default(T); + if (disposed) + { + return default(T); + } _locker.EnterReadLock(); try @@ -227,7 +245,10 @@ namespace DotNetCore.CAP.Internal /// The key pattern to remove. The Predicate has to return true to get key removed. public void Remove(Predicate keyPattern) { - if (disposed) return; + if (disposed) + { + return; + } _locker.EnterWriteLock(); try @@ -245,6 +266,7 @@ namespace DotNetCore.CAP.Internal catch { } + _timers.Remove(workKey); _cache.Remove(workKey); } @@ -262,7 +284,10 @@ namespace DotNetCore.CAP.Internal /// The cache-key to remove. public void Remove(K key) { - if (disposed) return; + if (disposed) + { + return; + } _locker.EnterWriteLock(); try @@ -276,6 +301,7 @@ namespace DotNetCore.CAP.Internal catch { } + _timers.Remove(key); _cache.Remove(key); } @@ -293,7 +319,10 @@ namespace DotNetCore.CAP.Internal /// True if the key exists in the cache, otherwise False. public bool Exists(K key) { - if (disposed) return false; + if (disposed) + { + return false; + } _locker.EnterReadLock(); try diff --git a/src/DotNetCore.CAP/Internal/ConsumerContext.cs b/src/DotNetCore.CAP/Internal/ConsumerContext.cs index cafd33f..5610d7a 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerContext.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerContext.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs b/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs index 093fbb3..d63bfd6 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs @@ -1,6 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP.Internal { @@ -19,4 +18,4 @@ namespace DotNetCore.CAP.Internal public string CallbackName { get; set; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs b/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs index b69f394..07dca25 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs @@ -1,4 +1,7 @@ -using System.Reflection; +// 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.Reflection; using DotNetCore.CAP.Abstractions; namespace DotNetCore.CAP.Internal diff --git a/src/DotNetCore.CAP/Internal/ConsumerInvokerFactory.cs b/src/DotNetCore.CAP/Internal/ConsumerInvokerFactory.cs index c6eb9e0..90633dc 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerInvokerFactory.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerInvokerFactory.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Abstractions; using Microsoft.Extensions.Logging; @@ -6,7 +9,7 @@ namespace DotNetCore.CAP.Internal { internal class ConsumerInvokerFactory : IConsumerInvokerFactory { - private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly IMessagePacker _messagePacker; private readonly IModelBinderFactory _modelBinderFactory; private readonly IServiceProvider _serviceProvider; @@ -17,7 +20,7 @@ namespace DotNetCore.CAP.Internal IModelBinderFactory modelBinderFactory, IServiceProvider serviceProvider) { - _logger = loggerFactory.CreateLogger(); + _loggerFactory = loggerFactory; _messagePacker = messagePacker; _modelBinderFactory = modelBinderFactory; _serviceProvider = serviceProvider; @@ -25,7 +28,7 @@ namespace DotNetCore.CAP.Internal public IConsumerInvoker CreateInvoker() { - return new DefaultConsumerInvoker(_logger, _serviceProvider, _messagePacker, _modelBinderFactory); + return new DefaultConsumerInvoker(_loggerFactory, _serviceProvider, _messagePacker, _modelBinderFactory); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/HashCodeCombiner.cs b/src/DotNetCore.CAP/Internal/HashCodeCombiner.cs index 842d8ff..43f000e 100644 --- a/src/DotNetCore.CAP/Internal/HashCodeCombiner.cs +++ b/src/DotNetCore.CAP/Internal/HashCodeCombiner.cs @@ -1,4 +1,7 @@ -using System.Collections; +// 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.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -10,7 +13,8 @@ namespace DotNetCore.CAP.Internal public int CombinedHash { - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _combinedHash64.GetHashCode(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _combinedHash64.GetHashCode(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -34,6 +38,7 @@ namespace DotNetCore.CAP.Internal Add(o); count++; } + Add(count); } } diff --git a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs index 1a4f446..1dc7fc8 100644 --- a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs +++ b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Infrastructure; @@ -10,10 +13,10 @@ namespace DotNetCore.CAP.Internal { internal class CallbackMessageSender : ICallbackMessageSender { - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; private readonly IContentSerializer _contentSerializer; + private readonly ILogger _logger; private readonly IMessagePacker _messagePacker; + private readonly IServiceProvider _serviceProvider; public CallbackMessageSender( ILogger logger, @@ -31,9 +34,13 @@ namespace DotNetCore.CAP.Internal { string body; if (bodyObj != null && Helper.IsComplexType(bodyObj.GetType())) + { body = _contentSerializer.Serialize(bodyObj); + } else + { body = bodyObj?.ToString(); + } _logger.LogDebug($"Callback message will publishing, name:{topicName},content:{body}"); @@ -56,8 +63,8 @@ namespace DotNetCore.CAP.Internal { var provider = scope.ServiceProvider; var callbackPublisher = provider.GetService(); - await callbackPublisher.PublishAsync(publishedMessage); + await callbackPublisher.PublishCallbackAsync(publishedMessage); } } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.cs b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.cs index 2f7d3e6..b66cf9b 100644 --- a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.cs +++ b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/IConsumerInvoker.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerInvoker.Default.cs index 7303f80..7fc4d0b 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerInvoker.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerInvoker.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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; using DotNetCore.CAP.Abstractions; using Microsoft.Extensions.DependencyInjection; @@ -10,11 +13,11 @@ namespace DotNetCore.CAP.Internal internal class DefaultConsumerInvoker : IConsumerInvoker { private readonly ILogger _logger; + private readonly IMessagePacker _messagePacker; private readonly IModelBinderFactory _modelBinderFactory; private readonly IServiceProvider _serviceProvider; - private readonly IMessagePacker _messagePacker; - public DefaultConsumerInvoker(ILogger logger, + public DefaultConsumerInvoker(ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IMessagePacker messagePacker, IModelBinderFactory modelBinderFactory) @@ -22,7 +25,7 @@ namespace DotNetCore.CAP.Internal _modelBinderFactory = modelBinderFactory; _serviceProvider = serviceProvider; _messagePacker = messagePacker; - _logger = logger; + _logger = loggerFactory.CreateLogger(); } public async Task InvokeAsync(ConsumerContext context) @@ -44,9 +47,14 @@ namespace DotNetCore.CAP.Internal object resultObj; if (executor.MethodParameters.Length > 0) + { resultObj = await ExecuteWithParameterAsync(executor, obj, message.Content); + } else + { resultObj = await ExecuteAsync(executor, obj); + } + return new ConsumerExecutedResult(resultObj, message.Id, message.CallbackName); } } @@ -54,7 +62,10 @@ namespace DotNetCore.CAP.Internal private async Task ExecuteAsync(ObjectMethodExecutor executor, object @class) { if (executor.IsMethodAsync) + { return await executor.ExecuteAsync(@class); + } + return executor.Execute(@class); } @@ -69,9 +80,13 @@ namespace DotNetCore.CAP.Internal if (bindResult.IsSuccess) { if (executor.IsMethodAsync) + { return await executor.ExecuteAsync(@class, bindResult.Model); + } + return executor.Execute(@class, bindResult.Model); } + throw new MethodBindException( $"Parameters:{firstParameter.Name} bind failed! ParameterString is: {parameterString} "); } diff --git a/src/DotNetCore.CAP/Internal/IConsumerInvoker.cs b/src/DotNetCore.CAP/Internal/IConsumerInvoker.cs index 13c7964..892c2e3 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerInvoker.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerInvoker.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/IConsumerInvokerFactory.cs b/src/DotNetCore.CAP/Internal/IConsumerInvokerFactory.cs index 44d38d5..9fd7e74 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerInvokerFactory.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerInvokerFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Internal +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Internal { internal interface IConsumerInvokerFactory { diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs index e20d306..7bf92f3 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Linq; using System.Reflection; @@ -10,7 +13,7 @@ namespace DotNetCore.CAP.Internal { /// /// - /// A default implementation. + /// A default implementation. /// internal class DefaultConsumerServiceSelector : IConsumerServiceSelector { @@ -18,7 +21,7 @@ namespace DotNetCore.CAP.Internal private readonly IServiceProvider _serviceProvider; /// - /// Creates a new . + /// Creates a new . /// public DefaultConsumerServiceSelector(IServiceProvider serviceProvider, CapOptions capOptions) { @@ -27,9 +30,9 @@ namespace DotNetCore.CAP.Internal } /// - /// Selects the best candidate from for - /// the - /// current message associated. + /// Selects the best candidate from for + /// the + /// current message associated. /// public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList executeDescriptor) @@ -61,10 +64,13 @@ namespace DotNetCore.CAP.Internal { var typeInfo = service.GetType().GetTypeInfo(); if (!typeof(ICapSubscribe).GetTypeInfo().IsAssignableFrom(typeInfo)) + { continue; + } executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); } + return executorDescriptorList; } } @@ -78,7 +84,9 @@ namespace DotNetCore.CAP.Internal { var typeInfo = type.GetTypeInfo(); if (Helper.IsController(typeInfo)) + { executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); + } } return executorDescriptorList; @@ -91,12 +99,18 @@ namespace DotNetCore.CAP.Internal var topicAttr = method.GetCustomAttributes(true); var topicAttributes = topicAttr as IList ?? topicAttr.ToList(); - if (!topicAttributes.Any()) continue; + if (!topicAttributes.Any()) + { + continue; + } foreach (var attr in topicAttributes) { if (attr.Group == null) + { attr.Group = _capOptions.DefaultGroup; + } + yield return InitDescriptor(attr, method, typeInfo); } } diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs index 3944d79..3c1dc94 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// 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.Collections.Generic; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/IContentSerializer.Json.cs b/src/DotNetCore.CAP/Internal/IContentSerializer.Json.cs index 21b656d..408b82c 100644 --- a/src/DotNetCore.CAP/Internal/IContentSerializer.Json.cs +++ b/src/DotNetCore.CAP/Internal/IContentSerializer.Json.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Infrastructure; @@ -6,7 +9,7 @@ namespace DotNetCore.CAP.Internal { internal class JsonContentSerializer : IContentSerializer { - public T DeSerialize(string messageObjStr) + public T DeSerialize(string messageObjStr) { return Helper.FromJson(messageObjStr); } diff --git a/src/DotNetCore.CAP/Internal/IMessagePacker.Default.cs b/src/DotNetCore.CAP/Internal/IMessagePacker.Default.cs index ea333fa..6de5ac8 100644 --- a/src/DotNetCore.CAP/Internal/IMessagePacker.Default.cs +++ b/src/DotNetCore.CAP/Internal/IMessagePacker.Default.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Abstractions; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Internal @@ -22,4 +25,4 @@ namespace DotNetCore.CAP.Internal return _serializer.DeSerialize(packingMessage); } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/IModelBinder.ComplexType.cs b/src/DotNetCore.CAP/Internal/IModelBinder.ComplexType.cs index c2bfc19..79cf4c3 100644 --- a/src/DotNetCore.CAP/Internal/IModelBinder.ComplexType.cs +++ b/src/DotNetCore.CAP/Internal/IModelBinder.ComplexType.cs @@ -1,4 +1,7 @@ -using System; +// 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.Reflection; using System.Threading.Tasks; using DotNetCore.CAP.Abstractions; diff --git a/src/DotNetCore.CAP/Internal/IModelBinder.SimpleType.cs b/src/DotNetCore.CAP/Internal/IModelBinder.SimpleType.cs index c6f6c80..3761891 100644 --- a/src/DotNetCore.CAP/Internal/IModelBinder.SimpleType.cs +++ b/src/DotNetCore.CAP/Internal/IModelBinder.SimpleType.cs @@ -1,4 +1,7 @@ -using System; +// 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.ComponentModel; using System.Globalization; using System.Reflection; @@ -22,7 +25,9 @@ namespace DotNetCore.CAP.Internal public Task BindModelAsync(string content) { if (content == null) + { throw new ArgumentNullException(nameof(content)); + } var parameterType = _parameterInfo.ParameterType; @@ -30,27 +35,43 @@ namespace DotNetCore.CAP.Internal { object model; if (parameterType == typeof(string)) + { if (string.IsNullOrWhiteSpace(content)) + { model = null; + } else + { model = content; + } + } else if (string.IsNullOrWhiteSpace(content)) + { model = null; + } else + { model = _typeConverter.ConvertFrom( null, CultureInfo.CurrentCulture, content); + } if (model == null && !IsReferenceOrNullableType(parameterType)) + { return Task.FromResult(ModelBindingResult.Failed()); + } + return Task.FromResult(ModelBindingResult.Success(model)); } catch (Exception exception) { var isFormatException = exception is FormatException; if (!isFormatException && exception.InnerException != null) + { exception = ExceptionDispatchInfo.Capture(exception.InnerException).SourceException; + } + throw; } } diff --git a/src/DotNetCore.CAP/Internal/ISubscriberExecutor.Default.cs b/src/DotNetCore.CAP/Internal/ISubscriberExecutor.Default.cs deleted file mode 100644 index ca13a7f..0000000 --- a/src/DotNetCore.CAP/Internal/ISubscriberExecutor.Default.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Models; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP.Internal -{ - internal class DefaultSubscriberExecutor : ISubscriberExecutor - { - private readonly ICallbackMessageSender _callbackMessageSender; - private readonly ILogger _logger; - private readonly MethodMatcherCache _selector; - - private IConsumerInvoker Invoker { get; } - - public DefaultSubscriberExecutor(MethodMatcherCache selector, - IConsumerInvokerFactory consumerInvokerFactory, - ICallbackMessageSender callbackMessageSender, - ILogger logger) - { - _selector = selector; - _callbackMessageSender = callbackMessageSender; - _logger = logger; - - Invoker = consumerInvokerFactory.CreateInvoker(); - } - - public async Task ExecuteAsync(CapReceivedMessage receivedMessage) - { - if (!_selector.TryGetTopicExector(receivedMessage.Name, receivedMessage.Group, - out var executor)) - { - var error = "message can not be found subscriber. Message:" + receivedMessage; - error += "\r\n see: https://github.com/dotnetcore/CAP/issues/63"; - throw new SubscriberNotFoundException(error); - } - - var consumerContext = new ConsumerContext(executor, receivedMessage.ToMessageContext()); - try - { - var ret = await Invoker.InvokeAsync(consumerContext); - if (!string.IsNullOrEmpty(ret.CallbackName)) - await _callbackMessageSender.SendAsync(ret.MessageId, ret.CallbackName, ret.Result); - - return OperateResult.Success; - } - catch (Exception ex) - { - _logger.ConsumerMethodExecutingFailed($"Group:{receivedMessage.Group}, Topic:{receivedMessage.Name}", - ex); - - return OperateResult.Failed(ex); - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/MethodBindException.cs b/src/DotNetCore.CAP/Internal/MethodBindException.cs index 63eb454..1d6c0bc 100644 --- a/src/DotNetCore.CAP/Internal/MethodBindException.cs +++ b/src/DotNetCore.CAP/Internal/MethodBindException.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs index 831a082..d6b40ba 100644 --- a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs +++ b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -24,14 +27,19 @@ namespace DotNetCore.CAP.Internal /// public ConcurrentDictionary> GetCandidatesMethodsOfGroupNameGrouped() { - if (Entries.Count != 0) return Entries; + if (Entries.Count != 0) + { + return Entries; + } var executorCollection = _selector.SelectCandidates(); var groupedCandidates = executorCollection.GroupBy(x => x.Attribute.Group); foreach (var item in groupedCandidates) + { Entries.TryAdd(item.Key, item.ToList()); + } return Entries; } @@ -44,7 +52,9 @@ namespace DotNetCore.CAP.Internal public IDictionary> GetTopicExector(string topicName) { if (Entries == null) + { throw new ArgumentNullException(nameof(Entries)); + } var dic = new Dictionary>(); foreach (var item in Entries) @@ -52,11 +62,13 @@ namespace DotNetCore.CAP.Internal 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 . + /// Attempts to get the topic exector associated with the specified topic name and group name from the + /// . /// /// The topic name of the value to get. /// The group name of the value to get. @@ -66,7 +78,9 @@ namespace DotNetCore.CAP.Internal out ConsumerExecutorDescriptor matchTopic) { if (Entries == null) + { throw new ArgumentNullException(nameof(Entries)); + } matchTopic = null; @@ -75,6 +89,7 @@ namespace DotNetCore.CAP.Internal matchTopic = groupMatchTopics.FirstOrDefault(x => x.Attribute.Name == topicName); return matchTopic != null; } + return false; } @@ -89,7 +104,9 @@ namespace DotNetCore.CAP.Internal } if (Entries == null) + { throw new ArgumentNullException(nameof(Entries)); + } _allTopics = new List(); @@ -97,6 +114,7 @@ namespace DotNetCore.CAP.Internal { _allTopics.AddRange(descriptors.Select(x => x.Attribute.Name)); } + return _allTopics; } } diff --git a/src/DotNetCore.CAP/Internal/ModelBinderFactory.cs b/src/DotNetCore.CAP/Internal/ModelBinderFactory.cs index a7ad0de..d8f0291 100644 --- a/src/DotNetCore.CAP/Internal/ModelBinderFactory.cs +++ b/src/DotNetCore.CAP/Internal/ModelBinderFactory.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Concurrent; using System.Reflection; using System.Runtime.CompilerServices; @@ -13,8 +16,8 @@ namespace DotNetCore.CAP.Internal /// internal class ModelBinderFactory : IModelBinderFactory { - private readonly IContentSerializer _serializer; private readonly ConcurrentDictionary _cache; + private readonly IContentSerializer _serializer; public ModelBinderFactory(IContentSerializer contentSerializer) { @@ -25,13 +28,17 @@ namespace DotNetCore.CAP.Internal public IModelBinder CreateBinder(ParameterInfo parameter) { if (parameter == null) + { throw new ArgumentNullException(nameof(parameter)); + } object token = parameter; var binder = CreateBinderCoreCached(parameter, token); if (binder == null) + { throw new InvalidOperationException("Format Could Not Create IModelBinder"); + } return binder; } @@ -39,11 +46,18 @@ namespace DotNetCore.CAP.Internal private IModelBinder CreateBinderCoreCached(ParameterInfo parameterInfo, object token) { if (TryGetCachedBinder(parameterInfo, token, out var binder)) + { return binder; + } + if (!Helper.IsComplexType(parameterInfo.ParameterType)) + { binder = new SimpleTypeModelBinder(parameterInfo); + } else + { binder = new ComplexTypeModelBinder(parameterInfo, _serializer); + } AddToCache(parameterInfo, token, binder); @@ -53,7 +67,9 @@ namespace DotNetCore.CAP.Internal private void AddToCache(ParameterInfo info, object cacheToken, IModelBinder binder) { if (cacheToken == null) + { return; + } _cache.TryAdd(new Key(info, cacheToken), binder); } diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs index 7046303..5c494e9 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// 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.Linq; diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs index 386e297..168e2e9 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// 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.Linq.Expressions; @@ -41,11 +41,13 @@ namespace Microsoft.Extensions.Internal if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type, out var coercerExpression, out var coercerResultType)) + { if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo)) { info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo); return true; } + } info = default(CoercedAwaitableInfo); return false; diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs index 9f33dfa..cf25fc9 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// 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.Collections.Generic; @@ -30,7 +30,9 @@ namespace Microsoft.Extensions.Internal private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) { if (methodInfo == null) + { throw new ArgumentNullException(nameof(methodInfo)); + } MethodInfo = methodInfo; MethodParameters = methodInfo.GetParameters(); @@ -48,7 +50,9 @@ namespace Microsoft.Extensions.Internal _executor = GetExecutor(methodInfo, targetTypeInfo); if (IsMethodAsync) + { _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); + } _parameterDefaultValues = parameterDefaultValues; } @@ -75,7 +79,9 @@ namespace Microsoft.Extensions.Internal object[] parameterDefaultValues) { if (parameterDefaultValues == null) + { throw new ArgumentNullException(nameof(parameterDefaultValues)); + } return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); } @@ -127,11 +133,15 @@ namespace Microsoft.Extensions.Internal public object GetDefaultValueForParameter(int index) { if (_parameterDefaultValues == null) + { throw new InvalidOperationException( $"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); + } if (index < 0 || index > MethodParameters.Length - 1) + { throw new ArgumentOutOfRangeException(nameof(index)); + } return _parameterDefaultValues[index]; } @@ -241,6 +251,7 @@ namespace Microsoft.Extensions.Internal var getResultParam = Expression.Parameter(typeof(object), "awaiter"); Func getResultFunc; if (awaitableInfo.ResultType == typeof(void)) + { getResultFunc = Expression.Lambda>( Expression.Block( Expression.Call( @@ -249,7 +260,9 @@ namespace Microsoft.Extensions.Internal Expression.Constant(null) ), getResultParam).Compile(); + } else + { getResultFunc = Expression.Lambda>( Expression.Convert( Expression.Call( @@ -257,6 +270,7 @@ namespace Microsoft.Extensions.Internal awaitableInfo.AwaiterGetResultMethod), typeof(object)), getResultParam).Compile(); + } // var onCompletedFunc = (object awaiter, Action continuation) => { // ((CustomAwaiterType)awaiter).OnCompleted(continuation); diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs index ed2fee8..e5becb1 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// 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.Runtime.CompilerServices; diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs index fb2be0f..09a8fda 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// 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.Linq; @@ -73,12 +73,17 @@ namespace Microsoft.Extensions.Internal { var typeFullName = possibleFSharpAsyncGenericType?.FullName; if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal)) + { return false; + } lock (_fsharpValuesCacheLock) { if (_fsharpCoreAssembly != null) + { return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly; + } + return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType); } } @@ -90,7 +95,9 @@ namespace Microsoft.Extensions.Internal var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"); if (fsharpOptionType == null || fsharpAsyncType == null) + { return false; + } // Get a reference to FSharpOption.None var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType diff --git a/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs new file mode 100644 index 0000000..b0f0a2c --- /dev/null +++ b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs @@ -0,0 +1,14 @@ +// 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; + +namespace DotNetCore.CAP.Internal +{ + public class PublisherSentFailedException : Exception + { + public PublisherSentFailedException(string message, Exception ex) : base(message, ex) + { + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/SubscriberExecutionFailedException.cs b/src/DotNetCore.CAP/Internal/SubscriberExecutionFailedException.cs new file mode 100644 index 0000000..bc35f9d --- /dev/null +++ b/src/DotNetCore.CAP/Internal/SubscriberExecutionFailedException.cs @@ -0,0 +1,14 @@ +// 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; + +namespace DotNetCore.CAP.Internal +{ + internal class SubscriberExecutionFailedException : Exception + { + public SubscriberExecutionFailedException(string message, Exception ex) : base(message, ex) + { + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/SubscriberNotFoundException.cs b/src/DotNetCore.CAP/Internal/SubscriberNotFoundException.cs index 6bdb4d3..c27ba29 100644 --- a/src/DotNetCore.CAP/Internal/SubscriberNotFoundException.cs +++ b/src/DotNetCore.CAP/Internal/SubscriberNotFoundException.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index 99cccba..031be9d 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -1,35 +1,33 @@ -using System; +// 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.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; namespace DotNetCore.CAP { + [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class LoggerExtensions { - private static readonly Action _serverStarting; + private static readonly Action _serverStarting; private static readonly Action _processorsStartingError; private static readonly Action _serverShuttingDown; private static readonly Action _expectedOperationCanceledException; - - private static readonly Action _enqueueingSentMessage; - private static readonly Action _enqueueingReceivdeMessage; - private static readonly Action _executingConsumerMethod; - private static readonly Action _receivedMessageRetryExecuting; private static readonly Action _modelBinderFormattingException; - - private static readonly Action _jobFailed; - private static readonly Action _jobFailedWillRetry; - private static readonly Action _jobExecuted; - private static readonly Action _jobRetrying; - private static readonly Action _exceptionOccuredWhileExecutingJob; - - private static readonly Action _messageQueueError; + private static readonly Action _consumerFailedWillRetry; + private static readonly Action _consumerExecuted; + private static readonly Action _senderRetrying; + private static readonly Action _exceptionOccuredWhileExecuting; + private static readonly Action _messageHasBeenSent; + private static readonly Action _messagePublishException; static LoggerExtensions() { - _serverStarting = LoggerMessage.Define( + _serverStarting = LoggerMessage.Define( LogLevel.Debug, 1, - "Starting the processing server. Detected {MachineProcessorCount} machine processor(s). Initiating {ProcessorCount} job processor(s)."); + "Starting the processing server."); _processorsStartingError = LoggerMessage.Define( LogLevel.Error, @@ -46,22 +44,12 @@ namespace DotNetCore.CAP 3, "Expected an OperationCanceledException, but found '{ExceptionMessage}'."); - _enqueueingSentMessage = LoggerMessage.Define( - LogLevel.Debug, - 2, - "Enqueuing a topic to the sent message store. NameKey: '{NameKey}' Content: '{Content}'."); - - _enqueueingReceivdeMessage = LoggerMessage.Define( - LogLevel.Debug, - 2, - "Enqueuing a topic to the received message store. NameKey: '{NameKey}. Content: '{Content}'."); - - _executingConsumerMethod = LoggerMessage.Define( + LoggerMessage.Define( LogLevel.Error, 5, "Consumer method '{methodName}' failed to execute."); - _receivedMessageRetryExecuting = LoggerMessage.Define( + LoggerMessage.Define( LogLevel.Error, 5, "Received message topic method '{topicName}' failed to execute."); @@ -72,81 +60,65 @@ namespace DotNetCore.CAP "When call subscribe method, a parameter format conversion exception occurs. MethodName:'{MethodName}' ParameterName:'{ParameterName}' Content:'{Content}'." ); - _jobRetrying = LoggerMessage.Define( + _senderRetrying = LoggerMessage.Define( LogLevel.Debug, 3, - "Retrying a job: {Retries}..."); + "Retrying send a message: {Retries}..."); - _jobExecuted = LoggerMessage.Define( + _consumerExecuted = LoggerMessage.Define( LogLevel.Debug, 4, - "Job executed. Took: {Seconds} secs."); + "Consumer executed. Took: {Seconds} secs."); - _jobFailed = LoggerMessage.Define( - LogLevel.Warning, - 1, - "Job failed to execute."); - - _jobFailedWillRetry = LoggerMessage.Define( + _consumerFailedWillRetry = LoggerMessage.Define( LogLevel.Warning, 2, - "Job failed to execute. Will retry."); + "Consumer failed to execute. Will retry."); - _exceptionOccuredWhileExecutingJob = LoggerMessage.Define( + _exceptionOccuredWhileExecuting = LoggerMessage.Define( LogLevel.Error, 6, - "An exception occured while trying to execute a message: '{MessageId}'. " + - "Requeuing for another retry."); + "An exception occured while trying to store a message: '{MessageId}'. "); - _messageQueueError = LoggerMessage.Define( - LogLevel.Error, - 7, - "The MessageQueue Client fires an internal error:'{error}'."); - } - - public static void JobFailed(this ILogger logger, Exception ex) - { - _jobFailed(logger, ex); - } - - public static void JobFailedWillRetry(this ILogger logger, Exception ex) - { - _jobFailedWillRetry(logger, ex); - } + _messageHasBeenSent = LoggerMessage.Define( + LogLevel.Debug, + 4, + "Message published. Took: {Seconds} secs."); - public static void JobRetrying(this ILogger logger, int retries) - { - _jobRetrying(logger, retries, null); + _messagePublishException = LoggerMessage.Define( + LogLevel.Error, + 6, + "An exception occured while publishing a message: '{MessageId}'. "); } - public static void JobExecuted(this ILogger logger, double seconds) + public static void ConsumerExecutionFailedWillRetry(this ILogger logger, Exception ex) { - _jobExecuted(logger, seconds, null); + _consumerFailedWillRetry(logger, ex); } - public static void ConsumerMethodExecutingFailed(this ILogger logger, string methodName, Exception ex) + public static void SenderRetrying(this ILogger logger, int retries) { - _executingConsumerMethod(logger, methodName, ex); + _senderRetrying(logger, retries, null); } - public static void ReceivedMessageRetryExecutingFailed(this ILogger logger, string topicName, Exception ex) + public static void MessageHasBeenSent(this ILogger logger, double seconds) { - _receivedMessageRetryExecuting(logger, topicName, ex); + _messageHasBeenSent(logger, seconds, null); } - public static void EnqueuingReceivedMessage(this ILogger logger, string nameKey, string content) + public static void MessagePublishException(this ILogger logger, int messageId, Exception ex) { - _enqueueingReceivdeMessage(logger, nameKey, content, null); + _messagePublishException(logger, messageId, ex); } - public static void EnqueuingSentMessage(this ILogger logger, string nameKey, string content) + public static void ConsumerExecuted(this ILogger logger, double seconds) { - _enqueueingSentMessage(logger, nameKey, content, null); + _consumerExecuted(logger, seconds, null); } - public static void ServerStarting(this ILogger logger, int machineProcessorCount, int processorCount) + public static void ServerStarting(this ILogger logger) { - _serverStarting(logger, machineProcessorCount, processorCount, null); + _serverStarting(logger, null); } public static void ProcessorsStartedError(this ILogger logger, Exception ex) @@ -166,7 +138,7 @@ namespace DotNetCore.CAP public static void ExceptionOccuredWhileExecuting(this ILogger logger, string messageId, Exception ex) { - _exceptionOccuredWhileExecutingJob(logger, messageId, ex); + _exceptionOccuredWhileExecuting(logger, messageId, ex); } public static void ModelBinderFormattingException(this ILogger logger, string methodName, string parameterName, @@ -174,10 +146,5 @@ namespace DotNetCore.CAP { _modelBinderFormattingException(logger, methodName, parameterName, content, ex); } - - public static void MessageQueueError(this ILogger logger, string error) - { - _messageQueueError(logger, error, null); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/MessageContext.cs b/src/DotNetCore.CAP/MessageContext.cs index 632d8ea..b42d7ca 100644 --- a/src/DotNetCore.CAP/MessageContext.cs +++ b/src/DotNetCore.CAP/MessageContext.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP { /// /// Message context diff --git a/src/DotNetCore.CAP/Models/CapMessageDto.cs b/src/DotNetCore.CAP/Models/CapMessageDto.cs index 0b3c699..c79fb97 100644 --- a/src/DotNetCore.CAP/Models/CapMessageDto.cs +++ b/src/DotNetCore.CAP/Models/CapMessageDto.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Infrastructure; namespace DotNetCore.CAP.Models @@ -14,7 +17,7 @@ namespace DotNetCore.CAP.Models public virtual string CallbackName { get; set; } } - public sealed class CapMessageDto: CapMessage + public sealed class CapMessageDto : CapMessage { public CapMessageDto() { diff --git a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs index cc7efa6..41c554d 100644 --- a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Models { diff --git a/src/DotNetCore.CAP/Models/CapQueue.cs b/src/DotNetCore.CAP/Models/CapQueue.cs index ccdd812..9a5b9f9 100644 --- a/src/DotNetCore.CAP/Models/CapQueue.cs +++ b/src/DotNetCore.CAP/Models/CapQueue.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Models +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Models { public class CapQueue { diff --git a/src/DotNetCore.CAP/Models/CapReceivedMessage.cs b/src/DotNetCore.CAP/Models/CapReceivedMessage.cs index eae744d..790aca2 100644 --- a/src/DotNetCore.CAP/Models/CapReceivedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapReceivedMessage.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Models { diff --git a/src/DotNetCore.CAP/Models/MessageType.cs b/src/DotNetCore.CAP/Models/MessageType.cs index 5097962..141040e 100644 --- a/src/DotNetCore.CAP/Models/MessageType.cs +++ b/src/DotNetCore.CAP/Models/MessageType.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Models +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Models { public enum MessageType { diff --git a/src/DotNetCore.CAP/MqLogType.cs b/src/DotNetCore.CAP/MqLogType.cs index 1a392cb..3d84a48 100644 --- a/src/DotNetCore.CAP/MqLogType.cs +++ b/src/DotNetCore.CAP/MqLogType.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP { @@ -21,4 +24,4 @@ namespace DotNetCore.CAP public MqLogType LogType { get; set; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptions.cs b/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptions.cs index 09e617d..008f568 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptions.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptions.cs @@ -1,4 +1,5 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs b/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs index cc0decc..8617520 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP; using DotNetCore.CAP.NodeDiscovery; using Microsoft.Extensions.DependencyInjection; @@ -44,7 +47,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseDiscovery(this CapOptions capOptions, Action options) { - if (options == null) throw new ArgumentNullException(nameof(options)); + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } capOptions.RegisterExtension(new DiscoveryOptionsExtension(options)); diff --git a/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.Default.cs b/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.Default.cs index e7639a6..5627282 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.Default.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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 Microsoft.Extensions.Logging; namespace DotNetCore.CAP.NodeDiscovery @@ -15,7 +18,9 @@ namespace DotNetCore.CAP.NodeDiscovery public INodeDiscoveryProvider Create(DiscoveryOptions options) { if (options == null) + { throw new ArgumentNullException(nameof(options)); + } return new ConsulNodeDiscoveryProvider(_loggerFactory, options); } diff --git a/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.cs b/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.cs index df3da3d..22dcee9 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.NodeDiscovery +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.NodeDiscovery { internal interface IDiscoveryProviderFactory { diff --git a/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.Consul.cs b/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.Consul.cs index 6f5cc59..1aca30b 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.Consul.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.Consul.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -36,8 +39,7 @@ namespace DotNetCore.CAP.NodeDiscovery foreach (var service in services.Response) { var serviceInfo = await _consul.Catalog.Service(service.Key); - var node = serviceInfo.Response. - SkipWhile(x => !x.ServiceTags.Contains("CAP")) + var node = serviceInfo.Response.SkipWhile(x => !x.ServiceTags.Contains("CAP")) .Select(info => new Node { Id = info.ServiceID, @@ -58,7 +60,8 @@ namespace DotNetCore.CAP.NodeDiscovery { CapCache.Global.AddOrUpdate("cap.nodes.count", 0, TimeSpan.FromSeconds(20)); - _logger.LogError($"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); + _logger.LogError( + $"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); return null; } } @@ -73,7 +76,7 @@ namespace DotNetCore.CAP.NodeDiscovery Name = _options.NodeName, Address = _options.CurrentNodeHostName, Port = _options.CurrentNodePort, - Tags = new[] { "CAP", "Client", "Dashboard" }, + Tags = new[] {"CAP", "Client", "Dashboard"}, Check = new AgentServiceCheck { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(30), @@ -86,7 +89,8 @@ namespace DotNetCore.CAP.NodeDiscovery } catch (Exception ex) { - _logger.LogError($"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); + _logger.LogError( + $"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); return null; } } diff --git a/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.cs b/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.cs index e2b07ca..960fbc4 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// 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.Collections.Generic; using System.Threading.Tasks; namespace DotNetCore.CAP.NodeDiscovery diff --git a/src/DotNetCore.CAP/NodeDiscovery/IProcessingServer.Consul.cs b/src/DotNetCore.CAP/NodeDiscovery/IProcessingServer.Consul.cs index 04541df..af3b065 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/IProcessingServer.Consul.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/IProcessingServer.Consul.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.NodeDiscovery +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.NodeDiscovery { internal class ConsulProcessingNodeServer : IProcessingServer { diff --git a/src/DotNetCore.CAP/NodeDiscovery/Node.cs b/src/DotNetCore.CAP/NodeDiscovery/Node.cs index 44d7bf2..ea077f7 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/Node.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/Node.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.NodeDiscovery +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.NodeDiscovery { public class Node { diff --git a/src/DotNetCore.CAP/OperateResult.cs b/src/DotNetCore.CAP/OperateResult.cs index 726f388..0be4570 100644 --- a/src/DotNetCore.CAP/OperateResult.cs +++ b/src/DotNetCore.CAP/OperateResult.cs @@ -1,4 +1,7 @@ -using System; +// 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.Collections.Generic; using System.Linq; @@ -47,7 +50,10 @@ namespace DotNetCore.CAP { var result = new OperateResult {Succeeded = false}; if (errors != null) + { result._errors.AddRange(errors); + } + return result; } @@ -59,7 +65,10 @@ namespace DotNetCore.CAP Exception = ex }; if (errors != null) + { result._errors.AddRange(errors); + } + return result; } diff --git a/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs b/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs index b678614..51fc1c3 100644 --- a/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs +++ b/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Processor +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Processor { public interface IAdditionalProcessor : IProcessor { diff --git a/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs b/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs index 8f76de1..95e9cdb 100644 --- a/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs +++ b/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs @@ -1,81 +1,101 @@ -using System; +// 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.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; +using DotNetCore.CAP.Models; +using Microsoft.Extensions.Logging; namespace DotNetCore.CAP.Processor { - public class DefaultDispatcher : IDispatcher + public class Dispatcher : IDispatcher, IDisposable { - internal static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); + private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + private readonly ISubscriberExecutor _executor; + private readonly ILogger _logger; + + private readonly BlockingCollection _publishedMessageQueue = + new BlockingCollection(new ConcurrentQueue()); - private readonly TimeSpan _pollingDelay; - private readonly IQueueExecutorFactory _queueExecutorFactory; + private readonly BlockingCollection _receivedMessageQueue = + new BlockingCollection(new ConcurrentQueue()); - public DefaultDispatcher(IQueueExecutorFactory queueExecutorFactory, - IOptions capOptions) + private readonly IPublishMessageSender _sender; + + public Dispatcher(ILogger logger, + IPublishMessageSender sender, + ISubscriberExecutor executor) { - _queueExecutorFactory = queueExecutorFactory; - _pollingDelay = TimeSpan.FromSeconds(capOptions.Value.PollingDelay); - } + _logger = logger; + _sender = sender; + _executor = executor; - public bool Waiting { get; private set; } + Task.Factory.StartNew(Sending); + Task.Factory.StartNew(Processing); + } - public Task ProcessAsync(ProcessingContext context) + public void EnqueueToPublish(CapPublishedMessage message) { - if (context == null) - throw new ArgumentNullException(nameof(context)); + _publishedMessageQueue.Add(message); + } - context.ThrowIfStopping(); + public void EnqueueToExecute(CapReceivedMessage message) + { + _receivedMessageQueue.Add(message); + } - return ProcessCoreAsync(context); + public void Dispose() + { + _cts.Cancel(); } - public async Task ProcessCoreAsync(ProcessingContext context) + private void Sending() { try { - var worked = await Step(context); - - context.ThrowIfStopping(); - - Waiting = true; - - if (!worked) + while (!_publishedMessageQueue.IsCompleted) { - var token = GetTokenToWaitOn(context); - await WaitHandleEx.WaitAnyAsync(PulseEvent, token.WaitHandle, _pollingDelay); + if (_publishedMessageQueue.TryTake(out var message, 100, _cts.Token)) + { + try + { + _sender.SendAsync(message); + } + catch (Exception ex) + { + _logger.ExceptionOccuredWhileExecuting(message.Name, ex); + } + } } } - finally + catch (OperationCanceledException) { - Waiting = false; + // expected } } - protected virtual CancellationToken GetTokenToWaitOn(ProcessingContext context) - { - return context.CancellationToken; - } - - private async Task Step(ProcessingContext context) + private void Processing() { - IFetchedMessage fetched; - using (var scopedContext = context.CreateScope()) + try { - var provider = scopedContext.Provider; - var connection = provider.GetRequiredService(); - - if ((fetched = await connection.FetchNextMessageAsync()) != null) - using (fetched) + foreach (var message in _receivedMessageQueue.GetConsumingEnumerable(_cts.Token)) + { + try + { + _executor.ExecuteAsync(message); + } + catch (Exception ex) { - var queueExecutor = _queueExecutorFactory.GetInstance(fetched.MessageType); - await queueExecutor.ExecuteAsync(connection, fetched); + _logger.ExceptionOccuredWhileExecuting(message.Name, ex); } + } + } + catch (OperationCanceledException) + { + // expected } - return fetched != null; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/IDispatcher.cs b/src/DotNetCore.CAP/Processor/IDispatcher.cs deleted file mode 100644 index f612d02..0000000 --- a/src/DotNetCore.CAP/Processor/IDispatcher.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DotNetCore.CAP.Processor -{ - public interface IDispatcher : IProcessor - { - bool Waiting { get; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs index 2118021..b9d8802 100644 --- a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs +++ b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs @@ -1,11 +1,13 @@ -using System; +// 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.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; namespace DotNetCore.CAP.Processor { @@ -14,39 +16,30 @@ namespace DotNetCore.CAP.Processor private readonly CancellationTokenSource _cts; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; - private readonly IList _messageDispatchers; - private readonly CapOptions _options; private readonly IServiceProvider _provider; + private Task _compositeTask; private ProcessingContext _context; private bool _disposed; - private IProcessor[] _processors; - public CapProcessingServer( ILogger logger, ILoggerFactory loggerFactory, - IServiceProvider provider, - IOptions options) + IServiceProvider provider) { _logger = logger; _loggerFactory = loggerFactory; _provider = provider; - _options = options.Value; _cts = new CancellationTokenSource(); - _messageDispatchers = new List(); } public void Start() { - var processorCount = _options.QueueProcessorCount; - _processors = GetProcessors(processorCount); - - _logger.ServerStarting(processorCount, _processors.Length); + _logger.ServerStarting(); _context = new ProcessingContext(_provider, _cts.Token); - var processorTasks = _processors + var processorTasks = GetProcessors() .Select(InfiniteRetry) .Select(p => p.ProcessAsync(_context)); _compositeTask = Task.WhenAll(processorTasks); @@ -54,18 +47,16 @@ namespace DotNetCore.CAP.Processor public void Pulse() { - if (!AllProcessorsWaiting()) - return; - - _logger.LogTrace("Pulsing the Queuer."); - - PublishQueuer.PulseEvent.Set(); + _logger.LogTrace("Pulsing the processor."); } public void Dispose() { if (_disposed) + { return; + } + _disposed = true; _logger.ServerShuttingDown(); @@ -78,35 +69,24 @@ namespace DotNetCore.CAP.Processor { var innerEx = ex.InnerExceptions[0]; if (!(innerEx is OperationCanceledException)) + { _logger.ExpectedOperationCanceledException(innerEx); + } } } - private bool AllProcessorsWaiting() - { - return _messageDispatchers.All(processor => processor.Waiting); - } - private IProcessor InfiniteRetry(IProcessor inner) { return new InfiniteRetryProcessor(inner, _loggerFactory); } - private IProcessor[] GetProcessors(int processorCount) + private IProcessor[] GetProcessors() { - var returnedProcessors = new List(); - for (var i = 0; i < processorCount; i++) + var returnedProcessors = new List { - var messageProcessors = _provider.GetRequiredService(); - _messageDispatchers.Add(messageProcessors); - } - returnedProcessors.AddRange(_messageDispatchers); - - returnedProcessors.Add(_provider.GetRequiredService()); - returnedProcessors.Add(_provider.GetRequiredService()); - returnedProcessors.Add(_provider.GetRequiredService()); - - returnedProcessors.Add(_provider.GetRequiredService()); + _provider.GetRequiredService(), + _provider.GetRequiredService() + }; return returnedProcessors.ToArray(); } diff --git a/src/DotNetCore.CAP/Processor/IProcessor.InfiniteRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.InfiniteRetry.cs index ed0508e..b7b5d43 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.InfiniteRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.InfiniteRetry.cs @@ -1,4 +1,7 @@ -using System; +// 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; using Microsoft.Extensions.Logging; @@ -20,6 +23,7 @@ namespace DotNetCore.CAP.Processor public async Task ProcessAsync(ProcessingContext context) { while (!context.IsStopping) + { try { await _inner.ProcessAsync(context); @@ -32,6 +36,7 @@ namespace DotNetCore.CAP.Processor { _logger.LogWarning(1, ex, "Processor '{ProcessorName}' failed. Retrying...", _inner.ToString()); } + } } public override string ToString() diff --git a/src/DotNetCore.CAP/Processor/IProcessor.Failed.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs similarity index 71% rename from src/DotNetCore.CAP/Processor/IProcessor.Failed.cs rename to src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index 6c158e4..b1109a3 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.Failed.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -1,6 +1,8 @@ -using System; +// 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; -using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; using DotNetCore.CAP.Processor.States; @@ -10,28 +12,25 @@ using Microsoft.Extensions.Options; namespace DotNetCore.CAP.Processor { - public class FailedProcessor : IProcessor + public class NeedRetryMessageProcessor : IProcessor { private readonly TimeSpan _delay = TimeSpan.FromSeconds(1); private readonly ILogger _logger; private readonly CapOptions _options; - private readonly IServiceProvider _provider; + private readonly IPublishExecutor _publishExecutor; private readonly IStateChanger _stateChanger; private readonly ISubscriberExecutor _subscriberExecutor; - private readonly IPublishExecutor _publishExecutor; private readonly TimeSpan _waitingInterval; - public FailedProcessor( + public NeedRetryMessageProcessor( IOptions options, - ILogger logger, - IServiceProvider provider, + ILogger logger, IStateChanger stateChanger, ISubscriberExecutor subscriberExecutor, IPublishExecutor publishExecutor) { _options = options.Value; _logger = logger; - _provider = provider; _stateChanger = stateChanger; _subscriberExecutor = subscriberExecutor; _publishExecutor = publishExecutor; @@ -41,32 +40,33 @@ namespace DotNetCore.CAP.Processor public async Task ProcessAsync(ProcessingContext context) { if (context == null) + { throw new ArgumentNullException(nameof(context)); + } - using (var scope = _provider.CreateScope()) - { - var provider = scope.ServiceProvider; - var connection = provider.GetRequiredService(); + var connection = context.Provider.GetRequiredService(); - await Task.WhenAll( - ProcessPublishedAsync(connection, context), - ProcessReceivedAsync(connection, context)); + await Task.WhenAll( + ProcessPublishedAsync(connection, context), + ProcessReceivedAsync(connection, context)); - await context.WaitAsync(_waitingInterval); - } + await context.WaitAsync(_waitingInterval); } private async Task ProcessPublishedAsync(IStorageConnection connection, ProcessingContext context) { - var messages = await connection.GetFailedPublishedMessages(); + var messages = await connection.GetPublishedMessagesOfNeedRetry(); var hasException = false; foreach (var message in messages) { if (message.Retries > _options.FailedRetryCount) + { continue; + } if (!hasException) + { try { _options.FailedCallback?.Invoke(MessageType.Publish, message.Name, message.Content); @@ -76,6 +76,7 @@ namespace DotNetCore.CAP.Processor hasException = true; _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); } + } using (var transaction = connection.CreateTransaction()) { @@ -88,9 +89,9 @@ namespace DotNetCore.CAP.Processor catch (Exception e) { message.Content = Helper.AddExceptionProperty(message.Content, e); - message.Retries++; transaction.UpdateMessage(message); } + await transaction.CommitAsync(); } @@ -102,15 +103,18 @@ namespace DotNetCore.CAP.Processor private async Task ProcessReceivedAsync(IStorageConnection connection, ProcessingContext context) { - var messages = await connection.GetFailedReceivedMessages(); + var messages = await connection.GetReceivedMessagesOfNeedRetry(); var hasException = false; foreach (var message in messages) { if (message.Retries > _options.FailedRetryCount) + { continue; + } if (!hasException) + { try { _options.FailedCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); @@ -120,23 +124,10 @@ namespace DotNetCore.CAP.Processor hasException = true; _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); } - - using (var transaction = connection.CreateTransaction()) - { - var ret = await _subscriberExecutor.ExecuteAsync(message); - if (ret.Succeeded) - { - _stateChanger.ChangeState(message, new SucceededState(), transaction); - } - else - { - message.Retries++; - message.Content = Helper.AddExceptionProperty(message.Content, ret.Exception); - transaction.UpdateMessage(message); - } - await transaction.CommitAsync(); } + await _subscriberExecutor.ExecuteAsync(message); + context.ThrowIfStopping(); await context.WaitAsync(_delay); diff --git a/src/DotNetCore.CAP/Processor/IProcessor.PublishQueuer.cs b/src/DotNetCore.CAP/Processor/IProcessor.PublishQueuer.cs deleted file mode 100644 index 90d1b5f..0000000 --- a/src/DotNetCore.CAP/Processor/IProcessor.PublishQueuer.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace DotNetCore.CAP.Processor -{ - public class PublishQueuer : IProcessor - { - public static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); - private readonly ILogger _logger; - private readonly TimeSpan _pollingDelay; - private readonly IServiceProvider _provider; - private readonly IStateChanger _stateChanger; - - public PublishQueuer( - ILogger logger, - IOptions options, - IStateChanger stateChanger, - IServiceProvider provider) - { - _logger = logger; - _stateChanger = stateChanger; - _provider = provider; - - var capOptions = options.Value; - _pollingDelay = TimeSpan.FromSeconds(capOptions.PollingDelay); - } - - public async Task ProcessAsync(ProcessingContext context) - { - _logger.LogDebug("Publish Queuer start calling."); - using (var scope = _provider.CreateScope()) - { - CapPublishedMessage sentMessage; - var provider = scope.ServiceProvider; - var connection = provider.GetRequiredService(); - - while ( - !context.IsStopping && - (sentMessage = await connection.GetNextPublishedMessageToBeEnqueuedAsync()) != null) - - { - var state = new EnqueuedState(); - - using (var transaction = connection.CreateTransaction()) - { - _stateChanger.ChangeState(sentMessage, state, transaction); - await transaction.CommitAsync(); - } - } - } - - context.ThrowIfStopping(); - - DefaultDispatcher.PulseEvent.Set(); - - await WaitHandleEx.WaitAnyAsync(PulseEvent, - context.CancellationToken.WaitHandle, _pollingDelay); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/IProcessor.SubscribeQueuer.cs b/src/DotNetCore.CAP/Processor/IProcessor.SubscribeQueuer.cs deleted file mode 100644 index 29913d5..0000000 --- a/src/DotNetCore.CAP/Processor/IProcessor.SubscribeQueuer.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace DotNetCore.CAP.Processor -{ - public class SubscribeQueuer : IProcessor - { - internal static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); - private readonly ILogger _logger; - private readonly TimeSpan _pollingDelay; - private readonly IServiceProvider _provider; - private readonly IStateChanger _stateChanger; - - public SubscribeQueuer( - ILogger logger, - IOptions options, - IStateChanger stateChanger, - IServiceProvider provider) - { - _logger = logger; - _stateChanger = stateChanger; - _provider = provider; - - var capOptions = options.Value; - _pollingDelay = TimeSpan.FromSeconds(capOptions.PollingDelay); - } - - public async Task ProcessAsync(ProcessingContext context) - { - _logger.LogDebug("SubscribeQueuer start calling."); - using (var scope = _provider.CreateScope()) - { - CapReceivedMessage message; - var provider = scope.ServiceProvider; - var connection = provider.GetRequiredService(); - - while ( - !context.IsStopping && - (message = await connection.GetNextReceivedMessageToBeEnqueuedAsync()) != null) - - { - var state = new EnqueuedState(); - - using (var transaction = connection.CreateTransaction()) - { - _stateChanger.ChangeState(message, state, transaction); - await transaction.CommitAsync(); - } - } - } - - context.ThrowIfStopping(); - - DefaultDispatcher.PulseEvent.Set(); - - await WaitHandleEx.WaitAnyAsync(PulseEvent, - context.CancellationToken.WaitHandle, _pollingDelay); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/IProcessor.cs b/src/DotNetCore.CAP/Processor/IProcessor.cs index 20d484f..3e83c6e 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; namespace DotNetCore.CAP.Processor { diff --git a/src/DotNetCore.CAP/Processor/ProcessingContext.cs b/src/DotNetCore.CAP/Processor/ProcessingContext.cs index 5129532..6a280b2 100644 --- a/src/DotNetCore.CAP/Processor/ProcessingContext.cs +++ b/src/DotNetCore.CAP/Processor/ProcessingContext.cs @@ -1,4 +1,7 @@ -using System; +// 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; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; diff --git a/src/DotNetCore.CAP/Processor/RetryBehavior.cs b/src/DotNetCore.CAP/Processor/RetryBehavior.cs index d1767c0..05115fc 100644 --- a/src/DotNetCore.CAP/Processor/RetryBehavior.cs +++ b/src/DotNetCore.CAP/Processor/RetryBehavior.cs @@ -1,4 +1,7 @@ -using System; +// 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; namespace DotNetCore.CAP.Processor { @@ -17,9 +20,9 @@ namespace DotNetCore.CAP.Processor static RetryBehavior() { - DefaultRetryCount = 15; + DefaultRetryCount = 3; DefaultRetryInThunk = retries => - (int) Math.Round(Math.Pow(retries - 1, 4) + 15 + _random.Next(30) * retries); + (int) Math.Round(Math.Pow(retries - 1, 4) + 3 + _random.Next(30) * retries); DefaultRetry = new RetryBehavior(true); NoRetry = new RetryBehavior(false); @@ -39,7 +42,12 @@ namespace DotNetCore.CAP.Processor public RetryBehavior(bool retry, int retryCount, Func retryInThunk) { if (retry) - if (retryCount < 0) throw new ArgumentOutOfRangeException(nameof(retryCount), "Can't be negative."); + { + if (retryCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(retryCount), "Can't be negative."); + } + } Retry = retry; RetryCount = retryCount; diff --git a/src/DotNetCore.CAP/Processor/States/IState.Enqueued.cs b/src/DotNetCore.CAP/Processor/States/IState.Enqueued.cs deleted file mode 100644 index ca53699..0000000 --- a/src/DotNetCore.CAP/Processor/States/IState.Enqueued.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.Processor.States -{ - public class EnqueuedState : IState - { - public const string StateName = "Enqueued"; - - public TimeSpan? ExpiresAfter => null; - - public string Name => StateName; - - public void Apply(CapPublishedMessage message, IStorageTransaction transaction) - { - transaction.EnqueueMessage(message); - } - - public void Apply(CapReceivedMessage message, IStorageTransaction transaction) - { - transaction.EnqueueMessage(message); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/States/IState.Failed.cs b/src/DotNetCore.CAP/Processor/States/IState.Failed.cs index 49fda9b..4fb34b6 100644 --- a/src/DotNetCore.CAP/Processor/States/IState.Failed.cs +++ b/src/DotNetCore.CAP/Processor/States/IState.Failed.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IState.Processing.cs b/src/DotNetCore.CAP/Processor/States/IState.Processing.cs deleted file mode 100644 index 9827e76..0000000 --- a/src/DotNetCore.CAP/Processor/States/IState.Processing.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.Processor.States -{ - public class ProcessingState : IState - { - public const string StateName = "Processing"; - - public TimeSpan? ExpiresAfter => null; - - public string Name => StateName; - - public void Apply(CapPublishedMessage message, IStorageTransaction transaction) - { - } - - public void Apply(CapReceivedMessage message, IStorageTransaction transaction) - { - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/States/IState.Scheduled.cs b/src/DotNetCore.CAP/Processor/States/IState.Scheduled.cs index 49f0c95..b5cd805 100644 --- a/src/DotNetCore.CAP/Processor/States/IState.Scheduled.cs +++ b/src/DotNetCore.CAP/Processor/States/IState.Scheduled.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IState.Succeeded.cs b/src/DotNetCore.CAP/Processor/States/IState.Succeeded.cs index fdb41e5..3c0d16d 100644 --- a/src/DotNetCore.CAP/Processor/States/IState.Succeeded.cs +++ b/src/DotNetCore.CAP/Processor/States/IState.Succeeded.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IState.cs b/src/DotNetCore.CAP/Processor/States/IState.cs index c43fc74..f1b8417 100644 --- a/src/DotNetCore.CAP/Processor/States/IState.cs +++ b/src/DotNetCore.CAP/Processor/States/IState.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IStateChanger.Default.cs b/src/DotNetCore.CAP/Processor/States/IStateChanger.Default.cs index 93af696..436dcb6 100644 --- a/src/DotNetCore.CAP/Processor/States/IStateChanger.Default.cs +++ b/src/DotNetCore.CAP/Processor/States/IStateChanger.Default.cs @@ -1,4 +1,7 @@ -using System; +// 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 DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States @@ -9,9 +12,13 @@ namespace DotNetCore.CAP.Processor.States { var now = DateTime.Now; if (state.ExpiresAfter != null) + { message.ExpiresAt = now.Add(state.ExpiresAfter.Value); + } else + { message.ExpiresAt = null; + } message.StatusName = state.Name; state.Apply(message, transaction); @@ -22,9 +29,13 @@ namespace DotNetCore.CAP.Processor.States { var now = DateTime.Now; if (state.ExpiresAfter != null) + { message.ExpiresAt = now.Add(state.ExpiresAfter.Value); + } else + { message.ExpiresAt = null; + } message.StatusName = state.Name; state.Apply(message, transaction); diff --git a/src/DotNetCore.CAP/Processor/States/IStateChanger.Extensions.cs b/src/DotNetCore.CAP/Processor/States/IStateChanger.Extensions.cs index 6bd1d12..b0e1e75 100644 --- a/src/DotNetCore.CAP/Processor/States/IStateChanger.Extensions.cs +++ b/src/DotNetCore.CAP/Processor/States/IStateChanger.Extensions.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IStateChanger.cs b/src/DotNetCore.CAP/Processor/States/IStateChanger.cs index 949ea31..c15e23a 100644 --- a/src/DotNetCore.CAP/Processor/States/IStateChanger.cs +++ b/src/DotNetCore.CAP/Processor/States/IStateChanger.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Models; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States { diff --git a/src/DotNetCore.CAP/Properties/AssemblyInfo.cs b/src/DotNetCore.CAP/Properties/AssemblyInfo.cs index 0e62b37..3008bc6 100644 --- a/src/DotNetCore.CAP/Properties/AssemblyInfo.cs +++ b/src/DotNetCore.CAP/Properties/AssemblyInfo.cs @@ -1,3 +1,6 @@ -using System.Runtime.CompilerServices; +// 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.Runtime.CompilerServices; [assembly: InternalsVisibleTo("DotNetCore.CAP.Test")] \ No newline at end of file diff --git a/src/DotNetCore.CAP/QueueExecutorFactory.cs b/src/DotNetCore.CAP/QueueExecutorFactory.cs deleted file mode 100644 index 4e6882f..0000000 --- a/src/DotNetCore.CAP/QueueExecutorFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Linq; -using DotNetCore.CAP.Models; -using Microsoft.Extensions.DependencyInjection; - -namespace DotNetCore.CAP -{ - public class QueueExecutorFactory : IQueueExecutorFactory - { - private readonly IServiceProvider _serviceProvider; - - public QueueExecutorFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public IQueueExecutor GetInstance(MessageType messageType) - { - var queueExecutors = _serviceProvider.GetServices(); - - return messageType == MessageType.Publish - ? queueExecutors.FirstOrDefault(x => x is BasePublishQueueExecutor) - : queueExecutors.FirstOrDefault(x => !(x is BasePublishQueueExecutor)); - } - } -} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MySql.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.MySql.Test/DatabaseTestHost.cs index 1fac1b6..66ffa33 100644 --- a/test/DotNetCore.CAP.MySql.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.MySql.Test/DatabaseTestHost.cs @@ -1,6 +1,5 @@ using System.Threading; using Dapper; -using Microsoft.EntityFrameworkCore; namespace DotNetCore.CAP.MySql.Test { @@ -59,8 +58,7 @@ CREATE DATABASE `{databaseName}`;"); { connection.Execute($@" TRUNCATE TABLE `cap.published`; -TRUNCATE TABLE `cap.received`; -TRUNCATE TABLE `cap.queue`;"); +TRUNCATE TABLE `cap.received`;"); } } } diff --git a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs index 50bc489..aefa831 100644 --- a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs @@ -40,27 +40,6 @@ namespace DotNetCore.CAP.MySql.Test Assert.Equal(StatusName.Scheduled, message.StatusName); } - [Fact] - public async Task FetchNextMessageAsync_Test() - { - var sql = "INSERT INTO `Cap.Queue`(`MessageId`,`MessageType`) VALUES(@MessageId,@MessageType);"; - var queue = new CapQueue - { - MessageId = 3333, - MessageType = MessageType.Publish - }; - using (var connection = ConnectionUtil.CreateConnection()) - { - connection.Execute(sql, queue); - } - using (var fetchedMessage = await _storage.FetchNextMessageAsync()) - { - Assert.NotNull(fetchedMessage); - Assert.Equal(MessageType.Publish, fetchedMessage.MessageType); - Assert.Equal(3333, fetchedMessage.MessageId); - } - } - [Fact] public async Task StoreReceivedMessageAsync_Test() { @@ -110,25 +89,5 @@ namespace DotNetCore.CAP.MySql.Test Assert.Equal("MySqlStorageConnectionTest", message.Name); Assert.Equal("mygroup", message.Group); } - - [Fact] - public async Task GetNextReceviedMessageToBeEnqueuedAsync_Test() - { - var receivedMessage = new CapReceivedMessage - { - Name = "MySqlStorageConnectionTest", - Content = "", - Group = "mygroup", - StatusName = StatusName.Scheduled - }; - await _storage.StoreReceivedMessageAsync(receivedMessage); - - var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync(); - - Assert.NotNull(message); - Assert.Equal(StatusName.Scheduled, message.StatusName); - Assert.Equal("MySqlStorageConnectionTest", message.Name); - Assert.Equal("mygroup", message.Group); - } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MySql.Test/MySqlStorageTest.cs b/test/DotNetCore.CAP.MySql.Test/MySqlStorageTest.cs index 715cb5c..49337f5 100644 --- a/test/DotNetCore.CAP.MySql.Test/MySqlStorageTest.cs +++ b/test/DotNetCore.CAP.MySql.Test/MySqlStorageTest.cs @@ -30,7 +30,6 @@ namespace DotNetCore.CAP.MySql.Test [Theory] [InlineData("cap.published")] - [InlineData("cap.queue")] [InlineData("cap.received")] public void DatabaseTable_IsExists(string tableName) { diff --git a/test/DotNetCore.CAP.PostgreSql.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.PostgreSql.Test/DatabaseTestHost.cs index 66ae8e1..ec59a74 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.PostgreSql.Test/DatabaseTestHost.cs @@ -59,8 +59,7 @@ CREATE DATABASE ""{databaseName}"";"); { connection.Execute($@" TRUNCATE TABLE ""cap"".""published""; -TRUNCATE TABLE ""cap"".""received""; -TRUNCATE TABLE ""cap"".""queue"";"); +TRUNCATE TABLE ""cap"".""received"";"); } } } diff --git a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs index df9dd64..b1cb25c 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs @@ -40,26 +40,6 @@ namespace DotNetCore.CAP.PostgreSql.Test Assert.Equal(StatusName.Scheduled, message.StatusName); } - [Fact] - public async Task FetchNextMessageAsync_Test() - { - var sql = @"INSERT INTO ""cap"".""queue""(""MessageId"",""MessageType"") VALUES(@MessageId,@MessageType);"; - var queue = new CapQueue - { - MessageId = 3333, - MessageType = MessageType.Publish - }; - using (var connection = ConnectionUtil.CreateConnection()) - { - connection.Execute(sql, queue); - } - var fetchedMessage = await _storage.FetchNextMessageAsync(); - fetchedMessage.Dispose(); - Assert.NotNull(fetchedMessage); - Assert.Equal(MessageType.Publish, fetchedMessage.MessageType); - Assert.Equal(3333, fetchedMessage.MessageId); - } - [Fact] public async Task StoreReceivedMessageAsync_Test() { @@ -109,25 +89,5 @@ namespace DotNetCore.CAP.PostgreSql.Test Assert.Equal("PostgreSqlStorageConnectionTest", message.Name); Assert.Equal("mygroup", message.Group); } - - [Fact] - public async Task GetNextReceviedMessageToBeEnqueuedAsync_Test() - { - var receivedMessage = new CapReceivedMessage - { - Name = "PostgreSqlStorageConnectionTest", - Content = "", - Group = "mygroup", - StatusName = StatusName.Scheduled - }; - await _storage.StoreReceivedMessageAsync(receivedMessage); - - var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync(); - - Assert.NotNull(message); - Assert.Equal(StatusName.Scheduled, message.StatusName); - Assert.Equal("PostgreSqlStorageConnectionTest", message.Name); - Assert.Equal("mygroup", message.Group); - } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs index c4f5748..ec0bc2b 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs +++ b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs @@ -32,7 +32,6 @@ namespace DotNetCore.CAP.PostgreSql.Test [Theory] [InlineData("cap.published")] - [InlineData("cap.queue")] [InlineData("cap.received")] public void DatabaseTable_IsExists(string tableName) { diff --git a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs index 5bf5a7d..96828f6 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs @@ -2,7 +2,6 @@ using System.Data; using System.Data.SqlClient; using System.Threading; using Dapper; -using Microsoft.EntityFrameworkCore; namespace DotNetCore.CAP.SqlServer.Test { diff --git a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs index 84eba4e..3408ee3 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs @@ -39,27 +39,7 @@ namespace DotNetCore.CAP.SqlServer.Test Assert.Equal("SqlServerStorageConnectionTest", message.Name); Assert.Equal(StatusName.Scheduled, message.StatusName); } - - [Fact] - public async Task FetchNextMessageAsync_Test() - { - var sql = "INSERT INTO [Cap].[Queue]([MessageId],[MessageType]) VALUES(@MessageId,@MessageType);"; - var queue = new CapQueue - { - MessageId = 3333, - MessageType = MessageType.Publish - }; - using (var connection = ConnectionUtil.CreateConnection()) - { - connection.Execute(sql, queue); - } - var fetchedMessage = await _storage.FetchNextMessageAsync(); - fetchedMessage.Dispose(); - Assert.NotNull(fetchedMessage); - Assert.Equal(MessageType.Publish, fetchedMessage.MessageType); - Assert.Equal(3333, fetchedMessage.MessageId); - } - + [Fact] public async Task StoreReceivedMessageAsync_Test() { @@ -109,25 +89,5 @@ namespace DotNetCore.CAP.SqlServer.Test Assert.Equal("SqlServerStorageConnectionTest", message.Name); Assert.Equal("mygroup", message.Group); } - - [Fact] - public async Task GetNextReceviedMessageToBeEnqueuedAsync_Test() - { - var receivedMessage = new CapReceivedMessage - { - Name = "SqlServerStorageConnectionTest", - Content = "", - Group = "mygroup", - StatusName = StatusName.Scheduled - }; - await _storage.StoreReceivedMessageAsync(receivedMessage); - - var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync(); - - Assert.NotNull(message); - Assert.Equal(StatusName.Scheduled, message.StatusName); - Assert.Equal("SqlServerStorageConnectionTest", message.Name); - Assert.Equal("mygroup", message.Group); - } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageTest.cs b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageTest.cs index af5fc41..427ed30 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageTest.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageTest.cs @@ -25,7 +25,6 @@ SELECT 'False'"; [Theory] [InlineData("[Cap].[Published]")] - [InlineData("[Cap].[Queue]")] [InlineData("[Cap].[Received]")] public void DatabaseTable_IsExists(string tableName) { diff --git a/test/DotNetCore.CAP.Test/CallbackMessageSenderTest.cs b/test/DotNetCore.CAP.Test/CallbackMessageSenderTest.cs index a29772d..6aa9a44 100644 --- a/test/DotNetCore.CAP.Test/CallbackMessageSenderTest.cs +++ b/test/DotNetCore.CAP.Test/CallbackMessageSenderTest.cs @@ -36,7 +36,7 @@ namespace DotNetCore.CAP.Test { // Arrange _mockCallbackPublisher - .Setup(x => x.PublishAsync(It.IsAny())) + .Setup(x => x.PublishCallbackAsync(It.IsAny())) .Returns(Task.CompletedTask).Verifiable(); _mockContentSerializer diff --git a/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs b/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs index 8ccdbf0..1f3c77e 100644 --- a/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs +++ b/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs @@ -1,89 +1,83 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Moq; -using Xunit; +//using System; +//using System.Threading; +//using System.Threading.Tasks; +//using DotNetCore.CAP.Models; +//using DotNetCore.CAP.Processor; +//using Microsoft.Extensions.DependencyInjection; +//using Microsoft.Extensions.Options; +//using Moq; +//using Xunit; -namespace DotNetCore.CAP.Test -{ - public class DefaultDispatcherTest - { - private CancellationTokenSource _cancellationTokenSource; - private ProcessingContext _context; - private IServiceProvider _provider; - private Mock _mockStorageConnection; - private Mock _mockQueueExecutorFactory; - private Mock _mockQueueExecutor; +//namespace DotNetCore.CAP.Test +//{ +// public class DefaultDispatcherTest +// { +// private CancellationTokenSource _cancellationTokenSource; +// private ProcessingContext _context; +// private IServiceProvider _provider; +// private Mock _mockStorageConnection; - public DefaultDispatcherTest() - { - _mockStorageConnection = new Mock(); - _mockQueueExecutorFactory = new Mock(); - _mockQueueExecutor = new Mock(); - _mockQueueExecutorFactory.Setup(x => x.GetInstance(MessageType.Publish)).Returns(_mockQueueExecutor.Object); - _cancellationTokenSource = new CancellationTokenSource(); +// public DefaultDispatcherTest() +// { +// _mockStorageConnection = new Mock(); + +// _cancellationTokenSource = new CancellationTokenSource(); - var services = new ServiceCollection(); - services.AddTransient(); - services.AddLogging(); - services.Configure>(x => { }); - services.AddOptions(); - services.AddSingleton(_mockStorageConnection.Object); - services.AddSingleton(_mockQueueExecutorFactory.Object); - _provider = services.BuildServiceProvider(); +// var services = new ServiceCollection(); +// services.AddLogging(); +// services.Configure>(x => { }); +// services.AddOptions(); +// services.AddSingleton(_mockStorageConnection.Object); +// _provider = services.BuildServiceProvider(); - _context = new ProcessingContext(_provider, _cancellationTokenSource.Token); - } +// _context = new ProcessingContext(_provider, _cancellationTokenSource.Token); +// } - [Fact] - public void MockTest() - { - Assert.NotNull(_provider.GetServices()); - } +// [Fact] +// public void MockTest() +// { +// Assert.NotNull(_provider.GetServices()); +// } - [Fact] - public async void ProcessAsync_CancellationTokenCancelled_ThrowsImmediately() - { - // Arrange - _cancellationTokenSource.Cancel(); - var fixture = Create(); +// [Fact] +// public async void ProcessAsync_CancellationTokenCancelled_ThrowsImmediately() +// { +// // Arrange +// _cancellationTokenSource.Cancel(); +// var fixture = Create(); - // Act - await Assert.ThrowsAsync(() => fixture.ProcessAsync(_context)); - } +// // Act +// await Assert.ThrowsAsync(() => fixture.ProcessAsync(_context)); +// } - [Fact] - public async Task ProcessAsync() - { - // Arrange - var job = new CapPublishedMessage - { - }; +// [Fact] +// public async Task ProcessAsync() +// { +// // Arrange +// var job = new CapPublishedMessage +// { +// }; - var mockFetchedJob = Mock.Get(Mock.Of(fj => fj.MessageId == 42 && fj.MessageType == MessageType.Publish)); +// var mockFetchedJob = Mock.Get(Mock.Of(fj => fj.MessageId == 42 && fj.MessageType == MessageType.Publish)); - _mockStorageConnection - .Setup(m => m.FetchNextMessageAsync()) - .ReturnsAsync(mockFetchedJob.Object).Verifiable(); +// _mockStorageConnection +// .Setup(m => m.FetchNextMessageAsync()) +// .ReturnsAsync(mockFetchedJob.Object).Verifiable(); - _mockQueueExecutor - .Setup(x => x.ExecuteAsync(_mockStorageConnection.Object, mockFetchedJob.Object)) - .Returns(Task.FromResult(OperateResult.Success)); +// _mockQueueExecutor +// .Setup(x => x.ExecuteAsync(_mockStorageConnection.Object, mockFetchedJob.Object)) +// .Returns(Task.FromResult(OperateResult.Success)); - var fixture = Create(); +// var fixture = Create(); - // Act - await fixture.ProcessAsync(_context); +// // Act +// await fixture.ProcessAsync(_context); - // Assert - _mockStorageConnection.VerifyAll(); - } +// // Assert +// _mockStorageConnection.VerifyAll(); +// } - private DefaultDispatcher Create() - => _provider.GetService(); - } -} \ No newline at end of file +// private DefaultDispatcher Create() +// => _provider.GetService(); +// } +//} \ No newline at end of file diff --git a/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs b/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs index 9ff5208..5a72ffa 100644 --- a/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs +++ b/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs @@ -16,7 +16,7 @@ namespace DotNetCore.CAP.Test var fixture = Create(); var message = new CapPublishedMessage { - StatusName = StatusName.Enqueued + StatusName = StatusName.Scheduled }; var state = Mock.Of(s => s.Name == "s" && s.ExpiresAfter == null); var mockTransaction = new Mock(); @@ -39,7 +39,7 @@ namespace DotNetCore.CAP.Test var fixture = Create(); var message = new CapPublishedMessage { - StatusName = StatusName.Enqueued + StatusName = StatusName.Scheduled }; var state = Mock.Of(s => s.Name == "s" && s.ExpiresAfter == TimeSpan.FromHours(1)); var mockTransaction = new Mock(); diff --git a/test/DotNetCore.CAP.Test/QueueExecutorFactoryTest.cs b/test/DotNetCore.CAP.Test/QueueExecutorFactoryTest.cs index 03302f0..107d074 100644 --- a/test/DotNetCore.CAP.Test/QueueExecutorFactoryTest.cs +++ b/test/DotNetCore.CAP.Test/QueueExecutorFactoryTest.cs @@ -1,46 +1,46 @@ -using System; -using DotNetCore.CAP.Internal; -using Microsoft.Extensions.DependencyInjection; -using Xunit; -using Moq; +//using System; +//using DotNetCore.CAP.Internal; +//using Microsoft.Extensions.DependencyInjection; +//using Xunit; +//using Moq; -namespace DotNetCore.CAP.Test -{ - public class QueueExecutorFactoryTest - { - private IServiceProvider _provider; +//namespace DotNetCore.CAP.Test +//{ +// public class QueueExecutorFactoryTest +// { +// private IServiceProvider _provider; - public QueueExecutorFactoryTest() - { - var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); +// public QueueExecutorFactoryTest() +// { +// var services = new ServiceCollection(); +// services.AddLogging(); +// services.AddOptions(); - services.AddCap(x => { }); - _provider = services.BuildServiceProvider(); - } +// services.AddCap(x => { }); +// _provider = services.BuildServiceProvider(); +// } - [Fact] - public void CanCreateInstance() - { - var queueExecutorFactory = _provider.GetService(); - Assert.NotNull(queueExecutorFactory); +// [Fact] +// public void CanCreateInstance() +// { +// var queueExecutorFactory = _provider.GetService(); +// Assert.NotNull(queueExecutorFactory); - var publishExecutor = queueExecutorFactory.GetInstance(Models.MessageType.Publish); - Assert.Null(publishExecutor); +// var publishExecutor = queueExecutorFactory.GetInstance(Models.MessageType.Publish); +// Assert.Null(publishExecutor); - var disPatchExector = queueExecutorFactory.GetInstance(Models.MessageType.Subscribe); - Assert.NotNull(disPatchExector); - } +// var disPatchExector = queueExecutorFactory.GetInstance(Models.MessageType.Subscribe); +// Assert.NotNull(disPatchExector); +// } - [Fact] - public void CanGetSubscribeExector() - { - var queueExecutorFactory = _provider.GetService(); - Assert.NotNull(queueExecutorFactory); +// [Fact] +// public void CanGetSubscribeExector() +// { +// var queueExecutorFactory = _provider.GetService(); +// Assert.NotNull(queueExecutorFactory); - var publishExecutor = queueExecutorFactory.GetInstance(Models.MessageType.Publish); - Assert.Null(publishExecutor); - } - } -} \ No newline at end of file +// var publishExecutor = queueExecutorFactory.GetInstance(Models.MessageType.Publish); +// Assert.Null(publishExecutor); +// } +// } +//} \ No newline at end of file