Skip to content

Instantly share code, notes, and snippets.

@veikkoeeva
Last active January 17, 2017 16:52
Show Gist options
  • Save veikkoeeva/4414609e828375c898f3 to your computer and use it in GitHub Desktop.
Save veikkoeeva/4414609e828375c898f3 to your computer and use it in GitHub Desktop.
Custom Orleans host in F#
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)
[<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