diff --git a/OrleansGettingStarted.sln b/OrleansGettingStarted.sln index f088b18..3482f8c 100644 --- a/OrleansGettingStarted.sln +++ b/OrleansGettingStarted.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kritner.OrleansGettingStart EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kritner.OrleansGettingStarted.SiloHost", "src\Kritner.OrleansGettingStarted.SiloHost\Kritner.OrleansGettingStarted.SiloHost.csproj", "{60C38B38-65C6-4F8F-AF7B-37ACC1D8E4FB}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kritner.OrleansGettingStarted.Common", "src\Kritner.OrleansGettingStarted.Common\Kritner.OrleansGettingStarted.Common.csproj", "{E7346FF3-2DB9-430A-8A6E-7949A8B470B2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +37,10 @@ Global {60C38B38-65C6-4F8F-AF7B-37ACC1D8E4FB}.Debug|Any CPU.Build.0 = Debug|Any CPU {60C38B38-65C6-4F8F-AF7B-37ACC1D8E4FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {60C38B38-65C6-4F8F-AF7B-37ACC1D8E4FB}.Release|Any CPU.Build.0 = Release|Any CPU + {E7346FF3-2DB9-430A-8A6E-7949A8B470B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7346FF3-2DB9-430A-8A6E-7949A8B470B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7346FF3-2DB9-430A-8A6E-7949A8B470B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7346FF3-2DB9-430A-8A6E-7949A8B470B2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -44,6 +50,7 @@ Global {A2997108-2330-4544-B3B0-4EF2B4537E14} = {E9107609-5D2A-4988-81F6-EDF031856CD0} {93D88312-0369-4858-8F2E-1A0F11FCB224} = {E9107609-5D2A-4988-81F6-EDF031856CD0} {60C38B38-65C6-4F8F-AF7B-37ACC1D8E4FB} = {E9107609-5D2A-4988-81F6-EDF031856CD0} + {E7346FF3-2DB9-430A-8A6E-7949A8B470B2} = {E9107609-5D2A-4988-81F6-EDF031856CD0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A3713286-1C26-4298-9843-247ACCE599CC} diff --git a/src/Kritner.OrleansGettingStarted.Client/ExtensionMethods/IClientBuilderExtensions.cs b/src/Kritner.OrleansGettingStarted.Client/ExtensionMethods/IClientBuilderExtensions.cs new file mode 100644 index 0000000..d1083c2 --- /dev/null +++ b/src/Kritner.OrleansGettingStarted.Client/ExtensionMethods/IClientBuilderExtensions.cs @@ -0,0 +1,54 @@ +using Kritner.OrleansGettingStarted.Common.Config; +using Microsoft.Extensions.Options; +using Orleans; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Kritner.OrleansGettingStarted.Client.ExtensionMethods +{ + public static class IClientBuilderExtensions + { + /// + /// Configures clustering for the Orleans Client based on + /// the Orleans environment. + /// + /// The client builder. + /// The Orleans configuration options. + /// The environment. + public static IClientBuilder ConfigureClustering( + this IClientBuilder builder, + IOptions orleansConfigOptions, + string environmentName + ) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + if (orleansConfigOptions.Value == default(OrleansConfig)) + { + throw new ArgumentException(nameof(orleansConfigOptions)); + } + + switch (environmentName.ToLower()) + { + case "dev": + builder.UseLocalhostClustering(); + break; + default: + var orleansConfig = orleansConfigOptions.Value; + List nodes = new List(); + foreach (var node in orleansConfig.NodeIpAddresses) + { + nodes.Add(new IPEndPoint(IPAddress.Parse(node), orleansConfig.GatewayPort)); + } + builder.UseStaticClustering(nodes.ToArray()); + break; + } + + return builder; + } + } +} diff --git a/src/Kritner.OrleansGettingStarted.Client/Kritner.OrleansGettingStarted.Client.csproj b/src/Kritner.OrleansGettingStarted.Client/Kritner.OrleansGettingStarted.Client.csproj index 9921da6..b142122 100644 --- a/src/Kritner.OrleansGettingStarted.Client/Kritner.OrleansGettingStarted.Client.csproj +++ b/src/Kritner.OrleansGettingStarted.Client/Kritner.OrleansGettingStarted.Client.csproj @@ -6,12 +6,19 @@ + + + + + + + diff --git a/src/Kritner.OrleansGettingStarted.Client/Program.cs b/src/Kritner.OrleansGettingStarted.Client/Program.cs index dc9009c..01cb673 100644 --- a/src/Kritner.OrleansGettingStarted.Client/Program.cs +++ b/src/Kritner.OrleansGettingStarted.Client/Program.cs @@ -1,21 +1,33 @@ using Kritner.OrleansGettingStarted.Client.OrleansFunctionExamples; +using Kritner.OrleansGettingStarted.Common.Config; +using Kritner.OrleansGettingStarted.Common.Helpers; using Kritner.OrleansGettingStarted.GrainInterfaces; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Orleans; using Orleans.Configuration; using Orleans.Runtime; using System; using System.Threading.Tasks; +using Kritner.OrleansGettingStarted.Common; +using Kritner.OrleansGettingStarted.Client.ExtensionMethods; namespace Kritner.OrleansGettingStarted.Client { public class Program { + private static Startup Startup; + private static IServiceProvider ServiceProvider; const int initializeAttemptsBeforeFailing = 5; private static int attempt = 0; static int Main(string[] args) { + Startup = ConsoleAppConfigurator.BootstrapApp(); + var serviceCollection = new ServiceCollection(); + Startup.ConfigureServices(serviceCollection); + ServiceProvider = serviceCollection.BuildServiceProvider(); + return RunMainAsync().Result; } @@ -44,7 +56,10 @@ private static async Task StartClientWithRetries() attempt = 0; IClusterClient client; client = new ClientBuilder() - .UseLocalhostClustering() + .ConfigureClustering( + ServiceProvider.GetService>(), + Startup.HostingEnvironment.EnvironmentName + ) .Configure(options => { options.ClusterId = "dev"; diff --git a/src/Kritner.OrleansGettingStarted.Common/Config/OrleansConfig.cs b/src/Kritner.OrleansGettingStarted.Common/Config/OrleansConfig.cs new file mode 100644 index 0000000..b34ae36 --- /dev/null +++ b/src/Kritner.OrleansGettingStarted.Common/Config/OrleansConfig.cs @@ -0,0 +1,25 @@ +using System; + +namespace Kritner.OrleansGettingStarted.Common.Config +{ + /// + /// Contains properties utilized for configuration Orleans + /// Clients and Cluster Nodes. + /// + public class OrleansConfig + { + /// + /// The IP addresses that will be utilized in the cluster. + /// First IP address is the primary. + /// + public string[] NodeIpAddresses { get; set; } + /// + /// The port used for Client to Server communication. + /// + public int GatewayPort { get; set; } + /// + /// The port for Silo to Silo communication + /// + public int SiloPort { get; set; } + } +} diff --git a/src/Kritner.OrleansGettingStarted.Common/Helpers/ConsoleAppConfigurator.cs b/src/Kritner.OrleansGettingStarted.Common/Helpers/ConsoleAppConfigurator.cs new file mode 100644 index 0000000..6e17e94 --- /dev/null +++ b/src/Kritner.OrleansGettingStarted.Common/Helpers/ConsoleAppConfigurator.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Hosting.Internal; +using System; +using System.IO; + +namespace Kritner.OrleansGettingStarted.Common.Helpers +{ + public static class ConsoleAppConfigurator + { + public static Startup BootstrapApp() + { + var environment = GetEnvironment(); + var hostingEnvironment = GetHostingEnvironment(environment); + var configurationBuilder = CreateConfigurationBuilder(environment); + + return new Startup(hostingEnvironment, configurationBuilder); + } + + private static string GetEnvironment() + { + var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + if (string.IsNullOrEmpty(environmentName)) + { + return "dev"; + } + + return environmentName; + } + + private static IHostingEnvironment GetHostingEnvironment(string environmentName) + { + return new HostingEnvironment + { + EnvironmentName = environmentName, + ApplicationName = AppDomain.CurrentDomain.FriendlyName, + ContentRootPath = AppDomain.CurrentDomain.BaseDirectory, + ContentRootFileProvider = new PhysicalFileProvider(AppDomain.CurrentDomain.BaseDirectory) + }; + } + + private static IConfigurationBuilder CreateConfigurationBuilder(string environmentName) + { + var config = new ConfigurationBuilder() + .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables(); + + return config; + } + } +} diff --git a/src/Kritner.OrleansGettingStarted.Common/Kritner.OrleansGettingStarted.Common.csproj b/src/Kritner.OrleansGettingStarted.Common/Kritner.OrleansGettingStarted.Common.csproj new file mode 100644 index 0000000..666f5aa --- /dev/null +++ b/src/Kritner.OrleansGettingStarted.Common/Kritner.OrleansGettingStarted.Common.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/src/Kritner.OrleansGettingStarted.Common/Startup.cs b/src/Kritner.OrleansGettingStarted.Common/Startup.cs new file mode 100644 index 0000000..f51995c --- /dev/null +++ b/src/Kritner.OrleansGettingStarted.Common/Startup.cs @@ -0,0 +1,28 @@ +using Kritner.OrleansGettingStarted.Common.Config; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Kritner.OrleansGettingStarted.Common +{ + public class Startup + { + public IHostingEnvironment HostingEnvironment { get; } + public IConfiguration Configuration { get; } + + public Startup( + IHostingEnvironment hostingEnvironment, + IConfigurationBuilder configurationBuilder + ) + { + HostingEnvironment = hostingEnvironment; + Configuration = configurationBuilder.Build(); + } + + public void ConfigureServices(IServiceCollection serviceCollection) + { + serviceCollection.AddOptions(); + serviceCollection.Configure(Configuration.GetSection(nameof(OrleansConfig))); + } + } +} diff --git a/src/Kritner.OrleansGettingStarted.SiloHost/ExtensionMethods/ISiloHostBuilderExtensions.cs b/src/Kritner.OrleansGettingStarted.SiloHost/ExtensionMethods/ISiloHostBuilderExtensions.cs new file mode 100644 index 0000000..5ec46b6 --- /dev/null +++ b/src/Kritner.OrleansGettingStarted.SiloHost/ExtensionMethods/ISiloHostBuilderExtensions.cs @@ -0,0 +1,54 @@ +using Kritner.OrleansGettingStarted.Common.Config; +using Microsoft.Extensions.Options; +using Orleans.Hosting; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Kritner.OrleansGettingStarted.SiloHost.ExtensionMethods +{ + public static class ISiloHostBuilderExtensions + { + /// + /// Configures clustering for the Orleans Silo Host based on + /// the Orleans environment. + /// + /// The silo host builder. + /// The Orleans configuration options. + /// The environment. + public static ISiloHostBuilder ConfigureClustering( + this ISiloHostBuilder builder, + IOptions orleansConfigOptions, + string environmentName + ) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + if (orleansConfigOptions.Value == default(OrleansConfig)) + { + throw new ArgumentException(nameof(orleansConfigOptions)); + } + + switch (environmentName.ToLower()) + { + case "dev": + builder.UseLocalhostClustering(); + break; + default: + var orleansConfig = orleansConfigOptions.Value; + builder.UseDevelopmentClustering( + new IPEndPoint( + IPAddress.Parse(orleansConfig.NodeIpAddresses[0]), + orleansConfig.SiloPort + ) + ); + break; + } + + return builder; + } + } +} diff --git a/src/Kritner.OrleansGettingStarted.SiloHost/Kritner.OrleansGettingStarted.SiloHost.csproj b/src/Kritner.OrleansGettingStarted.SiloHost/Kritner.OrleansGettingStarted.SiloHost.csproj index c2670aa..095d23e 100644 --- a/src/Kritner.OrleansGettingStarted.SiloHost/Kritner.OrleansGettingStarted.SiloHost.csproj +++ b/src/Kritner.OrleansGettingStarted.SiloHost/Kritner.OrleansGettingStarted.SiloHost.csproj @@ -12,8 +12,14 @@ + + + + + + diff --git a/src/Kritner.OrleansGettingStarted.SiloHost/Program.cs b/src/Kritner.OrleansGettingStarted.SiloHost/Program.cs index 5df0e62..2fb8b98 100644 --- a/src/Kritner.OrleansGettingStarted.SiloHost/Program.cs +++ b/src/Kritner.OrleansGettingStarted.SiloHost/Program.cs @@ -1,8 +1,12 @@ -using Kritner.OrleansGettingStarted.GrainInterfaces; +using Kritner.OrleansGettingStarted.Common; +using Kritner.OrleansGettingStarted.Common.Config; +using Kritner.OrleansGettingStarted.Common.Helpers; using Kritner.OrleansGettingStarted.Grains; +using Kritner.OrleansGettingStarted.SiloHost.ExtensionMethods; using Kritner.OrleansGettingStarted.SiloHost.Helpers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Orleans; using Orleans.Configuration; using Orleans.Hosting; @@ -14,8 +18,16 @@ namespace Kritner.OrleansGettingStarted.SiloHost { public class Program { + private static Startup Startup; + private static IServiceProvider ServiceProvider; + public static int Main(string[] args) { + Startup = ConsoleAppConfigurator.BootstrapApp(); + var serviceCollection = new ServiceCollection(); + Startup.ConfigureServices(serviceCollection); + ServiceProvider = serviceCollection.BuildServiceProvider(); + return RunMainAsync().Result; } @@ -42,7 +54,10 @@ private static async Task StartSilo() { // define the cluster configuration var builder = new SiloHostBuilder() - .UseLocalhostClustering() + .ConfigureClustering( + ServiceProvider.GetService>(), + Startup.HostingEnvironment.EnvironmentName + ) .Configure(options => { options.ClusterId = "dev"; diff --git a/src/_appsettings/appsettings.dev.json b/src/_appsettings/appsettings.dev.json new file mode 100644 index 0000000..19cec60 --- /dev/null +++ b/src/_appsettings/appsettings.dev.json @@ -0,0 +1,7 @@ +{ + "OrleansConfig" : { + "NodeIpAddresses" : ["0.0.0.0"], + "GatewayPort" : -1, + "SiloPort" : -1 + } +} \ No newline at end of file diff --git a/src/_appsettings/appsettings.json b/src/_appsettings/appsettings.json new file mode 100644 index 0000000..5d7d7a1 --- /dev/null +++ b/src/_appsettings/appsettings.json @@ -0,0 +1,7 @@ +{ + "OrleansConfig" : { + "NodeIpAddresses" : ["192.168.1.105", "192.168.1.106"], + "GatewayPort" : 30000, + "SiloPort" : 11111 + } +} \ No newline at end of file