Skip to content

Commit

Permalink
document new sockets
Browse files Browse the repository at this point in the history
  • Loading branch information
Garrett Johnson committed Sep 22, 2012
1 parent 574a052 commit 23b783e
Showing 1 changed file with 168 additions and 35 deletions.
203 changes: 168 additions & 35 deletions Readme.md
Expand Up @@ -11,12 +11,17 @@
- message oriented
- automated reconnection
- light-weight wire protocol
- light-weight implementation (~300 SLOC)
- supports arbitrary binary message (msgpack, json, BLOBS, etc)
- supports JSON messages out of the box
- push / pull pattern
- pub / sub pattern
- event emitter pattern

## Patterns

- push / pull
- pub / sub
- emitter
- req / rep
- router
- dealer

## Push / Pull

Expand Down Expand Up @@ -47,7 +52,7 @@ sock.on('message', function(msg){
});
```

Both `PushSocket`s and `PullSocket`s may `.bind()` or `.connect()`. In the
Both `PushSocket`s and `PullSocket`s may `.bind()` or `.connect()`. In the
following configuration the push socket is bound and pull "workers" connect
to it to receive work:

Expand All @@ -60,7 +65,9 @@ to push results:

## Pub / Sub

`PubSocket`s send messages to all subscribers without queueing:
`PubSocket`s send messages to all subscribers without queueing. This is an
important difference when compared to a `PushSocket`, where the delivery of
messages will be queued during disconnects and sent again upon the next connection.

```js
var axon = require('axon')
Expand All @@ -74,7 +81,7 @@ setInterval(function(){
}, 500);
```

`SubSocket` provides selective reception of messages from a `PubSocket`:
`SubSocket` simply recieves any messages from a `PubSocket`:

```js
var axon = require('axon')
Expand All @@ -90,8 +97,8 @@ sock.on('message', function(msg){
## EmitterSocket

`EmitterSocket`'s send and receive messages behaving like regular node `EventEmitter`s.
This is achieved by using pub / sub sockets behind the scenes, automatically assigned
the "json" codec. Currently we simply define the `EmitterSocket` as a `PubSocket` if you `.bind()`, and `SubSocket` if you `.connect()`, providing the natural API you're used to:
This is achieved by using pub / sub sockets behind the scenes and automatically formatting
messsages with the "json" codec. Currently we simply define the `EmitterSocket` as a `PubSocket` if you `.bind()`, and `SubSocket` if you `.connect()`, providing the natural API you're used to:

server.js:

Expand Down Expand Up @@ -121,12 +128,143 @@ sock.on('login', function(user){
});
```

## Req / Rep

`ReqSocket`s send and recieve messages, queueing messages on remote disconnects. There
is no "lock step" involved, allowing messages sent later to recieve replies prior to
previously sent messages. `RepSocket`s reply to recieved messages, there is no concept of `send()`. Each
recieved message will have a `reply` callback, which will send the response back to the remote peer:

client.js
```js
var axon = require('../..')
, sock = axon.socket('req');

sock.connect(3000);

sock.on('message', function(msg){
console.log('got: %s', msg.toString());
});

setInterval(function(){
sock.send('ping');
}, 150);
```

server.js
```js

var axon = require('../..')
, sock = axon.socket('rep');

sock.bind(3000);

sock.on('message', function(msg, reply){
console.log('got: %s', msg.toString());
reply('pong');
});
```

## Router

`RouterSocket`s send a message to an "identified" peer using the socket's "identity"
(see `socket options`). Sent messages are not queued. The message sent leverages
multipart messages by framing the "identity" first, the delimiter second, and then
the actual message body.

__Note:__ This will probably change due to the awkwardness of handling your own delimeters.

client.js
```js
var axon = require('../..')
, sock = axon.socket('router');

sock.bind(3000);

sock.on('message', function(from, delim, msg){
console.log(msg.toString());
});

setInterval(function(){
sock.send('foo', '\u0000', 'hello foo');
sock.send('bar', '\u0000', 'hello bar');
}, 150);
```

server.js
```js
var axon = require('../..')
, foo = axon.socket('rep')
, bar = axon.socket('rep');

foo.set('identity', 'foo');
foo.connect(3000);

foo.on('message', function(msg, reply){
reply('foo: pong');
});

bar.set('identity', 'bar');
bar.connect(3000);

bar.on('message', function(msg, reply){
reply('bar says: pong');
});
```

## Dealer

`DealerSocket`s receive messages and round-robin sent messages. There is no
correlation between the two. They can be thought of as a `PushSocket` and `PullSocket`
combined. Here the dealer the serves as an "echo-service", sending whatever is recieves:

dealer.js
```js
var axon = require('../..')
, sock = axon.socket('dealer');

sock.set('identity', 'echo-service');
sock.connect(3000);

sock.on('message', function(msg){
sock.send(msg);
});
```

client.js
```js
var axon = require('../..')
, sock = axon.socket('router');

sock.bind(3000);

sock.on('message', function(from, msg){
console.log('%s said: %s', from.toString(), msg.toString());
});

setInterval(function(){
sock.send('echo-service', 'hey tobi');
}, 500);
```

## Socket Options

Every socket has associated options that can be configured via `get/set`.

- `identity` - The "name" of the socket that uniqued identifies it.
- `retry timeout` - The amount of time until retries will not be attempted again.

PubSockets additionaly have options for batching:

- `batch max` - Max amount of messags to buffer in memory.
- `batch ttl` - Amount of time to buffer messages before sending.

## Binding / Connecting

In addition to passing a portno, binding to INADDR_ANY by default, you
may also specify the hostname via `.bind(port, host)`, another alternative
is to specify the url much like zmq via `tcp://<hostname>:<portno>`, thus
the following are equivalent:
In addition to passing a portno, binding to INADDR_ANY by default, you
may also specify the hostname via `.bind(port, host)`, another alternative
is to specify the url much like zmq via `tcp://<hostname>:<portno>`, thus
the following are equivalent:

```
sock.bind(3000)
Expand All @@ -140,11 +278,11 @@ sock.connect('tcp://0.0.0.0:3000')

## Protocol

The wire protocol is simple and very much zeromq-like, where `<length>` is
a BE 24 bit unsigned integer representing a maximum length of roughly ~16mb. The `<meta>`
data byte is currently only used to store the codec, for example "json" is simply `1`,
in turn JSON messages received on the client end will then be automatically decoded for
you by selecting this same codec.
The wire protocol is simple and very much zeromq-like, where `<length>` is
a BE 24 bit unsigned integer representing a maximum length of roughly ~16mb. The `<meta>`
data byte is currently only used to store the codec, for example "json" is simply `1`,
in turn JSON messages received on the client end will then be automatically decoded for
you by selecting this same codec.

```
octet: 0 1 2 3 <length>
Expand All @@ -153,31 +291,31 @@ sock.connect('tcp://0.0.0.0:3000')
+------+------+------+------+------------------...
```

Thus 5 bytes is the smallest message axon supports at the moment. Later if
necessary we can use the meta to indicate a small message and ditch octet 2 and 3
allowing for 3 byte messages.
Thus 5 bytes is the smallest message axon supports at the moment. Later if
necessary we can use the meta to indicate a small message and ditch octet 2 and 3
allowing for 3 byte messages.

## Codecs

To define a codec simply invoke the `ss.codec.define()` method, for example
here is the JSON codec:
To define a codec simply invoke the `axon.codec.define()` method, for example
here is the JSON codec:

```js
var ss = require('axon');
var axon = require('axon');

ss.codec.define('json', {
axon.codec.define('json', {
encode: JSON.stringify,
decode: JSON.parse
});
```

__Note:__ codecs must be defined on both the sending and receiving ends, otherwise
axon cannot properly decode the messages. You may of course ignore this
feature all together and simply pass encoded data to `.send()`.
__Note:__ codecs must be defined on both the sending and receiving ends, otherwise
axon cannot properly decode the messages. You may of course ignore this
feature all together and simply paaxon encoded data to `.send()`.

## Performance

I haven't profiled or tuned anything yet but so far for on my macbook pro.
I haven't profiled or tuned anything yet but so far for on my macbook pro.

64 byte messages:

Expand Down Expand Up @@ -231,20 +369,15 @@ $ make test
- more tests
- code cov
- acks
- make socket options configurable
- clean up queue / sock
- emitter style on top of pubsub with multipart
- weighted fair queuing
- use mocha for tests
- multipart frames
- cap batch size
- zero-copy for batches...
- make batching configurable... disable for lower latency
- binary support for EmitterSocket (requires multipart)
- subscriptions
- ...

## License
## License

(The MIT License)

Expand Down

0 comments on commit 23b783e

Please sign in to comment.