Playing with making an Actor-like distributed system in REBOL

April 12th, 2009

For 3 weeks I was continuously working on the stuff that needs to be worked on. It was interesting, but yesterday I needed a small break. I decided to take some time for playing/exploring.

I wanted to see how something similar to an actor based system with message passing can be made at all. I was thinking of trying to make one in REBOL ever since I watched an interesting video named “Concurrency and Distributed Computing with Python Today” by Jesse Noller from this year’s PyCon.

Don’t compare it too much to Erlang’s actors. I tried to name them agents to avoid the comparison but then I couldn’t find the good name for word act so I renamed them back.

So what is my goal for now: Create a minimal system that runs some separated blocks of code that communicate to each other via uni-directional messages.

Inside one process, with groups

At first I created this system within one process, the code was really simple so I added some group functionality. An actor could say, send message to random actor of this kind. For example: send “parse-page X” to one of the actors of the “webpage-parsers” group. I created 2 types of actors, one wakes up and acts only when it has a message waiting in his mailbox, the other, the timer-actor, wakes up on specified intervals.

Here is the code of the test I used. It creates 2 timer actors dogs-owner, and dogs-trainer that send messages to the 2 actors of “dogs” group.

dogs-owner: make timer-actor [
	timer-act: [ send-msg/group 'hey "dogs" print "dog owner->hey!"]
	interval: 0:00:3
]

dogs-trainer: make timer-actor [
	timer-act: [ send-msg/group 'stick "dogs" print "dog trainer->stick!"]
	interval: 0:00:8
]

dog-actor: make actor [
	act-hey: copy []
	act-stick: copy []
	act: [ switch take mbox [
			hey [ do act-hey ]
			stick [ do act-stick ]
		]
	]
]

big-dog: make dog-actor [
	act-hey: [ print "big dog<-wof" ]
	act-stick: [ print "big dog<-meh sleeping.." ]
]

small-dog: make dog-actor [
	act-hey: [ print "small dog<-bewsk bewsk" ]
	act-stick: [ print "small dog<-run & jump.." ]
]

add-actor/group small-dog "dogs"
add-actor/group big-dog "dogs"
add-actor dogs-owner
add-actor dogs-trainer
start-actors

We get an output that looks something like this:

dog owner->hey!
big dog<-wof
dog owner->hey!
small dog<-bewsk bewsk
dog trainer->stick!
big dog<-meh sleeping..
dog owner->hey!
mutt dog<-weff
dog owner->hey!
...

full source code

Distributed actors via TCP

OK, so now the in-process actors work and we can upgrade message passing so that it will also work over TCP connection. With this, actors will be able to send messages to actors that live inside different process on the same or another computer. I decided to remove the groups functionality because it was premature optimization feature. To test this we have two separate processes and so two scripts.

Cats process, runs one actor (cat-boss) that acts on 2 different messages.

print "Actor-net example (cats)"

do %actor-net-lib_v1.r
*my-port*: 5602

cat-boss: make actor [
	act: [ msg: take mbox
		switch first msg [
			still-seeking [ print "cat boss: our cat agent is still seeking" ]
			found-fish [ print "cat boss: yeey, we found a fish!!" ]
		]
	]
]

add-local-actor cat-boss

start-remote-listener
start-actors

Dogs process, consists of 3 actors. The cat-agent actor sends messages to cat-boss in the other process. His send message code is no different than when sending it in the same process, only the actor address is different.

print "Actor-net example (dogs)"

do %actor-net-lib_v1.r
*my-port*: 5601

dog-boss: make timer-actor [
	timer-act: [
		send-msg local-dog-addr [ watch-for-cats ]
		print "dog boss -> local dog: 'Watch for cats!'"
	]
	interval: 0:00:7
]

local-dog: make actor [
	act: [ msg: take mbox
		switch first msg [
			watch-for-cats [ print "local dog: I am! wof!" ]
		]
	]
]

cat-agent: make timer-actor [
	timer-act: [
		send-msg cat-boss-addr either equal? 1 random 3 [
				print "cat agent -> remote boss: 'found a fish'"
				[ found-fish ]
			] [
				print "cat agent -> remote boss: 'still seeking'"
				[ still-seeking ]
			]
	]
	interval: 0:00:6
]

cat-boss-addr: make actor-addr! [ port: 5602 id: 1 ]
local-dog-addr: make actor-addr! [ port: 5601 id: 2 ]

add-local-actor dog-boss
add-local-actor local-dog
add-local-actor cat-agent

start-remote-listener
start-actors

full source code (zip)

The code to make this work is really simple and only a little above 100loc, so you should have no big problems understanding and tweaking it. It uses async. tcp networking via async-protocol.r library . When we run both script we get something like this:

cats script:

Actor-net example (cats)
cat boss: our cat agent is still seeking
cat boss: yeey, we found a fish!!
cat boss: our cat agent is still seeking
cat boss: our cat agent is still seeking
...

dogs script:

Actor-net example (dogs)
cat agent -> remote boss: 'still seeking'
dog boss -> local dog: 'Watch for cats!'
local dog: I am! wof!
cat agent -> remote boss: 'found a fish'
dog boss -> local dog: 'Watch for cats!'
local dog: I am! wof!
cat agent -> remote boss: 'still seeking'
dog boss -> local dog: 'Watch for cats!'
local dog: I am! wof!
cat agent -> remote boss: 'still seeking'
dog boss -> local dog: 'Watch for cats!'
local dog: I am! wof!
...

What to improve?

Hm.. what not :) . No really, this is just some playing around. There is tons of things that I even don’t know how I want to look or function. Few that pop in my head right now:

  • elegant process addressing, passing process addresses around, the sender address?
  • more elegant switching on received message, Erlang has native pattern matching for example
  • rethink how actors are built (they are rebol objects now)
  • rethink how state in actors is handled, for example an actor might have a persistent connection to database
  • … tons more …

My goal is not to mimic Erlang actors exactly, but to see if I can build a nice and practical system for distributed systems with this.

I have made up from my head up to now, and I never want to over think stuff in advance, because concrete situations later show the real answers. I will try to build a real app for my real needs and I think this will give me many answers to open questions. I will soon need to make a network of bots for Site Assistant that are just “ocasionally online” and that provide some service to the main app server. I think this should be a nice little challenge to push this forward.

Reblog this post [with Zemanta]
Read and let read :)
  • del.icio.us
  • Reddit
  • Digg
  • DZone
  • email
  • Facebook
  • HackerNews
  • Twitter
  • StumbleUpon

2 Responses to “Playing with making an Actor-like distributed system in REBOL”

  1. Janko in a Jar » Blog Archive » Playing with making an Actor-like distributed system in REBOL #2 Says:

    [...] sitting on my disc for weeks, but didn’t find time to post it. There are few advances from #1, but it’s still focused on just playing, trying out stuff. I am also posting this because [...]

  2. Janko in a Jar » Blog Archive » Playing with making an Actor-like distributed system in REBOL #3 Says:

    [...] library source code. See the previous two posts to get more info about what is what and why: #1 , #2 [...]

Leave a Reply