9.0 KiB
Distributed Places
The racket/place/distributed
library provides support for distributed
programming.
The example bellow demonstrates how to launch a remote racket node instance, launch remote places on the new remote node instance, and start an event loop that monitors the remote node instance.
The example code can also be found in
"racket/distributed/examples/named/master.rkt"
.
#lang racket/base
(require racket/place/distributed
racket/class
racket/place
racket/runtime-path
"bank.rkt"
"tuple.rkt")
(define-runtime-path bank-path "bank.rkt")
(define-runtime-path tuple-path "tuple.rkt")
(provide main)
(define (main)
(define remote-node (spawn-remote-racket-node
"localhost"
#:listen-port 6344))
(define tuple-place (supervise-place-at
remote-node
#:named 'tuple-server
tuple-path
'make-tuple-server))
(define bank-place (supervise-place-at
remote-node bank-path
'make-bank))
(message-router
remote-node
(after-seconds 4
(displayln (bank-new-account bank-place 'user0))
(displayln (bank-add bank-place 'user0 10))
(displayln (bank-removeM bank-place 'user0 5)))
(after-seconds 2
(define c (connect-to-named-place remote-node
'tuple-server))
(define d (connect-to-named-place remote-node
'tuple-server))
(tuple-server-hello c)
(tuple-server-hello d)
(displayln (tuple-server-set c "user0" 100))
(displayln (tuple-server-set d "user2" 200))
(displayln (tuple-server-get c "user0"))
(displayln (tuple-server-get d "user2"))
(displayln (tuple-server-get d "user0"))
(displayln (tuple-server-get c "user2"))
)
(after-seconds 8
(node-send-exit remote-node))
(after-seconds 10
(exit 0))))
Figure 1: examples/named/master.rkt
The spawn-remote-racket-node
primitive connects to "localhost"
and
starts a racloud node there that listens on port 6344 for further
instructions. The handle to the new racloud node is assigned to the
remote-node
variable. Localhost is used so that the example can be run
using only a single machine. However localhost can be replaced by any
host with ssh publickey access and racket. The supervise-place-at
creates a new place on the remote-node
. The new place will be
identified in the future by its name symbol 'tuple-server
. A place
descriptor is expected to be returned by invoking dynamic-place
with
the tuple-path
module path and the 'make-tuple-server
symbol.
The code for the tuple-server place exists in the file "tuple.rkt"
.
The "tuple.rkt"
file contains the use of define-named-remote-server
form, which defines a RPC server suitiable for invocation by
supervise-place-at
.
#lang racket/base
(require racket/match
racket/place/define-remote-server)
(define-named-remote-server tuple-server
(define-state h (make-hash))
(define-rpc (set k v)
(hash-set! h k v)
v)
(define-rpc (get k)
(hash-ref h k #f))
(define-cast (hello)
(printf "Hello from define-cast\n")
(flush-output)))
Figure 2: examples/named/tuple.rkt
The define-named-remote-server
form takes an identifier and a list of
custom expressions as its arguments. From the identifier a place-thunk
function is created by prepending the make-
prefix. In this case
make-tuple-server
. The make-tuple-server
identifier is the
place-function-name
given to the supervise-named-dynamic-place-at
form above. The define-state
custom form translates into a simple
define
form, which is closed over by the define-rpc
form.
The define-rpc
form is expanded into two parts. The first part is the
client stubs that call the rpc functions. The client function name is
formed by concatenating the define-named-remote-server
identifier,
tuple-server
, with the RPC function name set
to form
tuple-server-set
. The RPC client functions take a destination argument
which is a remote-connection%
descriptor and then the RPC function
arguments. The RPC client function sends the RPC function name, set
,
and the RPC arguments to the destination by calling an internal function
named-place-channel-put
. The RPC client then calls
named-place-channel-get
to wait for the RPC response.
The second expansion part of define-rpc
is the server implementation
of the RPC call. The server is implemented by a match expression inside
the make-tuple-server
function. The match clause for
tuple-server-set
matches on messages beginning with the 'set
symbol.
The server executes the RPC call with the communicated arguments and
sends the result back to the RPC client.
The define-cast
form is similar to the define-rpc
form except there
is no reply message from the server to client
(module tuple racket/base
(require racket/place
racket/match)
(define/provide
(tuple-server-set dest k v)
(named-place-channel-put dest (list 'set k v))
(named-place-channel-get dest))
(define/provide
(tuple-server-get dest k)
(named-place-channel-put dest (list 'get k))
(named-place-channel-get dest))
(define/provide
(tuple-server-hello dest)
(named-place-channel-put dest (list 'hello)))
(define/provide
(make-tuple-server ch)
(let ()
(define h (make-hash))
(let loop ()
(define msg (place-channel-get ch))
(define (log-to-parent-real
msg
#:severity (severity 'info))
(place-channel-put
ch
(log-message severity msg)))
(syntax-parameterize
((log-to-parent (make-rename-transformer
#'log-to-parent-real)))
(match
msg
((list (list 'set k v) src)
(define result (let () (hash-set! h k v) v))
(place-channel-put src result)
(loop))
((list (list 'get k) src)
(define result (let () (hash-ref h k #f)))
(place-channel-put src result)
(loop))
((list (list 'hello) src)
(define result
(let ()
(printf "Hello from define-cast\n")
(flush-output)))
(loop))))
loop))))
Figure 3: Expansion of define-named-remote-server