I decided to look at starting down the path of implementing the Chord protocol (http://pdos.csail.mit.edu/papers/chord:sigcomm01/chord_sigcomm.pdf). The first problem that needed to be solved was that of communication between the nodes. The ideal solution seems to be the model provided by Erlang. Unfortunately, that model doesn't seem to be available in .NET land (there is MPI.NET, but that appears to require Windows HPC Server 2008 or Microsoft Compute Cluster Server 2003 for use across multiple machines). After some research, I finally decided on using .NET remoting (though I may end up changing this to a sockets based approach in the future). The POC for this is provided below.
Seeing it work:
1. First, run Polyphony.exe from the bin\Server directory.
2. Next, run Polyphony.exe from the bin\Debug directory.
3. Now, type "put 1 test1" in one of the consoles and "put 2 test2" in the other.
4. Now, type "get 1" in either console. The app. will first check it's local hash table. If the value is not found, it moves on to the second node.
The next step is to make this scale and more robust. Then it's on to some of the specific Chord protocol functionality. The source is available at http://github.com/dmohl/Polyphony and will be continually evolving. Let me know if you want to participate in the fun.
Example:
Program.fs
module Polyphony open System open System.Configuration let port = ConfigurationManager.AppSettings.Item("ServerPort") ChordServer.Initialize(Convert.ToInt32(port)) |> ignore ChordClient.Initialize() |> ignore Console.Write "\nEnter Command:" let mutable input = Console.ReadLine() while input <> "quit" do if input <> "quit" then ChordClient.RunCommand input Console.Write "\nEnter Command:" input <- Console.ReadLine()Chord.fs
module Shared open System open System.Collections open System.Runtime.Remoting type Chord = class val hashTable : Hashtable inherit MarshalByRefObject new () = {hashTable = new Hashtable()} member x.PutValueByKey key value = x.hashTable.Add(key, value) member x.GetValueByKey key = x.hashTable.Item(key) endChordServer.fs
module ChordServer open System open System.Runtime.Remoting open System.Runtime.Remoting.Channels open System.Runtime.Remoting.Channels.Tcp let Initialize port = let tcpChannel = new TcpChannel(port) Console.WriteLine("Polyphony Server started on port {0}...", port) ChannelServices.RegisterChannel(tcpChannel, false) |> ignore let commonType = typeof<Shared.Chord> RemotingConfiguration.RegisterWellKnownServiceType(commonType, "chord", WellKnownObjectMode.Singleton) |> ignoreChordClient.fs
module ChordClient open System open System.Configuration open System.Runtime.Remoting open System.Runtime.Remoting.Channels open System.Runtime.Remoting.Channels.Tcp let serverInformation = ConfigurationManager.AppSettings.Item("ClientServer") let selfServerInformation = ConfigurationManager.AppSettings.Item("SelfServer") let Initialize() = Console.WriteLine("Polyphony Client started and pointing to server {0}...", serverInformation) let localObject = Activator.GetObject(typeof<Shared.Chord>, selfServerInformation) :?> Shared.Chord localObject.PutValueByKey "test" "testValue" let RunCommand(input:string) : unit = let inputArguments = input.Split(' ') let result = match inputArguments.[0] with | "put" -> let localObject = Activator.GetObject(typeof<Shared.Chord>, selfServerInformation) :?> Shared.Chord localObject.PutValueByKey inputArguments.[1] inputArguments.[2] |> ignore sprintf "PUT Key:%A Value:%A" inputArguments.[1] inputArguments.[2] :> obj | "get" -> let rec getValue server = let chord = Activator.GetObject(typeof<Shared.Chord>, server) :?> Shared.Chord match chord.GetValueByKey inputArguments.[1] with | null -> getValue serverInformation | value -> value getValue selfServerInformation | _ -> "unknown command" :> obj Console.WriteLine(result) |> ignoreApp.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="ServerPort" value="9877"/> <add key="SelfServer" value="tcp://localhost:9877/chord"/> <add key="ClientServer" value="tcp://localhost:9876/chord"/> </appSettings> </configuration>
Nice article... BUT!
ReplyDeleteIn 2010 - *don't* use .NET Remoting. Please.
I'd suggest you use WCF with one of the binary bindings (netTcpBinding / netPipeBinding).
--larsw
:) Point well taken! I will definitely be moving to either WCF or a sockets based approach.
ReplyDeleteThanks for the comment.
- Dan
By the way, MPI.Net does not require running HPC, though you will need
ReplyDelete"Microsoft HPC Pack 2008 SDK" for the MSMPI implementation and utilities (notably, Microsoft's versions of smpd and mpiexec).
Once you have that and MPI.Net, you can build and run your distributed apps using mpiexec to specify which nodes run which processes, and smpd running as the process manager demon on each node (which can be 32 or 64 bit OS).
Of course then, life get interesting ...
David
Great to know! Thanks David!
ReplyDeleteI would like to say that you really made my day, it's wonderful when you just look around the web
ReplyDeleteand find something like this, reminds me of that ''How to make a dinner for a romantic...'' by Elsa Thomas,
you're a wonderful writer let me tell you!!! ñ_ñ
James Maverick (maverickhunterjames@gmail.com)
3453 Rardin Drive
San Mateo, CA 94403
Project Manager
650-627-8033
Great to know! Thanks David!
ReplyDeleteBy the way, MPI.Net does not require running HPC, though you will need
ReplyDelete"Microsoft HPC Pack 2008 SDK" for the MSMPI implementation and utilities (notably, Microsoft's versions of smpd and mpiexec).
Once you have that and MPI.Net, you can build and run your distributed apps using mpiexec to specify which nodes run which processes, and smpd running as the process manager demon on each node (which can be 32 or 64 bit OS).
Of course then, life get interesting ...
David
:) Point well taken! I will definitely be moving to either WCF or a sockets based approach.
ReplyDeleteThanks for the comment.
- Dan