├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── Ray 功能关系图.xml ├── Ray.xmind ├── Ray2.sln ├── example ├── Ray2.Client │ ├── Program.cs │ └── Ray2.Client.csproj ├── Ray2.Grain │ ├── Account.cs │ ├── AccountFlow.cs │ ├── Events │ │ ├── AccountAddBalanceEvent.cs │ │ └── AccountTransferEvent.cs │ ├── IAccount.cs │ ├── IAccountFlow.cs │ ├── IAccountStorage.cs │ ├── IEventHandler.cs │ ├── Ray2.Grain.csproj │ └── States │ │ └── AccountState.cs └── Ray2.Host │ ├── Program.cs │ └── Ray2.Host.csproj ├── src ├── Ray2.PostgreSQL │ ├── Configuration │ │ ├── PostgreSqlRayBuilderExpansions.cs │ │ └── PostgresqlOptions.cs │ ├── EventStorage.cs │ ├── IPostgreSqlEventStorage.cs │ ├── IPostgreSqlStateStorage.cs │ ├── IPostgreSqlTableStorage.cs │ ├── PostgreSqlDbContext.cs │ ├── PostgreSqlEventStorage.cs │ ├── PostgreSqlStateStorage.cs │ ├── PostgreSqlTableStorage.cs │ ├── Ray2.PostgreSQL.csproj │ └── StateStorage.cs ├── Ray2.Protobuf │ ├── PooledMemoryStream.cs │ ├── ProtobufSerializer.cs │ ├── Ray2.Protobuf.csproj │ ├── RaySiloHostBuilderExtensions.cs │ └── SerializationType.cs ├── Ray2.RabbitMQ │ ├── Configuration │ │ ├── RabbitConsumeOptions.cs │ │ ├── RabbitOptions.cs │ │ └── RabbitRayBuilderExpansions.cs │ ├── EventPublisher.cs │ ├── EventSubscriber.cs │ ├── IRabbitChannel.cs │ ├── IRabbitChannelFactory.cs │ ├── IRabbitConnection.cs │ ├── IRabbitConsumer.cs │ ├── IRabbitProducer.cs │ ├── PublishMessage.cs │ ├── RabbitChannel.cs │ ├── RabbitChannelFactory.cs │ ├── RabbitConnection.cs │ ├── RabbitConsumer.cs │ ├── RabbitProducer.cs │ └── Ray2.RabbitMQ.csproj ├── Ray2.SimpleMQ │ └── Ray2.SimpleMQ.csproj └── Ray2 │ ├── Configuration │ ├── Attributes │ │ ├── EventProcessorAttribute.cs │ │ ├── EventPublishAttribute.cs │ │ ├── EventSourcingAttribute.cs │ │ └── EventSubscribeAttribute.cs │ ├── Builder │ │ ├── EventProcessOptionsBuilder.cs │ │ ├── EventSourceOptionsBuilder.cs │ │ └── InternalConfigurationBuilder.cs │ ├── Creator │ │ ├── EventProcessOptionsCreator.cs │ │ ├── EventPublishOptionsCreator.cs │ │ ├── EventSourceOptionsCreator.cs │ │ ├── EventSubscribeOptionsCreator.cs │ │ ├── IEventProcessOptionsCreator.cs │ │ ├── IEventPublishOptionsCreator.cs │ │ ├── IEventSourceOptionsCreator.cs │ │ ├── IEventSubscribeOptionsCreator.cs │ │ ├── IInternalConfigurationCreator.cs │ │ └── InternalConfigurationCreator.cs │ ├── DependencyInjection │ │ ├── IRayBuilder.cs │ │ ├── RayBuilder.cs │ │ └── RaySiloHostBuilderExtensions.cs │ ├── EventInfo.cs │ ├── EventProcessOptions.cs │ ├── EventPublishOptions.cs │ ├── EventSourceOptions.cs │ ├── EventSubscribeOptions.cs │ ├── IInternalConfiguration.cs │ ├── InternalConfiguration.cs │ ├── SnapshotOptions.cs │ ├── StatusOptions.cs │ ├── StorageOptions.cs │ └── Validator │ │ ├── EventProcessOptionsFluentVaildator.cs │ │ ├── EventPublishOptionsFluentVaildator.cs │ │ ├── EventSourceOptionsFluentVaildator.cs │ │ ├── EventSubscribeOptionsFluentVaildator.cs │ │ ├── IInternalConfigurationValidator.cs │ │ ├── InternalConfigurationFluentVaildator.cs │ │ └── RayConfigurationException.cs │ ├── Event.cs │ ├── EventProcess │ ├── EventProcessCore.cs │ ├── EventProcessExtensions.cs │ ├── EventProcessState.cs │ ├── EventProcessorFactory.cs │ ├── EventProcessorGrainDispatch.cs │ ├── IEventProcessCore.cs │ ├── IEventProcessor.cs │ └── IEventProcessorFactory.cs │ ├── EventSource │ ├── EventModel.cs │ ├── EventSourceExtensions.cs │ ├── EventSourcing.cs │ ├── EventTransaction.cs │ ├── EventTransactionModel.cs │ ├── IEventSourcing.cs │ └── IEventTransaction.cs │ ├── IEvent.cs │ ├── IRay.cs │ ├── IState.cs │ ├── Internal │ ├── DataflowBufferBlock.cs │ ├── DataflowBufferBlockFactory.cs │ ├── DataflowBufferWrap.cs │ ├── IDataflowBufferBlock.cs │ ├── IDataflowBufferBlockFactory.cs │ └── IDataflowBufferWrap.cs │ ├── MQ │ ├── EventPublishModel.cs │ ├── IEventPublisher.cs │ ├── IEventSubscriber.cs │ ├── IMQPublisher.cs │ ├── IMQSubscriber.cs │ ├── MQPublishType.cs │ ├── MQPublisher.cs │ ├── MQPublisherExtensions.cs │ └── MQSubscriber.cs │ ├── Ray2.csproj │ ├── RayGrain.cs │ ├── RayProcessor.cs │ ├── RayProcessorGrain.cs │ ├── Serialization │ ├── ISerializer.cs │ ├── JsonSerializer.cs │ └── SerializationType.cs │ ├── State.cs │ └── Storage │ ├── IEventStorage.cs │ ├── IStateStorage.cs │ ├── IStorage.cs │ ├── IStorageFactory.cs │ ├── IStorageSharding.cs │ ├── Model │ ├── EventCollectionStorageModel.cs │ ├── EventQueryModel.cs │ ├── EventStorageInfo.cs │ └── EventStorageModel.cs │ ├── StorageFactory.cs │ ├── StorageTableNameBuild.cs │ └── StorageType.cs ├── test ├── Ray2.Integrated.Test │ └── Ray2.Integrated.Test.csproj ├── Ray2.PostgreSQL.Test │ ├── EventStorageTests.cs │ ├── FakeConfig.cs │ ├── Model │ │ ├── TestEvent.cs │ │ └── TestState.cs │ ├── PostgreSqlEventStorageTests.cs │ ├── PostgreSqlStateStorageTests.cs │ ├── PostgreSqlTableStorageTests.cs │ ├── Ray2.PostgreSQL.Test.csproj │ └── StateStorageTests.cs ├── Ray2.RabbitMQ.Test │ ├── EventPublisherTests.cs │ ├── EventSubscriberTests.cs │ ├── FakeConfig.cs │ ├── Model │ │ └── TestEvent.cs │ ├── RabbitChannelFactoryTests.cs │ ├── RabbitChannelTests.cs │ ├── RabbitConnectionTests.cs │ ├── RabbitConsumerTests.cs │ ├── RabbitProducerTests.cs │ └── Ray2.RabbitMQ.Test.csproj └── Ray2.Test │ ├── EventSource │ └── EventSourcingTests.cs │ ├── Internal │ ├── DataflowBufferBlockFactoryTests.cs │ └── DataflowBufferBlockTests.cs │ ├── MQ │ ├── MQPublisherTests.cs │ └── MQSubscriberTests.cs │ ├── Model │ ├── TestEvent.cs │ └── TestState.cs │ ├── Ray2.Test.csproj │ ├── Serialization │ └── JsonSerializerTests.cs │ └── Storage │ └── StorageFactoryTests.cs └── 未命名表单.drawio /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 阿凌 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ray2 2 | -------------------------------------------------------------------------------- /Ray 功能关系图.xml: -------------------------------------------------------------------------------- 1 | 7V1bc6M4Fv41VO0+xIXETTyC48zUdqc66ezOTO/LFLGJzQYbN+DE7l+/Enck2WBbgJNyHrpBCGzrfOc7Fx0JSRkvt7+FznpxH8xcX4LybCsptxKEQIU6/o+07NIWBOW0YR56s6xT2fDk/XKzxrzbxpu5Ua1jHAR+7K3rjdNgtXKnca3NCcPgvd7tJfDrn7p25i7T8DR1fLb1T28WL9JWRZHl8sLvrjdfZB+t6ii7ZenkvbOu0cKZBe+VJmUiKeMwCOL0aLkduz4ZvXxg0vvu9lwtvlnoruI2N1jf5Yef/1qOX+2H9Z9/GAt/8Z/wRkuf8ub4m+wXZ1823uVD4M7wiGSnQRgvgnmwcvxJ2WqHwWY1c8nHyPis7PM1CNa4EeDG/7lxvMvE62ziADct4qWfXX0JVnF2Eej4HP+icPdX9rzk5Ac5GWn56e22evF2l52xQ5KNUhRswql7YBxybDnh3I0P9MuGh4xJ5QOyAf/NDZYu/j64Q+j6Tuy91VHkZGCcF/1KceGDTGJHSE9VT5DWdBO+JcICIkTnbr34r1I4+OxHLhp8XMqJnOyqQktvglpNwg3ypXFyurxhS3kDQ7TAk1utMHR2lQ7rwFvFUeXJD6QBd8iItOCbjEYNROk61V8ztUP98UH6DUroFT/lDDQaLJlMNMlSJaRKE0MyZQnp0sSU8JexbWmCJHsimUCaqJJ9K9l3SR9VshAD6tjdxnXYhW7k/XKekw4EGdkA4t6aLWm3uMXxvfkKN0wxNtwQN7y5YexhZreyC0tvNksUwneeXd92pq/zRBvGgR+EyecqL8kfBTzIBd5B/SQf7W5r0MgMVvYTaiahhqvsrht5JKsyrAk1J63joFdiJe8SvLxEWAloMhKACMAA4ruzY6QbxWHwWphXWBc0tppr0m+5nRMPY/TiB+/ThRPGowgrb/w36fO+8GL3ae0kGv+Ou9UlBtX8PPtMjukEewS1VyCA0kikZefvpZtQ9FlUPARd7sgYmBdpyrunaOEM3VYKv+5vvo2Vx3f5xyN6Hdv/HZsRuNGPk8IqWAkZ9opJPcqiChUPd0C6MaCNFtJUUf0R6RfN7hJPdbBZ7rkB+krszUMQebEXEEP0HMRxsORYqJgIv0aGDFPm9LgOg6kbRXvIsIRX8pQURPIIEGJ0wmmGKp1DmwKYEmqXxpQKx0+BxBOxZBzGeisx/gfW0WykQcUd8d2XeK+oIyw2bzX/d6L0N+oxdHqEPAxKHgYrD4MjDtiVOFpQZqeq44SO77t+gB0M8qi1G3r4hxGfsX7tobzQQstKvUo8xkLpoNKNWNWLc0h44UCqZqnPb0nITg5sycZxgU5aTJmR/adSPhU0Kx/qU/nAkQ5LXxmgIq2QJxkqGaD9iQV8UtHSc3NC8lAeaEOOQHiYJjdjIGfL6Sb0d3aIo2UyJFwerGpqjRI70CdNpfQJsvoEOfoEOlMoaBzUH/EO/3Ep0uSM1hFxQVueL2tOpKqileY8lxBxjJUhISszTZiTEUiyWbeSicgBgp8pU5WjVkimSlWgWdPK3OM+M8aDyqie1synL7rPZKkcfPSi5tz0en6lMb1+ana9JxIA6KJIAHA8Vqj7ceoc4rBQn5Njlhj+QQjBvpOs9BpmCeOf+Z34m9RuplFzlAUt5hDJyTRYetNMdnuYIYNhGvxU6EJO/rhh0Yvn+5Wed8lfho47Z+n5BC5/uOHMWTkcviHnvA9iSK6Mj7SDbtoRvgAd2IKWEVBnvgA6kjX6mLCjBdZ/5jDX+mYfAXTiWDN2xaAzVHRI3HHuEPBgwuOdPUGzdVeJnq+sMzDrtM27dBeBmL26JkcF6JcZtRSlRR8tauGl2FiS+LxRSz7nKSJqgZoKhEYt3UclBVuV8p+8ucmHP8VB6F5pvz/aN4emfY2XwkiKbUwrKba5k5Bx/5gdmkkBjgUkNE6CFzMpwNElExKy6MN+7HdEj7QNJ9ihVqbi0HR2o6XQtH58Vx3WcQhkpV/nNTeJFdgR7iF1tUwO+bRCn5mDadyJCOgaKn1EZZVNOqvMiSR5U6SAHntxPt1lTtPsHesBPCm+fgBZp2RpUEnDVOU70w+FM7lyio92Nd2iTDeQTQoSsjaw7VZYR+6k6YcrSLoDid4jSLiuh9KLWzZ4WN/KN+NrERzM+JwlWNaDSlzyW+KMY+XHTrqlfIQonpXcIRwLCdp1AJSamuaFmGe6DZpRfyqAo97mGnMYH8bDlfr7Cu1NiIZ2D3gVq5gGULKgJgnjLVRp0ZLJgCtGenQPTE7hUc8gabGWs4sqrpkXYs5O63Hf3SgWNMCQrmcFrP/Vb2WX0iIGv9AyOTYKHnw0VXa90v3u6aefz2A+h9fJy+5pDCoULtTC0Rkukc1mTB6CKJ6H7tPj1ys8BoWHrAwPDzZXktCG/OSG+KddATIoQBAYHiC8+Mkg0TMyR6PRFQ79wcHkuBn9gkFnjQkDgK5nN88rThlsJhHoRv0Z4mYSuXkhXg5cJwlwHNWSSWuVzFIPlwI12uZARwDV06B6Mb19TB505kSLgnbOh0/zKiYoGmfnsXiLcLbLZaELd+vMydNqC0Kz1kJWUKJXUJcLPY06DYha8URlp4BsGO04trOFnhobGOdlSPYmYqT2qRZ0aogWh8mKw+zV4nGShX3ngTArkd8uZID1fWapinde7qK7EWYnbL44L6/O1bPsLScPAGrJet2hgDVQ353nZy++f7zGoENCAw6eitc5meI8AiUVmIZkK8kWaAb517y74mVQvGjKwHgxLiRILWKYYoX5IHUcetsiW/EhCz80RowPIncWGvN/KC82RpI1kaykyNtEZNVp15uqXMCKj0JTRBSPYF9dr8n1zAUfebGIWr+ju0oRnbdguccYuYNdx/aA4ewS7gsIk3XeAg1uJXB9x9RPHT+bmkbJyWRTxr3uiGRwEk8s19JCEb4cRlD6LzeRzQtWdNG29DwpwFbKgiQb64iSlTxZrLKcuubEnXpRQpR9rTkxVEgpgtaSsIpG8UJoYV/6WBp2Bvz1lvDXL2srEmOAWiNBlhdbWsb0ttuUy+gKxxy+7mGxVLl3D2y/Nz4tkNrmH5U5JrO2WQcYgZ5jNKS2VKy+NvHQ6QW5OlWwvSdEO3YrfgAZz7JhM37OHerhOxCjQUfeYGZV8Z3u9494WS7sHE0k20iKke8kHF+xRvuzxKI5qwiJRRVDU2sizB5+uZsPGLxwRpfMMVmtlmY2ycQ9ljpKWvC/BpnET5ezWUaLSOea9BRWmUO/r6CsCB5udyzeylhmK4IrSoZECYBDT7MhTiD4sWKQ/I1gzensy4pBECfp/FFiEEgXs8mcbbl4MYjaGY4P164NsfXfGZBWWkK6t20eFMbHBpQkRfn/9JtGgCwfds5Ng05x1m/oxjs391jX2i6lnFXF+AAbXrIrEOPKN89Sf1hXXuRGYoqSb+j4UVx5xFtf2m6T26sv1lktvbYnCTCgL8bbqYCXlCcLkrVk9zEgmeyG+FeUdIgS1CNKuHX8HId92Dp+eWRW868jPX/DyHG7mRhtdqITl2FtPXOn9/SKNaTQwSHQ6V0x9mymxfpE+Y5HB57V8XKTFnNb7eocKoh7Ltjo2yb2vZUrNczfHvHaLraggbO2ihfSiCho4I5g07bVlaHUf27Ie64TqrxJCxEs3AHo620yPPn1Q3tds7tUttvr+lPVS9Bvb+PWS+gd0b23fbKdL/KXtf+uPdjf1JtpvGjz5kN3NbPIy9hLbq9VN1Qkw2WyRv7kfi+VP46VYdI4w5S3nbulPJ1/UGgtbMuU9JN01C6uPYEmucPYYjWJePEOJjZqGs00TzVwQEWNz+pYcqyzfovF8hw44QxzpbMknLV6jtY8zjyxbAb7PUtv5cTEFebayj101woCeyZ6TU51QWeFfdyBbrFSsQsGrDqGVwY8RY/waRgQv6PsTgB9H8xc0uP/ -------------------------------------------------------------------------------- /Ray.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfzm/Ray2/8b152bd132a626c38a8e1507892ee399176e8469/Ray.xmind -------------------------------------------------------------------------------- /example/Ray2.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Orleans; 3 | using Orleans.Runtime; 4 | using Ray2.Grain; 5 | using System; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ray2.Client 11 | { 12 | class Program 13 | { 14 | static int Main(string[] args) 15 | { 16 | return RunMainAsync().Result; 17 | } 18 | 19 | private static async Task RunMainAsync() 20 | { 21 | try 22 | { 23 | Console.ReadLine(); 24 | using (var client = await StartClientWithRetries()) 25 | { 26 | while (true) 27 | { 28 | // var actor = client.GetGrain(0); 29 | // Console.WriteLine("Press Enter for times..."); 30 | Console.WriteLine("start"); 31 | Console.ReadLine(); 32 | var length = 10000;// int.Parse(Console.ReadLine()); 33 | var stopWatch = new Stopwatch(); 34 | stopWatch.Start(); 35 | await Task.WhenAll(Enumerable.Range(0, length).Select(x => 36 | { 37 | return client.GetGrain(1).AddAmount(1000); 38 | })); 39 | stopWatch.Stop(); 40 | Console.WriteLine($"{length }次操作完成,耗时:{stopWatch.ElapsedMilliseconds}ms"); 41 | await Task.Delay(200); 42 | Console.WriteLine($"余额为{await client.GetGrain(1).GetBalance()}"); 43 | } 44 | } 45 | } 46 | catch (Exception e) 47 | { 48 | Console.WriteLine(e); 49 | return 1; 50 | } 51 | } 52 | 53 | private static async Task StartClientWithRetries(int initializeAttemptsBeforeFailing = 5) 54 | { 55 | int attempt = 0; 56 | IClusterClient client; 57 | while (true) 58 | { 59 | try 60 | { 61 | var builder = new ClientBuilder() 62 | .UseLocalhostClustering() 63 | .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(IAccount).Assembly).WithReferences()) 64 | .ConfigureLogging(logging => logging.AddConsole()); 65 | client = builder.Build(); 66 | await client.Connect(); 67 | Console.WriteLine("Client successfully connect to silo host"); 68 | break; 69 | } 70 | catch (SiloUnavailableException) 71 | { 72 | attempt++; 73 | Console.WriteLine($"Attempt {attempt} of {initializeAttemptsBeforeFailing} failed to initialize the Orleans client."); 74 | if (attempt > initializeAttemptsBeforeFailing) 75 | { 76 | throw; 77 | } 78 | await Task.Delay(TimeSpan.FromSeconds(4)); 79 | } 80 | } 81 | 82 | return client; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /example/Ray2.Client/Ray2.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/Ray2.Grain/Account.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.Extensions.Logging; 3 | using Orleans; 4 | using Ray2.Grain.Events; 5 | using Ray2.Grain.States; 6 | 7 | namespace Ray2.Grain 8 | { 9 | [EventSourcing("postgresql", "rabbitmq")] 10 | public class Account : RayGrain, IAccount 11 | { 12 | public Account(ILogger logger) : base(logger) 13 | { 14 | 15 | } 16 | 17 | public override async Task OnActivateAsync() 18 | { 19 | await base.OnActivateAsync(); 20 | } 21 | protected override long StateId => this.GetPrimaryKeyLong(); 22 | 23 | public Task AddAmount(decimal amount, string uniqueId = null) 24 | { 25 | var evt = new AccountAddBalanceEvent(amount); 26 | evt.RelationEvent = uniqueId; 27 | return this.ConcurrentWriteAsync(evt, MQ.MQPublishType.Asynchronous); 28 | } 29 | 30 | public Task GetBalance() 31 | { 32 | return Task.FromResult(State.Balance); 33 | } 34 | 35 | public Task Transfer(long toAccountId, decimal amount) 36 | { 37 | var evt = new AccountTransferEvent(toAccountId, amount, State.Balance - amount); 38 | return base.WriteAsync(evt); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example/Ray2.Grain/AccountFlow.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Orleans; 3 | using Ray2.Grain.Events; 4 | using Ray2.Grain.States; 5 | using System.Threading.Tasks; 6 | 7 | 8 | namespace Ray2.Grain 9 | { 10 | [EventProcessor(typeof(Account), "rabbitmq", "postgresql",OnceProcessCount =2000)] 11 | public class AccountFlow : RayProcessorGrain 12 | { 13 | private readonly ILogger logger; 14 | public AccountFlow(ILogger logger) 15 | { 16 | this.logger = logger; 17 | } 18 | protected override long StateId => this.GetPrimaryKeyLong(); 19 | 20 | public override Task OnEventProcessing(IEvent @event) 21 | { 22 | switch (@event) 23 | { 24 | case AccountTransferEvent value: return TransferHandle(value); 25 | case AccountAddBalanceEvent value: return AddBalanceHandle(value); 26 | default: return Task.CompletedTask; 27 | } 28 | } 29 | 30 | private async Task TransferHandle(AccountTransferEvent evt) 31 | { 32 | var toActor = GrainFactory.GetGrain(evt.ToAccountId); 33 | await toActor.AddAmount(evt.Amount, evt.GetRelationKey()); 34 | } 35 | 36 | private Task AddBalanceHandle(AccountAddBalanceEvent evt) 37 | { 38 | //this.logger.LogError($"加款:{evt.Amount}; 余额:{evt.Balance}"); 39 | return Task.CompletedTask; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /example/Ray2.Grain/Events/AccountAddBalanceEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Ray2.Grain.Events 4 | { 5 | public class AccountAddBalanceEvent : Event 6 | { 7 | public AccountAddBalanceEvent() { } 8 | public AccountAddBalanceEvent(decimal amount) 9 | { 10 | Amount = amount; 11 | } 12 | public decimal Amount { get; set; } 13 | public decimal Balance { get; set; } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/Ray2.Grain/Events/AccountTransferEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Ray2.Grain.Events 4 | { 5 | public class AccountTransferEvent : Event 6 | { 7 | public AccountTransferEvent() { } 8 | public AccountTransferEvent(long toAccountId, decimal amount, decimal balance) 9 | { 10 | ToAccountId = toAccountId; 11 | Amount = amount; 12 | Balance = balance; 13 | } 14 | public long ToAccountId { get; set; } 15 | public decimal Amount { get; set; } 16 | public decimal Balance { get; set; } 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/Ray2.Grain/IAccount.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using Orleans.Concurrency; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ray2.Grain 6 | { 7 | public interface IAccount:IGrainWithIntegerKey 8 | { 9 | /// 10 | /// 获取账户余额 11 | /// 12 | /// 13 | [AlwaysInterleave] 14 | Task GetBalance(); 15 | /// 16 | /// 增加账户金额 17 | /// 18 | /// 金额 19 | /// 操作辨识ID(防止多次执行) 20 | /// 21 | [AlwaysInterleave] 22 | Task AddAmount(decimal amount, string uniqueId = null); 23 | /// 24 | /// 转账 25 | /// 26 | /// 目标账户ID 27 | /// 转账金额 28 | /// 29 | Task Transfer(long toAccountId, decimal amount); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/Ray2.Grain/IAccountFlow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Grain 6 | { 7 | interface IAccountFlow 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/Ray2.Grain/IAccountStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Grain 6 | { 7 | interface IAccountStorage 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/Ray2.Grain/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Grain 6 | { 7 | interface IEventHandler 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/Ray2.Grain/Ray2.Grain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | all 10 | runtime; build; native; contentfiles; analyzers 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/Ray2.Grain/States/AccountState.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Grain.Events; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Ray2.Grain.States 5 | { 6 | public class AccountState : State 7 | { 8 | protected override void PlayEvent(IEvent @event) 9 | { 10 | switch (@event) 11 | { 12 | case AccountTransferEvent e:BalanceChangeHandle(e.Balance);break; 13 | case AccountAddBalanceEvent e: AmountAddEventHandle(e); break; 14 | default: 15 | break; 16 | } 17 | } 18 | 19 | private void BalanceChangeHandle(decimal balance) 20 | { 21 | this.Balance = balance; 22 | } 23 | 24 | private void AmountAddEventHandle(AccountAddBalanceEvent evt) 25 | { 26 | this.Balance += evt.Amount; 27 | evt.Balance = this.Balance; 28 | } 29 | 30 | public decimal Balance { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/Ray2.Host/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Orleans.Configuration; 3 | using Orleans.Hosting; 4 | using System; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Ray2.Grain; 9 | using Orleans; 10 | 11 | namespace Ray2.Host 12 | { 13 | class Program 14 | { 15 | public static int Main(string[] args) 16 | { 17 | return RunMainAsync().Result; 18 | } 19 | 20 | private static async Task RunMainAsync() 21 | { 22 | try 23 | { 24 | var host = await StartSilo(); 25 | Console.WriteLine("Press Enter to terminate..."); 26 | Console.ReadLine(); 27 | 28 | await host.StopAsync(); 29 | 30 | return 0; 31 | } 32 | catch (Exception ex) 33 | { 34 | Console.WriteLine(ex); 35 | return 1; 36 | } 37 | } 38 | 39 | private static async Task StartSilo() 40 | { 41 | // define the cluster configuration 42 | var builder = new SiloHostBuilder() 43 | .UseLocalhostClustering() 44 | .UseRay(build => 45 | { 46 | build.AddRabbitMQ("rabbitmq", opt => 47 | { 48 | opt.HostName = "192.168.1.250"; 49 | opt.UserName = "admin"; 50 | opt.Password = "admin"; 51 | }); 52 | build.AddPostgreSQL("postgresql", opt => 53 | { 54 | opt.ConnectionString = "Server=localhost;Port=5432;Database=ray2;User Id=postgres; Password=sapass;Pooling=true;MaxPoolSize=50;Timeout=10;"; 55 | }); 56 | }) 57 | .Configure(options => 58 | { 59 | options.ClusterId = "dev"; 60 | options.ServiceId = "HelloWorldApp"; 61 | }) 62 | .Configure(options => options.AdvertisedIPAddress = IPAddress.Loopback) 63 | .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(Account).Assembly).WithReferences()) 64 | .ConfigureLogging(logging => logging.AddConsole()); 65 | 66 | 67 | var host = builder.Build(); 68 | await host.StartAsync(); 69 | return host; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /example/Ray2.Host/Ray2.Host.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/Configuration/PostgreSqlRayBuilderExpansions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Orleans.Runtime; 4 | using Ray2; 5 | using Ray2.PostgreSQL; 6 | using Ray2.Storage; 7 | using System; 8 | 9 | namespace Orleans.Hosting 10 | { 11 | public static class PostgreSqlRayBuilderExpansions 12 | { 13 | /// 14 | /// Add PostgreSql Ray storage 15 | /// 16 | /// 17 | /// Provider name 18 | /// PostgreSql configuration 19 | /// 20 | public static IRayBuilder AddPostgreSQL(this IRayBuilder build, string name, IConfiguration configuration) 21 | { 22 | build.Services.Configure(name, configuration); 23 | return build.AddPostgreSQL(name); 24 | } 25 | /// 26 | /// Add PostgreSql Ray storage 27 | /// 28 | /// 29 | /// Provider name 30 | /// PostgreSql configuration 31 | /// 32 | public static IRayBuilder AddPostgreSQL(this IRayBuilder build, string name, Action configuration) 33 | { 34 | build.Services.Configure(name, configuration); 35 | return build.AddPostgreSQL(name); 36 | } 37 | 38 | /// 39 | /// Add PostgreSql Ray storage 40 | /// 41 | /// 42 | /// Provider name 43 | /// 44 | private static IRayBuilder AddPostgreSQL(this IRayBuilder build, string name) 45 | { 46 | build.Services.AddSingletonNamedService(name, (sp, n) => 47 | { 48 | return new StateStorage(sp, n); 49 | }); 50 | build.Services.AddSingletonNamedService(name, (sp, n) => 51 | { 52 | return new EventStorage(sp, n); 53 | }); 54 | build.Services.AddSingletonNamedService(name, (sp, n) => 55 | { 56 | return new PostgreSqlTableStorage(sp, n); 57 | }); 58 | return build; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/Configuration/PostgresqlOptions.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Serialization; 2 | 3 | namespace Ray2.PostgreSQL 4 | { 5 | public class PostgreSqlOptions 6 | { 7 | public string ConnectionString { get; set; } 8 | 9 | public string SerializationType { get; set; } = Ray2.SerializationType.JsonUTF8; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/EventStorage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Options; 3 | using Orleans.Runtime; 4 | using Ray2.EventSource; 5 | using Ray2.Internal; 6 | using Ray2.Storage; 7 | using System; 8 | using System.Collections.Concurrent; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Threading.Tasks; 12 | 13 | namespace Ray2.PostgreSQL 14 | { 15 | public class EventStorage : IEventStorage 16 | { 17 | private readonly ConcurrentDictionary storageList = new ConcurrentDictionary(); 18 | private readonly IServiceProvider _serviceProvider; 19 | private readonly IPostgreSqlTableStorage _tableStorage; 20 | private readonly PostgreSqlOptions _options; 21 | private readonly string _providerName; 22 | public EventStorage(IServiceProvider serviceProvider, string name) 23 | { 24 | this._providerName = name; 25 | this._serviceProvider = serviceProvider; 26 | this._tableStorage = serviceProvider.GetRequiredServiceByName(name); 27 | this._options = serviceProvider.GetRequiredService>().Get(name); 28 | } 29 | 30 | public Task GetAsync(string tableName, object stateId, long version) 31 | { 32 | var stotage = this.GetStorage(tableName, stateId); 33 | return stotage.GetAsync(stateId, version); 34 | } 35 | 36 | public Task> GetListAsync(string tableName, EventQueryModel queryModel) 37 | { 38 | var stotage = this.GetStorage(tableName, queryModel.StateId); 39 | return stotage.GetListAsync(queryModel); 40 | } 41 | 42 | public Task SaveAsync(List> wrapList) 43 | { 44 | Dictionary>> eventsList = wrapList.GroupBy(f => f.Data.StorageTableName).ToDictionary(x => x.Key, v => v.ToList()); 45 | foreach (var key in eventsList.Keys) 46 | { 47 | var events = eventsList[key]; 48 | var stotage = this.GetStorage(key, events.First().Data.StateId); 49 | stotage.SaveAsync(events); 50 | } 51 | return Task.CompletedTask; 52 | } 53 | 54 | public Task SaveAsync(EventCollectionStorageModel events) 55 | { 56 | var stotage = this.GetStorage(events.StorageTableName, events.GetStateId()); 57 | return stotage.SaveAsync(events); 58 | } 59 | 60 | private IPostgreSqlEventStorage GetStorage(string tableName, object stateId) 61 | { 62 | return storageList.GetOrAdd(tableName, (key) => 63 | { 64 | this._tableStorage.CreateEventTable(tableName, stateId); 65 | return new PostgreSqlEventStorage(this._serviceProvider, _options, this._providerName, key); 66 | }); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/IPostgreSqlEventStorage.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventSource; 2 | using Ray2.Internal; 3 | using Ray2.Storage; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ray2.PostgreSQL 10 | { 11 | public interface IPostgreSqlEventStorage 12 | { 13 | Task> GetListAsync( EventQueryModel queryModel); 14 | Task GetAsync(object stateId, long version); 15 | Task SaveAsync(List> wrapList); 16 | Task SaveAsync(EventCollectionStorageModel eventList); 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/IPostgreSqlStateStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.PostgreSQL 7 | { 8 | public interface IPostgreSqlStateStorage 9 | { 10 | Task DeleteAsync(object stateId); 11 | Task InsertAsync(object stateId, TState state) where TState : IState, new(); 12 | Task ReadAsync(object stateId) where TState : IState, new(); 13 | Task UpdateAsync(object stateId, TState state) where TState : IState, new(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/IPostgreSqlTableStorage.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Ray2.PostgreSQL 4 | { 5 | public interface IPostgreSqlTableStorage 6 | { 7 | void CreateEventTable(string name,object stateId); 8 | void CreateStateTable(string name, object stateId); 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/PostgreSqlDbContext.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using Npgsql; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ray2.PostgreSQL 9 | { 10 | public class PostgreSqlDbContext : IDisposable 11 | { 12 | private readonly NpgsqlConnection dbConnection; 13 | private readonly PostgreSqlOptions options; 14 | public PostgreSqlDbContext(PostgreSqlOptions options) 15 | { 16 | this.options = options; 17 | dbConnection = new NpgsqlConnection(); 18 | dbConnection.ConnectionString = this.options.ConnectionString; 19 | } 20 | public IDbTransaction BeginTransaction() 21 | { 22 | return this.dbConnection.BeginTransaction(); 23 | } 24 | public IDbTransaction BeginTransaction(IsolationLevel il) 25 | { 26 | return this.dbConnection.BeginTransaction(il); 27 | } 28 | public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand) 29 | { 30 | return this.dbConnection.BeginBinaryExport(copyToCommand); 31 | } 32 | public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand) 33 | { 34 | return this.dbConnection.BeginBinaryImport(copyFromCommand); 35 | } 36 | public void Dispose() 37 | { 38 | this.dbConnection.Dispose(); 39 | } 40 | public Task OpenAsync() 41 | { 42 | this.dbConnection.Open(); 43 | return Task.CompletedTask; 44 | } 45 | public Task ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) 46 | { 47 | return this.dbConnection.ExecuteAsync(sql, param, transaction, commandTimeout, commandType); 48 | } 49 | public Task ExecuteScalarAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) 50 | { 51 | return this.dbConnection.ExecuteScalarAsync(sql, param, transaction, commandTimeout, commandType); 52 | } 53 | public Task> QueryAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) 54 | { 55 | return this.dbConnection.QueryAsync(sql, param, transaction, commandTimeout, commandType); 56 | } 57 | 58 | 59 | public static PostgreSqlDbContext Create(PostgreSqlOptions options) 60 | { 61 | return new PostgreSqlDbContext(options); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/PostgreSqlStateStorage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using Orleans.Runtime; 5 | using Ray2.Serialization; 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ray2.PostgreSQL 11 | { 12 | public class PostgreSqlStateStorage : IPostgreSqlStateStorage 13 | { 14 | private readonly IServiceProvider _serviceProvider; 15 | private readonly ILogger _logger; 16 | private readonly PostgreSqlOptions _options; 17 | private readonly string providerName; 18 | private readonly string tableName; 19 | private string insertSql; 20 | private string updateSql; 21 | private string deleteSql; 22 | private string selectSql; 23 | 24 | public PostgreSqlStateStorage(IServiceProvider serviceProvider, PostgreSqlOptions options, string name, string tableName) 25 | { 26 | this.providerName = name; 27 | this.tableName = tableName; 28 | this._serviceProvider = serviceProvider; 29 | this._options = serviceProvider.GetRequiredService>().Get(name); 30 | this._logger = serviceProvider.GetRequiredService>(); 31 | this.BuildSql(tableName); 32 | } 33 | 34 | public async Task DeleteAsync( object stateId) 35 | { 36 | using (var db = PostgreSqlDbContext.Create(this._options)) 37 | { 38 | return await db.ExecuteAsync(this.deleteSql, new { StateId = stateId.ToString() }) > 0; 39 | } 40 | } 41 | public async Task InsertAsync( object stateId, TState state) where TState : IState, new() 42 | { 43 | using (var db = PostgreSqlDbContext.Create(this._options)) 44 | { 45 | var data = this.GetSerializer().Serialize(state); 46 | return await db.ExecuteAsync(this.insertSql, new { StateId = stateId.ToString(), Data = data, DataType = this._options.SerializationType }) > 0; 47 | } 48 | } 49 | public async Task ReadAsync( object stateId) where TState : IState, new() 50 | { 51 | using (var db = PostgreSqlDbContext.Create(this._options)) 52 | { 53 | var list = await db.QueryAsync(this.selectSql, new { StateId = stateId.ToString() }); 54 | if (list.Count() == 0) 55 | { 56 | return default(TState); 57 | } 58 | var data = list.FirstOrDefault(); 59 | return this.GetSerializer(data.datatype).Deserialize(data.data); 60 | } 61 | } 62 | public async Task UpdateAsync( object stateId, TState state) where TState : IState, new() 63 | { 64 | using (var db = PostgreSqlDbContext.Create(this._options)) 65 | { 66 | var data = this.GetSerializer().Serialize(state); 67 | return await db.ExecuteAsync(this.updateSql, new { StateId = stateId.ToString(), Data = data, DataType = this._options.SerializationType }) > 0; 68 | } 69 | } 70 | private ISerializer GetSerializer(string name) 71 | { 72 | return this._serviceProvider.GetRequiredServiceByName(name); 73 | } 74 | private ISerializer GetSerializer() 75 | { 76 | return this.GetSerializer(this._options.SerializationType); 77 | } 78 | private void BuildSql(string tableName) 79 | { 80 | this.updateSql = $"Update {tableName} set data=@Data,datatype =@DataType WHERE stateid=@StateId"; 81 | this.selectSql = $"SELECT data,datatype FROM {tableName} WHERE stateid=@StateId"; 82 | this.insertSql = $"INSERT INTO {tableName}(stateid,data,datatype) VALUES (@StateId,@Data,@DataType)"; 83 | this.deleteSql = $"DELETE FROM {tableName} WHERE stateid=@StateId"; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/Ray2.PostgreSQL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Ray2.PostgreSQL/StateStorage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Options; 3 | using Orleans.Runtime; 4 | using Ray2.Storage; 5 | using System; 6 | using System.Collections.Concurrent; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ray2.PostgreSQL 10 | { 11 | public class StateStorage : IStateStorage 12 | { 13 | private readonly ConcurrentDictionary storageList = new ConcurrentDictionary(); 14 | private readonly IServiceProvider _serviceProvider; 15 | private readonly IPostgreSqlTableStorage _tableStorage; 16 | private readonly PostgreSqlOptions _options; 17 | private readonly string _providerName; 18 | 19 | public StateStorage(IServiceProvider serviceProvider, string name) 20 | { 21 | this._providerName = name; 22 | this._serviceProvider = serviceProvider; 23 | this._tableStorage = serviceProvider.GetRequiredServiceByName(name); 24 | this._options = serviceProvider.GetRequiredService>().Get(name); 25 | } 26 | public Task DeleteAsync(string tableName, object stateId) 27 | { 28 | var stotage = this.GetStorage(tableName, stateId); 29 | return stotage.DeleteAsync(stateId); 30 | } 31 | 32 | public Task InsertAsync(string tableName, object stateId, TState state) where TState : IState, new() 33 | { 34 | var stotage = this.GetStorage(tableName, stateId); 35 | return stotage.InsertAsync(stateId, state); 36 | } 37 | 38 | public Task ReadAsync(string tableName, object stateId) where TState : IState, new() 39 | { 40 | var stotage = this.GetStorage(tableName, stateId); 41 | return stotage.ReadAsync(stateId); 42 | } 43 | 44 | public Task UpdateAsync(string tableName, object stateId, TState state) where TState : IState, new() 45 | { 46 | var stotage = this.GetStorage(tableName, stateId); 47 | return stotage.UpdateAsync(stateId, state); 48 | } 49 | private IPostgreSqlStateStorage GetStorage(string tableName, object stateId) 50 | { 51 | return storageList.GetOrAdd(tableName, (key) => 52 | { 53 | this._tableStorage.CreateStateTable(tableName, stateId); 54 | return new PostgreSqlStateStorage(this._serviceProvider, _options, this._providerName, key); 55 | }); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Ray2.Protobuf/ProtobufSerializer.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Serialization; 2 | using System; 3 | using System.Text; 4 | 5 | namespace Ray2.Protobuf 6 | { 7 | public class ProtobufSerializer : ISerializer 8 | { 9 | Encoding encoding = Encoding.UTF8; 10 | public T Deserialize(byte[] bytes) 11 | { 12 | using (PooledMemoryStream ms = new PooledMemoryStream(bytes)) 13 | { 14 | return ProtoBuf.Serializer.Deserialize(ms); 15 | } 16 | } 17 | 18 | public object Deserialize(Type type, byte[] bytes) 19 | { 20 | using (PooledMemoryStream ms = new PooledMemoryStream(bytes)) 21 | { 22 | return ProtoBuf.Serializer.Deserialize(type, ms); 23 | } 24 | } 25 | 26 | public byte[] Serialize(object instance) 27 | { 28 | using (PooledMemoryStream ms = new PooledMemoryStream()) 29 | { 30 | ProtoBuf.Serializer.Serialize(ms, instance); 31 | var data = ms.ToArray(); 32 | if (data.Length == 0) 33 | throw new Exception("Protobuf serialization failed, data is empty after serialization"); 34 | else 35 | return data; 36 | } 37 | } 38 | public byte[] Serialize(T instance) 39 | { 40 | using (PooledMemoryStream ms = new PooledMemoryStream()) 41 | { 42 | ProtoBuf.Serializer.Serialize(ms, instance); 43 | var data = ms.ToArray(); 44 | if (data.Length == 0) 45 | throw new Exception("Protobuf serialization failed, data is empty after serialization"); 46 | else 47 | return data; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Ray2.Protobuf/Ray2.Protobuf.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Ray2.Protobuf/RaySiloHostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | using Ray2; 3 | using Ray2.Protobuf; 4 | using Ray2.Serialization; 5 | 6 | namespace Microsoft.Extensions.DependencyInjection 7 | { 8 | public static class RaySiloHostBuilderExtensions 9 | { 10 | /// 11 | /// Add Protobuf serialization 12 | /// 13 | /// 14 | /// 15 | public static IRayBuilder AddProtobuf(this IRayBuilder build) 16 | { 17 | build.Services.AddSingletonNamedService(Ray2.Protobuf.SerializationType.Protobuf); 18 | return build; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Ray2.Protobuf/SerializationType.cs: -------------------------------------------------------------------------------- 1 | namespace Ray2.Protobuf 2 | { 3 | public class SerializationType 4 | { 5 | /// 6 | /// Protobuf 7 | /// 8 | public const string Protobuf = "Protobuf"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/Configuration/RabbitConsumeOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Ray2.RabbitMQ.Configuration 2 | { 3 | public class RabbitConsumeOptions 4 | { 5 | public string Group { get; set; } 6 | public string Topic { get; set; } 7 | public bool AutoAck { get; set; } 8 | public int NoticeRetriesCount { get; set; } 9 | public ushort OneFetchCount { get; set; } = 20; 10 | public int MaxConsumerCount { get; set; } = 10; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/Configuration/RabbitOptions.cs: -------------------------------------------------------------------------------- 1 | using RabbitMQ.Client; 2 | using System.Collections.Generic; 3 | 4 | namespace Ray2.RabbitMQ.Configuration 5 | { 6 | public class RabbitOptions 7 | { 8 | /// 9 | /// This is the UserName of Rebbit 10 | /// 11 | public string UserName { get; set; } 12 | /// 13 | /// This is the Password of Rebbit 14 | /// 15 | public string Password { get; set; } 16 | /// 17 | /// /// 18 | /// This is the VirtualHost of Rebbit 19 | /// 20 | /// 21 | public string VirtualHost { get; set; } = "/"; 22 | /// 23 | /// Data serialization type 24 | /// 25 | public string SerializationType { get; set; } = Ray2.SerializationType.JsonUTF8; 26 | /// 27 | /// This is the HostName of Rebbit 28 | /// 29 | public string HostName { get; set; } 30 | /// 31 | /// This is the HostName of multiple Rebbits. 32 | /// 33 | public string[] HostNames { get; set; } = new string[0]; 34 | /// 35 | /// This is the number of connection pools 36 | /// 37 | public int ConnectionPoolCount { get; set; } = 10; 38 | /// 39 | ///This is the Consumer Options 40 | /// 41 | public List ConsumeOptions { get; set; } = new List(); 42 | public List EndPoints 43 | { 44 | get 45 | { 46 | 47 | var list = new List(); 48 | foreach (var host in HostNames) 49 | { 50 | list.Add(AmqpTcpEndpoint.Parse(host)); 51 | } 52 | if (!string.IsNullOrEmpty(HostName)) 53 | { 54 | list.Add(AmqpTcpEndpoint.Parse(this.HostName)); 55 | 56 | } 57 | return list; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/Configuration/RabbitRayBuilderExpansions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Orleans.Runtime; 4 | using Ray2; 5 | using Ray2.MQ; 6 | using Ray2.RabbitMQ; 7 | using Ray2.RabbitMQ.Configuration; 8 | using System; 9 | 10 | namespace Orleans.Hosting 11 | { 12 | public static class RabbitRayBuilderExpansions 13 | { 14 | /// 15 | /// Add RabbitMQ 16 | /// 17 | /// 18 | /// Provider name 19 | /// RabbitMQ configuration 20 | /// 21 | public static IRayBuilder AddRabbitMQ(this IRayBuilder build, string name, IConfiguration configuration) 22 | { 23 | build.Services.Configure(name, configuration); 24 | return build.AddRabbitMQ(name); 25 | } 26 | 27 | /// 28 | /// Add RabbitMQ 29 | /// 30 | /// 31 | /// Provider name 32 | /// RabbitMQ configuration 33 | /// 34 | public static IRayBuilder AddRabbitMQ(this IRayBuilder build, string name, Action configuration) 35 | { 36 | build.Services.Configure(name, configuration); 37 | return build.AddRabbitMQ(name); 38 | } 39 | 40 | /// 41 | /// Add RabbitMQ 42 | /// 43 | /// 44 | /// Provider name 45 | /// 46 | private static IRayBuilder AddRabbitMQ(this IRayBuilder build, string name) 47 | { 48 | build.Services.AddSingletonNamedService(name, (sp, n) => 49 | { 50 | return new EventPublisher(sp, n); 51 | }); 52 | build.Services.AddSingletonNamedService(name, (sp, n) => 53 | { 54 | return new EventSubscriber(sp, n); 55 | }); 56 | build.Services.AddSingletonNamedService(name, (sp, n) => 57 | { 58 | return new RabbitChannelFactory(sp, n); 59 | }); 60 | return build; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/EventPublisher.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using Orleans.Runtime; 5 | using Ray2.EventSource; 6 | using Ray2.MQ; 7 | using Ray2.RabbitMQ.Configuration; 8 | using Ray2.Serialization; 9 | using System; 10 | using System.Collections.Concurrent; 11 | using System.Collections.Generic; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace Ray2.RabbitMQ 16 | { 17 | public class EventPublisher : IEventPublisher 18 | { 19 | private readonly ConcurrentQueue ProducersPool = new ConcurrentQueue(); 20 | private readonly IServiceProvider _serviceProvider; 21 | private readonly RabbitOptions _options; 22 | private readonly ILogger _logger; 23 | private readonly ISerializer _serializer; 24 | private readonly string providerName; 25 | private int MaxChannelCount = 0; 26 | public EventPublisher(IServiceProvider serviceProvider, string providerName) 27 | { 28 | this.providerName = providerName; 29 | this._serviceProvider = serviceProvider; 30 | this._logger = this._serviceProvider.GetRequiredService>(); 31 | this._options = serviceProvider.GetRequiredService>().Get(providerName); 32 | this._serializer = this._serviceProvider.GetRequiredServiceByName(_options.SerializationType); 33 | this.MaxChannelCount = this._options.ConnectionPoolCount * 20;//20 channels per connection pool 34 | } 35 | 36 | public Task Publish(string topic, EventModel model) 37 | { 38 | IRabbitProducer producer = this.GetProducer(); 39 | return this.Publish(topic, model, producer); 40 | } 41 | 42 | public async Task Publish(string topic, EventModel model, IRabbitProducer producer) 43 | { 44 | var message = new PublishMessage(model, this._serializer); 45 | bool ret = await producer.Publish(topic, topic, message); 46 | this.EnqueuePool(producer); 47 | return ret; 48 | } 49 | 50 | public IRabbitProducer GetProducer() 51 | { 52 | IRabbitProducer producer; 53 | if (ProducersPool.TryDequeue(out producer)) 54 | { 55 | if (producer.IsAvailable()) 56 | { 57 | return producer; 58 | } 59 | else 60 | { 61 | Interlocked.CompareExchange(ref this.MaxChannelCount, 1, 0); 62 | } 63 | } 64 | else 65 | { 66 | if (this.MaxChannelCount > 0) 67 | { 68 | Interlocked.Decrement(ref this.MaxChannelCount); 69 | producer = new RabbitProducer(this._serviceProvider, this.providerName, this._serializer); 70 | return producer; 71 | } 72 | else 73 | { 74 | Task.Delay(500).GetAwaiter().GetResult(); 75 | } 76 | } 77 | return this.GetProducer(); 78 | } 79 | 80 | public void EnqueuePool(IRabbitProducer producer) 81 | { 82 | ProducersPool.Enqueue(producer); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/IRabbitChannel.cs: -------------------------------------------------------------------------------- 1 | using RabbitMQ.Client; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ray2.RabbitMQ 7 | { 8 | public interface IRabbitChannel 9 | { 10 | IRabbitConnection Connection { get; } 11 | bool IsOpen(); 12 | void Close(); 13 | IModel Model { get; } 14 | uint MessageCount(string quene); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/IRabbitChannelFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.RabbitMQ 7 | { 8 | public interface IRabbitChannelFactory 9 | { 10 | IRabbitChannel GetChannel(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/IRabbitConnection.cs: -------------------------------------------------------------------------------- 1 | using RabbitMQ.Client; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ray2.RabbitMQ 7 | { 8 | public interface IRabbitConnection 9 | { 10 | IConnection Connection { get; } 11 | IRabbitChannel CreateChannel(); 12 | void Close(); 13 | bool IsOpen(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/IRabbitConsumer.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventProcess; 2 | using Ray2.RabbitMQ.Configuration; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ray2.RabbitMQ 6 | { 7 | /// 8 | /// This is the consumer interface of Rebbit. 9 | /// 10 | public interface IRabbitConsumer 11 | { 12 | string Queue { get; } 13 | string Exchange { get; } 14 | RabbitConsumeOptions Options { get; } 15 | IEventProcessor Processor { get; } 16 | /// 17 | /// Subscribe to RabbitMQ 18 | /// 19 | /// 20 | Task Subscribe(string queue, string exchange, IEventProcessor processor, RabbitConsumeOptions options); 21 | /// 22 | /// Is this consumer available? 23 | /// 24 | /// 25 | bool IsAvailable(); 26 | /// 27 | /// Need to expand 28 | /// 29 | /// 30 | bool IsExpand(); 31 | /// 32 | /// Stop listening 33 | /// 34 | /// 35 | Task Close(); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/IRabbitProducer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.RabbitMQ 7 | { 8 | /// 9 | /// This is the producer interface of Rebbit. 10 | /// 11 | public interface IRabbitProducer 12 | { 13 | /// 14 | /// Is this producer available? 15 | /// 16 | /// 17 | bool IsAvailable(); 18 | 19 | void Close(); 20 | Task Publish(string exchange, string routingKey, PublishMessage message); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/PublishMessage.cs: -------------------------------------------------------------------------------- 1 | //using ProtoBuf; 2 | 3 | using Ray2.EventSource; 4 | using Ray2.Serialization; 5 | using System; 6 | using System.Runtime.Serialization; 7 | 8 | namespace Ray2.RabbitMQ 9 | { 10 | [DataContract] 11 | public class PublishMessage 12 | { 13 | public PublishMessage() { } 14 | public PublishMessage(EventModel model, ISerializer serializer) 15 | { 16 | this.TypeCode = model.TypeCode; 17 | this.Version = model.Version; 18 | this.Data = serializer.Serialize(model.Event); 19 | } 20 | [DataMember(Order = 1)] 21 | public string TypeCode { get; set; } 22 | [DataMember(Order = 2)] 23 | public Int64 Version { get; set; } 24 | [DataMember(Order = 3)] 25 | public byte[] Data { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/RabbitChannel.cs: -------------------------------------------------------------------------------- 1 | using RabbitMQ.Client; 2 | 3 | namespace Ray2.RabbitMQ 4 | { 5 | public class RabbitChannel : IRabbitChannel 6 | { 7 | public IRabbitConnection Connection { get; } 8 | public IModel Model { get; } 9 | 10 | public RabbitChannel(IRabbitConnection conn) 11 | { 12 | this.Connection = conn; 13 | this.Model = conn.Connection.CreateModel(); 14 | } 15 | 16 | public void Close() 17 | { 18 | this.Model.Close(); 19 | this.Model.Dispose(); 20 | } 21 | public bool IsOpen() 22 | { 23 | if (!this.Model.IsClosed && this.Model.IsOpen) 24 | { 25 | return true; 26 | } 27 | else 28 | { 29 | this.Model.Dispose(); 30 | return false; 31 | } 32 | } 33 | public uint MessageCount(string quene) 34 | { 35 | if (this.IsOpen()) 36 | { 37 | return this.Model.MessageCount(quene); 38 | } 39 | else 40 | { 41 | return 0; 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/RabbitChannelFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using Ray2.RabbitMQ.Configuration; 5 | using System; 6 | using System.Collections.Concurrent; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ray2.RabbitMQ 11 | { 12 | public class RabbitChannelFactory : IRabbitChannelFactory 13 | { 14 | private int RemainderCount;//Number of remaining connection pools 15 | private readonly ConcurrentQueue ConnectionPoll = new ConcurrentQueue(); 16 | private readonly RabbitOptions Options; 17 | private readonly IServiceProvider _serviceProvider; 18 | private readonly ILogger _logger; 19 | public RabbitChannelFactory(IServiceProvider serviceProvider, string providerName) 20 | { 21 | this._serviceProvider = serviceProvider; 22 | this._logger = serviceProvider.GetRequiredService>(); 23 | this.Options = serviceProvider.GetRequiredService>().Get(providerName); 24 | this.RemainderCount = this.Options.ConnectionPoolCount > 0 ? this.Options.ConnectionPoolCount : 10; 25 | } 26 | 27 | public IRabbitChannel GetChannel() 28 | { 29 | IRabbitConnection connection; 30 | if (this.RemainderCount > 0) 31 | { 32 | Interlocked.Decrement(ref this.RemainderCount); 33 | connection = new RabbitConnection(this._serviceProvider, this.Options); 34 | } 35 | else 36 | { 37 | if (ConnectionPoll.TryDequeue(out IRabbitConnection conn)) 38 | { 39 | connection = conn; 40 | } 41 | else 42 | { 43 | Task.Delay(1000).GetAwaiter().GetResult(); 44 | return this.GetChannel(); 45 | } 46 | } 47 | if (connection.IsOpen()) 48 | { 49 | //Continue to line up 50 | var channel = connection.CreateChannel(); 51 | ConnectionPoll.Enqueue(connection); 52 | return channel; 53 | } 54 | else 55 | { 56 | Interlocked.CompareExchange(ref this.RemainderCount, 1, 0); 57 | return this.GetChannel(); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/RabbitConnection.cs: -------------------------------------------------------------------------------- 1 | using RabbitMQ.Client; 2 | using Ray2.RabbitMQ.Configuration; 3 | using System; 4 | 5 | namespace Ray2.RabbitMQ 6 | { 7 | public class RabbitConnection : IRabbitConnection 8 | { 9 | private readonly RabbitOptions Options; 10 | private readonly IServiceProvider _serviceProvider; 11 | private readonly ConnectionFactory _connectionFactory; 12 | public IConnection Connection { get; } 13 | public RabbitConnection(IServiceProvider serviceProvider, RabbitOptions options) 14 | { 15 | this._serviceProvider = serviceProvider; 16 | this.Options = options; 17 | this._connectionFactory = new ConnectionFactory 18 | { 19 | UserName = this.Options.UserName, 20 | Password = this.Options.Password, 21 | VirtualHost = this.Options.VirtualHost, 22 | AutomaticRecoveryEnabled = false 23 | }; 24 | this.Connection = this._connectionFactory.CreateConnection(this.Options.EndPoints); 25 | } 26 | 27 | public void Close() 28 | { 29 | this.Connection.Close(); 30 | this.Connection.Dispose(); 31 | } 32 | 33 | public IRabbitChannel CreateChannel() 34 | { 35 | if (!this.Connection.IsOpen) 36 | { 37 | throw new Exception("rabbitMQ service has been disconnected"); 38 | } 39 | return new RabbitChannel(this); 40 | } 41 | 42 | public bool IsOpen() 43 | { 44 | return this.Connection.IsOpen; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/RabbitProducer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using Orleans.Runtime; 4 | using RabbitMQ.Client; 5 | using Ray2.Serialization; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ray2.RabbitMQ 10 | { 11 | public class RabbitProducer : IRabbitProducer 12 | { 13 | private readonly IServiceProvider _serviceProvider; 14 | private readonly ISerializer _serializer; 15 | private readonly ILogger _logger; 16 | private readonly IRabbitChannel _channel; 17 | private readonly string providerName; 18 | public RabbitProducer(IServiceProvider serviceProvider, string providerName, ISerializer serializer) 19 | { 20 | this.providerName = providerName; 21 | this._serviceProvider = serviceProvider; 22 | this._serializer = serializer; 23 | this._logger = this._serviceProvider.GetRequiredService>(); 24 | var channelFactory = serviceProvider.GetRequiredServiceByName(providerName); 25 | this._channel = channelFactory.GetChannel(); 26 | } 27 | 28 | public void Close() 29 | { 30 | this._channel.Close(); 31 | } 32 | 33 | public bool IsAvailable() 34 | { 35 | return this._channel.IsOpen(); 36 | } 37 | 38 | public Task Publish(string exchange, string routingKey, PublishMessage message) 39 | { 40 | try 41 | { 42 | var data = this._serializer.Serialize(message); 43 | this._channel.Model.BasicPublish(exchange, routingKey, null, data); 44 | return Task.FromResult(true); 45 | } 46 | catch (Exception ex) 47 | { 48 | this._logger.LogError(ex, $"{providerName}-{exchange}&{routingKey} RabbitMQ publishing failed"); 49 | return Task.FromResult(false); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Ray2.RabbitMQ/Ray2.RabbitMQ.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Ray2.SimpleMQ/Ray2.SimpleMQ.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Attributes/EventProcessorAttribute.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration; 2 | using System; 3 | 4 | namespace Ray2 5 | { 6 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 7 | public class EventProcessorAttribute : Attribute 8 | { 9 | public EventProcessorAttribute(string eventSourceName, string mqProviderName, string storageProviderName) 10 | { 11 | this.EventSourceName = eventSourceName; 12 | this.Topic = this.EventSourceName; 13 | 14 | this.MQProvider = mqProviderName; 15 | this.StorageProvider = storageProviderName; 16 | } 17 | public EventProcessorAttribute( Type eventSource, string mqProviderName, string storageProviderName):this(eventSource.Name,mqProviderName,storageProviderName) 18 | { 19 | 20 | } 21 | public string Name { get; set; } 22 | public string EventSourceName { get; set; } 23 | public string MQProvider { get; set; } 24 | public string Topic { get; set; } 25 | public string StorageProvider { get; set; } 26 | public string ShardingStrategy { get; set; } 27 | public int OnceProcessCount { get; set; } = 1; 28 | public TimeSpan OnceProcessTimeout { get; set; } = TimeSpan.FromMinutes(5); 29 | public StatusMode StatusMode { get; set; } = StatusMode.Asynchronous; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Attributes/EventPublishAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Ray2 4 | { 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 6 | public class EventPublishAttribute : Attribute 7 | { 8 | public EventPublishAttribute(string topic,string mqprovider) 9 | { 10 | this.Topic = topic; 11 | this.MQProvider = mqprovider; 12 | } 13 | /// 14 | /// This is a message queue provider 15 | /// 16 | public string MQProvider { get; set; } 17 | /// 18 | /// mq topic 19 | /// 20 | public string Topic { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Attributes/EventSourcingAttribute.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration; 2 | using System; 3 | 4 | namespace Ray2 5 | { 6 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 7 | public class EventSourcingAttribute : Attribute 8 | { 9 | public EventSourcingAttribute(string storageProviderName, string mqProviderName) 10 | { 11 | this.MQProvider = mqProviderName; 12 | this.StorageProvider = storageProviderName; 13 | } 14 | public string Name { get; set; } 15 | public string MQProvider { get; set; } 16 | public string Topic { get; set; } 17 | public string StorageProvider { get; set; } 18 | public string ShardingStrategy { get; set; } 19 | public SnapshotType SnapshotType { get; set; } = SnapshotType.Asynchronous; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Attributes/EventSubscribeAttribute.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ray2 7 | { 8 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 9 | public class EventSubscribeAttribute : Attribute 10 | { 11 | public EventSubscribeAttribute(string topic, string mqprovider) 12 | { 13 | this.Topic = topic; 14 | this.MQProvider = mqprovider; 15 | } 16 | public string Topic { get; set; } 17 | public string MQProvider { get; set; } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Builder/EventProcessOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Ray2.Configuration.Builder 6 | { 7 | public class EventProcessOptionsBuilder 8 | { 9 | private string _processorName; 10 | private string _processorFullName; 11 | private string _eventSourceName; 12 | private int _onceProcessCount; 13 | private TimeSpan _onceProcessTimeout; 14 | private StatusOptions _statusOptions; 15 | private IList _eventSubscribeOptions; 16 | private ProcessorType _processorType; 17 | private Type _processorHandle; 18 | public EventProcessOptionsBuilder WithProcessor(string name, Type type) 19 | { 20 | this._processorHandle = type; 21 | this._processorName = name; 22 | this._processorFullName = type.FullName; 23 | if (typeof(Grain).IsAssignableFrom(type)) 24 | { 25 | _processorType = ProcessorType.GrainProcessor; 26 | } 27 | else 28 | { 29 | _processorType = ProcessorType.SimpleProcessor; 30 | } 31 | return this; 32 | } 33 | 34 | public EventProcessOptionsBuilder WithEventSourceName(string name) 35 | { 36 | this._eventSourceName = name; 37 | return this; 38 | } 39 | 40 | public EventProcessOptionsBuilder WithOnceProcessConfig(int onceProcessCount, TimeSpan onceProcessTimeout) 41 | { 42 | this._onceProcessCount = onceProcessCount; 43 | this._onceProcessTimeout = onceProcessTimeout; 44 | return this; 45 | } 46 | 47 | public EventProcessOptionsBuilder WithStatusOptions(StatusOptions statusOptions) 48 | { 49 | this._statusOptions = statusOptions; 50 | return this; 51 | } 52 | 53 | public EventProcessOptionsBuilder WithEventSubscribeOptions(IList eventSubscribeOptions) 54 | { 55 | this._eventSubscribeOptions = eventSubscribeOptions; 56 | return this; 57 | } 58 | 59 | public EventProcessOptions Build() 60 | { 61 | return new EventProcessOptions(_processorName, _processorFullName, _processorType, _processorHandle, _eventSourceName, _onceProcessCount, _onceProcessTimeout, _statusOptions, _eventSubscribeOptions); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Builder/EventSourceOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Ray2.Configuration.Builder 4 | { 5 | public class EventSourceOptionsBuilder 6 | { 7 | private string _eventSourceName; 8 | private string _fourcingFullName; 9 | private SnapshotOptions _snapshotOptions; 10 | private StorageOptions _storageOptions; 11 | 12 | public EventSourceOptionsBuilder WithEventSourcing(string name, Type type) 13 | { 14 | this._eventSourceName = name; 15 | this._fourcingFullName = type.FullName; 16 | return this; 17 | } 18 | 19 | public EventSourceOptionsBuilder WithSnapshotOptions(SnapshotOptions snapshotOptions) 20 | { 21 | this._snapshotOptions = snapshotOptions; 22 | return this; 23 | } 24 | 25 | public EventSourceOptionsBuilder WithStorageOptions(StorageOptions storageOptions) 26 | { 27 | this._storageOptions = storageOptions; 28 | return this; 29 | } 30 | public EventSourceOptions Build() 31 | { 32 | return new EventSourceOptions(_eventSourceName, _fourcingFullName, _snapshotOptions, _storageOptions); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Builder/InternalConfigurationBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Ray2.Configuration.Builder 4 | { 5 | /// 6 | /// System internal configuration builder 7 | /// 8 | public class InternalConfigurationBuilder 9 | { 10 | InternalConfiguration configuration; 11 | public InternalConfigurationBuilder() 12 | { 13 | configuration = new InternalConfiguration(); 14 | } 15 | 16 | public void WithEventProcessOptions(EventProcessOptions options) 17 | { 18 | this.configuration._eventProcessOptions.Add(options.ProcessorName, options); 19 | this.configuration._eventProcessOptionsByProcessor.Add(options.ProcessorFullName, options); 20 | } 21 | 22 | public void WithEventProcessOptions(IList options) 23 | { 24 | foreach (var item in options) 25 | { 26 | this.WithEventProcessOptions(item); 27 | } 28 | } 29 | public void WithEventSourceOptions(EventSourceOptions options) 30 | { 31 | this.configuration._eventSourceOptions.Add(options.EventSourceName, options); 32 | this.configuration._eventSourceOptionsBySource.Add(options.SourcingFullName, options); 33 | } 34 | public void WithEventPublishOptions(string name, EventPublishOptions options) 35 | { 36 | this.configuration._eventPublishOptions.Add(name, options); 37 | } 38 | public void WithEventInfo(EventInfo info) 39 | { 40 | this.configuration._eventInfos.Add(info.Name, info); 41 | } 42 | public InternalConfiguration Build() 43 | { 44 | return configuration; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/EventProcessOptionsCreator.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace Ray2.Configuration.Creator 7 | { 8 | public class EventProcessOptionsCreator : IEventProcessOptionsCreator 9 | { 10 | private readonly IEventSubscribeOptionsCreator _eventSubscribeOptionsCreator; 11 | public EventProcessOptionsCreator(IEventSubscribeOptionsCreator eventSubscribeOptionsCreator) 12 | { 13 | this._eventSubscribeOptionsCreator = eventSubscribeOptionsCreator; 14 | } 15 | 16 | public EventProcessOptions Create(Type type) 17 | { 18 | List options = new List(); 19 | var attribute = type.GetCustomAttribute(); 20 | if (attribute == null) 21 | { 22 | throw new Exception($"The {type.FullName} processor does not have an {nameof(EventProcessorAttribute)} configured."); 23 | } 24 | 25 | var statusOptions = this.CreateStatusOptions(attribute); 26 | var eventSubscribeOptionsList = this._eventSubscribeOptionsCreator.Create(type); 27 | string name = attribute.Name; 28 | if (string.IsNullOrEmpty(name)) 29 | { 30 | name = type.Name; 31 | } 32 | return new EventProcessOptionsBuilder() 33 | .WithProcessor(name, type) 34 | .WithEventSourceName(attribute.EventSourceName) 35 | .WithOnceProcessConfig(attribute.OnceProcessCount, attribute.OnceProcessTimeout) 36 | .WithStatusOptions(statusOptions) 37 | .WithEventSubscribeOptions(eventSubscribeOptionsList) 38 | .Build(); 39 | } 40 | 41 | private StatusOptions CreateStatusOptions(EventProcessorAttribute attribute) 42 | { 43 | return new StatusOptions(attribute.StorageProvider, attribute.ShardingStrategy, attribute.StatusMode); 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/EventPublishOptionsCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Ray2.Configuration.Creator 5 | { 6 | public class EventPublishOptionsCreator : IEventPublishOptionsCreator 7 | { 8 | public EventPublishOptions Create(Type type) 9 | { 10 | var attr = type.GetCustomAttribute(); 11 | if (attr != null) 12 | { 13 | string topic = attr.Topic; 14 | if (string.IsNullOrEmpty(topic)) 15 | { 16 | topic = type.Name; 17 | } 18 | return new EventPublishOptions(topic, attr.MQProvider, type.FullName); 19 | } 20 | 21 | var attribute = type.GetCustomAttribute(); 22 | if (attribute == null) 23 | { 24 | return null; 25 | } 26 | return new EventPublishOptions(attribute.Topic, attribute.MQProvider, type.FullName); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/EventSourceOptionsCreator.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration.Builder; 2 | using System; 3 | using System.Reflection; 4 | 5 | namespace Ray2.Configuration.Creator 6 | { 7 | public class EventSourceOptionsCreator : IEventSourceOptionsCreator 8 | { 9 | public EventSourceOptions Create(Type type) 10 | { 11 | var attr = type.GetCustomAttribute(); 12 | if (attr == null) 13 | { 14 | throw new Exception($"The {type.FullName} does not have an {nameof(EventSourcingAttribute)} configured."); 15 | } 16 | 17 | var snapshotOptions = this.CreateSnapshotOptions(attr); 18 | var storageOptions = this.CreateStorageOptions(attr); 19 | string name = attr.Name; 20 | if (string.IsNullOrEmpty(name)) 21 | { 22 | name = type.Name; 23 | } 24 | 25 | return new EventSourceOptionsBuilder().WithEventSourcing(name, type) 26 | .WithSnapshotOptions(snapshotOptions) 27 | .WithStorageOptions(storageOptions) 28 | .Build(); 29 | } 30 | 31 | private SnapshotOptions CreateSnapshotOptions(EventSourcingAttribute attribute) 32 | { 33 | if (attribute.SnapshotType == SnapshotType.NoSnapshot) 34 | { 35 | return new SnapshotOptions(); 36 | } 37 | else 38 | { 39 | return new SnapshotOptions(attribute.StorageProvider, attribute.ShardingStrategy, attribute.SnapshotType); 40 | } 41 | } 42 | private StorageOptions CreateStorageOptions(EventSourcingAttribute attribute) 43 | { 44 | return new StorageOptions(attribute.StorageProvider, attribute.ShardingStrategy); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/EventSubscribeOptionsCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Ray2.Configuration.Creator 7 | { 8 | public class EventSubscribeOptionsCreator : IEventSubscribeOptionsCreator 9 | { 10 | public IList Create(Type type) 11 | { 12 | IList optionsList = new List(); 13 | var attribute = type.GetCustomAttribute(); 14 | var options = this.CreateEventSubscribeOptions(type.FullName, attribute); 15 | optionsList.Add(options); 16 | 17 | var attributes = type.GetCustomAttributes(); 18 | if (attributes == null || attributes.Count() == 0) 19 | return optionsList; 20 | 21 | foreach (var attr in attributes) 22 | { 23 | options = this.CreateEventSubscribeOptions(type.FullName, attr); 24 | optionsList.Add(options); 25 | } 26 | return optionsList; 27 | } 28 | 29 | 30 | private EventSubscribeOptions CreateEventSubscribeOptions(string fullName, EventSubscribeAttribute attribute) 31 | { 32 | return new EventSubscribeOptions(attribute.MQProvider, attribute.Topic, fullName); 33 | } 34 | private EventSubscribeOptions CreateEventSubscribeOptions(string fullName, EventProcessorAttribute attribute) 35 | { 36 | return new EventSubscribeOptions(attribute.MQProvider, attribute.Topic, fullName); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/IEventProcessOptionsCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Ray2.Configuration.Creator 5 | { 6 | public interface IEventProcessOptionsCreator 7 | { 8 | EventProcessOptions Create(Type type); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/IEventPublishOptionsCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Ray2.Configuration.Creator 4 | { 5 | public interface IEventPublishOptionsCreator 6 | { 7 | EventPublishOptions Create(Type type); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/IEventSourceOptionsCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Ray2.Configuration.Creator 4 | { 5 | public interface IEventSourceOptionsCreator 6 | { 7 | EventSourceOptions Create(Type type); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/IEventSubscribeOptionsCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Ray2.Configuration.Creator 5 | { 6 | public interface IEventSubscribeOptionsCreator 7 | { 8 | IList Create(Type type); 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Creator/IInternalConfigurationCreator.cs: -------------------------------------------------------------------------------- 1 | namespace Ray2.Configuration.Creator 2 | { 3 | public interface IInternalConfigurationCreator 4 | { 5 | InternalConfiguration Create(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/DependencyInjection/IRayBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Ray2 5 | { 6 | public interface IRayBuilder 7 | { 8 | IServiceCollection Services { get; } 9 | IConfiguration Configuration { get; } 10 | void Build(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/DependencyInjection/RayBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Ray2.Configuration; 4 | using Ray2.Configuration.Creator; 5 | using Ray2.Configuration.Validator; 6 | using Ray2.EventProcess; 7 | using Ray2.EventSource; 8 | using Ray2.MQ; 9 | using Ray2.Serialization; 10 | using Orleans.Runtime; 11 | using Ray2.Internal; 12 | 13 | namespace Ray2 14 | { 15 | public class RayBuilder : IRayBuilder 16 | { 17 | public IServiceCollection Services { get; } 18 | public IConfiguration Configuration { get; } 19 | public RayBuilder(IServiceCollection services, IConfiguration configuration) 20 | { 21 | this.Configuration = configuration; 22 | this.Services = services; 23 | } 24 | public void Build() 25 | { 26 | this.AddCoreServices(); 27 | 28 | this.AddConfigurationServices(); 29 | 30 | this.AddInternalConfiguration(); 31 | } 32 | 33 | private void AddCoreServices() 34 | { 35 | this.Services.AddOptions(); 36 | this.Services.AddTransient(typeof(IEventSourcing<,>), typeof(EventSourcing<,>)); 37 | this.Services.AddTransient(); 38 | this.Services.AddTransient(typeof(IEventProcessCore<,>), typeof(EventProcessCore<,>)); 39 | this.Services.AddTransient(); 40 | 41 | this.Services.AddSingleton(); 42 | this.Services.AddSingleton(); 43 | this.Services.AddSingleton(); 44 | this.Services.AddSingleton(); 45 | this.Services.AddSingletonNamedService(SerializationType.JsonUTF8); 46 | } 47 | 48 | private void AddConfigurationServices() 49 | { 50 | this.Services.AddSingleton(); 51 | this.Services.AddSingleton(); 52 | this.Services.AddSingleton(); 53 | this.Services.AddSingleton(); 54 | this.Services.AddSingleton(); 55 | 56 | this.Services.AddSingleton(); 57 | this.Services.AddSingleton(); 58 | this.Services.AddSingleton(); 59 | this.Services.AddSingleton(); 60 | this.Services.AddSingleton(); 61 | } 62 | 63 | private void AddInternalConfiguration() 64 | { 65 | var configuration = new InternalConfigurationCreator().Create(); 66 | this.Services.AddSingleton(configuration); 67 | this.Services.BuildServiceProvider().GetRequiredService().IsValid(configuration); 68 | 69 | //Inject the Processor into the DI system 70 | var processList = configuration.EventProcessOptionsList; 71 | if (processList == null || processList.Count == 0) 72 | return; 73 | foreach (var p in processList) 74 | { 75 | if (p.ProcessorType == ProcessorType.SimpleProcessor) 76 | { 77 | this.Services.AddSingleton(p.ProcessorHandle); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/DependencyInjection/RaySiloHostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Orleans.Runtime; 4 | using Ray2; 5 | using Ray2.Configuration.Validator; 6 | using Ray2.MQ; 7 | using System; 8 | 9 | namespace Orleans.Hosting 10 | { 11 | public static class RaySiloHostBuilderExtensions 12 | { 13 | /// 14 | /// Use Ray 15 | /// 16 | /// 17 | /// Ray and Ray module configuration 18 | /// Provide a action for building Ray 19 | /// 20 | public static ISiloHostBuilder UseRay(this ISiloHostBuilder hostBuilder, Action builder) 21 | { 22 | hostBuilder.ConfigureServices((HostBuilderContext build, IServiceCollection services) => 23 | { 24 | 25 | services.AddRay(build.Configuration, builder); 26 | }); 27 | hostBuilder.EnableDirectClient(); 28 | hostBuilder.AddStartupTask((sp,cancellationToken)=> 29 | { 30 | return sp.GetRequiredService().Start(); 31 | }); 32 | return hostBuilder; 33 | } 34 | 35 | /// 36 | /// Add Ray 37 | /// 38 | /// 39 | /// Ray and Ray module configuration 40 | /// Provide a action for building Ray 41 | /// 42 | internal static IServiceCollection AddRay(this IServiceCollection services, IConfiguration configuration, Action builder) 43 | { 44 | services.AddSingleton(typeof(IKeyedServiceCollection<,>), typeof(KeyedServiceCollection<,>)); 45 | services.AddLogging(); 46 | var build = new RayBuilder(services, configuration); 47 | if (builder == null) 48 | { 49 | throw new RayConfigurationException("Did not inject MQ providers and Storage providers into Ray"); 50 | } 51 | builder.Invoke(build); 52 | 53 | //Ray builder 54 | build.Build(); 55 | return services; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/EventInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Configuration 6 | { 7 | /// 8 | /// Ray event information 9 | /// 10 | public class EventInfo 11 | { 12 | /// 13 | /// The name of the event 14 | /// 15 | public string Name { get; set; } 16 | /// 17 | /// Type of event 18 | /// 19 | public Type Type { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/EventProcessOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Ray2.Configuration 5 | { 6 | public class EventProcessOptions 7 | { 8 | public EventProcessOptions(string processorName, string processorFullName, ProcessorType processorType, Type processorHandle, string eventSourceName, int onceProcessCount, TimeSpan onceProcessTimeout, StatusOptions statusOptions, IList subscribeOptions) 9 | { 10 | this.ProcessorName = processorName; 11 | this.ProcessorFullName = processorFullName; 12 | this.EventSourceName = eventSourceName; 13 | this.OnceProcessCount = onceProcessCount; 14 | this.OnceProcessTimeout = onceProcessTimeout; 15 | this.StatusOptions = statusOptions; 16 | this.SubscribeOptions = subscribeOptions; 17 | this.ProcessorType = processorType; 18 | this.ProcessorHandle = processorHandle; 19 | } 20 | public string ProcessorName { get; private set; } 21 | public string EventSourceName { get; private set; } 22 | public string ProcessorFullName { get; private set; } 23 | public ProcessorType ProcessorType { get; private set; } 24 | public Type ProcessorHandle { get; private set; } 25 | public int OnceProcessCount { get; private set; } 26 | public TimeSpan OnceProcessTimeout { get; private set; } 27 | public StatusOptions StatusOptions { get; private set; } 28 | public IList SubscribeOptions { get; private set; } 29 | } 30 | 31 | /// 32 | /// Event processor type 33 | /// 34 | public enum ProcessorType 35 | { 36 | GrainProcessor, 37 | SimpleProcessor 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/EventPublishOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Configuration 6 | { 7 | public class EventPublishOptions 8 | { 9 | public EventPublishOptions(string topic, string mqProvider,string fullName) 10 | { 11 | this.Topic = topic; 12 | this.MQProvider = mqProvider; 13 | this.EventPublishFullName = fullName; 14 | } 15 | public string EventPublishFullName { get; private set; } 16 | public string Topic { get; private set; } 17 | public string MQProvider { get; private set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/EventSourceOptions.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Storage; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ray2.Configuration 7 | { 8 | public class EventSourceOptions 9 | { 10 | public EventSourceOptions(string eventSourceName,string sourcingFullName, SnapshotOptions snapshotOptions , StorageOptions storageOptions) 11 | { 12 | this.EventSourceName = eventSourceName; 13 | this.SourcingFullName = sourcingFullName; 14 | this.SnapshotOptions = snapshotOptions; 15 | this.StorageOptions = storageOptions; 16 | } 17 | public string EventSourceName { get; private set; } 18 | public string SourcingFullName { get; private set; } 19 | public SnapshotOptions SnapshotOptions { get; private set; } 20 | public StorageOptions StorageOptions { get; private set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/EventSubscribeOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Configuration 6 | { 7 | public class EventSubscribeOptions 8 | { 9 | public EventSubscribeOptions(string mqProvider, string topic, string fullName) 10 | { 11 | this.MQProvider = mqProvider; 12 | this.Topic = topic; 13 | this.EventSubscribeFullName = fullName; 14 | } 15 | public string EventSubscribeFullName { get; private set; } 16 | public string MQProvider { get; private set; } 17 | public string Topic { get; private set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/IInternalConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventProcess; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ray2.Configuration 7 | { 8 | /// 9 | /// Ray system internal configuration 10 | /// 11 | public interface IInternalConfiguration 12 | { 13 | /// 14 | /// Get all event source configurations 15 | /// 16 | /// 17 | IList EventSourceOptions { get; } 18 | /// 19 | /// Get all event publishing configurations 20 | /// 21 | /// 22 | IList EventPublishOptionsList { get; } 23 | /// 24 | /// Get all processor configurations 25 | /// 26 | /// 27 | IList EventProcessOptionsList { get; } 28 | 29 | /// 30 | /// Get the event type by name 31 | /// 32 | /// The name of the event 33 | /// event type 34 | /// 35 | bool GetEvenType(string name, out Type type); 36 | /// 37 | /// Get event information by name 38 | /// 39 | /// The name of the event 40 | /// 41 | EventInfo GetEvenInfo(string name); 42 | /// 43 | /// Get all event types 44 | /// 45 | /// 46 | IList GetEvenInfoList(); 47 | /// 48 | /// Get the configuration of the event processor based on the event processor name 49 | /// 50 | /// The name of the event processor 51 | /// 52 | EventProcessOptions GetEventProcessOptions(string name); 53 | /// 54 | /// Get the event processor configuration based on the event processor 55 | /// 56 | /// processor 57 | /// 58 | EventProcessOptions GetEventProcessOptions(IEventProcessor eventProcessor); 59 | 60 | /// 61 | /// Get the event source configuration based on the event source name 62 | /// 63 | /// The name of the event source 64 | /// 65 | EventSourceOptions GetEventSourceOptions(string name); 66 | /// 67 | /// Get the event source configuration based on the event source 68 | /// 69 | /// Event source 70 | /// 71 | EventSourceOptions GetEventSourceOptions(IRay ray); 72 | 73 | /// 74 | /// Get the event publishing configuration in the event source 75 | /// 76 | /// Event source 77 | /// 78 | EventPublishOptions GetEventPublishOptions(IRay ray); 79 | /// 80 | /// Get the event publishing configuration in the event processor 81 | /// 82 | /// event 83 | /// 84 | EventPublishOptions GetEventPublishOptions(IEvent @event); 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/SnapshotOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Configuration 6 | { 7 | public class SnapshotOptions : StorageOptions 8 | { 9 | public SnapshotOptions(string storageProvider, string shardingStrategy, SnapshotType snapshotType) 10 | : base(storageProvider, shardingStrategy) 11 | { 12 | this.SnapshotType = snapshotType; 13 | } 14 | 15 | public SnapshotOptions() 16 | : base(null, null) 17 | { 18 | this.SnapshotType = SnapshotType.NoSnapshot; 19 | } 20 | public SnapshotType SnapshotType { get; private set; } 21 | 22 | } 23 | public enum SnapshotType 24 | { 25 | Synchronous, 26 | Asynchronous, 27 | NoSnapshot 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/StatusOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Configuration 6 | { 7 | public class StatusOptions : StorageOptions 8 | { 9 | public StatusOptions(string storageProvider, string shardingStrategy, StatusMode statusMode) : base(storageProvider, shardingStrategy) 10 | { 11 | this.StatusMode = statusMode; 12 | } 13 | public StatusMode StatusMode { get; private set; } 14 | } 15 | 16 | public enum StatusMode 17 | { 18 | Synchronous, 19 | Asynchronous 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/StorageOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Configuration 6 | { 7 | public class StorageOptions 8 | { 9 | public StorageOptions(string storageProvider,string shardingStrategy) 10 | { 11 | this.StorageProvider = storageProvider; 12 | this.ShardingStrategy = shardingStrategy; 13 | } 14 | public string StorageProvider { get; private set; } 15 | public string ShardingStrategy { get; private set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Validator/EventProcessOptionsFluentVaildator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Orleans.Runtime; 3 | using Ray2.Storage; 4 | using System; 5 | using System.Linq; 6 | 7 | namespace Ray2.Configuration.Validator 8 | { 9 | public class EventProcessOptionsFluentVaildator : AbstractValidator 10 | { 11 | private readonly IServiceProvider serviceProvider; 12 | 13 | public EventProcessOptionsFluentVaildator(IServiceProvider provider, EventSubscribeOptionsFluentVaildator eventSubscribeOptionsFluentVaildator) 14 | { 15 | this.serviceProvider = provider; 16 | 17 | base.RuleFor(x => x.ProcessorName) 18 | .NotEmpty() 19 | .WithMessage(x => $"EventProcessorAttribute.Name in {x.ProcessorFullName} cannot be empty"); 20 | 21 | base.When(x => x.ProcessorType == ProcessorType.GrainProcessor, () => 22 | { 23 | base.RuleFor(x => x.EventSourceName) 24 | .NotEmpty() 25 | .WithMessage(x => $"EventProcessorAttribute.EventSourceName in {x.ProcessorFullName} cannot be empty"); 26 | }); 27 | 28 | base.RuleFor(x => x.OnceProcessCount) 29 | .GreaterThan(0) 30 | .WithMessage(x => $"The EventProcessorAttribute.OnceProcessCount configuration in {x.ProcessorFullName} must be greater than zero"); 31 | 32 | base.RuleFor(x => x.StatusOptions) 33 | .Must(x => !string.IsNullOrEmpty(x.ShardingStrategy) || !string.IsNullOrEmpty(x.StorageProvider)) 34 | .WithMessage(x => $" Need to configure EventProcessorAttribute.StorageProvider or EventProcessorAttribute.ShardingStrategy storage provider in {x.ProcessorFullName}"); 35 | 36 | base.When(x => !string.IsNullOrEmpty(x.StatusOptions.ShardingStrategy), () => 37 | { 38 | base.RuleFor(x => x.StatusOptions.ShardingStrategy) 39 | .Must(this.HavaShardingStrategyRegistered) 40 | .WithMessage("{PropertyValue} IStorageSharding is not injected into the Ray"); 41 | }); 42 | base.When(x => !string.IsNullOrEmpty(x.StatusOptions.StorageProvider), () => 43 | { 44 | base.RuleFor(x => x.StatusOptions.StorageProvider) 45 | .Must(this.HavaStateStorageProviderRegistered) 46 | .WithMessage("{PropertyValue} IEventStorage provider is not injected into the Ray"); 47 | }); 48 | 49 | base.RuleFor(x => x.SubscribeOptions.Count()) 50 | .GreaterThan(0) 51 | .WithMessage(x => $"{x.ProcessorFullName} is not configured with EventSubscribeAttribute and cannot be subscribed to"); 52 | base.RuleForEach(x => x.SubscribeOptions) 53 | .SetValidator(eventSubscribeOptionsFluentVaildator); 54 | 55 | } 56 | 57 | private bool HavaStateStorageProviderRegistered(string storageProvider) 58 | { 59 | var provider = this.serviceProvider.GetServiceByName(storageProvider); 60 | return provider != null; 61 | } 62 | 63 | private bool HavaShardingStrategyRegistered(string shardingStrategy) 64 | { 65 | var provider = this.serviceProvider.GetServiceByName(shardingStrategy); 66 | return provider != null; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Validator/EventPublishOptionsFluentVaildator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Orleans.Runtime; 3 | using Ray2.MQ; 4 | using System; 5 | 6 | namespace Ray2.Configuration.Validator 7 | { 8 | public class EventPublishOptionsFluentVaildator : AbstractValidator 9 | { 10 | private readonly IServiceProvider serviceProvider; 11 | public EventPublishOptionsFluentVaildator(IServiceProvider provider) 12 | { 13 | this.serviceProvider = provider; 14 | 15 | base.RuleFor(x => x.MQProvider) 16 | .NotEmpty() 17 | .WithMessage(x=>$"EventPublishAttribute.MQProvider in {x.EventPublishFullName} cannot be empty"); 18 | 19 | base.RuleFor(x => x.MQProvider) 20 | .Must(this.HaveMQProviderRegistered) 21 | .WithMessage("{PropertyValue} MQ provider is not injected into the Ray"); 22 | 23 | base.RuleFor(x => x.Topic) 24 | .NotEmpty() 25 | .WithMessage(x=> $"EventPublishAttribute.MQTopic in {x.EventPublishFullName} cannot be empty"); 26 | } 27 | 28 | private bool HaveMQProviderRegistered(string MQProvider) 29 | { 30 | var provider = this.serviceProvider.GetServiceByName(MQProvider); 31 | return provider != null; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Validator/EventSourceOptionsFluentVaildator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Orleans.Runtime; 3 | using Ray2.Storage; 4 | using System; 5 | 6 | namespace Ray2.Configuration.Validator 7 | { 8 | public class EventSourceOptionsFluentVaildator : AbstractValidator 9 | { 10 | private readonly IServiceProvider serviceProvider; 11 | public EventSourceOptionsFluentVaildator(IServiceProvider provider) 12 | { 13 | this.serviceProvider = provider; 14 | 15 | base.RuleFor(x => x.EventSourceName) 16 | .NotEmpty() 17 | .WithMessage(x => $"EventSourcingAttribute.Name in {x.SourcingFullName} cannot be empty"); 18 | 19 | base.RuleFor(x => x.StorageOptions) 20 | .Must(x => !string.IsNullOrEmpty(x.ShardingStrategy) || !string.IsNullOrEmpty(x.StorageProvider)) 21 | .WithMessage(x => $" Need to configure EventSourcingAttribute.StorageProvider or EventSourcingAttribute.ShardingStrategy storage provider in {x.SourcingFullName}"); 22 | base.When(x => !string.IsNullOrEmpty(x.StorageOptions.ShardingStrategy), () => 23 | { 24 | base.RuleFor(x => x.StorageOptions.ShardingStrategy) 25 | .Must(this.HavaShardingStrategyRegistered) 26 | .WithMessage("{PropertyValue} IStorageSharding is not injected into the Ray"); 27 | }); 28 | base.When(x => !string.IsNullOrEmpty(x.StorageOptions.StorageProvider), () => 29 | { 30 | base.RuleFor(x => x.StorageOptions.StorageProvider) 31 | .Must(this.HavaEventStorageProviderRegistered) 32 | .WithMessage("{PropertyValue} IEventStorage provider is not injected into the Ray"); 33 | }); 34 | 35 | base.When(x => x.SnapshotOptions.SnapshotType != SnapshotType.NoSnapshot, () => 36 | { 37 | //base.RuleFor(x => x.SnapshotOptions) 38 | // .Must(x => string.IsNullOrEmpty(x.ShardingStrategy) && string.IsNullOrEmpty(x.StorageProvider)) 39 | // .WithMessage(x => $" Need to configure EventSourcingAttribute.StorageProvider or EventSourcingAttribute.ShardingStrategy storage provider in {x.SourcingFullName}"); 40 | 41 | base.When(x => !string.IsNullOrEmpty(x.SnapshotOptions.ShardingStrategy), () => 42 | { 43 | base.RuleFor(x => x.SnapshotOptions.ShardingStrategy) 44 | .Must(this.HavaShardingStrategyRegistered) 45 | .WithMessage("{PropertyValue} IStorageSharding is not injected into the Ray"); 46 | }); 47 | base.When(x => !string.IsNullOrEmpty(x.SnapshotOptions.StorageProvider), () => 48 | { 49 | base.RuleFor(x => x.SnapshotOptions.StorageProvider) 50 | .Must(this.HavaSnapshotStorageProviderRegistered) 51 | .WithMessage("{PropertyValue} IStateStorage provider is not injected into the Ray"); 52 | }); 53 | }); 54 | } 55 | 56 | private bool HavaEventStorageProviderRegistered(string storageProvider) 57 | { 58 | var provider = this.serviceProvider.GetServiceByName(storageProvider); 59 | return provider != null; 60 | } 61 | 62 | private bool HavaSnapshotStorageProviderRegistered(string storageProvider) 63 | { 64 | var provider = this.serviceProvider.GetServiceByName(storageProvider); 65 | return provider != null; 66 | } 67 | 68 | private bool HavaShardingStrategyRegistered(string shardingStrategy) 69 | { 70 | var provider = this.serviceProvider.GetServiceByName(shardingStrategy); 71 | return provider != null; 72 | } 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Validator/EventSubscribeOptionsFluentVaildator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using Orleans.Runtime; 3 | using Ray2.MQ; 4 | using System; 5 | 6 | namespace Ray2.Configuration.Validator 7 | { 8 | public class EventSubscribeOptionsFluentVaildator : AbstractValidator 9 | { 10 | private readonly IServiceProvider serviceProvider; 11 | public EventSubscribeOptionsFluentVaildator(IServiceProvider provider) 12 | { 13 | this.serviceProvider = provider; 14 | 15 | base.RuleFor(x => x.MQProvider) 16 | .NotEmpty() 17 | .WithMessage(x => $"EventSubscribeAttribute.MQProvider in {x.EventSubscribeFullName} cannot be empty"); 18 | 19 | base.RuleFor(x => x.MQProvider) 20 | .Must(this.HaveMQProviderRegistered) 21 | .WithMessage("{PropertyValue} MQ provider is not injected into the Ray"); 22 | 23 | base.RuleFor(x => x.Topic) 24 | .NotEmpty() 25 | .WithMessage(x => $"EventSubscribeAttribute.Topic in {x.EventSubscribeFullName} cannot be empty"); 26 | 27 | } 28 | 29 | 30 | private bool HaveMQProviderRegistered(string MQProvider) 31 | { 32 | var provider = this.serviceProvider.GetServiceByName(MQProvider); 33 | return provider != null; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Validator/IInternalConfigurationValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Ray2.Configuration.Validator 4 | { 5 | public interface IInternalConfigurationValidator 6 | { 7 | void IsValid(InternalConfiguration configuration); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Validator/InternalConfigurationFluentVaildator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ray2.Configuration.Validator 6 | { 7 | public class InternalConfigurationFluentVaildator : AbstractValidator, IInternalConfigurationValidator 8 | { 9 | public InternalConfigurationFluentVaildator(EventProcessOptionsFluentVaildator eventProcessOptionsFluentVaildator, EventSourceOptionsFluentVaildator eventSourceOptionsFluentVaildator, EventPublishOptionsFluentVaildator eventPublishOptionsFluentVaildator) 10 | { 11 | //Determine if the event processor name is duplicated 12 | base.RuleFor(f => f.EventProcessOptionsList.GroupBy(t => t.ProcessorName).Where(t => t.Count() > 1).FirstOrDefault()) 13 | .Null() 14 | .WithMessage((optionsList, options) => $"{options.Key} event processor name is repeatedly configured and cannot be repeated"); 15 | 16 | //Determine whether the event source name is duplicated 17 | base.RuleFor(f => f.EventSourceOptions.GroupBy(t => t.EventSourceName).Where(t => t.Count() > 1).FirstOrDefault()) 18 | .Null() 19 | .WithMessage((optionsList, options) => $"Event source name {options.Key} repeat configuration"); 20 | 21 | base.RuleForEach(f => f.EventSourceOptions) 22 | .SetValidator(eventSourceOptionsFluentVaildator); 23 | 24 | base.RuleForEach(f => f.EventProcessOptionsList) 25 | .SetValidator(eventProcessOptionsFluentVaildator); 26 | 27 | base.RuleForEach(f => f.EventProcessOptionsList) 28 | .Must((options, processOptions) => this.ProcessHavaEventSource(options, processOptions)) 29 | .WithMessage((options, processOptions) => $"Configuration in {processOptions.ProcessorFullName} EventProcessorAttribute.EventSourceName Value {processOptions.EventSourceName} Cannot find the event source configuration for "); 30 | 31 | base.RuleForEach(f => f.EventPublishOptionsList) 32 | .SetValidator(eventPublishOptionsFluentVaildator); 33 | } 34 | private bool ProcessHavaEventSource(InternalConfiguration configuration, EventProcessOptions eventProcessOptions) 35 | { 36 | if (eventProcessOptions.ProcessorType == ProcessorType.SimpleProcessor) 37 | return true; 38 | 39 | var es = configuration.GetEventSourceOptions(eventProcessOptions.EventSourceName); 40 | if (es == null) 41 | return false; 42 | else 43 | return true; 44 | } 45 | 46 | public void IsValid(InternalConfiguration configuration) 47 | { 48 | var validateResult = this.Validate(configuration); 49 | if (validateResult.IsValid) 50 | { 51 | return; 52 | } 53 | var error = validateResult.Errors.FirstOrDefault(); 54 | throw new RayConfigurationException("Ray configuration error : " + error.ErrorMessage); 55 | } 56 | 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Ray2/Configuration/Validator/RayConfigurationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Ray2.Configuration.Validator 4 | { 5 | public class RayConfigurationException : Exception 6 | { 7 | public RayConfigurationException(string message) : base(message) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Ray2/Event.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | 7 | namespace Ray2 8 | { 9 | /// 10 | /// Event abstract class 11 | /// 12 | /// StateId Type 13 | public abstract class Event : IEvent 14 | { 15 | public Event() 16 | { 17 | this.TypeCode = this.GetType().FullName; 18 | this.Timestamp = this.GetCurrentTimeUnix(); 19 | } 20 | /// 21 | /// Event abstract class 22 | /// 23 | /// Relation Event 24 | public Event(IEvent @event) : this() 25 | { 26 | this.RelationEvent = @event.GetRelationKey(); 27 | } 28 | /// 29 | /// State Id 30 | /// 31 | public virtual TStateKey StateId { get; set; } 32 | /// 33 | /// the version number of 34 | /// 35 | public virtual long Version { get; set; } 36 | /// 37 | /// Event release timestamp 38 | /// 39 | [JsonProperty] 40 | public virtual long Timestamp { get; private set; } 41 | /// 42 | /// Event type fullname 43 | /// 44 | [JsonProperty] 45 | public virtual string TypeCode { get; private set; } 46 | /// 47 | /// Relation Event 48 | /// 49 | public virtual string RelationEvent { get; set; } 50 | /// 51 | /// Generate Relation key 52 | /// 53 | /// 54 | public string GetRelationKey() 55 | { 56 | return $"{StateId}-{TypeCode}-{Version}"; 57 | } 58 | /// 59 | /// Get StateId 60 | /// 61 | /// 62 | public object GetStateId() 63 | { 64 | return this.StateId; 65 | } 66 | public override string ToString() 67 | { 68 | return $"TypeCode:{TypeCode},StateId:{StateId},VersionNo:{Version},Timestamp:{Timestamp}"; 69 | } 70 | 71 | 72 | public long GetCurrentTimeUnix() 73 | { 74 | TimeSpan cha = (DateTime.Now - TimeZoneInfo.ConvertTimeToUtc(new System.DateTime(1970, 1, 1))); 75 | long t = (long)cha.TotalMilliseconds; 76 | return t; 77 | } 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Ray2/EventProcess/EventProcessExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Ray2.Configuration; 3 | using System; 4 | 5 | namespace Ray2.EventProcess 6 | { 7 | public static class EventProcessExtensions 8 | { 9 | public static IEventProcessCore GetEventProcessCore(this IServiceProvider serviceProvider, string ProcessorName) 10 | { 11 | var internalConfiguration = serviceProvider.GetRequiredService(); 12 | var options = internalConfiguration.GetEventProcessOptions(ProcessorName); 13 | return serviceProvider.GetEventProcessCore(options); 14 | } 15 | 16 | public static IEventProcessCore GetEventProcessCore(this IServiceProvider serviceProvider, IEventProcessor eventProcessor) 17 | { 18 | var internalConfiguration = serviceProvider.GetRequiredService(); 19 | var options = internalConfiguration.GetEventProcessOptions(eventProcessor); 20 | return serviceProvider.GetEventProcessCore(options); 21 | } 22 | private static IEventProcessCore GetEventProcessCore(this IServiceProvider serviceProvider, EventProcessOptions options) 23 | { 24 | var eventProcessCore = serviceProvider.GetRequiredService(); 25 | eventProcessCore.Options = options; 26 | return eventProcessCore; 27 | } 28 | 29 | public static IEventProcessCore GetEventProcessCore(this IServiceProvider serviceProvider, string ProcessorName) 30 | where TState : IState, new() 31 | { 32 | var internalConfiguration = serviceProvider.GetRequiredService(); 33 | var options = internalConfiguration.GetEventProcessOptions(ProcessorName); 34 | return serviceProvider.GetEventProcessCore(options); 35 | } 36 | 37 | public static IEventProcessCore GetEventProcessCore(this IServiceProvider serviceProvider, IEventProcessor eventProcessor) 38 | where TState : IState, new() 39 | { 40 | var internalConfiguration = serviceProvider.GetRequiredService(); 41 | var options = internalConfiguration.GetEventProcessOptions(eventProcessor); 42 | return serviceProvider.GetEventProcessCore(options); 43 | } 44 | 45 | private static IEventProcessCore GetEventProcessCore(this IServiceProvider serviceProvider, EventProcessOptions options) 46 | where TState : IState, new() 47 | { 48 | var eventProcessCore = serviceProvider.GetRequiredService>(); 49 | eventProcessCore.Options = options; 50 | return eventProcessCore; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Ray2/EventProcess/EventProcessState.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Ray2.EventProcess 4 | { 5 | /// 6 | /// Status of event processing, record processing version number time, 7 | /// etc. 8 | /// 9 | /// 10 | [DataContract] 11 | public class EventProcessState : State 12 | { 13 | protected override void PlayEvent(IEvent @event) 14 | { 15 | return; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Ray2/EventProcess/EventProcessorFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using Ray2.Configuration; 4 | using System; 5 | 6 | namespace Ray2.EventProcess 7 | { 8 | public class EventProcessorFactory : IEventProcessorFactory 9 | { 10 | private readonly IServiceProvider _serviceProvider; 11 | private readonly IInternalConfiguration _configuration; 12 | private readonly ILogger _logger; 13 | public EventProcessorFactory(IInternalConfiguration configuration, IServiceProvider serviceProvider, ILogger logger) 14 | { 15 | this._configuration = configuration; 16 | this._serviceProvider = serviceProvider; 17 | this._logger = logger; 18 | } 19 | public IEventProcessor Create(string processName) 20 | { 21 | var options = this._configuration.GetEventProcessOptions(processName); 22 | if (options == null) 23 | { 24 | throw new Exception($"Did not find the configuration of the {processName} processor "); 25 | } 26 | if (options.ProcessorType == ProcessorType.GrainProcessor) 27 | { 28 | return new EventProcessorGrainDispatch(options.ProcessorFullName, _serviceProvider); 29 | } 30 | else 31 | { 32 | return (IEventProcessor)_serviceProvider.GetRequiredService(options.ProcessorHandle); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Ray2/EventProcess/EventProcessorGrainDispatch.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Orleans; 3 | using Ray2.EventSource; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ray2.EventProcess 8 | { 9 | public class EventProcessorGrainDispatch : IEventProcessor 10 | { 11 | private readonly string _grainClassName; 12 | private readonly IServiceProvider _serviceProvider; 13 | private readonly IClusterClient client; 14 | public EventProcessorGrainDispatch(string grainClassName, IServiceProvider serviceProvider) 15 | { 16 | this._grainClassName = grainClassName; 17 | this._serviceProvider = serviceProvider; 18 | this.client = serviceProvider.GetRequiredService(); 19 | } 20 | public Task Tell(EventModel model) 21 | { 22 | object id = model.Event.GetStateId(); 23 | IEventProcessor eventProcessor; 24 | if (id is Guid _guid) 25 | { 26 | eventProcessor = client.GetGrain(primaryKey: _guid, grainClassNamePrefix: _grainClassName); 27 | } 28 | else if (id is long _id) 29 | { 30 | eventProcessor = client.GetGrain(primaryKey: _id, grainClassNamePrefix: _grainClassName); 31 | } 32 | else 33 | { 34 | eventProcessor = client.GetGrain(primaryKey: id.ToString(), grainClassNamePrefix: _grainClassName); 35 | } 36 | return eventProcessor.Tell(model); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Ray2/EventProcess/IEventProcessCore.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration; 2 | using Ray2.EventSource; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.EventProcess 7 | { 8 | using EventProcessor = Func; 9 | public interface IEventProcessCore 10 | { 11 | EventProcessOptions Options { get; set; } 12 | Task Init(EventProcessor eventProcessor); 13 | Task Tell(EventModel model); 14 | } 15 | 16 | 17 | public interface IEventProcessCore : IEventProcessCore 18 | where TState : IState, new() 19 | { 20 | Task> Init(TStateKey stateKey, EventProcessor eventProcessor); 21 | Task SaveStateAsync(); 22 | Task ReadStateAsync(); 23 | Task ClearStateAsync(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Ray2/EventProcess/IEventProcessor.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using Ray2.EventSource; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ray2.EventProcess 6 | { 7 | /// 8 | /// This is the Event process interface 9 | /// 10 | public interface IEventProcessor : IGrainWithGuidKey, IGrainWithIntegerKey, IGrainWithStringKey 11 | { 12 | Task Tell(EventModel model); 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/Ray2/EventProcess/IEventProcessorFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.EventProcess 6 | { 7 | public interface IEventProcessorFactory 8 | { 9 | IEventProcessor Create(string processName); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Ray2/EventSource/EventModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Ray2.EventSource 4 | { 5 | public class EventModel 6 | { 7 | public EventModel(IEvent @event, string typeCode, long version) 8 | { 9 | Event = @event; 10 | TypeCode = typeCode; 11 | Version = version; 12 | StateId = @event.GetStateId(); 13 | } 14 | public EventModel(IEvent @event):this(@event, @event.TypeCode, @event.Version) 15 | { 16 | 17 | } 18 | public IEvent Event { get; } 19 | public string TypeCode { get; } 20 | public Int64 Version { get; } 21 | public object StateId { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Ray2/EventSource/EventSourceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Ray2.Configuration; 3 | using System; 4 | 5 | namespace Ray2.EventSource 6 | { 7 | public static class EventSourceExtensions 8 | { 9 | public static IEventSourcing GetEventSourcing(this IServiceProvider serviceProvider, string eventSourceName) 10 | { 11 | var internalConfiguration = serviceProvider.GetRequiredService(); 12 | var options = internalConfiguration.GetEventSourceOptions(eventSourceName); 13 | return serviceProvider.GetEventSourcing(options); 14 | } 15 | 16 | public static IEventSourcing GetEventSourcing(this IServiceProvider serviceProvider, IRay ray) 17 | { 18 | var internalConfiguration = serviceProvider.GetRequiredService(); 19 | var options = internalConfiguration.GetEventSourceOptions(ray); 20 | return serviceProvider.GetEventSourcing(options); 21 | } 22 | 23 | private static IEventSourcing GetEventSourcing(this IServiceProvider serviceProvider, EventSourceOptions options) 24 | { 25 | var eventSourcing = serviceProvider.GetRequiredService(); 26 | eventSourcing.Options = options; 27 | return eventSourcing; 28 | } 29 | 30 | public static IEventSourcing GetEventSourcing(this IServiceProvider serviceProvider, string eventSourceName) 31 | where TState : IState, new() 32 | { 33 | var internalConfiguration = serviceProvider.GetRequiredService(); 34 | var options = internalConfiguration.GetEventSourceOptions(eventSourceName); 35 | return serviceProvider.GetEventSourcing(options); 36 | } 37 | 38 | public static IEventSourcing GetEventSourcing(this IServiceProvider serviceProvider, IRay ray) 39 | where TState : IState, new() 40 | { 41 | var internalConfiguration = serviceProvider.GetRequiredService(); 42 | var options = internalConfiguration.GetEventSourceOptions(ray); 43 | return serviceProvider.GetEventSourcing(options); 44 | } 45 | 46 | private static IEventSourcing GetEventSourcing(this IServiceProvider serviceProvider, EventSourceOptions options) 47 | where TState : IState, new() 48 | { 49 | var eventSourcing = serviceProvider.GetRequiredService>(); 50 | eventSourcing.Options = options; 51 | return eventSourcing; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Ray2/EventSource/EventTransactionModel.cs: -------------------------------------------------------------------------------- 1 | using Ray2.MQ; 2 | 3 | namespace Ray2.EventSource 4 | { 5 | public class EventTransactionModel 6 | { 7 | public EventTransactionModel(IEvent @event, MQPublishType publishType) 8 | { 9 | this.Event = @event; 10 | this.PublishType = publishType; 11 | } 12 | /// 13 | /// Event 14 | /// 15 | public IEvent Event { get;} 16 | /// 17 | /// MQ publish type 18 | /// 19 | public MQPublishType PublishType { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Ray2/EventSource/IEventSourcing.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration; 2 | using Ray2.Storage; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.EventSource 7 | { 8 | /// 9 | /// This is the event source interface 10 | /// 11 | public interface IEventSourcing 12 | { 13 | EventSourceOptions Options { get; set; } 14 | Task> GetListAsync(EventQueryModel queryModel); 15 | Task ClearSnapshotAsync(string stateId); 16 | } 17 | 18 | /// 19 | /// This is the event source interface 20 | /// 21 | public interface IEventSourcing : IEventSourcing 22 | where TState : IState, new() 23 | { 24 | 25 | Task> Init(TStateKey stateKey); 26 | Task SaveAsync(IEvent @event); 27 | Task SaveAsync(IList> events); 28 | new Task>> GetListAsync(EventQueryModel queryModel); 29 | 30 | Task ReadSnapshotAsync(); 31 | Task SaveSnapshotAsync(TState state); 32 | Task ClearSnapshotAsync(); 33 | 34 | } 35 | 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/Ray2/EventSource/IEventTransaction.cs: -------------------------------------------------------------------------------- 1 | using Ray2.MQ; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ray2.EventSource 6 | { 7 | /// 8 | /// Transaction batch event 9 | /// 10 | /// 11 | /// 12 | public interface IEventTransaction : IEventTransaction 13 | { 14 | /// 15 | /// Transaction copy status 16 | /// 17 | TState State { get; } 18 | /// 19 | /// Write events to a transaction 20 | /// 21 | /// event 22 | void WriteEventAsync(EventTransactionModel model); 23 | /// 24 | /// Write events to a transaction 25 | /// 26 | /// event 27 | /// Whether to publish to mq 28 | void WriteEventAsync(IEvent @event, MQPublishType publishType = MQPublishType.Asynchronous); 29 | /// 30 | /// Write events to a transaction 31 | /// 32 | /// event list 33 | /// Whether to publish to mq 34 | void WriteEventAsync(IList> events, MQPublishType publishType = MQPublishType.Asynchronous); 35 | } 36 | /// 37 | /// Transaction batch event 38 | /// 39 | public interface IEventTransaction 40 | { 41 | TransactionState TransactionState { get; } 42 | /// 43 | /// Transaction volume 44 | /// 45 | /// 46 | int Count(); 47 | /// 48 | /// Commit transaction 49 | /// 50 | /// 51 | Task Commit(); 52 | /// 53 | /// roll back transaction 54 | /// 55 | void Rollback(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Ray2/IEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2 6 | { 7 | /// 8 | /// This is the event data interface 9 | /// 10 | public interface IEvent 11 | { 12 | /// 13 | /// the version number of 14 | /// 15 | Int64 Version { get; set; } 16 | /// 17 | /// Event release timestamp 18 | /// 19 | long Timestamp { get; } 20 | /// 21 | /// Event type fullname 22 | /// 23 | string TypeCode { get; } 24 | /// 25 | /// Related event 26 | /// 27 | string RelationEvent { get; } 28 | /// 29 | /// Generate Relation key 30 | /// 31 | /// 32 | string GetRelationKey(); 33 | /// 34 | /// Get StateId 35 | /// 36 | /// 37 | object GetStateId(); 38 | } 39 | /// 40 | /// This is the event data interface 41 | /// 42 | public interface IEvent: IEvent 43 | { 44 | /// 45 | /// State Id 46 | /// 47 | TPrimaryKey StateId { get; set; } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Ray2/IRay.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2 6 | { 7 | public interface IRay 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Ray2/IState.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ray2 7 | { 8 | /// 9 | /// This is the state interface 10 | /// 11 | public interface IState 12 | { 13 | /// 14 | /// State version number 15 | /// 16 | Int64 Version { get; } 17 | /// 18 | /// Event time corresponding to the status version number 19 | /// 20 | long VersionTime { get; } 21 | /// 22 | /// State type fullname 23 | /// 24 | string TypeCode { get; } 25 | /// 26 | /// Play event modification status 27 | /// 28 | /// 29 | void Player(IEvent @event); 30 | /// 31 | /// Play event modification status 32 | /// 33 | /// 34 | void Player(IList events); 35 | 36 | /// 37 | /// next version no 38 | /// 39 | /// 40 | Int64 NextVersion(); 41 | } 42 | /// 43 | /// This is the state interface 44 | /// 45 | public interface IState : IState 46 | { 47 | /// 48 | /// State Id 49 | /// 50 | TStateKey StateId { get; set; } 51 | /// 52 | /// Play event modification status 53 | /// 54 | /// 55 | void Player(IList> events); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Ray2/Internal/DataflowBufferBlock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace Ray2.Internal 7 | { 8 | public class DataflowBufferBlock : IDataflowBufferBlock 9 | { 10 | private int isProcessing = 0; 11 | private readonly BufferBlock> dataflowChannel = new BufferBlock>(); 12 | private readonly Func>, Task> processor; 13 | 14 | public int Count => dataflowChannel.Count; 15 | 16 | public DataflowBufferBlock(Func>, Task> processor) 17 | { 18 | this.processor = processor; 19 | } 20 | public Task SendAsync(TData data, bool isWallHandle) 21 | { 22 | return Task.Run(async () => 23 | { 24 | var wrap = new DataflowBufferWrap(data); 25 | //First use the synchronous method to quickly write to the BufferBlock, if the failure is using the asynchronous method 26 | if (!dataflowChannel.Post(wrap)) 27 | { 28 | if (!await dataflowChannel.SendAsync(wrap)) 29 | { 30 | return false; 31 | } 32 | } 33 | if (isProcessing == 0) 34 | TriggerProcessor(); 35 | if (!isWallHandle) 36 | return true; 37 | //Determine if you need to wait for processing 38 | return await wrap.Wall(); 39 | }); 40 | } 41 | public Task SendAsync(TData data) 42 | { 43 | return this.SendAsync(data, true); 44 | } 45 | 46 | public async void TriggerProcessor() 47 | { 48 | await Task.Run(async () => 49 | { 50 | if (Interlocked.CompareExchange(ref isProcessing, 1, 0) == 1) 51 | return; 52 | try 53 | { 54 | while (await dataflowChannel.OutputAvailableAsync()) 55 | { 56 | await this.processor(dataflowChannel); 57 | } 58 | } 59 | finally 60 | { 61 | Interlocked.Exchange(ref isProcessing, 0); 62 | } 63 | }); 64 | 65 | } 66 | 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Ray2/Internal/DataflowBufferBlockFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading.Tasks; 4 | using System.Threading.Tasks.Dataflow; 5 | 6 | namespace Ray2.Internal 7 | { 8 | public class DataflowBufferBlockFactory : IDataflowBufferBlockFactory 9 | { 10 | ConcurrentDictionary DataflowBufferBlocks = new ConcurrentDictionary(); 11 | public IDataflowBufferBlock Create(string name, Func>, Task> processor) 12 | where TData : class 13 | { 14 | return (IDataflowBufferBlock)DataflowBufferBlocks.GetOrAdd(name, (key) => 15 | { 16 | return new DataflowBufferBlock(processor); 17 | }); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Ray2/Internal/DataflowBufferWrap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.Internal 7 | { 8 | public class DataflowBufferWrap : IDataflowBufferWrap 9 | { 10 | private TaskCompletionSource TaskSource; 11 | 12 | public DataflowBufferWrap() 13 | { 14 | this.TaskSource = new TaskCompletionSource(); 15 | } 16 | public DataflowBufferWrap(TData data) : this() 17 | { 18 | this.Data = data; 19 | } 20 | public TData Data { get; } 21 | 22 | public void Canceled() 23 | { 24 | this.TaskSource.SetCanceled(); 25 | } 26 | 27 | public void Canceled(CancellationToken cancellationToken) 28 | { 29 | this.TaskSource.TrySetCanceled(cancellationToken); 30 | } 31 | 32 | public void CompleteHandler(bool result) 33 | { 34 | this.TaskSource.SetResult(result); 35 | } 36 | 37 | public Task CompleteHandler(Task result) 38 | { 39 | return result.ContinueWith((res) => 40 | { 41 | try 42 | { 43 | var r = res.GetAwaiter().GetResult(); 44 | this.CompleteHandler(r); 45 | } 46 | catch (Exception ex) 47 | { 48 | this.ExceptionHandler(ex); 49 | } 50 | 51 | }); 52 | } 53 | 54 | public void ExceptionHandler(Exception exception) 55 | { 56 | this.TaskSource.SetException(exception); 57 | } 58 | 59 | public void ExceptionHandler(IEnumerable exceptions) 60 | { 61 | this.TaskSource.SetException(exceptions); 62 | } 63 | 64 | public Task Wall() 65 | { 66 | return this.TaskSource.Task; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Ray2/Internal/IDataflowBufferBlock.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Ray2.Internal 4 | { 5 | public interface IDataflowBufferBlock : IDataflowBufferBlock 6 | { 7 | Task SendAsync(TData data); 8 | Task SendAsync(TData data,bool isWallHandle); 9 | } 10 | 11 | public interface IDataflowBufferBlock 12 | { 13 | int Count { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Ray2/Internal/IDataflowBufferBlockFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Threading.Tasks.Dataflow; 4 | 5 | namespace Ray2.Internal 6 | { 7 | public interface IDataflowBufferBlockFactory 8 | { 9 | IDataflowBufferBlock Create(string name, Func>, Task> processor) 10 | where TData : class; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Ray2/Internal/IDataflowBufferWrap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.Internal 7 | { 8 | public interface IDataflowBufferWrap 9 | { 10 | TData Data { get; } 11 | void Canceled(); 12 | void Canceled(CancellationToken cancellationToken); 13 | void ExceptionHandler(Exception exception); 14 | void ExceptionHandler(IEnumerable exception); 15 | void CompleteHandler(bool result); 16 | Task CompleteHandler(Task result); 17 | Task Wall(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Ray2/MQ/EventPublishModel.cs: -------------------------------------------------------------------------------- 1 | namespace Ray2.MQ 2 | { 3 | public class EventPublishModel 4 | { 5 | public EventPublishModel(IEvent evt, string topic,string mqProviderName, MQPublishType publishType = MQPublishType.Asynchronous) 6 | { 7 | this.Event = evt; 8 | this.Topic = topic; 9 | this.MQProviderName = mqProviderName; 10 | this.Type = publishType; 11 | } 12 | public IEvent Event { get; } 13 | /// 14 | /// MQ publish type 15 | /// 16 | public MQPublishType Type { get; } 17 | /// 18 | /// MQ Topic 19 | /// 20 | public string Topic { get; } 21 | /// 22 | /// MQ provider name 23 | /// 24 | public string MQProviderName { get; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Ray2/MQ/IEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventSource; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ray2.MQ 6 | { 7 | /// 8 | /// this is the event publish interface 9 | /// 10 | public interface IEventPublisher 11 | { 12 | /// 13 | /// Publish a message to the message queue 14 | /// 15 | /// topic 16 | /// Event object 17 | /// 18 | Task Publish(string topic, EventModel model); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Ray2/MQ/IEventSubscriber.cs: -------------------------------------------------------------------------------- 1 | using Ray2.MQ; 2 | using System.Threading.Tasks; 3 | 4 | namespace Ray2.MQ 5 | { 6 | /// 7 | /// this is the subscribe interface 8 | /// 9 | public interface IEventSubscriber 10 | { 11 | Task Subscribe(string group,string topic); 12 | 13 | /// 14 | /// Stop listening 15 | /// 16 | /// 17 | Task Stop(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Ray2/MQ/IMQPublisher.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ray2.MQ 6 | { 7 | /// 8 | /// this is the event publish interface 9 | /// 10 | public interface IMQPublisher 11 | { 12 | /// 13 | /// Publish a message to the message queue 14 | /// 15 | /// Send event to MQ publish model 16 | /// 17 | Task PublishAsync(EventPublishModel model); 18 | /// 19 | /// Publish a message to the message queue 20 | /// 21 | /// Event object 22 | /// This is the message queue topic 23 | /// This is a message queue provider 24 | /// MQ publish type 25 | /// 26 | Task PublishAsync(IEvent evt, string topic, string mqProviderName, MQPublishType publishType = MQPublishType.Asynchronous); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Ray2/MQ/IMQSubscriber.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Ray2.MQ 5 | { 6 | public interface IMQSubscriber 7 | { 8 | /// 9 | /// Start MQ subscription 10 | /// 11 | /// 12 | Task Start(); 13 | 14 | /// 15 | /// Stop listening 16 | /// 17 | /// 18 | Task Stop(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Ray2/MQ/MQPublishType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.MQ 6 | { 7 | /// 8 | /// MQ publishing mode 9 | /// 10 | public enum MQPublishType 11 | { 12 | /// 13 | /// Can know if the publish to MQ is successful 14 | /// 15 | Synchronous, 16 | /// 17 | /// After the publish, I will hand it over to Ray. I don’t know if it is successful to publish to MQ. 18 | /// 19 | Asynchronous, 20 | /// 21 | /// not publish MQ 22 | /// 23 | NotPublish 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Ray2/MQ/MQPublisher.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Orleans.Runtime; 3 | using Ray2.EventSource; 4 | using Ray2.Internal; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using System.Threading.Tasks.Dataflow; 10 | 11 | namespace Ray2.MQ 12 | { 13 | public class MQPublisher : IMQPublisher 14 | { 15 | private readonly ILogger _logger; 16 | private readonly IServiceProvider _serviceProvider; 17 | private readonly IDataflowBufferBlockFactory _dataflowBufferBlockFactory; 18 | 19 | public MQPublisher(IServiceProvider serviceProvider, IDataflowBufferBlockFactory dataflowBufferBlockFactory, ILogger logger) 20 | { 21 | this._serviceProvider = serviceProvider; 22 | this._dataflowBufferBlockFactory = dataflowBufferBlockFactory; 23 | this._logger = logger; 24 | } 25 | 26 | public Task PublishAsync(IEvent evt, string topic, string mqProviderName, MQPublishType publishType = MQPublishType.Asynchronous) 27 | { 28 | EventPublishModel wrap = new EventPublishModel(evt, topic, mqProviderName, publishType); 29 | return this.PublishAsync(wrap); 30 | } 31 | 32 | public Task PublishAsync(EventPublishModel model) 33 | { 34 | if (model.Type == MQPublishType.NotPublish) 35 | { 36 | return Task.FromResult(false); 37 | } 38 | else 39 | { 40 | var bufferBlock = _dataflowBufferBlockFactory.Create(model.MQProviderName, this.LazyPublishAsync); 41 | if (model.Type == MQPublishType.Synchronous) 42 | { 43 | return bufferBlock.SendAsync(model); 44 | } 45 | else 46 | { 47 | return bufferBlock.SendAsync(model, false); 48 | } 49 | } 50 | } 51 | 52 | public Task LazyPublishAsync(BufferBlock> eventBuffer) 53 | { 54 | List> eventWraps = new List>(); 55 | while (eventBuffer.TryReceive(out var wrap)) 56 | { 57 | eventWraps.Add(wrap); 58 | if (eventWraps.Count >= 1000) 59 | break; 60 | } 61 | if (eventWraps.Count == 0) 62 | return Task.CompletedTask; 63 | var provider = this._serviceProvider.GetRequiredServiceByName(eventWraps[0].Data.MQProviderName); 64 | var tasks = eventWraps.Select(warp => 65 | { 66 | var result = this.PublishAsync(provider, warp.Data.Topic, new EventModel(warp.Data.Event)); 67 | return warp.CompleteHandler(result); 68 | }).ToList(); 69 | Task.WaitAll(tasks.ToArray()); 70 | return Task.CompletedTask; 71 | } 72 | 73 | private async Task PublishAsync(IEventPublisher publisher, string topic, EventModel model) 74 | { 75 | try 76 | { 77 | return await publisher.Publish(topic, model); 78 | } 79 | catch (Exception ex) 80 | { 81 | this._logger.LogError(ex, $"Publishing {topic} event failed"); 82 | return false; 83 | } 84 | 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Ray2/MQ/MQPublisherExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Configuration; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ray2.MQ 9 | { 10 | public static class MQPublisherExtensions 11 | { 12 | private static ConcurrentDictionary eventPublishOptions = new ConcurrentDictionary(); 13 | 14 | public static Task PublishAsync(this IMQPublisher publisher, IEvent @event, MQPublishType publishType = MQPublishType.Asynchronous) 15 | { 16 | var options = GetAttributeOptions(@event.GetType()); 17 | if (options == null) 18 | { 19 | throw new Exception($"No eventPublishAttribute is marked for {@event.GetType().FullName} events"); 20 | } 21 | return publisher.PublishAsync(@event, options.Topic, options.MQProvider, publishType); 22 | } 23 | 24 | public static async Task PublishAsync(this IMQPublisher publisher, IList events, MQPublishType publishType = MQPublishType.Asynchronous) 25 | { 26 | if (events == null) 27 | { 28 | return; 29 | } 30 | foreach (var e in events) 31 | { 32 | await publisher.PublishAsync(e, publishType); 33 | } 34 | } 35 | 36 | public static async Task PublishAsync(this IMQPublisher publisher, IList events, string topic, string mqProviderName, MQPublishType publishType = MQPublishType.Asynchronous) 37 | { 38 | if (events == null || events.Count==0) 39 | { 40 | return; 41 | } 42 | foreach (var e in events) 43 | { 44 | await publisher.PublishAsync(e, topic, mqProviderName, publishType); 45 | } 46 | } 47 | 48 | public static EventPublishOptions GetAttributeOptions(Type type) 49 | { 50 | return eventPublishOptions.GetOrAdd(type, (key) => 51 | { 52 | var attribute = type.GetCustomAttribute(); 53 | if (attribute != null) 54 | { 55 | string fullName = type.FullName; 56 | if (string.IsNullOrEmpty(attribute.Topic)) 57 | { 58 | throw new ArgumentNullException($"EventPublishAttribute.Topic in {fullName} cannot be empty"); 59 | } 60 | if (string.IsNullOrEmpty(attribute.MQProvider)) 61 | { 62 | throw new ArgumentNullException($"EventPublishAttribute.MQProvider in {fullName} cannot be empty"); 63 | } 64 | return new EventPublishOptions(attribute.Topic, attribute.MQProvider, fullName); 65 | } 66 | else 67 | { 68 | return null; 69 | } 70 | }); 71 | } 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Ray2/MQ/MQSubscriber.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Orleans.Runtime; 3 | using Ray2.Configuration; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ray2.MQ 8 | { 9 | public class MQSubscriber : IMQSubscriber 10 | { 11 | private readonly IServiceProvider _serviceProvider; 12 | private readonly IInternalConfiguration _configuration; 13 | private readonly ILogger _logger; 14 | public MQSubscriber(IServiceProvider serviceProvider, IInternalConfiguration configuration, ILogger logger) 15 | { 16 | this._serviceProvider = serviceProvider; 17 | this._configuration = configuration; 18 | this._logger = logger; 19 | } 20 | 21 | /// 22 | /// Start subscribing to a single processor event 23 | /// 24 | /// 25 | public Task Start() 26 | { 27 | return this.Do((sub, processorName) => 28 | { 29 | var eventSubscriber = this._serviceProvider.GetRequiredServiceByName(sub.MQProvider); 30 | return eventSubscriber.Subscribe(processorName, sub.Topic); 31 | }); 32 | } 33 | 34 | public Task Stop() 35 | { 36 | return this.Do((sub, processorName) => 37 | { 38 | var eventSubscriber = this._serviceProvider.GetRequiredServiceByName(sub.MQProvider); 39 | return eventSubscriber.Stop(); 40 | }); 41 | } 42 | 43 | public async Task Do(Func func) 44 | { 45 | var options = this._configuration.EventProcessOptionsList; 46 | if (options == null || options.Count == 0) 47 | return; 48 | foreach (var o in options) 49 | { 50 | foreach (var sub in o.SubscribeOptions) 51 | { 52 | await func(sub, o.ProcessorName); 53 | } 54 | } 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Ray2/Ray2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Ray2/RayProcessor.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventProcess; 2 | using Ray2.EventSource; 3 | using System; 4 | using System.Runtime.CompilerServices; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ray2 8 | { 9 | public abstract class RayProcessor : IEventProcessor 10 | { 11 | private readonly IEventProcessCore _eventProcessCore; 12 | protected readonly IServiceProvider ServiceProvider; 13 | public RayProcessor(IServiceProvider serviceProvider) 14 | { 15 | this.ServiceProvider = serviceProvider; 16 | this._eventProcessCore = this.ServiceProvider.GetEventProcessCore(this).Init(this.OnEventProcessing).GetAwaiter().GetResult(); 17 | } 18 | public Task Tell(EventModel model) 19 | { 20 | return this._eventProcessCore.Tell(model); 21 | } 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public abstract Task OnEventProcessing(IEvent @event); 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Ray2/RayProcessorGrain.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using Ray2.EventProcess; 3 | using Ray2.EventSource; 4 | using System.Runtime.CompilerServices; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ray2 8 | { 9 | /// 10 | /// This is the event process grain 11 | /// 12 | public abstract class RayProcessorGrain : Grain, IEventProcessor 13 | where TState : IState, new() 14 | { 15 | private IEventProcessCore _eventProcessCore; 16 | protected TState State { get { return _eventProcessCore.ReadStateAsync().GetAwaiter().GetResult(); } } 17 | protected abstract TStateKey StateId { get; } 18 | public override async Task OnActivateAsync() 19 | { 20 | this._eventProcessCore = await this.ServiceProvider.GetEventProcessCore(this) 21 | .Init(this.StateId, this.OnEventProcessing); 22 | await base.OnActivateAsync(); 23 | } 24 | public override async Task OnDeactivateAsync() 25 | { 26 | await this._eventProcessCore.SaveStateAsync(); 27 | await base.OnDeactivateAsync(); 28 | } 29 | public Task Tell(EventModel model) 30 | { 31 | return this._eventProcessCore.Tell(model); 32 | } 33 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 34 | public abstract Task OnEventProcessing(IEvent @event); 35 | } 36 | public abstract class RayProcessorGrain : RayProcessorGrain, TStateKey> 37 | { 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/Ray2/Serialization/ISerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Serialization 6 | { 7 | public interface ISerializer { 8 | T Deserialize(byte[] bytes); 9 | object Deserialize(Type type, byte[] bytes); 10 | byte[] Serialize(object obj); 11 | byte[] Serialize(T obj); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Ray2/Serialization/JsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Text; 4 | 5 | namespace Ray2.Serialization 6 | { 7 | public class JsonSerializer : ISerializer 8 | { 9 | Encoding encoding = Encoding.UTF8; 10 | public T Deserialize(byte[] bytes) 11 | { 12 | var json = encoding.GetString(bytes); 13 | if (string.IsNullOrEmpty(json)) 14 | return default(T); 15 | return JsonConvert.DeserializeObject(json); 16 | } 17 | 18 | public object Deserialize(Type type, byte[] bytes) 19 | { 20 | var json = encoding.GetString(bytes); 21 | 22 | if (string.IsNullOrEmpty(json)) 23 | return null; 24 | return JsonConvert.DeserializeObject(json, type); 25 | } 26 | public byte[] Serialize(object obj) 27 | { 28 | string json = JsonConvert.SerializeObject(obj); 29 | return encoding.GetBytes(json); 30 | } 31 | 32 | public byte[] Serialize(T obj) 33 | { 34 | string json = JsonConvert.SerializeObject(obj); 35 | return encoding.GetBytes(json); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Ray2/Serialization/SerializationType.cs: -------------------------------------------------------------------------------- 1 | namespace Ray2 2 | { 3 | public class SerializationType 4 | { 5 | /// 6 | /// Json and Utf-8 7 | /// 8 | public const string JsonUTF8 = "JsonUTF8"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Ray2/State.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using Newtonsoft.Json; 5 | 6 | namespace Ray2 7 | { 8 | /// 9 | /// State abstract class 10 | /// 11 | /// State id type 12 | public abstract class State : IState 13 | { 14 | public State() 15 | { 16 | this.TypeCode = this.GetType().FullName; 17 | } 18 | /// 19 | /// State Id 20 | /// 21 | public virtual TStateKey StateId { get; set; } 22 | /// 23 | /// State version number 24 | /// 25 | [JsonProperty] 26 | public virtual long Version { get; private set; } = 0; 27 | /// 28 | /// Event time corresponding to the status version number 29 | /// 30 | [JsonProperty] 31 | public virtual long VersionTime { get; private set; } 32 | /// 33 | /// State type fullname 34 | /// 35 | [JsonProperty] 36 | public virtual string TypeCode { get; } 37 | /// 38 | /// next version no 39 | /// 40 | /// 41 | public long NextVersion() 42 | { 43 | return this.Version+1; 44 | } 45 | /// 46 | /// Play event modification status 47 | /// 48 | /// 49 | public void Player(IEvent @event) 50 | { 51 | if (@event == null) 52 | return; 53 | this.PlayEvent(@event); 54 | this.Version = @event.Version; 55 | this.VersionTime = @event.Timestamp; 56 | } 57 | public void Player(IList events) 58 | { 59 | if (events == null || events.Count == 0) 60 | return ; 61 | foreach (var @event in events) 62 | { 63 | this.Player( @event); 64 | } 65 | } 66 | public void Player(IList> events) 67 | { 68 | if (events == null || events.Count == 0) 69 | return; 70 | foreach (var @event in events) 71 | { 72 | this.Player(@event); 73 | } 74 | } 75 | 76 | /// 77 | /// Play event 78 | /// 79 | /// Event 80 | protected abstract void PlayEvent(IEvent @event); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Ray2/Storage/IEventStorage.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventSource; 2 | using Ray2.Internal; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.Storage 7 | { 8 | public interface IEventStorage: IStorage 9 | { 10 | Task> GetListAsync(string tableName, EventQueryModel queryModel); 11 | Task GetAsync(string tableName, object stateId, long version); 12 | Task SaveAsync(List> wrapList); 13 | Task SaveAsync(EventCollectionStorageModel eventList); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Ray2/Storage/IStateStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.Storage 7 | { 8 | public interface IStateStorage: IStorage 9 | { 10 | Task ReadAsync(string tableName, object stateId) where TState : IState, new(); 11 | Task InsertAsync(string tableName, object stateId, TState state) where TState : IState, new(); 12 | Task UpdateAsync(string tableName, object stateId, TState state) where TState : IState, new(); 13 | Task DeleteAsync(string tableName, object stateId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Ray2/Storage/IStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Storage 6 | { 7 | /// 8 | /// Basic interface for data storage 9 | /// 10 | public interface IStorage 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Ray2/Storage/IStorageFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Ray2.Storage 5 | { 6 | /// 7 | /// Repository factory 8 | /// 9 | public interface IStorageFactory 10 | { 11 | /// 12 | /// Obtain event storage based on repository name and status key 13 | /// 14 | /// storage name 15 | /// State Key 16 | /// 17 | Task GetEventStorage(string name, string stateKey); 18 | /// 19 | /// Get all event storage in the repository based on the repository name and storage creation time 20 | /// 21 | /// storage name 22 | /// storage creation time 23 | /// 24 | Task> GetEventStorageList(string name, long? createTime); 25 | /// 26 | /// Get state storage based on repository name and state key 27 | /// 28 | /// storage name 29 | /// state storage Type 30 | /// State Key 31 | /// 32 | Task GetStateStorage(string name, StorageType storageType, string stateKey); 33 | /// 34 | /// Get the storage table name based on the repository name and state key 35 | /// 36 | /// storage name 37 | /// state storage Type 38 | /// State Key 39 | /// 40 | Task GetTable(string name, StorageType storageType, string stateKey); 41 | /// 42 | /// Get all storage table names based on repository name and state key 43 | /// 44 | /// storage name 45 | /// state storage Type 46 | /// State Key 47 | /// storage creation time 48 | /// 49 | Task> GetTableList(string name, StorageType storageType, string stateKey, long? createTime); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Ray2/Storage/IStorageSharding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Ray2.Storage 7 | { 8 | public interface IStorageSharding 9 | { 10 | Task GetProvider(string name, StorageType type, string stateKey); 11 | Task GetTable(string name, StorageType type, string stateKey); 12 | Task> GetTableList(string name, StorageType type, string stateKey, long? createTime); 13 | Task> GetProviderList(string name, StorageType type, long? createTime); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Ray2/Storage/Model/EventCollectionStorageModel.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventSource; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Ray2.Storage 6 | { 7 | public class EventCollectionStorageModel 8 | { 9 | public EventCollectionStorageModel(string eventSourceName, string storageTableName) 10 | { 11 | this.EventSourceName = eventSourceName; 12 | this.StorageTableName = storageTableName; 13 | this.Events = new List(); 14 | } 15 | public string EventSourceName { get; } 16 | public string StorageTableName { get; } 17 | public List Events { get; } 18 | public object GetStateId() 19 | { 20 | return Events.First().Event.GetStateId(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Ray2/Storage/Model/EventQueryModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Storage 6 | { 7 | public class EventQueryModel 8 | { 9 | public EventQueryModel() 10 | { 11 | 12 | } 13 | public EventQueryModel(Int64 startVersion, Int64? startTime = null) 14 | { 15 | this.StartVersion = startVersion; 16 | this.StartTime = startTime; 17 | } 18 | public EventQueryModel(Int64 startVersion, Int64 endVersion, Int64? startTime = null) : this(startVersion, startTime) 19 | { 20 | this.EndVersion = endVersion; 21 | } 22 | public object StateId { get; set; } 23 | public Int64 StartVersion { get; set; } 24 | public Int64 EndVersion { get; set; } 25 | public string EventTypeCode { get; set; } 26 | public string RelationEvent { get; set; } 27 | public Int64? StartTime { get; set; } 28 | public Int32 Limit { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Ray2/Storage/Model/EventStorageInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Ray2.Storage 4 | { 5 | public class EventStorageInfo 6 | { 7 | public string EventSource { get; set; } 8 | public string Provider { get; set; } 9 | public IEventStorage Storage { get; set; } 10 | public IList Tables { get; set; } = new List(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Ray2/Storage/Model/EventStorageModel.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventSource; 2 | 3 | namespace Ray2.Storage 4 | { 5 | public class EventStorageModel : EventModel 6 | { 7 | public EventStorageModel(object stateId, IEvent @event, string eventSourceName, string storageTableName) :base(@event) 8 | { 9 | this.EventSourceName = eventSourceName; 10 | this.StorageTableName = storageTableName; 11 | } 12 | public string EventSourceName { get; } 13 | public string StorageTableName { get; } 14 | public bool Result { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Ray2/Storage/StorageTableNameBuild.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Storage 6 | { 7 | public static class StorageTableNameBuild 8 | { 9 | /// 10 | /// Building snapshot event storage table name 11 | /// 12 | /// storage table name 13 | /// storage type 14 | /// 15 | public static string BuildTableName(string name, StorageType type) 16 | { 17 | switch (type) 18 | { 19 | case StorageType.EventSource: 20 | return $"ES_{name}"; 21 | case StorageType.EventSourceSnapshot: 22 | return $"ESS_{name}"; 23 | case StorageType.EventProcessState: 24 | return $"EP_{name}"; 25 | default: 26 | return ""; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Ray2/Storage/StorageType.cs: -------------------------------------------------------------------------------- 1 | namespace Ray2.Storage 2 | { 3 | public enum StorageType 4 | { 5 | EventSource, 6 | EventSourceSnapshot, 7 | EventProcessState 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/Ray2.Integrated.Test/Ray2.Integrated.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/Ray2.PostgreSQL.Test/EventStorageTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.PostgreSQL.Test 6 | { 7 | public class EventStorageTests 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/Ray2.PostgreSQL.Test/FakeConfig.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Orleans.Runtime; 3 | using Ray2.Configuration; 4 | using Ray2.Serialization; 5 | using Ray2.Storage; 6 | using System; 7 | using Moq; 8 | 9 | namespace Ray2.PostgreSQL.Test 10 | { 11 | public static class FakeConfig 12 | { 13 | public const string ConnectionString = "Server=localhost;Port=5432;Database=ray2;User Id=postgres; Password=sapass;Pooling=true;MaxPoolSize=50;Timeout=10;"; 14 | 15 | 16 | public const string ProviderName = "Default"; 17 | 18 | public static PostgreSqlOptions Options = new PostgreSqlOptions() 19 | { 20 | ConnectionString = FakeConfig.ConnectionString 21 | }; 22 | 23 | public static IServiceProvider BuildServiceProvider() 24 | { 25 | IServiceCollection services = new ServiceCollection(); 26 | services.AddOptions().Configure(ProviderName, opt => 27 | { 28 | opt.ConnectionString = Options.ConnectionString; 29 | }); 30 | services.AddLogging(); 31 | Mock internalConfiguration = new Mock(); 32 | 33 | var type = typeof(TestEvent); 34 | string name = type.FullName; 35 | internalConfiguration.Setup(f => f.GetEvenType(name, out type)).Returns(true); 36 | services.AddSingleton(internalConfiguration.Object); 37 | services.AddSingleton(typeof(IKeyedServiceCollection<,>), typeof(KeyedServiceCollection<,>)); 38 | services.AddSingletonNamedService(SerializationType.JsonUTF8); 39 | services.AddSingletonNamedService(ProviderName, (Func)((sp, n) => 40 | { 41 | return new PostgreSQL.StateStorage(sp, n); 42 | })); 43 | services.AddSingletonNamedService(ProviderName, (sp, n) => 44 | { 45 | return new EventStorage(sp, n); 46 | }); 47 | services.AddSingletonNamedService(ProviderName, (sp, n) => 48 | { 49 | return new PostgreSqlTableStorage(sp, n); 50 | }); 51 | return services.BuildServiceProvider(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/Ray2.PostgreSQL.Test/Model/TestEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xunit; 5 | 6 | namespace Ray2.PostgreSQL.Test 7 | { 8 | public class TestEvent : Event 9 | { 10 | public static TestEvent Create(long v) 11 | { 12 | return new TestEvent() 13 | { 14 | StateId = 100, 15 | Version = v 16 | }; 17 | } 18 | 19 | public void Valid(TestEvent state) 20 | { 21 | Assert.Equal(state.StateId, this.StateId); 22 | Assert.Equal(state.Version, this.Version); 23 | Assert.Equal(state.TypeCode, this.TypeCode); 24 | Assert.Equal(state.Version, this.Version); 25 | Assert.Equal(state.Timestamp, this.Timestamp); 26 | Assert.Equal(state.RelationEvent, this.RelationEvent); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Ray2.PostgreSQL.Test/Model/TestState.cs: -------------------------------------------------------------------------------- 1 | using Ray2.EventProcess; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using Xunit; 7 | 8 | namespace Ray2.PostgreSQL.Test 9 | { 10 | [DataContract] 11 | public class TestState : EventProcessState 12 | { 13 | [DataMember(Order = 1)] 14 | public string Test { get; set; } 15 | 16 | public void Valid(TestState state) 17 | { 18 | Assert.Equal(state.StateId, this.StateId); 19 | Assert.Equal(state.Test, this.Test); 20 | Assert.Equal(state.TypeCode, this.TypeCode); 21 | Assert.Equal(state.Version, this.Version); 22 | Assert.Equal(state.VersionTime, this.VersionTime); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/Ray2.PostgreSQL.Test/PostgreSqlTableStorageTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using TestStack.BDDfy; 3 | using Xunit; 4 | 5 | namespace Ray2.PostgreSQL.Test 6 | { 7 | public class PostgreSqlTableStorageTests 8 | { 9 | private PostgreSqlTableStorage storage; 10 | public PostgreSqlTableStorageTests() 11 | { 12 | this.Given_Build_PostgreSqlTableStorage(); 13 | } 14 | 15 | [Fact] 16 | public void should_CreateEventTable_Success() 17 | { 18 | this.When(f => f.When_CreateEventTable("es_createTable", 123)) 19 | .Then(f => f.ThenSuccess()) 20 | .BDDfy(); 21 | } 22 | 23 | [Fact] 24 | public void should_CreateEventTable_StateIdIsNull() 25 | { 26 | this.When(f => f.When_CreateEventTable("es_createTable1", null)) 27 | .Then(f => f.ThenSuccess()) 28 | .BDDfy(); 29 | } 30 | [Fact] 31 | public void should_CreateEventTable_Existed() 32 | { 33 | string tableName = "es_createTable_existed"; 34 | this.When(f => f.When_CreateEventTable(tableName, Guid.NewGuid())) 35 | .When(f => f.When_CreateEventTable(tableName, Guid.NewGuid())) 36 | .Then(f => f.ThenSuccess()) 37 | .BDDfy(); 38 | } 39 | [Fact] 40 | public void should_CreateEventTable_Repeat() 41 | { 42 | string tableName = "es_createTable_repeat"; 43 | this 44 | .When(f => f.When_CreateEventTable(tableName,Guid.NewGuid())) 45 | .Given(f => f.Given_Build_PostgreSqlTableStorage()) 46 | .When(f => f.When_CreateEventTable(tableName, Guid.NewGuid())) 47 | .Then(f => f.ThenSuccess()) 48 | .BDDfy(); 49 | 50 | } 51 | [Fact] 52 | public void should_CreateStateTable_Success() 53 | { 54 | this.When(f => f.When_CreateStateTable("st_createTable", "abc")) 55 | .Then(f => f.ThenSuccess()) 56 | .BDDfy(); 57 | } 58 | [Fact] 59 | public void should_CreateStateTable_Existed() 60 | { 61 | string tableName = "st_createTable_existed"; 62 | this.When(f => f.When_CreateStateTable(tableName,DateTime.Now.Ticks)) 63 | .When(f => f.When_CreateStateTable(tableName, DateTime.Now.Ticks)) 64 | .Then(f => f.ThenSuccess()) 65 | .BDDfy(); 66 | } 67 | [Fact] 68 | public void should_CreateStateTable_Repeat() 69 | { 70 | string tableName = "st_createTable_repeat"; 71 | this.When(f => f.When_CreateStateTable(tableName,11)) 72 | .Given(f => f.Given_Build_PostgreSqlTableStorage()) 73 | .When(f => f.When_CreateStateTable(tableName,11)) 74 | .Then(f => f.ThenSuccess()) 75 | .BDDfy(); 76 | } 77 | 78 | private void Given_Build_PostgreSqlTableStorage() 79 | { 80 | IServiceProvider serviceProvider = FakeConfig.BuildServiceProvider(); 81 | storage = new PostgreSqlTableStorage(serviceProvider, FakeConfig.ProviderName); 82 | } 83 | private void When_CreateEventTable(string name,object stateId) 84 | { 85 | this.storage.CreateEventTable(name, stateId); 86 | } 87 | private void When_CreateStateTable(string name, object stateId) 88 | { 89 | this.storage.CreateStateTable(name, stateId); 90 | } 91 | private void ThenSuccess() 92 | { 93 | Assert.True(true); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /test/Ray2.PostgreSQL.Test/Ray2.PostgreSQL.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/Ray2.PostgreSQL.Test/StateStorageTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using TestStack.BDDfy; 5 | using Xunit; 6 | 7 | namespace Ray2.PostgreSQL.Test 8 | { 9 | public class StateStorageTests 10 | { 11 | private PostgreSQL.StateStorage container; 12 | private string TableName; 13 | private TestState selectState; 14 | private TestState deleteState; 15 | 16 | public StateStorageTests() 17 | { 18 | this.TableName = "st_ContainerTest"; 19 | var sp = FakeConfig.BuildServiceProvider(); 20 | container = new PostgreSQL.StateStorage(sp, FakeConfig.ProviderName); 21 | } 22 | [Fact] 23 | public void should_Success() 24 | { 25 | TestState state = new TestState() 26 | { 27 | StateId = 1, 28 | Test = DateTime.Now.Ticks.ToString() 29 | }; 30 | this.When(f => f.WhenInsert(state.StateId, state)) 31 | .When(f => f.WhenUpdate(state.StateId, state)) 32 | .When(f => f.WhenSelect(state.StateId, false)) 33 | .When(f => f.WhenDelete(state.StateId)) 34 | .When(f => f.WhenSelect(state.StateId, true)) 35 | .Then(f => f.ThenSuccess(state)) 36 | .BDDfy(); 37 | } 38 | 39 | private void WhenDelete(object stateId) 40 | { 41 | container.DeleteAsync(TableName, stateId).GetAwaiter().GetResult(); 42 | } 43 | private void WhenInsert(object stateId, TestState state) 44 | { 45 | container.InsertAsync(TableName, stateId, state).GetAwaiter().GetResult(); 46 | } 47 | private void WhenUpdate(object stateId, TestState state) 48 | { 49 | state.Test = "abc"; 50 | container.UpdateAsync(TableName, stateId, state).GetAwaiter().GetResult(); 51 | } 52 | private void WhenSelect(object stateId, bool isdelete) 53 | { 54 | if (isdelete) 55 | deleteState = container.ReadAsync(TableName, stateId).GetAwaiter().GetResult(); 56 | else 57 | selectState = container.ReadAsync(TableName, stateId).GetAwaiter().GetResult(); 58 | } 59 | 60 | private void ThenSuccess(TestState state) 61 | { 62 | Assert.Null(deleteState); 63 | Assert.NotNull(selectState); 64 | selectState.Valid(state); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/Ray2.RabbitMQ.Test/EventPublisherTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xunit; 5 | using TestStack.BDDfy; 6 | using Ray2.EventSource; 7 | using Ray2.RabbitMQ.Test.Model; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ray2.RabbitMQ.Test 11 | { 12 | public class EventPublisherTests 13 | { 14 | private readonly EventPublisher publisher; 15 | private string topic = "EventPublisherTests"; 16 | private bool IsPublish; 17 | private int producerCount; 18 | public EventPublisherTests() 19 | { 20 | IServiceProvider serviceProvider = FakeConfig.BuildServiceProvider(); 21 | this.publisher = new EventPublisher(serviceProvider, FakeConfig.ProviderName); 22 | } 23 | 24 | [Fact] 25 | public void should_PublishSingle_Normal() 26 | { 27 | this.When(f => f.WhenPublishSingle()) 28 | .Then(f => f.ThenPublish_Success()) 29 | .BDDfy(); 30 | } 31 | 32 | 33 | [Fact] 34 | public void should_GetProducer_300() 35 | { 36 | this.When(f => f.WhenGetProducerList(300)) 37 | .Then(f => f.ThenGetProducer_Success(300)) 38 | .BDDfy(); 39 | } 40 | [Fact] 41 | public void should_GetProducer_Concurrent1000() 42 | { 43 | this.When(f => f.WhenGetProducerList_Concurrent(1000)) 44 | .Then(f => f.ThenNormal()) 45 | .BDDfy(); 46 | } 47 | 48 | private void WhenPublishSingle() 49 | { 50 | EventModel model = new EventModel(TestEvent.Create(100)); 51 | this.IsPublish = this.publisher.Publish(this.topic, model).GetAwaiter().GetResult(); 52 | } 53 | 54 | 55 | 56 | private void WhenGetProducerList(int count) 57 | { 58 | for (int i = 0; i < count; i++) 59 | { 60 | var producer = this.publisher.GetProducer(); 61 | Assert.True(producer.IsAvailable()); 62 | this.publisher.EnqueuePool(producer); 63 | this.producerCount++; 64 | if (i == 1) 65 | { 66 | producer.Close(); 67 | } 68 | 69 | } 70 | } 71 | private void WhenGetProducerList_Concurrent(int count) 72 | { 73 | var tasks = new Task[count]; 74 | Parallel.For(0, count, (i) => 75 | { 76 | var producer = this.publisher.GetProducer(); 77 | Assert.True(producer.IsAvailable()); 78 | Task.Delay(1).GetAwaiter().GetResult(); 79 | this.publisher.EnqueuePool(producer); 80 | tasks[i] = Task.CompletedTask; 81 | }); 82 | Task.WaitAll(tasks); 83 | } 84 | 85 | private void ThenPublish_Success() 86 | { 87 | Assert.True(this.IsPublish); 88 | } 89 | 90 | private void ThenGetProducer_Success(int count) 91 | { 92 | Assert.Equal(this.producerCount, count); 93 | } 94 | 95 | private void ThenNormal() 96 | { 97 | Assert.True(true); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /test/Ray2.RabbitMQ.Test/FakeConfig.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Moq; 3 | using Orleans.Runtime; 4 | using Ray2.Configuration; 5 | using Ray2.EventProcess; 6 | using Ray2.MQ; 7 | using Ray2.RabbitMQ.Configuration; 8 | using Ray2.RabbitMQ.Test.Model; 9 | using Ray2.Serialization; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Text; 13 | 14 | namespace Ray2.RabbitMQ.Test 15 | { 16 | public static class FakeConfig 17 | { 18 | public static Mock EventProcessorFactory = new Mock(); 19 | public static Mock InternalConfiguration = new Mock(); 20 | 21 | public const string ProviderName = "Default"; 22 | public static RabbitOptions Options = new RabbitOptions() 23 | { 24 | HostName = "192.168.1.250", 25 | UserName= "admin", 26 | Password = "admin", 27 | ConsumeOptions = new List 28 | { 29 | new RabbitConsumeOptions() 30 | { 31 | Group="subGroup", 32 | Topic="subTopic", 33 | NoticeRetriesCount=3 34 | } 35 | } 36 | }; 37 | public static IServiceProvider BuildServiceProvider() 38 | { 39 | IServiceCollection services = new ServiceCollection(); 40 | services.AddOptions().Configure(ProviderName, opt => 41 | { 42 | opt.HostName = Options.HostName; 43 | opt.UserName = Options.UserName; 44 | opt.Password = Options.Password; 45 | opt.ConnectionPoolCount = Options.ConnectionPoolCount; 46 | opt.ConsumeOptions = Options.ConsumeOptions; 47 | opt.HostNames = Options.HostNames; 48 | opt.SerializationType = Options.SerializationType; 49 | opt.VirtualHost = Options.VirtualHost; 50 | opt.ConsumeOptions = Options.ConsumeOptions; 51 | }); 52 | services.AddLogging(); 53 | var type = typeof(TestEvent); 54 | InternalConfiguration.Setup(f => f.GetEvenType(type.FullName, out type)).Returns(true); 55 | 56 | services.AddSingleton(InternalConfiguration.Object); 57 | services.AddSingleton(typeof(IKeyedServiceCollection<,>), typeof(KeyedServiceCollection<,>)); 58 | services.AddSingletonNamedService(SerializationType.JsonUTF8); 59 | services.AddSingleton(EventProcessorFactory.Object); 60 | services.AddSingletonNamedService(ProviderName, (sp, n) => 61 | { 62 | return new EventPublisher(sp, n); 63 | }); 64 | services.AddSingletonNamedService(ProviderName, (sp, n) => 65 | { 66 | return new EventSubscriber(sp, n); 67 | }); 68 | services.AddSingletonNamedService(ProviderName, (sp, n) => 69 | { 70 | return new RabbitChannelFactory(sp, n); 71 | }); 72 | return services.BuildServiceProvider(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/Ray2.RabbitMQ.Test/Model/TestEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xunit; 5 | 6 | namespace Ray2.RabbitMQ.Test.Model 7 | { 8 | public class TestEvent : Event 9 | { 10 | public static TestEvent Create(long v) 11 | { 12 | return new TestEvent() 13 | { 14 | StateId = 100, 15 | Version = v 16 | }; 17 | } 18 | 19 | public void Valid(TestEvent state) 20 | { 21 | Assert.Equal(state.StateId, this.StateId); 22 | Assert.Equal(state.Version, this.Version); 23 | Assert.Equal(state.TypeCode, this.TypeCode); 24 | Assert.Equal(state.Version, this.Version); 25 | Assert.Equal(state.Timestamp, this.Timestamp); 26 | Assert.Equal(state.RelationEvent, this.RelationEvent); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Ray2.RabbitMQ.Test/RabbitChannelFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using TestStack.BDDfy; 5 | using Xunit; 6 | 7 | namespace Ray2.RabbitMQ.Test 8 | { 9 | public class RabbitChannelFactoryTests 10 | { 11 | private readonly RabbitChannelFactory channelFactory; 12 | public RabbitChannelFactoryTests() 13 | { 14 | IServiceProvider serviceProvider = FakeConfig.BuildServiceProvider(); 15 | this.channelFactory = new RabbitChannelFactory(serviceProvider, FakeConfig.ProviderName); 16 | } 17 | 18 | [Fact] 19 | public void should_GetChannel_Success() 20 | { 21 | this.When(f => f.WhenGetChannel(1)) 22 | .Then(f => f.ThenSuccess()) 23 | .BDDfy(); 24 | } 25 | 26 | [Fact] 27 | public void should_GetChannel_100() 28 | { 29 | this.When(f => f.WhenGetChannel(100)) 30 | .Then(f => f.ThenSuccess()) 31 | .BDDfy(); 32 | } 33 | 34 | [Fact] 35 | public void should_GetChannel_Concurrent500() 36 | { 37 | this.When(f => f.WhenGetChannel_Concurrent(500)) 38 | .Then(f => f.ThenSuccess()) 39 | .BDDfy(); 40 | } 41 | 42 | private void WhenGetChannel(int count) 43 | { 44 | for (int i = 0; i < count; i++) 45 | { 46 | var channel = this.channelFactory.GetChannel(); 47 | Assert.True(channel.IsOpen()); 48 | if (i == 50) 49 | { 50 | channel.Connection.Close(); 51 | } 52 | } 53 | } 54 | 55 | private void WhenGetChannel_Concurrent(int count) 56 | { 57 | var tasks = new Task[count]; 58 | Parallel.For(0, count, (i) => 59 | { 60 | var channel = this.channelFactory.GetChannel(); 61 | Assert.True(channel.IsOpen()); 62 | tasks[i] = Task.CompletedTask; 63 | }); 64 | Task.WaitAll(tasks); 65 | } 66 | 67 | private void ThenSuccess() 68 | { 69 | Assert.True(true); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/Ray2.RabbitMQ.Test/RabbitChannelTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using TestStack.BDDfy; 3 | using Xunit; 4 | 5 | namespace Ray2.RabbitMQ.Test 6 | { 7 | public class RabbitChannelTests 8 | { 9 | private readonly RabbitChannel channel; 10 | private readonly IServiceProvider serviceProvider; 11 | private readonly IRabbitConnection connection; 12 | private bool IsOpen; 13 | private uint MessageCount; 14 | public RabbitChannelTests() 15 | { 16 | this.serviceProvider = FakeConfig.BuildServiceProvider(); 17 | this.connection = new RabbitConnection(this.serviceProvider,FakeConfig.Options); 18 | this.channel = new RabbitChannel(this.connection); 19 | } 20 | 21 | [Fact] 22 | public void should_IsOpen_Success() 23 | { 24 | this.When(f => f.WhenIsOpen()) 25 | .Then(f => f.ThenIsOpenSuccess()) 26 | .BDDfy(); 27 | } 28 | [Fact] 29 | public void should_IsOpen_Closed() 30 | { 31 | this.When(f => f.WhenClose()) 32 | .When(f => f.WhenIsOpen()) 33 | .Then(f => f.ThenIsOpenClose()) 34 | .BDDfy(); 35 | 36 | } 37 | [Fact] 38 | public void should_Close() 39 | { 40 | this.When(f => f.WhenClose()) 41 | .Then(f => f.ThenSuccess()) 42 | .BDDfy(); 43 | } 44 | [Fact] 45 | public void should_MessageCount_Success() 46 | { 47 | this.Given(f=>f.GrainCreateModel()) 48 | .When(f => f.WhenMessageCount()) 49 | .Then(f => f.ThenSuccess()) 50 | .BDDfy(); 51 | } 52 | 53 | private void GrainCreateModel() 54 | { 55 | this.channel.Model.QueueDeclare("RabbitChannelTests", false, false, false, null); 56 | } 57 | 58 | private void WhenClose() 59 | { 60 | this.channel.Close(); 61 | } 62 | 63 | private void WhenIsOpen() 64 | { 65 | this.IsOpen = this.channel.IsOpen(); 66 | } 67 | 68 | private void WhenMessageCount() 69 | { 70 | this.MessageCount = this.channel.MessageCount("RabbitChannelTests"); 71 | } 72 | 73 | private void ThenSuccess() 74 | { 75 | Assert.True(true); 76 | } 77 | 78 | private void ThenIsOpenSuccess() 79 | { 80 | Assert.True(this.IsOpen); 81 | } 82 | 83 | private void ThenIsOpenClose() 84 | { 85 | Assert.False(this.IsOpen); 86 | } 87 | 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test/Ray2.RabbitMQ.Test/RabbitConnectionTests.cs: -------------------------------------------------------------------------------- 1 | using Ray2.RabbitMQ.Configuration; 2 | using System; 3 | using TestStack.BDDfy; 4 | using Xunit; 5 | 6 | namespace Ray2.RabbitMQ.Test 7 | { 8 | public class RabbitConnectionTests 9 | { 10 | private readonly RabbitOptions options; 11 | private readonly IServiceProvider serviceProvider; 12 | private readonly RabbitConnection connection; 13 | private Exception exception; 14 | private IRabbitChannel channel; 15 | 16 | public RabbitConnectionTests() 17 | { 18 | this.options = FakeConfig.Options; 19 | this.serviceProvider = FakeConfig.BuildServiceProvider(); 20 | this.connection = new RabbitConnection(this.serviceProvider, this.options); 21 | } 22 | 23 | [Fact] 24 | public void should_createChannel_Success() 25 | { 26 | this.When(f => f.WhenCreateChannel()) 27 | .Then(f => f.ThenChannelIsOpen()) 28 | .BDDfy(); 29 | } 30 | 31 | [Fact] 32 | public void should_createChannel_ConnectionClosed() 33 | { 34 | this.Given(f => f.WhenCloseCoonnection()) 35 | .When(f => f.WhenCreateChannel_ConnectionClosed()) 36 | .Then(f => f.ThenException()) 37 | .BDDfy(); 38 | } 39 | 40 | [Fact] 41 | public void should_Close() 42 | { 43 | this.When(f => f.WhenCloseCoonnection()) 44 | .Then(f => f.ThenScuucess()) 45 | .BDDfy(); 46 | } 47 | 48 | private void WhenCloseCoonnection() 49 | { 50 | this.connection.Close(); 51 | } 52 | 53 | private void WhenCreateChannel() 54 | { 55 | channel=this.connection.CreateChannel(); 56 | } 57 | 58 | private void WhenCreateChannel_ConnectionClosed() 59 | { 60 | try 61 | { 62 | channel= this.connection.CreateChannel(); 63 | } 64 | catch (Exception ex) 65 | { 66 | this.exception = ex; 67 | } 68 | } 69 | 70 | private void ThenException() 71 | { 72 | Assert.NotNull(this.exception); 73 | } 74 | private void ThenScuucess() 75 | { 76 | Assert.True(true); 77 | } 78 | 79 | private void ThenChannelIsOpen() 80 | { 81 | Assert.True(this.channel.IsOpen()); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/Ray2.RabbitMQ.Test/Ray2.RabbitMQ.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/Ray2.Test/EventSource/EventSourcingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Test.EventSource 6 | { 7 | public class EventSourcingTests 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/Ray2.Test/Internal/DataflowBufferBlockFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Internal; 2 | using Ray2.Test.Model; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Threading.Tasks.Dataflow; 9 | using Xunit; 10 | 11 | namespace Ray2.Test.Internal 12 | { 13 | public class DataflowBufferBlockFactoryTests 14 | { 15 | private DataflowBufferBlockFactory dataflowBufferBlockFactory; 16 | private List Models; 17 | private TestDataflowWrap Model1; 18 | 19 | public DataflowBufferBlockFactoryTests() 20 | { 21 | dataflowBufferBlockFactory = new DataflowBufferBlockFactory(); 22 | Models = new List(); 23 | } 24 | 25 | [Fact] 26 | public void should_Create_Success() 27 | { 28 | TestDataflowWrap model1 = new TestDataflowWrap { Test = "1" }; 29 | TestDataflowWrap model2 = new TestDataflowWrap { Test = "2" }; 30 | TestDataflowWrap model3 = new TestDataflowWrap { Test = "3" }; 31 | 32 | var dataflowBufferBlock1 = this.dataflowBufferBlockFactory.Create("test", TriggerProcessor); 33 | var dataflowBufferBlock2 = this.dataflowBufferBlockFactory.Create("test", TriggerProcessor); 34 | var dataflowBufferBlock3 = this.dataflowBufferBlockFactory.Create("test1", TriggerProcessor1); 35 | 36 | var tasks = new Task[3]; 37 | tasks[0] = dataflowBufferBlock1.SendAsync(model1); 38 | tasks[1] = dataflowBufferBlock2.SendAsync(model2); 39 | tasks[2] = dataflowBufferBlock3.SendAsync(model3); 40 | Task.WhenAll(tasks).Wait(); 41 | 42 | 43 | foreach (var task in tasks) 44 | { 45 | Assert.True(task.Result); 46 | } 47 | for (int i = 0; i < Models.Count(); i++) 48 | { 49 | if (Models[i].Test == "1") 50 | { 51 | Models[i].Valid(model1); 52 | } 53 | else if (Models[i].Test == "2") 54 | { 55 | Models[i].Valid(model2); 56 | } 57 | else 58 | { 59 | throw new Exception("eventy bu yiz"); 60 | } 61 | } 62 | Model1.Valid(model3); 63 | 64 | } 65 | 66 | 67 | private Task TriggerProcessor(BufferBlock bufferBlock) 68 | { 69 | while (bufferBlock.TryReceive(out TestDataflowWrap model)) 70 | { 71 | model.TaskSource.SetResult(true); 72 | Models.Add(model); 73 | } 74 | return Task.CompletedTask; 75 | } 76 | 77 | private Task TriggerProcessor1(BufferBlock bufferBlock) 78 | { 79 | while (bufferBlock.TryReceive(out TestDataflowWrap model)) 80 | { 81 | model.TaskSource.SetResult(true); 82 | this.Model1 = model; 83 | } 84 | return Task.CompletedTask; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /test/Ray2.Test/Internal/DataflowBufferBlockTests.cs: -------------------------------------------------------------------------------- 1 | using Ray2.Internal; 2 | using Ray2.Test.Model; 3 | using System; 4 | using System.Threading.Tasks; 5 | using System.Threading.Tasks.Dataflow; 6 | using TestStack.BDDfy; 7 | using Xunit; 8 | 9 | namespace Ray2.Test.Internal 10 | { 11 | public class DataflowBufferBlockTests 12 | { 13 | private DataflowBufferBlock dataflowBufferBlock; 14 | private TestDataflowWrap Model; 15 | private bool IsSuccess; 16 | 17 | public DataflowBufferBlockTests() 18 | { 19 | dataflowBufferBlock = new DataflowBufferBlock(this.TriggerProcessor); 20 | } 21 | 22 | [Fact] 23 | public void should_Send_Success() 24 | { 25 | TestDataflowWrap model = new TestDataflowWrap { Test = "should_Send_Success" }; 26 | 27 | this.When(f => f.WhenSendAsync(model)) 28 | .Then(f => f.ThenSuccess(model)) 29 | .BDDfy(); 30 | } 31 | 32 | private void WhenSendAsync(TestDataflowWrap model) 33 | { 34 | IsSuccess = this.dataflowBufferBlock.SendAsync(model).GetAwaiter().GetResult(); 35 | } 36 | 37 | private void ThenSuccess(TestDataflowWrap model) 38 | { 39 | Assert.True(IsSuccess); 40 | this.Model.Valid(model); 41 | } 42 | 43 | private Task TriggerProcessor(BufferBlock bufferBlock) 44 | { 45 | while (bufferBlock.TryReceive(out TestDataflowWrap model)) 46 | { 47 | model.TaskSource.SetResult(true); 48 | this.Model = model; 49 | } 50 | return Task.CompletedTask; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/Ray2.Test/MQ/MQSubscriberTests.cs: -------------------------------------------------------------------------------- 1 | using Ray2.MQ; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Orleans.Runtime; 7 | using Moq; 8 | using Ray2.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | using Xunit; 11 | using TestStack.BDDfy; 12 | using System.Threading.Tasks; 13 | 14 | namespace Ray2.Test.MQ 15 | { 16 | public class MQSubscriberTests 17 | { 18 | private readonly MQSubscriber subscriber; 19 | private readonly Mock eventSubscriber = new Mock(); 20 | private readonly Mock internalConfiguration = new Mock(); 21 | private readonly string providerName = "Default"; 22 | private readonly string name = "group"; 23 | private int count = 0; 24 | public MQSubscriberTests() 25 | { 26 | IServiceCollection services = new ServiceCollection(); 27 | services.AddLogging(); 28 | services.AddSingleton(typeof(IKeyedServiceCollection<,>), typeof(KeyedServiceCollection<,>)); 29 | services.AddSingletonNamedService(providerName, (s, key) => eventSubscriber.Object); 30 | services.AddSingletonNamedService(providerName+"1", (s, key) => eventSubscriber.Object); 31 | 32 | IServiceProvider sp = services.BuildServiceProvider(); 33 | 34 | subscriber = new MQSubscriber(sp, internalConfiguration.Object, sp.GetRequiredService>()); 35 | } 36 | 37 | [Fact] 38 | public void should_Start() 39 | { 40 | this.Given(f => f.GivenInitConfiguration()) 41 | .Given(f => f.GivenInitEventSubscriber()) 42 | .When(f => f.WhenStart()) 43 | .Then(f => ThenExeNumber(2)) 44 | .BDDfy(); 45 | } 46 | 47 | [Fact] 48 | public void should_Stop() 49 | { 50 | this.Given(f => f.GivenInitConfiguration()) 51 | .Given(f => f.GivenInitEventSubscriber()) 52 | .When(f => f.WhenStop()) 53 | .Then(f => ThenExeNumber(2)) 54 | .BDDfy(); 55 | } 56 | 57 | 58 | private void GivenInitConfiguration() 59 | { 60 | var opts = new List(); 61 | EventProcessOptions processOptions = new EventProcessOptions(name,null, ProcessorType.GrainProcessor,null,null,0,TimeSpan.FromDays(1),null,new List()); 62 | opts.Add(processOptions); 63 | processOptions.SubscribeOptions.Add(new EventSubscribeOptions(providerName, name, name)); 64 | processOptions.SubscribeOptions.Add(new EventSubscribeOptions(providerName + "1", name, name)); 65 | internalConfiguration.Setup(f => f.EventProcessOptionsList).Returns(opts); 66 | } 67 | 68 | private void GivenInitEventSubscriber() 69 | { 70 | eventSubscriber.Setup(f => f.Stop()).Returns(Task.CompletedTask).Callback(() => { this.count++; }); 71 | eventSubscriber.Setup(f => f.Subscribe(name, name)).Returns(Task.CompletedTask).Callback(() => { this.count++; }); 72 | } 73 | 74 | private void WhenStart() 75 | { 76 | this.subscriber.Start().Wait(); 77 | } 78 | 79 | private void WhenStop() 80 | { 81 | this.subscriber.Stop(); 82 | } 83 | 84 | private void ThenExeNumber(int number) 85 | { 86 | Assert.Equal(number, this.count); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/Ray2.Test/Model/TestEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xunit; 5 | 6 | namespace Ray2.Test.Model 7 | { 8 | [EventPublish("test", "Default")] 9 | public class TestEvent : Event 10 | { 11 | public static TestEvent Create(long v) 12 | { 13 | return new TestEvent() 14 | { 15 | StateId = 100, 16 | Version = v 17 | }; 18 | } 19 | 20 | public void Valid(TestEvent state) 21 | { 22 | Assert.Equal(state.StateId, this.StateId); 23 | Assert.Equal(state.Version, this.Version); 24 | Assert.Equal(state.TypeCode, this.TypeCode); 25 | Assert.Equal(state.Version, this.Version); 26 | Assert.Equal(state.Timestamp, this.Timestamp); 27 | Assert.Equal(state.RelationEvent, this.RelationEvent); 28 | } 29 | } 30 | 31 | public class TestEvent1 : Event 32 | { 33 | public static TestEvent1 Create(long v) 34 | { 35 | return new TestEvent1() 36 | { 37 | StateId = 100, 38 | Version = v 39 | }; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/Ray2.Test/Model/TestState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Test.Model 6 | { 7 | public class TestState : State 8 | { 9 | protected override void PlayEvent(IEvent @event) 10 | { 11 | 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/Ray2.Test/Ray2.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/Ray2.Test/Serialization/JsonSerializerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ray2.Test.Serialization 6 | { 7 | public class JsonSerializerTests 8 | { 9 | } 10 | } 11 | --------------------------------------------------------------------------------