Node.jsでもUNIXドメインソケットを使いたい


はじめに

Node.jsで使うことのできる通信方式としては、TCPソケット、ストリーミングIPCエンドポイント、そしてHTTPインターフェースです。HTTPインターフェースに関しては、実際にはTCPソケットとして動いているので、実質前の2つになります。
では、TCPソケットとストリーミングIPCエンドポイントの違いは何でしょうか?

TCPソケットは、TCP/IPの上でソケットを用いて行う通信方式です。
一方のストリーミングIPCエンドポイントは、IPC(Inter-Process Communication)の下、同一マシーン内でプロセス間通信をストリーミングで行う通信方式です。

このストリーミングIPCエンドポイントをUNIX上で実現されている技術が、『 Unix domain sockets 』です。

UNIXドメインソケットとは?

ローカルで開かれたソケットファイルを通じて、サーバー側とクライアント側とで通信を行う方法です。
Node.jsでは適用されていませんが、一般的には unix:// でURLが始まります。

TCPで行う通信と異なり、ローカルファイルを指定して通信を行うため、ドメインの解決や外部通信をしません。
そのため、TCPに比べ速いです。
その検証に関しては、 Performance Analysis of Various Mechanisms for Inter-process Communication をご覧ください。


使ってみよう!

この記事のサンプルプログラムは unix-domain-socket にあります。

UNIXドメインソケットは、Node.jsのビルドインモジュールの net の中で提供されています。
そのため、この記事では net を主に用いてプログラムを書いていきます。
Net | Node.js

送信側

以下のようなプログラムを作成しました。

import net from 'net';

// UNIXドメインソケットのコネクションを作成する
// net.createConnectionの引数にファイルを指定するとUNIXドメインソケットで繋がる
const client = net.createConnection('/tmp/unix.sock');
client.on('connect', () => {
  console.log('connected.');
});
client.on('data', (data) => {
  console.log(data.toString());
});
client.on('end', () => {
  console.log('disconnected.');
});
client.on('error', (err) => {
  console.error(err.message);
});
client.write('hello');

受信側

以下のようなプログラムを作成しました。

import net from 'net';
import fs from 'fs';

// サーバーを設定
const server = net.createServer((connection) => {
  console.log('connected.');
  connection.on('close', () => {
    console.log('disconnected.');
  });
  connection.on('data', (data) => {
    console.log(data.toString());
  });
  connection.on('error', (err) => {
    console.error(err.message);
  });
  connection.write('unix domain socket');
  connection.end();
});

// ソケットファイルを削除(存在するとlistenできない)
try {
  fs.unlinkSync('/tmp/unix.sock');
} catch (error) {}

// UNIXドメインソケットでlistenする
server.listen('/tmp/unix.sock');

実行してみる

ともに同じ仮想サーバー内で実行しています。
左側は送信側で、右側は受信側です。

おわりに

このUNIXドメインソケットは、ローカルマシーン内でプロセス間で通信を行う場合に効果を発揮します。
例えば、KubernetesのPod内のコンテナ間で通信を行う場合に使うことができます。

ボリュームを共有してマウントすることで通信ができるので、ドメインの名前解決に縛られることが無くなります。
ポートで通信を行う場合と比べて制限を受けない部分もあるので、一度使ってみてはどうですか?