Porquê fazer streams em Orleans?
Já existe uma ampla gama de tecnologias que permitem construir sistemas de processamento de fluxo. Isso inclui sistemas para armazenar dados de fluxo de forma durável (por exemplo, Hubs de Eventos e Kafka) e sistemas para expressar operações de computação sobre dados de fluxo (por exemplo, Azure Stream Analytics, Apache Storm e Apache Spark Streaming). Esses são ótimos sistemas que permitem que você crie 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 sobre dados de fluxo. Os sistemas de computação de streaming mencionados acima permitem especificar um gráfico de fluxo de dados unificado de operações que são aplicadas da mesma maneira a todos os itens de fluxo. Este é 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 em que você precisa expressar operações fundamentalmente diferentes em itens de dados diferentes. E em alguns deles, como parte desse processamento, você ocasionalmente precisa fazer uma chamada externa, como invocar alguma API REST arbitrária. Os mecanismos unificados de processamento de fluxo de dados não suportam esses cenários, oferecem suporte a eles de forma limitada e restrita ou são ineficientes em apoiá-los. Isso ocorre porque eles são inerentemente otimizados para um grande volume de itens semelhantes, e geralmente limitados em termos de expressividade, processamento. Orleans Os fluxos visam esses outros cenários.
Motivação
Tudo começou com solicitações de Orleans usuários para dar suporte ao retorno de uma sequência de itens de uma chamada de método grain. Como podem imaginar, essa foi apenas a ponta do icebergue. Precisavam de muito mais do que isso.
Um cenário típico para 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 utilizadores, mas alguns deles estão interessados em meteorologia e podem subscrever alertas meteorológicos para um determinado local, enquanto outros estão interessados em eventos desportivos; outra pessoa está a acompanhar o estado de um determinado voo. O processamento desses eventos requer uma lógica diferente, mas você não deseja executar duas instâncias independentes de processamento de fluxo. Alguns usuários estão interessados em apenas um estoque específico e somente se uma determinada condição externa se aplicar, uma condição que pode não necessariamente fazer parte dos dados de fluxo (e, portanto, precisa ser verificada dinamicamente em tempo de execução como parte do processamento).
Os usuários mudam seus interesses o tempo todo, portanto, suas assinaturas para fluxos específicos de eventos vêm e vão dinamicamente, portanto , a topologia de streaming muda dinâmica e rapidamente. 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 eventos externos. Eventos externos podem modificar a lógica de processamento para um usuário específico. Por exemplo, em um sistema de deteção de trapaça de jogo, quando uma nova maneira de trapacear é descoberta, a lógica de processamento precisa ser atualizada com a nova regra para detetar essa nova violação. Isso precisa ser feito, é claro , sem interromper o pipeline de processamento em curso. Os mecanismos de processamento de fluxo de dados em massa não foram criados para dar suporte a esses cenários.
Escusado será dizer que tal sistema tem que ser executado em várias máquinas conectadas à rede, não em um único nó. Assim, a lógica de processamento deve ser distribuída de forma escalável e elástica em um cluster de servidores.
Novos requisitos
Identificamos 4 requisitos básicos para o nosso sistema de Stream Processing que lhe permitirão atingir os cenários acima.
- Lógica flexível de processamento de fluxo
- Suporte para topologias altamente dinâmicas
- Granularidade de fluxo de grão fino
- Distribuição
Lógica flexível de processamento de fluxo
Queremos que o sistema suporte diferentes formas de expressar a lógica de processamento de fluxo. Os sistemas existentes que mencionamos acima exigem que o desenvolvedor escreva um gráfico de cálculo 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. Orleans Os fluxos 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 stateful ou stateless, pode ou não ter efeitos colaterais e pode desencadear ações externas. Todo o poder vai para o desenvolvedor.
Suporte para topologias dinâmicas
Queremos que o sistema permita topologias em evolução dinâmica. Os sistemas existentes que mencionamos acima geralmente são limitados apenas a topologias estáticas que são fixadas no momento da implantação e não podem evoluir em tempo de execução. No exemplo a seguir de uma expressão de fluxo de dados, tudo é agradável e simples até que você precise 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 Where condição de limite no filtro, adicione Select instrução ou adicione outra ramificação no gráfico 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 o cálculo existente e poderão reiniciar a partir 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. Tal reinicialização torna-se especialmente impraticável quando estamos falando de um grande número de 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 evoluir o gráfico de processamento de fluxo em tempo de execução, adicionando novos links ou nós ao gráfico de computação, ou alterando a lógica de processamento dentro dos nós de computação.
Granularidade de fluxo de grão fino
Nos sistemas existentes, a menor unidade de abstração é geralmente todo o fluxo (topologia). No entanto, muitos de nossos cenários de destino exigem que um nó/link individual na topologia seja uma entidade lógica por si só. Desta forma, cada entidade pode ser potencialmente gerida 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 por filas confiáveis. Links diferentes podem ter diferentes garantias de entrega. Nós diferentes podem ter diferentes estratégias de ponto de verificação, e sua lógica de processamento pode ser expressa em modelos diferentes ou até mesmo linguagens diferentes. Esta flexibilidade não é geralmente possível nos sistemas existentes.
O argumento da unidade de abstração e flexibilidade é semelhante a uma comparação de SoA (Service Oriented Architectures) vs. Actors. Os sistemas de intervenientes permitem uma maior flexibilidade, uma vez que cada interveniente é essencialmente um «pequeno serviço» gerido de forma independente. Da mesma forma, queremos que o sistema de fluxo permita esse controle minucioso.
Distribuição
E, claro, o nosso sistema deve ter todas as propriedades de um "bom sistema distribuído". Isso inclui:
- Escalabilidade - suporta um grande número de fluxos e elementos de computação.
- Elasticidade - permite adicionar/remover recursos para crescer/encolher com base na carga.
- Fiabilidade - ser resiliente a falhas
- Eficiência - utilizar os recursos subjacentes de forma eficiente
- Capacidade de resposta - permita cenários quase em tempo real.
Estes eram os requisitos que tínhamos em mente para construir Orleans o Streaming.
Esclarecimento: Orleans atualmente não suporta diretamente a escrita de expressões declarativas de fluxo de dados como no exemplo acima. As APIs de streaming atuais Orleans são blocos de construção de nível mais baixo, conforme descrito aqui. Fornecer expressões declarativas de fluxo de dados é nosso objetivo futuro.