Compartilhar via


Por que fluxos no Orleans?

Já existem uma ampla gama de tecnologias que permitem criar sistemas de processamento de fluxo. Elas incluem sistemas para armazenar duravelmente dados de fluxo (por exemplo, Hubs de Eventos e Kafka) e sistemas para expressar operações de computação em dados de fluxo (por exemplo, Azure Stream Analytics, Apache Storm e Apache Spark Streaming). Eles são ótimos sistemas que permitem criar pipelines de processamento de fluxo de dados eficientes.

Limitações dos sistemas existentes

No entanto, esses sistemas não são adequados para computação de forma livre refinada em dados de fluxo. Os sistemas de computação de streaming mencionados acima de tudo permitem que você especifique um grafo unificado de fluxo de dados de operações que são aplicadas da mesma forma a todos os itens de fluxo. Esse é um modelo poderoso quando os dados são uniformes e você deseja expressar o mesmo conjunto de operações de transformação, filtragem ou agregação sobre esses dados. Mas há outros casos de uso nos quais você precisa expressar operações fundamentalmente diferentes em diferentes itens de dados. E em alguns deles como parte desse processamento, ocasionalmente você precisa fazer uma chamada externa, como invocar alguma API REST arbitrária. Os mecanismos de processamento de transmissão de fluxo de fluxo de dados unificados não dão suporte a esses cenários, dão suporte a eles de forma limitada e restrita ou são ineficientes em dar suporte a eles. Isso ocorre porque eles são inerentemente otimizados para um grande volume de itens semelhantes e geralmente limitados em termos de expressividade, processamento. O Orleans Streams tem como alvo esses outros cenários.

Motivação

Tudo começou com solicitações de usuários do Orleans para ter suporte ao retorno de uma sequência de itens de uma chamada de método de granularidade. Como você pode imaginar, essa era apenas a ponta do iceberg. Eles precisavam muito mais do que isso.

Um cenário típico para o Orleans Streams é quando você tem fluxos por usuário e deseja executar um processamento diferente para cada usuário, dentro do contexto de um usuário individual. Podemos ter milhões de usuários, mas alguns deles estão interessados no clima e podem assinar alertas meteorológicos para um local específico, enquanto alguns estão interessados em eventos esportivos; outra pessoa está acompanhando o status de um voo específico. O processamento desses eventos exige uma lógica diferente, mas você não quer executar duas instâncias independentes de processamento de fluxo. Alguns usuários estão interessados apenas em uma ação da bolsa em específico e somente se uma determinada condição externa se aplicar, uma condição que pode não fazer parte necessariamente dos dados de fluxo (e, portanto, precisa ser verificada dinamicamente no runtime como parte do processamento).

Os usuários mudam seus interesses o tempo todo, portanto, suas assinaturas de fluxos específicos de eventos mudam de forma dinâmica; portanto, a topologia de streaming muda de forma rápida e dinâmica. Além disso, a lógica de processamento por usuário também evolui e muda dinamicamente, com base no estado do usuário e em eventos externos. Eventos externos podem modificar a lógica de processamento para um usuário específico. Por exemplo, em um sistema de detecção de fraude em jogos, quando uma nova maneira de trapacear é descoberta, a lógica de processamento precisa ser atualizada com a nova regra para detectar essa nova violação. Isso precisa ser feito, é claro, sem interromper o pipeline de processamento em andamento. Os mecanismos de processamento de transmissão de fluxo de dados em massa não foram criados para dar suporte a esses cenários.

Nem é preciso dizer que esse sistema precisa ser executado em vários computadores conectados à rede, não em um único nó. Portanto, a lógica de processamento deve ser distribuída de forma escalonável e elástica em um cluster de servidores.

Novos requisitos

Identificamos 4 requisitos básicos para nosso sistema de Processamento de Fluxo que permitirão que ele resolva os cenários acima.

  1. Lógica de processamento de fluxo flexível
  2. Suporte para topologias altamente dinâmicas
  3. Granularidade de fluxo refinado
  4. Distribuição

Lógica de processamento de fluxo flexível

