A resposta curta é que apenas novos dados são enviados pela rede. É assim que funciona.
Existem três partes importantes do servidor Meteor que gerenciam as assinaturas: a função de publicação , que define a lógica de quais dados a assinatura fornece; o driver Mongo , que observa o banco de dados em busca de alterações; e a caixa de mesclagem , que combina todas as assinaturas ativas de um cliente e as envia pela rede para o cliente.
Publicar funções
Cada vez que um cliente Meteor se inscreve em uma coleção, o servidor executa uma
função de publicação . O trabalho da função de publicação é descobrir o conjunto de documentos que seu cliente deve ter e enviar cada propriedade do documento para a caixa de mesclagem. Ele é executado uma vez para cada novo cliente assinante. Você pode colocar qualquer JavaScript que desejar na função de publicação, como o uso de controle de acesso arbitrariamente complexo this.userId
. A função de publicação envia dados para a caixa de mesclagem chamando this.added
, this.changed
e
this.removed
. Veja a
documentação de publicação completa para mais detalhes.
A maioria dos publicar funções não tem que mexer com o de baixo nível
added
, changed
e removed
API, no entanto. Se a publicar função retorna um cursor Mongo, o servidor Meteor conecta automaticamente a saída do controlador de Mongo ( insert
, update
e removed
chamadas de retorno) para a entrada da caixa de merge ( this.added
, this.changed
e this.removed
). É muito legal que você possa fazer todas as verificações de permissão antecipadamente em uma função de publicação e, em seguida, conectar diretamente o driver de banco de dados à caixa de mesclagem sem nenhum código de usuário no caminho. E quando a publicação automática está ativada, até mesmo este pequeno detalhe fica oculto: o servidor configura automaticamente uma consulta para todos os documentos em cada coleção e os coloca na caixa de mesclagem.
Por outro lado, você não está limitado a publicar consultas de banco de dados. Por exemplo, você pode escrever uma função de publicação que lê uma posição GPS de um dispositivo dentro de um Meteor.setInterval
ou pesquisa uma API REST legada de outro serviço da web. Nesses casos, você emitem alterações na caixa de junção, chamando o de baixo nível added
, changed
e removed
DDP API.
O motorista Mongo
O trabalho do driver Mongo é observar o banco de dados Mongo em busca de alterações nas consultas ao vivo. Essas consultas funcionar continuamente e voltar atualizações como a mudança resultados chamando added
, removed
e changed
retornos de chamada.
Mongo não é um banco de dados em tempo real. Então, o motorista faz pesquisas. Ele mantém uma cópia na memória do último resultado da consulta para cada consulta ativa ativa. Em cada ciclo de sondagem, ele compara o novo resultado com o resultado anterior salvo, computando o conjunto mínimo de added
, removed
e changed
eventos que descrevem a diferença. Se vários chamadores registrarem retornos de chamada para a mesma consulta ao vivo, o driver observará apenas uma cópia da consulta, chamando cada retorno de chamada registrado com o mesmo resultado.
Cada vez que o servidor atualiza uma coleção, o driver recalcula cada consulta ativa nessa coleção (versões futuras do Meteor irão expor uma API de escalonamento para limitar quais consultas ao vivo recalculam na atualização.) O driver também consulta cada consulta ativa em um cronômetro de 10 segundos para capturar atualizações de banco de dados fora da banda que contornaram o servidor Meteor.
A caixa de mesclagem
O trabalho da caixa de junção é combinar os resultados ( added
, changed
e removed
chamadas) de todos os ativos publicar funções de um cliente em um único fluxo de dados. Há uma caixa de mesclagem para cada cliente conectado. Ele contém uma cópia completa do cache de minimongo do cliente.
Em seu exemplo, com apenas uma única assinatura, a caixa de mesclagem é essencialmente uma passagem. Mas um aplicativo mais complexo pode ter várias assinaturas que podem se sobrepor. Se duas assinaturas definirem o mesmo atributo no mesmo documento, a caixa de mesclagem decide qual valor tem prioridade e apenas o envia ao cliente. Ainda não expusemos a API para definir a prioridade de assinatura. Por enquanto, a prioridade é determinada pelo pedido em que o cliente assina os conjuntos de dados. A primeira assinatura que um cliente faz tem a prioridade mais alta, a segunda assinatura é a próxima mais alta e assim por diante.
Como a caixa de mesclagem contém o estado do cliente, ela pode enviar a quantidade mínima de dados para manter cada cliente atualizado, independentemente de qual função de publicação o alimenta.
O que acontece em uma atualização
Portanto, agora preparamos o cenário para o seu cenário.
Temos 1.000 clientes conectados. Cada um está inscrito na mesma consulta Mongo ao vivo ( Somestuff.find({})
). Como a consulta é a mesma para cada cliente, o driver está executando apenas uma consulta ativa. Existem 1.000 caixas de mesclagem ativas. E a função de publicação de cada cliente registrou um added
, changed
e
removed
naquela consulta ativa que alimenta uma das caixas de mesclagem. Nada mais está conectado às caixas de mesclagem.
Primeiro, o driver Mongo. Quando um dos clientes insere um novo documento Somestuff
, ele dispara uma recomputação. O driver Mongo executa novamente a consulta para todos os documentos em Somestuff
, compara o resultado com o resultado anterior na memória, descobre que há um novo documento e chama cada um dos 1.000 insert
retornos de chamada registrados .
Em seguida, as funções de publicação. Há muito pouca coisa acontecendo aqui: cada um dos 1.000 insert
retornos de chamada envia dados para a caixa de mesclagem chamando added
.
Por fim, cada caixa de mesclagem verifica esses novos atributos em relação à sua cópia na memória do cache do cliente. Em cada caso, ele descobre que os valores ainda não estão no cliente e não ocultam um valor existente. Portanto, a caixa de mesclagem emite uma DATA
mensagem DDP na conexão SockJS para seu cliente e atualiza sua cópia na memória do lado do servidor.
O custo total da CPU é o custo para comparar uma consulta Mongo, mais o custo de 1.000 caixas de mesclagem verificando o estado de seus clientes e construindo uma nova carga útil de mensagem DDP. Os únicos dados que fluem pela conexão são um único objeto JSON enviado a cada um dos 1.000 clientes, correspondendo ao novo documento no banco de dados, mais uma mensagem RPC para o servidor do cliente que fez a inserção original.
Otimizações
Aqui está o que definitivamente planejamos.
Driver Mongo mais eficiente. Nós
otimizamos o motorista
em 0.5.1 para executar apenas um único observador por consulta distinta.
Nem toda mudança no banco de dados deve acionar uma recomputação de uma consulta. Podemos fazer algumas melhorias automatizadas, mas a melhor abordagem é uma API que permite ao desenvolvedor especificar quais consultas precisam ser executadas novamente. Por exemplo, é óbvio para um desenvolvedor que inserir uma mensagem em uma sala de chat não deve invalidar uma consulta ao vivo para as mensagens em uma segunda sala.
O driver Mongo, a função de publicação e a caixa de mesclagem não precisam ser executados no mesmo processo ou mesmo na mesma máquina. Alguns aplicativos executam consultas dinâmicas complexas e precisam de mais CPU para monitorar o banco de dados. Outros têm apenas algumas consultas distintas (imagine um mecanismo de blog), mas possivelmente muitos clientes conectados - eles precisam de mais CPU para caixas de mesclagem. Separar esses componentes nos permitirá dimensionar cada peça independentemente.
Muitos bancos de dados oferecem suporte a gatilhos que disparam quando uma linha é atualizada e fornecem as linhas novas e antigas. Com esse recurso, um driver de banco de dados pode registrar um gatilho em vez de pesquisar as alterações.
skip
,$near
e$where
contendo consultas) que é muito mais eficiente na carga da CPU, largura de banda da rede e permite escalar a aplicação servidores.Pela minha experiência, usar muitos clientes enquanto compartilha uma coleção enorme no Meteor é essencialmente impraticável, a partir da versão 0.7.0.1. Vou tentar explicar o porquê.
Conforme descrito na postagem acima e também em https://github.com/meteor/meteor/issues/1821 , o servidor do meteoro deve manter uma cópia dos dados publicados para cada cliente na caixa de mesclagem . Isso é o que permite que a mágica do Meteor aconteça, mas também resulta em qualquer grande banco de dados compartilhado sendo repetidamente mantido na memória do processo do nó. Mesmo ao usar uma possível otimização para coleções estáticas como em ( Existe uma maneira de dizer ao meteoro que uma coleção é estática (nunca mudará)? ), Tivemos um grande problema com o uso de CPU e memória do processo Node.
Em nosso caso, estávamos publicando uma coleção de 15k documentos para cada cliente que era completamente estático. O problema é que copiar esses documentos para uma caixa de mesclagem do cliente (na memória) na conexão basicamente trouxe o processo do Node para 100% da CPU por quase um segundo e resultou em um grande uso adicional de memória. Isso é inerentemente não escalável, porque qualquer cliente conectado deixará o servidor de joelhos (e as conexões simultâneas se bloquearão) e o uso de memória aumentará linearmente no número de clientes. Em nosso caso, cada cliente causou um uso adicional de ~ 60 MB de memória, embora os dados brutos transferidos fossem de apenas 5 MB.
Em nosso caso, como a coleção era estática, resolvemos esse problema enviando todos os documentos como um
.json
arquivo, que foi compactado pelo nginx, e carregando-os em uma coleção anônima, resultando em apenas uma transferência de dados de aproximadamente 1 MB sem CPU adicional ou memória no processo do nó e um tempo de carregamento muito mais rápido. Todas as operações sobre esta coleção foram feitas usando_id
s de publicações muito menores no servidor, permitindo reter a maioria dos benefícios do Meteor. Isso permitiu que o aplicativo se expandisse para muitos mais clientes. Além disso, como nosso aplicativo é principalmente somente leitura, melhoramos ainda mais a escalabilidade executando várias instâncias do Meteor atrás do nginx com balanceamento de carga (embora com um único Mongo), já que cada instância do Node é de thread único.No entanto, a questão de compartilhar grandes coleções graváveis entre vários clientes é um problema de engenharia que precisa ser resolvido pelo Meteor. Provavelmente existe uma maneira melhor do que manter uma cópia de tudo para cada cliente, mas isso requer uma reflexão séria como um problema de sistemas distribuídos. Os problemas atuais de uso massivo de CPU e memória simplesmente não escalam.
fonte
A experiência que você pode usar para responder a esta pergunta:
meteor create --example todos
Para obter dicas sobre como usar o WKI, consulte este artigo . Está um pouco desatualizado, mas ainda é válido, especialmente para esta questão.
fonte
Isso ainda tem um ano de idade e, portanto, acho que o conhecimento anterior ao "Meteor 1.0", então as coisas podem ter mudado de novo? Ainda estou investigando isso. http://meteorhacks.com/does-meteor-scale.html leva a uma pergunta "Como escalar o Meteoro?" artigo http://meteorhacks.com/how-to-scale-meteor.html
fonte