Desactive el algoritmo de Nagle usando OkHttp o Scarlet | Roger Iyengar | Octubre de 2020

TCP incluye una función llamada algoritmo de Nagle que retrasa el envío de mensajes pequeños con la esperanza de combinarlos con mensajes futuros. Esto puede ahorrar ancho de banda porque TCP debe incluir información adicional con cada segmento. La combinación de varios mensajes pequeños en un segmento reduce el ancho de banda total utilizado. Sin embargo, esto aumenta el tiempo que tardará en recibirse algunos mensajes pequeños.

Si quieres que todos tus mensajes sean segundoSi envió con la latencia más baja posible y está dispuesto a usar más ancho de banda, debe desactivar el algoritmo de Nagle. Las conexiones WebSocket creadas con OkHttp tienen el algoritmo Nagle habilitado de forma predeterminada. Ella estaba ahí problema al respecto en 2013 y la solución fue agregar socketFactory y sslSocketFactory opciones.

Para deshabilitar el algoritmo de Nagle usando estas opciones, debe crear clases que extiendan SocketFactory y SSLSocketFactory y llamar a setTcpNoDelay (true) en sus sockets subyacentes. Puedes usar las siguientes clases que creé y que usan Ingredientes:

SocketFactory

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

import javax.net.SocketFactory;

public class SocketFactoryTcpNoDelay extends SocketFactory {
private final SocketFactory socketFactory;

public SocketFactoryTcpNoDelay() {
socketFactory = SocketFactory.getDefault();
}

@Override
public Socket createSocket() throws IOException {
Socket socket = socketFactory.createSocket();
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(String host, int port) throws IOException {
Socket socket = socketFactory.createSocket(host, port);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws
IOException {
Socket socket = socketFactory.createSocket(host, port, localHost, localPort);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Socket socket = socketFactory.createSocket(host, port);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort) throws IOException {
Socket socket = socketFactory.createSocket(address, port, localAddress, localPort);
socket.setTcpNoDelay(true);
return socket;
}
}

SSLSocketFactory

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class SSLSocketFactoryTcpNoDelay extends SSLSocketFactory {
private final X509TrustManager trustManager;
private final SSLSocketFactory sslSocketFactory;

public SSLSocketFactoryTcpNoDelay() throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException {
// From
// https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/-builder/ssl-socket-factory
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
trustManager = (X509TrustManager) trustManagers[0];

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
sslSocketFactory = sslContext.getSocketFactory();
}

public X509TrustManager getTrustManager() {
return trustManager;
}

public SSLSocketFactory getSslSocketFactory() {
return sslSocketFactory;
}

@Override
public String[] getDefaultCipherSuites() {
return sslSocketFactory.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
return sslSocketFactory.getSupportedCipherSuites();
}

@Override
public Socket createSocket() throws IOException {
Socket socket = sslSocketFactory.createSocket();
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws
IOException {
Socket socket = sslSocketFactory.createSocket(s, host, port, autoClose);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(String host, int port) throws IOException {
Socket socket = sslSocketFactory.createSocket(host, port);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws
IOException {
Socket socket = sslSocketFactory.createSocket(host, port, localHost, localPort);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Socket socket = sslSocketFactory.createSocket(host, port);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort) throws IOException {
Socket socket = sslSocketFactory.createSocket(address, port, localAddress, localPort);
socket.setTcpNoDelay(true);
return socket;
}
}

Vamos a armarlo

Puede crear un OkHttpClient que use estas clases de la siguiente manera:

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
try {
SSLSocketFactoryTcpNoDelay sSLSocketFactoryTcpNoDelay =
new SSLSocketFactoryTcpNoDelay();
okHttpClientBuilder.sslSocketFactory(sSLSocketFactoryTcpNoDelay.getSslSocketFactory(),
sSLSocketFactoryTcpNoDelay.getTrustManager());
} catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
// Handle Exception
}
SocketFactoryTcpNoDelay socketFactoryTcpNoDelay = new SocketFactoryTcpNoDelay();
okHttpClientBuilder.socketFactory(socketFactoryTcpNoDelay);
OkHttpClient okHttpClient = okHttpClientBuilder.build();

La conexión WebSocket que crea su OkHttpClient tendrá el algoritmo de Nagle deshabilitado.

Scarlet es una biblioteca de Tinder que usa OkHttp para conectarse a WebSocket. Si pasa OkHttpClient, que creó anteriormente, OkHttpClientUtils # newWebSocketFactory, todas las conexiones WebSocket creadas con Scarlet tendrán el algoritmo de Nagle deshabilitado.

Tenga en cuenta que todas las conexiones Scarlet WebSocket utilizan OkHttp. En el momento de escribir este artículo, Scarlet todavía usa OkHttp versión 3.11. OkHttp 4 ha estado disponible durante más de un año. Si está ejecutando un nuevo proyecto, considere usar OkHttp directamente, no Scarlet.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *