-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherlang-messaging.html
More file actions
167 lines (158 loc) · 8.89 KB
/
Copy patherlang-messaging.html
File metadata and controls
167 lines (158 loc) · 8.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
---
layout: base
project: remoc
title: "Erlang-style message passing in Rust: typed channels between machines"
description: >-
How to get distributed Erlang's programming model, sending messages to processes on
other machines as naturally as local ones, in Rust with typed channels. What Remoc
provides, and what OTP has that it does not.
social_description: >-
Message passing between machines as natural as between tasks, with messages that are
typed and channels that have backpressure.
canonical: /erlang-messaging.html
faq:
- q: "Is Remoc an actor framework?"
a: >-
No. Remoc is the communication layer: typed channels, remote functions and trait
calls between two Rust programs. Tasks reading from channels give you actor-style
patterns, but there is no actor runtime, no registry and no lifecycle management.
- q: "Does it have supervision trees like OTP?"
a: >-
Not ready-made. When a peer or connection fails, the error surfaces on the
affected channels and calls, and what to do about it is your code. The pattern of
running services as OS processes that a parent supervises and restarts over Remoc
provides the ingredients of a supervision tree, with the kernel enforcing the
isolation, but you assemble the restart logic yourself.
- q: "How do the messages differ from Erlang's?"
a: >-
They are typed and checked at compile time: a channel carries values of one Serde
type, so a message of the wrong shape is a compile error rather than a term that
sits in a mailbox until a receive clause fails to match. Channels are also
bounded and apply backpressure, where Erlang mailboxes grow without limit, which
is a classic failure mode of overloaded Erlang systems.
- q: "Can I form a cluster with a global process registry?"
a: >-
Not with Remoc alone. A Remoc connection is point-to-point between two peers, and
channels belong to their connection. Node membership, discovery and a global
registry are yours to build on top if you need them; distributed Erlang provides
them out of the box.
- q: "What about hot code upgrades?"
a: >-
Running code is not replaced in place. However, since Remoc serializes with the
Postbag codec, programs built from different versions of your message types keep
understanding each other, so endpoints can be upgraded and restarted one at a
time without stopping the system.
---
<header class="page-header">
{% include nav.html %}
<div class="wrap">
<h1>Erlang-style message passing in Rust</h1>
<p class="lead">
Erlang made sending a message to a process on another machine as natural as
sending one within the program, and few languages since have offered the same.
<a href="/">Remoc</a> brings this model to Rust, with channels that work
between machines and messages that are typed.
</p>
</div>
</header>
<main id="content">
<section class="section">
<div class="wrap narrow">
<h2>Location-transparent message passing</h2>
<p>
In Erlang, communication does not change shape when it crosses a machine
boundary. A process sends to a Pid, and whether the receiver lives in the
same VM or on another node is not visible in the code. Handing that Pid to a
third process is itself just a message, so communication paths form and
dissolve as the program runs.
</p>
<p>
In most ecosystems this is different. As soon as communication crosses the
machine boundary, what was a channel or a method call becomes a socket with
a wire format and an RPC framework, and the natural in-process style is
lost. Rust's actor and channel libraries largely stop at the process
boundary too.
</p>
</div>
</section>
<section class="section alt">
<div class="wrap narrow">
<h2>Typed channels between machines</h2>
<p>
Remoc's <a href="/remote-channels.html">channels work between processes and
machines</a>, and their endpoints are ordinary Serde values. Sending a
channel's sender inside a message is the equivalent of sending a Pid: the
receiver can now talk to whatever the sender belongs to, over the existing
connection, with nothing to register or look up. Tasks reading from channels
take the role of processes, and <a href="/server-push.html">either side can
push</a>.
</p>
<p>
Two things are deliberately different from Erlang. Messages are typed:
a channel carries one Serde type, the compiler checks what may be sent, and
a trait carrying <code>#[rtc::remote]</code> gives an interface more precise
than any receive clause. And channels are bounded: a slow receiver stalls
only its own sender instead of accumulating an unbounded mailbox.
</p>
<figure class="code-single">
<figcaption>Sending a sender, like sending a Pid</figcaption>
<pre><code><span class="k">use</span> remoc::prelude::*;
<span class="a">#[derive(Serialize, Deserialize)]</span>
<span class="k">struct</span> <span class="t">Subscribe</span> {
topic: <span class="t">String</span>,
<span class="c">// The subscriber's address, travelling inside the message.</span>
events_tx: rch::mpsc::<span class="t">Sender</span><<span class="t">Event</span>>,
}</code></pre>
</figure>
</div>
</section>
<section class="section">
<div class="wrap narrow">
<h2>Supervision, clustering and hot upgrades</h2>
<p>
Remoc is a communication library, and OTP is an architecture; the honest
comparison is between layers, not features. OTP additionally gives you
supervision trees with declared restart strategies, node clustering with a
global process registry, preemptive scheduling that isolates a runaway
process, and hot code upgrades of a running system. With Remoc, failures
surface as errors on channels and calls, and the recovery architecture is
yours to design.
</p>
<p>
A similar architecture can use a separate OS process for each service and a
parent process that supervises them over Remoc. The kernel then provides
preemptive scheduling and memory isolation even stronger than that of BEAM
processes. A crash becomes a child exit that the parent observes and answers
with a restart, and since
<a href="/postbag/">Postbag</a> keeps message types compatible across
versions, restarting a child with a newer binary takes the place of a hot
upgrade. This is the let-it-crash approach, with the operating system
providing the isolation. OTP ships this machinery ready-made; with Remoc
you assemble it yourself.
</p>
<p>
Rust provides compile-time checked messages, no garbage collector, single
static binaries and native-code performance in a general-purpose language.
Parts of a system that are not message passing, such as numerics, embedded
targets or a user interface, can remain in the same language. To keep the
connection itself alive across link failures and network changes,
<a href="/aggligator/">Aggligator</a> provides a resilient transport
underneath Remoc.
</p>
</div>
</section>
{% include faq.html alt=true %}
<section class="section">
<div class="wrap narrow">
<h2>Related pages</h2>
<p>
<a href="/remote-channels.html">Channels between processes and machines</a>
shows the model in working code, and the
<a href="/examples/channels.html">channels example</a> is complete and
runnable. For request/response-shaped services,
<a href="/comparison.html">Remoc vs. other RPC libraries</a> compares Remoc
with tarpc, gRPC and Cap'n Proto.
</p>
</div>
</section>
</main>