Estou tentando medir a latência entre meu publicador Java e um corretor de mensagens Cpp (padrão do setor).
O broker registra o horário em que recebe cada mensagem e, para o publicador Java, estou usando o seguinte código para obter registros de data e hora com precisão de microssegundos (que são gravados na mensagem de saída):
private static final long NANOTIME_OFFSET;
static {
Instant instant = Instant.now(); // Get absolute time
long nanoTime = System.nanoTime(); // Get relative time
// Offset to convert from relative to absolute time
NANOTIME_OFFSET = TimeUnit.SECONDS.toNanos(instant.getEpochSecond()) + instant.getNano() - nanoTime;
}
public static long currentTimeNanos() {
return System.nanoTime() + NANOTIME_OFFSET;
}
No entanto, estou percebendo que a latência medida entre o publicador e o corretor aumenta ao longo do dia e não diminui até que eu reinicie meu publicador Java.
A latência começa em <<1 ms e, de repente, salta para 500 ms. Saltos subsequentes a aproximam da marca de 1 segundo, o que é difícil de acreditar, visto que ambos os processos residem na mesma máquina.
A criação de perfil com o VisualVM indica que não há problemas de recursos no meu processo Java, e a captura de pacotes com o Wireshark confirma que o problema está nos registros de data e hora produzidos por currentTimeNanos()
.
Então por que System.nanoTime()
perde precisão ao longo do dia?
Acho que a coisa correta a fazer é sempre usar Instant.now()
, mas é uma chamada mais pesada que System.nanoTime()
(gera um novo Instance
objeto toda vez), então adicionaria mais sobrecarga, especialmente com um alto volume de mensagens.
Editar: Eu também acrescentaria que optei por um nanoTime()
relógio baseado em -, já que Instance.now()
só produzia precisão de milissegundos na minha máquina
Nunca misture
System.nanoTime
comInstant.now
O código-fonte do OpenJDK mostra a
System.nanoTime
implementação como:Isso significa que o método é implementado como código nativo.
Sem nos aprofundarmos mais, sabemos que
nanoTime
não usa a mesma fonte queInstant.now
. AInstant
classe usa o relógio de data e hora do sistema operacional host. Em hardware de computador convencional (laptops, desktops, servidores comuns), o relógio de data e hora do sistema operacional host é resolvido em microssegundos, na melhor das hipóteses, e não em nanossegundos. Portanto, onanoTime
recurso deve usar outra fonte para rastrear nanossegundos decorridos.A CPU é a fonte provável. As CPUs modernas têm uma "pulsação" regular que as mantém funcionando, cronometrando seus cálculos. Por exemplo, uma CPU de 3 GHz tem três bilhões de pulsações por segundo. Imagino que o
nanoTime
código nativo utilize essa pulsação para contar os nanossegundos decorridos.A questão aqui é que essas duas fontes de tempo são completamente separadas e distintas.
Instant.now
salto, para frente ou para trás(!). O salto pode ser minúsculo ou grande, dependendo (a) de quanto tempo se passou desde a última correção do desvio e (b) da tendência de desvio do relógio do seu computador. (Outro problema é a precisão do seu servidor de horário.†)Você misturou essas duas fontes de tempo separadas com seu código:
A
Instant
classe é um alvo em movimento, sendo atualizada repetidamente a qualquer momento, avançando ou retrocedendo. Portanto, sua classeNANOTIME_OFFSET
é ingênua e inválida.nanoTime
diz o Javadoc: 👉🏽 "Este método só pode ser usado para medir o tempo decorrido e não está relacionado a nenhuma outra noção de tempo de sistema ou de relógio de parede."Para completar, mencionarei que
System.currentTimeMillis
(agora legado ) foi suplantado porInstant.now
. Ambos usam o relógio de data e hora do sistema operacional host. Nenhum deles deve ser misturado comSystem.nanoTime
.† Hoje em dia, você pode comprar um relógio atômico por muito dinheiro (embora talvez menos do que você espera). Um tópico recente no YouTube são servidores de tempo superprecisos e muito baratos, feitos por você mesmo, baseados na captura do momento atual transmitido por navegação por satélite ( GPS , etc.) e, em seguida, servidos por computadores baratos como o Raspberry Pi. Para explorar essa toca do coelho dos gênios do tempo, você pode começar com os canais de Jeff Geerling .