net_kernel(3)
β¨ NAME β¨
net_kernel β the Erlang networking kernel, bestie π (it's a module in the Erlang manual, page net_kernel(3))
π«Ά DESCRIPTION π«Ά
okay so. The net kernel is a whole entire system process, and it's registered under the name net_kernel. It must be up and running for distributed Erlang to work at all β no net kernel, no distribution, periodt. What it does is implement parts of the BIFs spawn/4 and spawn_link/4, and it also handles monitoring of the network. She's the main character of the distribution story, fr fr π
An Erlang node gets started using the command-line flag -name or -sname:
$ erl -sname foobar
You can also just call net_kernel:start(foobar, #{}) straight from the normal Erlang shell prompt, like the iconique thing that it is:
1> net_kernel:start(foobar, #{name_domain => shortnames}).
{ok,<0.64.0>}
(foobar@gringotts)2>
If the node is started with the command-line flag -sname, the node name is foobar@Host, where Host is the short name of the host (not the fully qualified domain name). If it's started with the flag -name, the node name is foobar@Host, where Host is the fully qualified domain name. For more info, see erl.
Normally, connections just establish themselves automatically whenever another node gets referenced. If you wanna turn that off you can set the Kernel configuration parameter dist_auto_connect to never, see kernel(6). In that case, connections have to be established explicitly by calling connect_node/1.
Which nodes are allowed to talk to each other is handled by the magic cookie system, see the section Distributed Erlang in the Erlang Reference Manual. πͺβ¨
β οΈ WARNING: Starting a distributed node without also specifying -proto_dist inet_tls will expose the node to attacks that may give the attacker complete access to the node and, in extension, the cluster ππ This is NOT the vibe. When using un-secure distributed nodes, make sure that the network is configured to keep potential attackers out. See the Using SSL for Erlang Distribution User's Guide for details on how to set up a secure distributed node. Lock it down bestie π
π DATA TYPES π
connection_state() :: check_pending | pending | up | up_pending.
connection_type() :: normal | hidden.
π FUNCTIONS π
allow(Nodes) -> ok | error | ignored when Nodes :: [node()]
Permits access to the specified set of nodes. She's the bouncer at the club πͺ
Before the very first call to allow/1, any node with the correct cookie can connect. But the moment allow/1 gets called, a list of allowed nodes is established. Any access attempts made from (or to) nodes that are not in that list will be rejected. Sorry babe, not on the list π
Subsequent calls to allow/1 will add the specified nodes to the list of allowed nodes. It is not possible to remove nodes from the list. Once you're in, you're in β no takebacks π
Disallowing an already connected node will not cause it to be disconnected. It will, however, prevent any future reconnection attempts. So they can stay for the party but they're not getting back in later β
Passing Nodes as an empty list has never any affect at all. Like literally zero. Nothing happens.
Returns error if any element in Nodes is not an atom, and ignored if the local node is not alive.
allowed() -> {ok, Nodes} | ignored when Nodes :: [node()]
Returns a list of nodes that are explicitly allowed to connect to the node by calling allow/1. If an empty list comes back, that means any node using the same cookie will be able to connect. It's giving open door policy π¦
connect_node(Node) -> boolean() | ignored when Node :: node()
Establishes a connection to Node. Manifesting that link β¨
Returns true if a connection was established, or was already established, or if Node is the local node itself. Returns false if the connection attempt failed, and ignored if the local node is not alive.
get_net_ticktime() -> Res
when
Res ::
NetTicktime |
{ongoing_change_to, NetTicktime} |
ignored,
NetTicktime :: pos_integer().
Returns the currently used net tick time, in seconds β±οΈ
For more info, see the net_ticktime Kernel parameter.
Defined return values (Res):
NetTicktimeβnet_ticktimeisNetTicktimeseconds. Simple as that.{ongoing_change_to, NetTicktime}βnet_kernelis currently changingnet_ticktimetoNetTicktimeseconds. Mid-transition, don't rush her π§ignoredβ the local node is not alive.
get_state() -> #{started => no | static | dynamic, name => atom(), name_type => static | dynamic, name_domain => shortnames | longnames}
get_state() ->
#{started => no | static | dynamic,
name => atom(),
name_type => static | dynamic,
name_domain => shortnames | longnames}.
Get the current state of the distribution for the local node. A little vibe check on the whole setup π
Returns a map with (at least) the following key-value pairs:
started => Startedβ valid values forStarted:noβ the distribution is not started. In this state none of the other keys below are present in the map. Empty map energy.staticβ the distribution was started with the command line arguments-nameor-sname.dynamicβ the distribution was started withnet_kernel:start/1and can be stopped withnet_kernel:stop/0.
name => Nameβ the name of the node. Same as returned byerlang:node/0, except whenname_typeisdynamic, in which caseNamemay beundefined(instead ofnonode@nohost).name_type => NameTypeβ valid values forNameType:staticβ the node has a static node name set by the node itself.dynamicβ the distribution was started in dynamic node name mode, and will get its node name assigned from the first node it connects to. If keynamehas valueundefined, that has not happened yet. She's still waiting on her name to arrive π¦
name_domain => NameDomainβ valid values forNameDomain:shortnamesβ the distribution was started to use node names with a short host portion (not fully qualified).longnamesβ the distribution was started to use node names with a long fully qualified host portion.
getopts(Node, Options) -> {ok, OptionValues} | {error, Reason} | ignored
when
Node :: node(),
Options :: [inet:socket_getopt()],
OptionValues :: [inet:socket_setopt()],
Reason :: inet:posix() | noconnection.
Get one or more options for the distribution socket connected to Node π
If Node is a connected node, the return value is the same as from inet:getopts(Sock, Options) where Sock is the distribution socket for Node.
Returns ignored if the local node is not alive, or {error, noconnection} if Node is not connected.
monitor_nodes(Flag, Options) -> ok | Error
when
Flag :: boolean(),
Options :: OptionsList | OptionsMap,
OptionsList :: [ListOption],
ListOption ::
connection_id |
{node_type, NodeType} |
nodedown_reason,
OptionsMap ::
#{connection_id => boolean(),
node_type => NodeType,
nodedown_reason => boolean()},
NodeType :: visible | hidden | all,
Error :: error | {error, term()}.
The calling process subscribes or unsubscribes to node status change messages π A nodeup message is delivered to all subscribing processes when a new node is connected, and a nodedown message is delivered when a node is disconnected. It's giving notifications turned ON π
If Flag is true, a new subscription is started. If Flag is false, all previous subscriptions started with the same Options are stopped. Two option lists are considered the same if they contain the same set of options.
Delivery guarantees of nodeup/nodedown messages β these are lowkey sacred, do not skip:
nodeupmessages are delivered before delivery of any signals from the remote node through the newly established connection.nodedownmessages are delivered after all the signals from the remote node over the connection have been delivered.nodeupmessages are delivered after the corresponding node appears in results fromerlang:nodes().nodedownmessages are delivered after the corresponding node has disappeared in results fromerlang:nodes().- As of OTP 23.0, a
nodedownmessage for a connection being taken down will be delivered before anodeupmessage due to a new connection to the same node. Prior to OTP 23.0, this was not guaranteed to be the case. (old behaviour walked so new behaviour could run πβοΈ)
The format of the node status change messages depends on Options. If Options is the empty list, or if net_kernel:monitor_nodes/1 is called, the format is as follows:
{nodeup, Node} | {nodedown, Node}
Node = node()
When Options is the empty map or empty list, the caller will only subscribe for status change messages for visible nodes. That is, only nodes that appear in the result of erlang:nodes/0.
If Options equals anything other than the empty list, the format of the status change messages is as follows:
{nodeup, Node, Info} | {nodedown, Node, Info}
Node = node()
Info = #{Tag => Val} | [{Tag, Val}]
Info is either a map or a list of 2-tuples. Its content depends on Options. If Options is a map, Info will also be a map. If Options is a list, Info will also be a list. Matching energy, always π
When Options is a map, currently the following associations are allowed:
connection_id => boolean()β if the value of the association equalstrue, aconnection_id => ConnectionIdassociation will be included in theInfomap, whereConnectionIdis the connection identifier of the connection coming up or going down. For more info about this connection identifier, see the documentation oferlang:nodes/2.node_type => NodeTypeβ valid values forNodeType:visibleβ subscribe to node status change messages for visible nodes only. The associationnode_type => visiblewill be included in theInfomap.hiddenβ subscribe to node status change messages for hidden nodes only. The associationnode_type => hiddenwill be included in theInfomap.
allβ bestie, you want everything. Subscribe to node status change messages for visible and hidden nodes. The associationnode_type => visible | hiddenwill be included in theInfomap. π
If no node_type => NodeType association is included in the Options map, the caller will subscribe for status change messages for visible nodes only, but no node_type => visible association will be included in the Info map. (Yes, that's the sneaky default. Visible-only, and it doesn't even tell you. π)
nodedown_reason => boolean()β set this association's value totrueand anodedown_reason => Reasonassociation will be included in theInfomap fornodedownmessages. You deserve the lore, not just the headline. π
Reason can, depending on which distribution module or process that is used, be any term, but for the standard TCP distribution module it is one of the following:
connection_setup_failedβ the connection setup failed (afternodeupmessages were sent).no_networkβ no network is available.net_kernel_terminatedβ thenet_kernelprocess terminated.shutdownβ unspecified connection shutdown.connection_closedβ the connection was closed.disconnectβ the connection was disconnected (forced from the current node).net_tick_timeoutβ net tick time-out.send_net_tick_failedβ failed to send net tick over the connection.get_status_failedβ status information retrieval from thePortholding the connection failed.
When Options is a list, currently ListOption can be one of the following:
connection_idβ a{connection_id, ConnectionId}tuple will be included inInfowhereConnectionIdis the connection identifier of the connection coming up or going down. For more info about this connection identifier see the documentation oferlang:nodes/2. It's giving receipts, but make them official ones. π§Ύ{node_type, NodeType}β valid values forNodeType:visibleβ subscribe to node status change messages for visible nodes only. The tuple{node_type, visible}will be included in theInfolist.hiddenβ subscribe to node status change messages for hidden nodes only. The tuple{node_type, hidden}will be included in theInfolist.allβ subscribe to node status change messages for both visible and hidden nodes. The tuple{node_type, visible | hidden}will be included in theInfolist.
If no {node_type, NodeType} option has been given. The caller will subscribe for status change messages for visible nodes only, but no {node_type, visible} tuple will be included in the Info list.
nodedown_reasonβ the tuple{nodedown_reason, Reason}will be included in theInfolist fornodedownmessages. See the documentation of thenodedown_reason => boolean()association above for information about possibleReasonvalues. Yes, same energy, list edition. π
Example:
(a@localhost)1> net_kernel:monitor_nodes(true, #{connection_id=>true, node_type=>all, nodedown_reason=>true}).
ok
(a@localhost)2> flush().
Shell got {nodeup,b@localhost,
#{connection_id => 3067552,node_type => visible}}
Shell got {nodeup,c@localhost,
#{connection_id => 13892107,node_type => hidden}}
Shell got {nodedown,b@localhost,
#{connection_id => 3067552,node_type => visible,
nodedown_reason => connection_closed}}
Shell got {nodedown,c@localhost,
#{connection_id => 13892107,node_type => hidden,
nodedown_reason => net_tick_timeout}}
Shell got {nodeup,b@localhost,
#{connection_id => 3067553,node_type => visible}}
ok
(a@localhost)3>
β¨ set_net_ticktime(NetTicktime, TransitionPeriod) -> Res β¨
set_net_ticktime(NetTicktime, TransitionPeriod) -> Res
when
NetTicktime :: pos_integer(),
TransitionPeriod :: non_neg_integer(),
Res ::
unchanged | change_initiated |
{ongoing_change_to, NewNetTicktime},
NewNetTicktime :: pos_integer().
Sets net_ticktime (see kernel(6)) to NetTicktime seconds. TransitionPeriod defaults to 60. Slay, we're doing a whole coordinated vibe shift. π
Some definitions, bestie, write these down:
- Minimum transition traffic interval (MTTI) β
minimum(NetTicktime, PreviousNetTicktime)*1000 div 4milliseconds. - Transition period β the time of the least number of consecutive MTTIs to cover
TransitionPeriodseconds following the call toset_net_ticktime/2(that is,((TransitionPeriod*1000 - 1) div MTTI + 1)*MTTImilliseconds).
If NetTicktime < PreviousNetTicktime, the net_ticktime change is done at the end of the transition period; otherwise at the beginning. During the transition period, net_kernel ensures that there is outgoing traffic on all connections at least every MTTI millisecond. It's literally keeping the convo alive on every single line. π«Ά
NOTE: Thenet_ticktimechanges must be initiated on all nodes in the network (with the sameNetTicktime) before the end of any transition period on any node; otherwise connections can erroneously be disconnected. No cap, this one bites. Do it everywhere or do it nowhere. π
Returns one of the following:
unchangedβnet_ticktimealready has the value ofNetTicktimeand is left unchanged. Nothing to do, iconic behaviour honestly.change_initiatedβnet_kernelinitiated the change ofnet_ticktimetoNetTicktimeseconds.{ongoing_change_to, NewNetTicktime}β the request is ignored becausenet_kernelis busy changingnet_ticktimetoNewNetTicktimeseconds. It's in its main character era, please hold. β³
β¨ setopts(Node, Options) -> ok | {error, Reason} | ignored β¨
setopts(Node, Options) -> ok | {error, Reason} | ignored
when
Node :: node() | new,
Options :: [inet:socket_setopt()],
Reason :: inet:posix() | noconnection.
Set one or more options for distribution sockets. Argument Node can be either one node name or the atom new to affect the distribution sockets of all future connected nodes. Yes, new means "and also all my future friendships." π¦
The return value is the same as from inet:setopts/2 or {error, noconnection} if Node is not a connected node or new.
If Node is new the Options will then also be added to kernel configuration parameters inet_dist_listen_options and inet_dist_connect_options. Double-dipping, but make it configuration. π
Returns ignored if the local node is not alive. Ghost mode = request denied. π»
β¨ start(Options) -> {ok, pid()} | {error, Reason} β¨
start(Options) -> {ok, pid()} | {error, Reason}
when
Options :: [Name | NameDomain | TickTime, ...],
Name :: atom(),
NameDomain :: shortnames | longnames,
TickTime :: pos_integer(),
Reason :: {already_started, pid()} | term().
Deprecated: Use start/2 instead β she's retired but we still tell her story. π°οΈ
Turns a non-distributed node into a distributed node by starting net_kernel and other necessary processes. Glow-up time. β¨
Options list can only be exactly one of the following lists (order is important, so don't shuffle it like a playlist):
[Name]β the same asnet_kernel:start([Name, longnames, 15000]).[Name, NameDomain]β the same asnet_kernel:start([Name, NameDomain, 15000]).[Name, NameDomain, TickTime]β the same asnet_kernel:start(Name, #{name_domain => NameDomain, net_ticktime => ((TickTime*4-1) div 1000) + 1, net_tickintensity => 4}).Note thatTickTimeis not the same as net tick time expressed in milliseconds.TickTimeis the time between ticks when net tick intensity equals4.
β¨ start(Name, Options) -> {ok, pid()} | {error, Reason} β¨
start(Name, Options) -> {ok, pid()} | {error, Reason}
when
Options ::
#{name_domain => NameDomain,
net_ticktime => NetTickTime,
net_tickintensity => NetTickIntensity,
dist_listen => boolean(),
hidden => boolean()},
Name :: atom(),
NameDomain :: shortnames | longnames,
NetTickTime :: pos_integer(),
NetTickIntensity :: 4..1000,
Reason :: {already_started, pid()} | term().
Turns a non-distributed node into a distributed node by starting net_kernel and other necessary processes. The non-deprecated, thriving version. π
If Name is set to undefined the distribution will be started to request a dynamic node name from the first node it connects to. See Dynamic Node Name. Setting Name to undefined implies options dist_listen => false and hidden => true. Mystery era, but make it intentional. π
Currently supported options:
name_domain => NameDomainβ determines the host name part of the node name. IfNameDomainequalslongnames, fully qualified domain names will be used which also is the default. IfNameDomainequalsshortnames, only the short name of the host will be used. Full legal name or cute nickname, your call. πnet_ticktime => NetTickTimeβ net tick time to use in seconds. Defaults to the value of thenet_ticktimekernel(6)parameter. For more information about net tick time, see the kernel parameter. However, note that if the value of the kernel parameter is invalid, it will silently be replaced by a valid value, but if an invalidNetTickTimevalue is passed as option value to this function, the call will fail. So the kernel parameter gets a free pass and you don't. Unfair? Maybe. Real? Absolutely. πnet_tickintensity => NetTickIntensityβ net tick intensity to use. Defaults to the value of thenet_tickintensitykernel(6)parameter. For more information about net tick intensity, see the kernel parameter. However, note that if the value of the kernel parameter is invalid, it will silently be replaced by a valid value, but if an invalidNetTickIntensityvalue is passed as option value to this function, the call will fail. Same plot twist as above, different option. πdist_listen => boolean()β turn listening for incoming connections on or off. Defaults to whatever the-dist_listenerl command line argument says. Heads up bestie:dist_listen => falseimplieshidden => true, no takebacks π
And if undefined got passed as Name? Then the dist_listen option gets overridden with dist_listen => false. Periodt. π
hidden => boolean()β turn hidden node on/off. Defaults totrueif the-hiddenerl command line argument has been passed; otherwise it'sfalse.
β οΈ overridden alert β οΈ if undefined was passed as Name, or if the option dist_listen equals false, then the hidden option gets overridden with hidden => true. The vibes override your flags, sorry not sorry π
π« stop() π«
stop() -> ok | {error, Reason}
when Reason :: not_allowed | not_found.
Okay so this one's a whole glow-down on purpose β it turns a distributed node into a non-distributed node. π¦
For every other node out there in the network, this is literally the same as your node going down. Not "kind of similar", not "close enough" β the same. Nobody else can tell the difference. π
It is only possible when the net kernel was started usingstart/2. Try it any other way and you get{error, not_allowed}returned to you. Rude, but fair.
And if the local node isn't alive? You get {error, not_found}. Can't stop what isn't running, bestie π
That's the whole page, babes. Go forth and distribute responsibly π
This one came to you straight from Ericsson AB, rockin' kernel 11.0.3 β the net_kernel(3) era, and yes she ate πβ¨
π¦ where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms