Sunday, April 4, 2010

A Distributed Hash Table (DHT) in F#: Moving from .NET Remoting to WCF

In my last post I provided a start to a distributed hash table (DHT) using F#, the Chord protocol, and .NET remoting.  In this post, I'll show how that code has changed due to a migration from .NET remoting to WCF.

As a side note, I've codenamed this little project Polyphony.

Here's a summary of the primary changes:

- Chord.fs has gone away.
- ChordServer now contains contract specific information.
- ChordClient now calls the service operations.

The primary code changes are provided below.  The complete project is available at http://github.com/dmohl/polyphony.

ChordServer.fs
module ChordServer

open System
open System.ServiceModel
open System.Collections
open System.Configuration
open System.Net

[<ServiceContract>]  
type IChordServer = interface   
    [<OperationContract>]  
    abstract PutValueByKey : key:obj -> value:obj -> unit  
    [<OperationContract>]  
    abstract GetValueByKey : value:obj -> obj  
end

[<ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)>]
type ChordServer = class
    val hashTable : Hashtable
    new () = {hashTable = new Hashtable()}
    interface IChordServer with
        member x.PutValueByKey key value =
            x.hashTable.Add(key, value)
        member x.GetValueByKey key =
            x.hashTable.Item(key)
end

let logException (ex:Exception) =
    Console.WriteLine("Error: {0}", ex.Message)
    Console.WriteLine(ex.Source)
    
let Initialize () =
    try
        let localServer = ConfigurationManager.AppSettings.Item("LocalServer")
        Console.WriteLine("Starting Server: {0}", localServer)
        let host = new ServiceHost(typeof<ChordServer>)
        host.AddServiceEndpoint(typeof<IChordServer>,
                    new NetTcpBinding(), localServer) |> ignore       
        host.Open()
        Some(host)
    with
    | ex -> logException ex
            None
           
let Stop (host: ServiceHost option)  =
    try
        match host with
        | None -> ()
        | Some(host) ->
            if host.State <> CommunicationState.Closed then
                host.Close()
                Console.WriteLine("Stopping Server")               
    with
    | ex -> logException ex           
ChordClient.fs
module ChordClient

open System
open System.Configuration
open System.ServiceModel

let remoteServer = ConfigurationManager.AppSettings.Item("RemoteServer")
let localServer = ConfigurationManager.AppSettings.Item("LocalServer")

let CallServer (server:string) (operationContract:string) (inputArguments:string[]) =
    let service = new ChannelFactory<ChordServer.IChordServer>(
                        new NetTcpBinding(), server)  
    try                    
        try
            let proxy = service.CreateChannel()        
            let result = match operationContract with
                         | "put" ->
                             proxy.PutValueByKey inputArguments.[1] inputArguments.[2] 
                             "Put Complete" :> obj
                         | "get" -> 
                             proxy.GetValueByKey inputArguments.[1]     
                         | _ -> "Unknown" :> obj 
            result             
        with
        | ex -> 
            Console.WriteLine ex.Message
            "Unknown" :> obj
    finally                 
        service.Close |> ignore

let RunCommand(input:string) : unit =
    let inputArguments = input.Split(' ')
    let result = 
        match inputArguments.[0] with
        | "put" ->
            CallServer localServer inputArguments.[0] inputArguments |> ignore
            sprintf "PUT Key:%A Value:%A" inputArguments.[1] inputArguments.[2] :> obj   
        | "get" -> 
            let rec getValue server =
                let value = CallServer server inputArguments.[0] inputArguments
                match value with
                | null -> getValue remoteServer    
                | _ -> value
            getValue localServer
        | _ -> "unknown command" :> obj   
    Console.WriteLine(result) |> ignore

No comments:

Post a Comment