<commit_before>---
version: 1.0.0
title: OTP 分布式
---

## 分布式简介

我们可以把我们的 Elixir 应用运行分布在单主机，或者多主机的不同的节点上。Elixir 允许这些不同的节点通过以下课程中列出来的不同的机制来相互通信。

{% include toc.html %}

## 节点间通信

Elixir 运行在 Erlang 虚拟机之上，也就意味着它拥有了 Erlang 强大的[分布式特性。](http://erlang.org/doc/reference_manual/distributed.html)

> 一个分布式 Erlang 系统由相互沟通的 Erlang 运行环境组成。每个这样的环境称之为一个节点。

一个节点就是赋予了名字的 Erlang 运行环境。我们可以通过打开 `iex` 会话，并设置名称的方式来启动一个节点：

```bash
iex --sname alex@localhost
iex(alex@localhost)>
```

然后我们可以在另一个命令行窗口启动另一个节点：

```bash
iex --sname kate@localhost
iex(kate@localhost)>
```

这两个节点可以通过 `Node.spawn_link/1` 来相互发送消息。

### 通过 `Node.spawn_link/1` 来通信

这个函数需要接收两个参数：

* 需要连接的节点名称  
* 需要远程节点运行的函数  

它会和远程节点建立连接，然后在对方那运行指定的函数，然后返回关联进程的 PID。

让我们先定义一个模块，`Kate`，在 `kate` 节点上，并懂得如何介绍 Kate 这个人：

```elixir
iex(kate@localhost)> defmodule Kate do
...(kate@localhost)>   def say_name do
...(kate@localhost)>     IO.puts "Hi, my name is Kate"
...(kate@localhost)>   end
...(kate@localhost)> end
```

#### 发送消息

现在，我们就可以使用 [`Node.spawn_link/2`](https://hexdocs.pm/elixir/Node.html#spawn_link/2) 来让 `alex` 节点要求 `kate` 节点运行 `say_name/0` 这个函数：

```elixir
iex(alex@localhost)> Node.spawn_link(:kate@localhost, fn -> Kate.say_name end)
Hi, my name is Kate
#PID<10507.132.0>
```

#### 关于 I/O 和节点要注意的地方

注意到，尽管 `Kate.say_name/0` 是在远程节点调用的，但确实本地，或者说调用节点这里接收到 `IO.puts` 的输出。

这是因为本地节点是 **群组领导节点（group leader）**。Erlang 虚拟机通过进程来管理 I/O。这就使得我们可以在分布式的节点间执行 I/O 操作，比如 `IO.puts`。这些分布式的进程是通过群组领导节点的 I/O 进程来管理的。群组领导节点总是发起进程的那个节点。

因为 `alex` 节点是我们调用 `spawn_link/2` 函数的节点，所以它就是群组领导节点，`IO.puts` 的返回也会指向这个节点的标准输出流上。

#### 回应消息

如果我们希望接收到消息的节点发送某些 **回应** 到发送方？我们可以简单的使用 `receive/1` 和 [`send/2`](https://hexdocs.pm/elixir/Process.html#send/3) 来实现这一点。

我们可以让 `alex` 节点建立一个通道到 `kate` 节点，并指定 `kate` 节点运行某个匿名函数。这个匿名函数会监听是否收到某个描述了特定消息和 `alex` 节点 PID 的元组。如果收到了这个元组消息，它就会通过相应的 PID 回应一条消息到 `alex` 节点上：
<commit_msg>[zh-hans] Align otp-distribution version (#1843)<commit_after>---
version: 1.0.1
title: OTP 分布式
---

## 分布式简介

我们可以把我们的 Elixir 应用运行分布在单主机，或者多主机的不同的节点上。Elixir 允许这些不同的节点通过以下课程中列出来的不同的机制来相互通信。

{% include toc.html %}

## 节点间通信

Elixir 运行在 Erlang 虚拟机之上，也就意味着它拥有了 Erlang 强大的[分布式特性。](http://erlang.org/doc/reference_manual/distributed.html)

> 一个分布式 Erlang 系统由相互沟通的 Erlang 运行环境组成。每个这样的环境称之为一个节点。

一个节点就是赋予了名字的 Erlang 运行环境。我们可以通过打开 `iex` 会话，并设置名称的方式来启动一个节点：

```bash
iex --sname alex@localhost
iex(alex@localhost)>
```

然后我们可以在另一个命令行窗口启动另一个节点：

```bash
iex --sname kate@localhost
iex(kate@localhost)>
```

这两个节点可以通过 `Node.spawn_link/2` 来相互发送消息。

### 通过 `Node.spawn_link/2` 来通信

这个函数需要接收两个参数：

* 需要连接的节点名称  
* 需要远程节点运行的函数  

它会和远程节点建立连接，然后在对方那运行指定的函数，然后返回关联进程的 PID。

让我们先定义一个模块，`Kate`，在 `kate` 节点上，并懂得如何介绍 Kate 这个人：

```elixir
iex(kate@localhost)> defmodule Kate do
...(kate@localhost)>   def say_name do
...(kate@localhost)>     IO.puts "Hi, my name is Kate"
...(kate@localhost)>   end
...(kate@localhost)> end
```

#### 发送消息

现在，我们就可以使用 [`Node.spawn_link/2`](https://hexdocs.pm/elixir/Node.html#spawn_link/2) 来让 `alex` 节点要求 `kate` 节点运行 `say_name/0` 这个函数：

```elixir
iex(alex@localhost)> Node.spawn_link(:kate@localhost, fn -> Kate.say_name end)
Hi, my name is Kate
#PID<10507.132.0>
```

#### 关于 I/O 和节点要注意的地方

注意到，尽管 `Kate.say_name/0` 是在远程节点调用的，但确实本地，或者说调用节点这里接收到 `IO.puts` 的输出。

这是因为本地节点是 **群组领导节点（group leader）**。Erlang 虚拟机通过进程来管理 I/O。这就使得我们可以在分布式的节点间执行 I/O 操作，比如 `IO.puts`。这些分布式的进程是通过群组领导节点的 I/O 进程来管理的。群组领导节点总是发起进程的那个节点。

因为 `alex` 节点是我们调用 `spawn_link/2` 函数的节点，所以它就是群组领导节点，`IO.puts` 的返回也会指向这个节点的标准输出流上。

#### 回应消息

如果我们希望接收到消息的节点发送某些 **回应** 到发送方？我们可以简单的使用 `receive/1` 和 [`send/3`](https://hexdocs.pm/elixir/Process.html#send/3) 来实现这一点。

我们可以让 `alex` 节点建立一个通道到 `kate` 节点，并指定 `kate` 节点运行某个匿名函数。这个匿名函数会监听是否收到某个描述了特定消息和 `alex` 节点 PID 的元组。如果收到了这个元组消息，它就会通过相应的 PID 回应一条消息到 `alex` 节点上：

{'fraction_non_alphanumeric': 0.15560333243751678, 'fraction_numerical': 0.012899758129535071, 'mean_word_length': 5.730560578661844, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 25, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4814151', 'n_tokens_mistral': 2533, 'n_tokens_neox': 2734, 'n_words': 296}