Queremos que o sistema dê suporte a diferentes maneiras de expressar a lógica de processamento de fluxo. Os sistemas existentes mencionados acima exigem que o desenvolvedor escreva um grafo de computação de fluxo de dados declarativo, geralmente seguindo um estilo de programação funcional. Isso limita a expressividade e a flexibilidade da lógica de processamento. Os streams do Orleans são indiferentes à forma como a lógica de processamento é expressa. Ele pode ser expresso como um fluxo de dados (por exemplo, usando Extensões Reativas (Rx) no .NET); como um programa funcional; como uma consulta declarativa; ou em uma lógica imperativa geral. A lógica pode ser com estado ou sem estado, pode ou não ter efeitos colaterais e pode disparar ações externas. Toda a energia vai para o desenvolvedor.

Suporte para topologias dinâmicas

Queremos que o sistema permita topologias em evolução dinâmica. Os sistemas existentes mencionados acima geralmente estão limitados apenas a topologias estáticas que são corrigidas no momento da implantação e não podem evoluir no runtime. No exemplo a seguir de uma expressão de fluxo de dados, tudo é legal e simples até você precisar alterá-la.

Stream.GroupBy(x=> x.key).Extract(x=>x.field).Select(x=>x+2).AverageWindow(x, 5sec).Where(x=>x > 0.8) *

Altere a condição de limite no filtro Where, adicione a instrução Select ou adicione outro branch no grafo de fluxo de dados e produza um novo fluxo de saída. Em sistemas existentes, isso não é possível sem derrubar toda a topologia e reiniciar o fluxo de dados do zero. Praticamente, esses sistemas verificarão a computação existente e poderão ser reiniciados do ponto de verificação mais recente. Ainda assim, essa reinicialização é disruptiva e dispendiosa para um serviço online que produz resultados em tempo real. Essa reinicialização torna-se especialmente impraticável quando estamos falando de um grande número dessas expressões sendo executadas com parâmetros semelhantes, mas diferentes (por usuário, por dispositivo, etc.) e que mudam continuamente.

Queremos que o sistema permita a evolução do grafo de processamento de fluxo no runtime, adicionando novos links ou nós ao grafo de computação ou alterando a lógica de processamento dentro dos nós de computação.

Granularidade de fluxo refinado

Nos sistemas existentes, a menor unidade de abstração geralmente é todo o fluxo (topologia). No entanto, muitos de nossos cenários alvo exigem que um nó/link individual na topologia seja uma entidade lógica por si só. Dessa forma, cada entidade pode ser potencialmente gerenciada de forma independente. Por exemplo, na topologia de fluxo grande que compreende vários links, links diferentes podem ter características diferentes e podem ser implementados em diferentes transportes físicos. Alguns links podem passar por soquetes TCP, enquanto outros passam por filas confiáveis. Links diferentes podem ter garantias de entrega diferentes. Nós diferentes podem ter diferentes estratégias de ponto de verificação e sua lógica de processamento pode ser expressa em diferentes modelos ou até mesmo diferentes idiomas. Essa flexibilidade geralmente não é possível em sistemas existentes.

A unidade de abstração e o argumento de flexibilidade são semelhantes a uma comparação de SoA (Arquiteturas Voltadas ao Serviço) versus Atores. Os sistemas de ator permitem mais flexibilidade, pois cada ator é essencialmente um "serviço minúsculo" gerenciado de forma independente. Da mesma forma, queremos que o sistema de fluxo permita esse controle refinado.

Distribuição

E, claro, nosso sistema deve ter todas as propriedades de um "bom sistema distribuído". Isso inclui:

  1. Escalabilidade - dá suporte a um grande número de fluxos e elementos de computação.
  2. Elasticidade - permite adicionar/remover recursos para aumentar/reduzir com base na carga.
  3. Confiabilidade - ser resiliente a falhas
  4. Eficiência - usar os recursos subjacentes com eficiência
  5. Capacidade de resposta - habilitar cenários quase em tempo real.

Estes eram os requisitos que tínhamos em mente para construir o Orleans Streaming.

Esclarecimento: atualmente, o Orleans não dá suporte direto à gravação de expressões declarativas de fluxo de dados, como no exemplo acima. As APIs de Streaming atuais do Orleans são mais blocos de construção de baixo nível, conforme descrito aqui. Fornecer expressões de fluxo de dados declarativo é nossa meta futura.

Confira também

APIs de Programação de Streams do Orleans