Comunicação entre processos com soquetes de domínio gRPC e Unix

Por James Newton-King

O .NET dá suporte à IPC (comunicação entre processos) usando gRPC. Para saber mais sobre como começar a usar o gRPC para se comunicar entre processos, confira Comunicação entre processos com gRPC.

Os UDS (soquetes de domínio do Unix) são um transporte IPC amplamente compatível que é mais eficiente do que o TCP quando o cliente e o servidor estão no mesmo computador. Este artigo discute como configurar a comunicação gRPC em UDS.

Pré-requisitos

Configuração de Servidor

Os soquetes de domínio Unix são compatíveis com Kestrel, que está configurado em Program.cs:

var socketPath = Path.Combine(Path.GetTempPath(), "socket.tmp");

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenUnixSocket(socketPath, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http2;
    });
});

O exemplo anterior:

Configuração do cliente

GrpcChannel dá suporte à realização de chamadas gRPC em transportes personalizados. Quando um canal é criado, ele pode ser configurado com um SocketsHttpHandler que tem um ConnectCallback personalizado. O retorno de chamada permite que o cliente faça conexões por meio de transportes personalizados e envie solicitações HTTP por esse transporte.

Observação

Alguns recursos de conectividade de GrpcChannel, como o balanceamento de carga do lado do cliente e o status do canal, não podem ser utilizados em conjunto com soquetes de domínio Unix.

Exemplo de fábrica de conexões de soquetes de domínio Unix:

public class UnixDomainSocketsConnectionFactory
{
    private readonly EndPoint endPoint;

    public UnixDomainSocketsConnectionFactory(EndPoint endPoint)
    {
        this.endPoint = endPoint;
    }

    public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
        CancellationToken cancellationToken = default)
    {
        var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);

        try
        {
            await socket.ConnectAsync(this.endPoint, cancellationToken).ConfigureAwait(false);
            return new NetworkStream(socket, true);
        }
        catch
        {
            socket.Dispose();
            throw;
        }
    }
}

Usar a fábrica de conexões personalizadas para criar um canal:

public static readonly string SocketPath = Path.Combine(Path.GetTempPath(), "socket.tmp");

public static GrpcChannel CreateChannel()
{
    var udsEndPoint = new UnixDomainSocketEndPoint(SocketPath);
    var connectionFactory = new UnixDomainSocketsConnectionFactory(udsEndPoint);
    var socketsHttpHandler = new SocketsHttpHandler
    {
        ConnectCallback = connectionFactory.ConnectAsync
    };

    return GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
    {
        HttpHandler = socketsHttpHandler
    });
}

Os canais criados usando o código anterior enviam chamadas gRPC por soquetes de domínio Unix.