JEP 380 Unix Domain Socket Channels
Contexte
Les sockets Unix sont un moyen de communication inter-processus. Ils se basent sur le système de fichiers pour leurs fonctionnements. (Et oui, tout est fichier dans l’univers Unix). Plusieurs processus peuvent ainsi communiquer ensemble sans passer par la pile réseau. Cela est pris en charge par le noyau et c’est plus performant, même si on le compare avec l’utilisation de l’interface locale.
Nous comparons avec le réseau local car la limitation est que les processus doivent être sur la même machine.
Cependant, dans le monde des conteneurs, beaucoup de processus tournent localement sur la machine. Moyennant l’accès à un système de fichiers partagés, ils pourraient ainsi utiliser ce type de socket afin d' améliorer les performances.
De plus, étant un fichier, cela permet de gérer les droits avec les permissions Unix comme n’importe quel fichier.
Pour information, il existe déjà des librairies afin de réaliser cette opération. Par exemple, le projet junixsocket, mais c’est une librairie utilisant JNI.
L’objectif est de pouvoir en bénéfier avec un simple JDK sans dépendance.
Windows 10 et Windows Server 2019 supportent aussi les sockets Unix.
Illustration
Pour illuster l’usage de ce type de socket, nous allons prendre l’exemple de PostgreSQL.
Sur une base de données PostgreSQL, nous pouvons se connecter localement au serveur de base de données sans utiliser le réseau. Par exemple, si nous utilisons la commande psql -l
pour lister les bases présentes. Nous pouvons obtenir par exemple le mesage suivant :
> psql -l
psql: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
Si maintenant, nous prenons un utilisateur qui a le droit sur le fichier, cela va fonctionner.
> psql -l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-------------+--------------+----------+---------+-------+-----------------------
postgres | postgres | UTF8 | C | C |
template0 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
De même, si l’emplacement du fichier n’est pas celui par défaut, nous pouvons utiliser la variable PGHOST
pour définir le chemin.
Il faudra toujours que l’utilisateur possède les droits sur le fichier.
export PGHOST=/var/run/postgresql/srv
Cela va permettre d’accorder des privilièges particuliers si nous sommes connectés à la machine en SSH. Ainsi, nous allons pouvoir au contraire réduire les accès au minimum côté réseau.
Socket Channel (classique)
Revenons à Java et notamment au code associé à cette évolution. Mais avant, pour mieux comprendre la différence, regardons le code pour la création d’une socket classique, nous avons le code suivant coté serveur :
ServerSocketChannel ssc = ServerSocketChannel.open(/*StandardProtocolFamily.INET*/);
ssc.bind(new InetSocketAddress("127.0.0.1", 8085));
while ( true ) {
SocketChannel socketChannel = ssc.accept();
// Utilisation du SocketChannel sc
...
}
Coté client, nous avons le code suivant :
SocketChannel sc = SocketChannel.open(/*StandardProtocolFamily.INET*/);
sc.connect(new InetSocketAddress("127.0.0.1", 8085));
// Utilisation du SocketChannel sc
...
La méthode SocketChannel.open() et ServerSocketChannel peuvent prendre un paramètre : ProtocolFamily
. Avant le JDK 16, nous avons le choix entre StandardProtocolFamily.INET
et StandardProtocolFamily.INET6
en fonction si nous souhaitons utiliser IPv4 ou IPv6. Sans argument, la JVM choisit le protocole en fonction de la présence ou non du support d’IPv6
Nous pouvons utiliser :
-
L’api Stream (
InputStream
/OutputStream
) de la socket avec la méthodeSocketChannel.getSocket()`
. -
L’api
ByteByffer
Socket Channel (unix domain)
Coté socket Unix, cela n’est pas plus compliqué. Nous avons une nouvelle famille de protocole StandardProtocolFamily.UNIX
et nous avons une classe qui représente l’adresse correspondante, soit un simple fichier.
ServerSocketChannel ssc = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
ssc.bind(UnixDomainSocketAddress.of("/tmp/mon-sock"));
while ( true ) {
SocketChannel sc = ssc.accept();
// Utilisation du SocketChannel sc
...
}
Coté client, nous avons le code suivant :
SocketChannel sc = SocketChannel.open(StandardProtocolFamily.UNIX);
sc.connect(UnixDomainSocketAddress.of("/tmp/mon-sock"));
// Utilisation du SocketChannel sc
...
Seul, l’API ByteBuffer
est disponible.
La méthode SocketChannel.getSocket() n’est pas implémentée.
Moteur de recherche
"Eduquer, ce n'est pas remplir des vases mais c'est d'allumer des feux." - Michel Montaigne
Billets récents
- Eclipse plante systématiquement sous Debian (et autres distribution Linux)
- JEP 463, Implicitly Declared Classes and Instance Main Methods (Second Preview)
- Debian - Montée de version de Debian 11 (Bullseye) à Debian 12 (Bookworm)
- JEP 451, Prepare to Disallow the Dynamic Loading of Agents
- JEP 444, Virtual Threads