Servir
Um servidor guarda uma série e responde a pedidos pequenos sobre ela: a
sua descrição, tiles para um gráfico no navegador e imagens. O
protocolo é HTTP GET simples, então qualquer cliente, cache ou proxy o
entende. O nonius-server é o servidor de referência, um programa que
você instala pelo Cargo:
cargo install nonius-server
Este capítulo serve as leituras de Seus dados: um ponto por milissegundo durante 100 segundos, num CSV.
De um CSV a um servidor no ar
nonius-server csv readings.csv readings.nsb
nonius-server build readings.nsb readings.pyr.nsb --base-ns 1000000 --levels 4
nonius-server verify readings.pyr.nsb
A primeira linha escreve o arquivo de série. A segunda constrói um arquivo de pirâmide: a série e a sua pirâmide, guardadas como os tiles do protocolo, então um tile é lido direto dele. A terceira confere os hashes e constrói a pirâmide de novo para comparar. Cada uma diz o que fez:
readings.nsb: 100000 points from readings.csv; 2 decimal places, the most any value has; in time order
readings.pyr.nsb: 100000 points, 3 levels kept from level 1, 32810 buckets; 4.5 MB, 45.0 B a point
Os pontos estão a um milissegundo de distância, então a base é um milissegundo. O nível 0 teria um ponto por bucket, e foi liberado (veja O que custa): três níveis ficam.
Um servidor também pode servir o arquivo de série simples, construindo a pirâmide na memória ao iniciar. Um arquivo de pirâmide é construído uma vez e depois mapeado: o servidor inicia na hora, seja qual for o tamanho, e só as partes que um pedido lê vão para a memória.
nonius-server readings.pyr.nsb --id readings --addr 127.0.0.1:8080 &
O & o deixa rodando em segundo plano, para os comandos abaixo. O id
de uma série é o nome do arquivo, a menos que --id dê outro; vários
arquivos servem várias séries.
Perguntar a ele
A descrição da série, os seus metadados:
curl -s http://127.0.0.1:8080/v1/series/readings
{"protocol":1,"series":"readings","base_ns":"1000000","levels":4,"ratio":4,"tile_columns":2048,"raw_tile_ns":"4096000000","max_k":4096,"v_exponent":2,"rows":"100000","first_t":"1760000000000000000","last_t":"1760000099999000004","last":{"t":"1760000099999000004","v":"10099","w":"1"}}
Os inteiros de 64 bits são strings, porque números JSON perdem precisão
depois de 2⁵³. O attachTiles do navegador lê isso primeiro, depois
pede tiles: /v1/series/{id}/columns/{level}/{k}/{i} para resumos e
/v1/series/{id}/raw/{i} para pontos. Os endereços e o conteúdo deles
são as seções 4 e 5 do
protocolo;
o gráfico os calcula sozinho, e você nunca escreve um à mão.
Uma imagem dos 100 segundos inteiros, clara, com título e unidade:
image='http://127.0.0.1:8080/v1/series/readings/png?from=1760000000000000000&to=1760000100000000000&width=1600&height=600&ratio=2&theme=light&title=Readings&unit=%C2%B0C'
curl -s -o readings.png "$image"
from e to são o tempo mostrado, [from, to): o to não entra,
então a série inteira termina no tempo do último ponto mais um, ou
depois. width e height são os pixels do próprio PNG; ratio=2 desenha
rótulos e linhas para uma tela de dois pixels do dispositivo por pixel
CSS, então essa imagem preenche uma caixa de 800 × 300 numa tela assim.
A unidade °C vai codificada com porcentagem, como o URLSearchParams
do navegador escreve. O tema é escuro, a menos que venha
theme=light. Todo parâmetro está na página do
nonius-server; um que o
servidor não conhece é ignorado, então um erro de digitação
(timezone= em vez de tz=) volta em silêncio ao padrão.
Cache
Um tile ou uma imagem nunca muda: o mesmo endereço sempre responde os
mesmos bytes. O servidor diz isso — Cache-Control: public, max-age=31536000, immutable — e dá a cada resposta um ETag, um hash
dos seus bytes. Um cliente que já a tem pergunta com a etiqueta e
recebe 304 Not Modified, sem corpo:
etag=$(curl -s -o /dev/null -w '%header{etag}' "$image")
curl -s -o /dev/null -w '%{http_code}\n' -H "If-None-Match: $etag" "$image"
Isso imprime 304. Então um navegador, um proxy ou uma CDN podem
guardar tiles e imagens pelo tempo que quiserem. Os metadados são a
exceção: dizem no-cache, porque uma série que cresce muda o seu
último ponto.
Uma página em outra origem
Uma página servida de um endereço só lê um servidor em outro se o
servidor permitir. O nonius-server permite qualquer página: toda
resposta leva Access-Control-Allow-Origin: *, expõe o seu ETag e os
cabeçalhos da imagem aos scripts (Access-Control-Expose-Headers), e
Cross-Origin-Resource-Policy: cross-origin, então uma página que se
isola para a memória compartilhada do feed ao vivo ainda consegue lê-lo.
Uma imagem também diz onde está o seu gráfico: Nonius-Plot: x y width height, o retângulo do gráfico nos pixels da imagem, e Nonius-Range: lo hi, os valores nas suas linhas de baixo e de cima. Uma página que
mostra o PNG transforma um clique num tempo e num valor com eles, e
pede ao /pick do servidor o ponto embaixo dele.
Em produção
O nonius-server é a referência do protocolo e um apoio de testes, não
um servidor de produção: sem TLS, sem limite de conexões, uma thread
por conexão. Diante de usuários, coloque-o atrás de um proxy reverso
que cuide do TLS e limite as conexões; como toda resposta, menos os
metadados, é imutável, o proxy pode guardar todas em cache.
Para um servidor seu, dois caminhos:
- O protocolo dentro do seu programa Rust, pela biblioteca do crate:
#![allow(unused)]
fn main() {
let served = Served::new("day", (t, v, w), 2, Options::default())?;
}
served.answer(&request) responde a um pedido sem socket, e serve
o coloca num TcpListener; a página do
crate mostra os dois.
- O protocolo em qualquer linguagem. Ele foi escrito para ser implementado sem o código do Nonius: o protocolo dá cada endereço, byte e regra, e as imagens são desenhadas pelo pacote .NET como pelo Rust.