Last active
January 17, 2017 16:52
-
-
Save veikkoeeva/4414609e828375c898f3 to your computer and use it in GitHub Desktop.
Custom Orleans host in F#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace OrleansHostingUtilities | |
open System | |
open Orleans.Runtime.Configuration | |
open Orleans.Runtime | |
open System.Reflection | |
open System.Globalization | |
open System.Threading | |
open System.Net | |
type SiloHost(siloName, siloType, clusterConfig, appDomainSetupProvider) = | |
/// If this host has been disposed or not. | |
let mutable disposed = false | |
let loadSiloInNewAppDomain (siloName:string) (siloType:Orleans.Runtime.Silo.SiloType) (clusterConfig:ClusterConfiguration) (appDomainSetupProvider: unit -> AppDomainSetup) = | |
let appDomain = AppDomain.CreateDomain(siloName, null, appDomainSetupProvider()) | |
let args:obj[] = [|siloName; siloType; clusterConfig|] | |
let silo = appDomain.CreateInstanceAndUnwrap("OrleansRuntime", typeof<Silo>.FullName, false, BindingFlags.Default, null, args, CultureInfo.InvariantCulture, Array.empty) :?> Silo | |
silo, appDomain | |
let silo, siloAppDomain = loadSiloInNewAppDomain siloName siloType clusterConfig appDomainSetupProvider | |
member __.SiloName = siloName | |
member __.SiloType = siloType | |
member __.StartSilo() = | |
silo.Start() | |
member __.StopSilo() = | |
silo.Stop() | |
member private x.Dispose(disposing) = | |
if not disposed then | |
if disposing then | |
try | |
try | |
silo.Stop() | |
with _ -> () | |
finally | |
AppDomain.Unload siloAppDomain | |
disposed <- true | |
interface System.IDisposable with | |
member x.Dispose() = | |
x.Dispose(true) | |
GC.SuppressFinalize(x) | |
type SiloClusterManager(mainClusterConfigurationProvider:unit -> ClusterConfiguration, appDomainSetupProvider:unit -> AppDomainSetup) = | |
///If this host has been disposed or not. | |
let mutable disposed = false | |
///The cluster of silos managed. | |
let siloCluster:ResizeArray<SiloHost> = ResizeArray<_>() | |
let mutable siloInstanceCount = -1 | |
let preConditionSiloName siloName = | |
let countOfExistingWithTheSameName = siloCluster.FindAll(fun i -> i.SiloName = siloName).Count | |
if countOfExistingWithTheSameName = 0 then siloName else siloName + "-" + countOfExistingWithTheSameName.ToString() | |
member __.ExistsPrimarySilo = | |
siloCluster.Exists(fun i -> i.SiloType = Silo.SiloType.Primary) | |
member val MainClusterConfigurationProvider = mainClusterConfigurationProvider with get, set | |
member val AppDomainSetupProvider = appDomainSetupProvider with get, set | |
member x.AddSilo(siloName) = | |
let preconditionedSiloName = preConditionSiloName siloName | |
let secondarySilo = new SiloHost(preconditionedSiloName, Silo.SiloType.None, x.SiloConfig(preconditionedSiloName), x.AppDomainSetupProvider) | |
secondarySilo.StartSilo() | |
siloCluster.Add(secondarySilo) | |
member x.DisposeAllSilos(?timeout:TimeSpan) = | |
let timeout = defaultArg timeout Timeout.InfiniteTimeSpan | |
//The secondary disposables need to be disposed before the primary silo. If all the silos are being disposed simultaneously, | |
//the secondaries try to reconnect to the primary and the shutdown takes a long time. | |
let secondaryDisposables = siloCluster.FindAll(fun d -> d.SiloType = Silo.SiloType.Secondary) |> Seq.map(fun a -> async { return (a :> IDisposable).Dispose() }) |> Async.Parallel | |
Async.RunSynchronously(secondaryDisposables, int timeout.TotalMilliseconds) |> ignore | |
siloCluster.FindAll(fun s -> s.SiloType = Silo.SiloType.Primary).ForEach(fun d -> (d :> IDisposable).Dispose()) | |
siloCluster.Clear() | |
member x.SiloConfig(siloName) = | |
let config = x.MainClusterConfigurationProvider() | |
let basePort = config.Defaults.Port | |
let siloInstancePort = System.Threading.Interlocked.Increment(&siloInstanceCount) + basePort | |
let nodeConfig = config.GetConfigurationForNode(siloName) | |
nodeConfig.HostNameOrIPAddress <- "loopback" | |
nodeConfig.Port <- siloInstancePort | |
nodeConfig.DefaultTraceLevel <- config.Defaults.DefaultTraceLevel | |
nodeConfig.PropagateActivityId <- config.Defaults.PropagateActivityId | |
nodeConfig.BulkMessageLimit <- config.Defaults.BulkMessageLimit | |
nodeConfig.SiloName <- siloName | |
if nodeConfig.ProxyGatewayEndpoint <> null && nodeConfig.ProxyGatewayEndpoint.Address <> null then | |
//nodeConfig.ProxyGatewayEndpoint <- new IPEndPoint(nodeConfig.ProxyGatewayEndpoint.Address, basePort + instances) | |
config.Globals.ExpectedClusterSize <- siloInstanceCount + 1; | |
config.Overrides.[siloName] <- nodeConfig | |
config | |
member private x.Dispose(disposing) = | |
if not disposed then | |
if disposing then | |
x.DisposeAllSilos() | |
disposed <- true | |
interface System.IDisposable with | |
member x.Dispose() = | |
x.Dispose(true) | |
GC.SuppressFinalize(x) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[<EntryPoint>] | |
let main argv = | |
let domainErrorHandler (sender:obj) (args:UnhandledExceptionEventArgs) = | |
let problemDomain = sender :?> AppDomain | |
printfn "Unhandled exception in app domain: %s" problemDomain.FriendlyName | |
let fileConfig() = | |
let config = new ClusterConfiguration() | |
config.LoadFromFile("DeveloperSiloHostsConfiguration.xml") | |
config | |
let currentAppDomainSetup() = | |
let currentAppDomain = AppDomain.CurrentDomain | |
new AppDomainSetup( | |
ApplicationBase = Environment.CurrentDirectory, | |
ConfigurationFile = currentAppDomain.SetupInformation.ConfigurationFile, | |
ShadowCopyFiles = currentAppDomain.SetupInformation.ShadowCopyFiles, | |
ShadowCopyDirectories = currentAppDomain.SetupInformation.ShadowCopyDirectories, | |
CachePath = currentAppDomain.SetupInformation.CachePath | |
) | |
let clusterManager = new SiloClusterManager(fileConfig, currentAppDomainSetup) | |
let siloNames = ["silo-1"; "silo-2"] | |
let startSilos name = async { clusterManager.AddSilo(name) } | |
let x = List.map startSilos siloNames |> Async.Parallel |> Async.RunSynchronously | |
GrainClient.Initialize("DeveloperClientConfiguration.xml") | |
let helloer = GrainClient.GrainFactory.GetGrain<IHello>(Guid.Empty) | |
Console.WriteLine("\n\n{0}\n\n", helloer.SayHelloAsync("Good morning!").Result) | |
Console.ReadLine() |> ignore | |
(clusterManager :> IDisposable).Dispose() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment