Estruturar microsserviços: Ingestão e fluxo de trabalhoDesigning microservices: Ingestion and workflow

Normalmente, os Microsserviços têm um fluxo de trabalho que abrange vários serviços para uma única transação.Microservices often have a workflow that spans multiple services for a single transaction. O fluxo de trabalho tem de ser fiável; ela não pode perder transações ou deixá-los num Estado parcialmente concluído.The workflow must be reliable; it can't lose transactions or leave them in a partially completed state. Também é fundamental para controlar a velocidade de ingestão de pedidos recebidos.It's also critical to control the ingestion rate of incoming requests. Com vários serviços pequenos comunicar entre si, uma explosão das solicitações de entrada pode sobrecarregar a comunicação originar.With many small services communicating with each other, a burst of incoming requests can overwhelm the interservice communication.

Diagrama do fluxo de trabalho de ingestão

O fluxo de trabalho de entrega por droneThe drone delivery workflow

No aplicativo de entrega por Drone, as seguintes operações devem ser executadas para agendar uma entrega:In the Drone Delivery application, the following operations must be performed to schedule a delivery:

  1. Verificar o estado da conta do cliente (serviço de conta).Check the status of the customer's account (Account service).
  2. Crie uma nova entidade de pacote (serviço de pacote).Create a new package entity (Package service).
  3. Verifique se qualquer transportes de terceiros é necessário para essa entrega, com base nos locais de recolha e de entrega (serviço de transporte de terceiros).Check whether any third-party transportation is required for this delivery, based on the pickup and delivery locations (Third-party Transportation service).
  4. Agende um drone para recolha (serviço de Drones).Schedule a drone for pickup (Drone service).
  5. Crie uma nova entidade de entrega (serviço de entrega).Create a new delivery entity (Delivery service).

Este é o núcleo do aplicativo inteiro, portanto, o processo de ponto-a-ponto deve ser confiável, bem como de elevado desempenho.This is the core of the entire application, so the end-to-end process must be performant as well as reliable. Alguns desafios específicos devem ser resolvidos:Some particular challenges must be addressed:

  • Nivelamento de carga.Load leveling. Demasiados pedidos de cliente podem sobrecarregar o sistema com originar tráfego de rede.Too many client requests can overwhelm the system with interservice network traffic. Ele também pode sobrecarregar as dependências de back-end como o armazenamento ou de serviços remoto.It can also overwhelm backend dependencies such as storage or remote services. Estes podem reagir pela limitação de serviços ao chamá-los, criar backpressure no sistema.These may react by throttling the services calling them, creating backpressure in the system. Portanto, é importante para a carga de nível de pedidos entram no sistema, colocando-os num buffer ou fila para processamento.Therefore, it's important to load level the requests coming into the system, by putting them into a buffer or queue for processing.

  • Entrega garantida.Guaranteed delivery. Para evitar a remover quaisquer pedidos de cliente, o componente de ingestão tem de garantir pelo-menos-uma vez entrega de mensagens.To avoid dropping any client requests, the ingestion component must guarantee at-least-once delivery of messages.

  • Tratamento de erros.Error handling. Se qualquer um dos serviços devolve um código de erro ou sofre uma falha não transitórias, não é possível agendar a entrega.If any of the services returns an error code or experiences a non-transient failure, the delivery cannot be scheduled. Um código de erro pode indicar uma condição de erro esperados (por exemplo, a conta do cliente está suspenso) ou um erro inesperado do servidor (HTTP 5xx).An error code might indicate an expected error condition (for example, the customer's account is suspended) or an unexpected server error (HTTP 5xx). Um serviço também pode ser indisponível, fazendo com que a chamada de rede para o tempo limite.A service might also be unavailable, causing the network call to time out.

Em primeiro lugar, examinaremos o lado de ingestão da equação — como o sistema pode ingerir recebidos pedidos de utilizador num débito elevado.First we'll look at the ingestion side of the equation — how the system can ingest incoming user requests at high throughput. Em seguida, analisaremos como o aplicativo de entrega por drone pode implementar um fluxo de trabalho fiável.Then we'll consider how the drone delivery application can implement a reliable workflow. Acontece que o design do subsistema de ingestão de afeta o back-end do fluxo de trabalho.It turns out that the design of the ingestion subsystem affects the workflow backend.

IngestãoIngestion

Com base nos requisitos empresariais, a equipe de desenvolvimento identificados os seguintes requisitos não funcionais para ingestão:Based on business requirements, the development team identified the following non-functional requirements for ingestion:

  • Débito constante de 10 mil pedidos/seg.Sustained throughput of 10K requests/sec.
  • Capaz de lidar com picos de até 50 mil/seg sem a ignorar pedidos de cliente ou exceder o tempo limite.Able to handle spikes of up to 50K/sec without dropping client requests or timing out.
  • Inferior a 500 MS de latência no percentil de 99.Less than 500ms latency in the 99th percentile.

O requisito para lidar com picos ocasionais no tráfego apresenta um desafio de design.The requirement to handle occasional spikes in traffic presents a design challenge. Em teoria, o sistema pode ser dimensionado para processar o tráfego esperado máximo.In theory, the system could be scaled out to handle the maximum expected traffic. No entanto, o aprovisionamento que muitos recursos seria muito ineficientes.However, provisioning that many resources would be very inefficient. A maioria das vezes, a aplicação não terá muito capacidade, para que fique ociosos núcleos, dinheiro e sem adicionar valor.Most of the time, the application will not need that much capacity, so there would be idle cores, costing money without adding value.

Uma abordagem melhor é colocar os pedidos de entrada num buffer e permitir que a memória intermédia de atuar como equilibrador de carga.A better approach is to put the incoming requests into a buffer, and let the buffer act as a load leveler. Com esta estrutura, o serviço de ingestão deve ser capaz de lidar com a taxa de ingestão máximo durante períodos curtos, mas os serviços de back-end só tem de processar a carga constante máxima.With this design, the Ingestion service must be able to handle the maximum ingestion rate over short periods, but the backend services only need to handle the maximum sustained load. Ao buffer em front-end, os serviços de back-end não deverá ser preciso lidar com grandes picos de tráfego.By buffering at the front end, the backend services shouldn't need to handle large spikes in traffic. À escala necessária para a aplicação de entrega por Drone os Hubs de eventos do Azure é uma boa opção para a redistribuição de carga.At the scale required for the Drone Delivery application, Azure Event Hubs is a good choice for load leveling. Os Hubs de eventos garante baixa latência e alto débito e é uma solução económica em volumes de ingestão elevada.Event Hubs offers low latency and high throughput, and is a cost effective solution at high ingestion volumes.

Para nosso teste, usamos um hub de eventos do escalão Standard com 32 partições e 100 unidades de débito.For our testing, we used a Standard tier event hub with 32 partitions and 100 throughput units. Observamos aproximadamente 32 mil eventos / segundo ingestão, com latência em torno de 90ms.We observed about 32K events / second ingestion, with latency around 90ms. Atualmente o limite predefinido é de 20 unidades de débito, mas os clientes do Azure podem solicitar unidades de débito adicionais ao preencher um pedido de suporte.Currently the default limit is 20 throughput units, but Azure customers can request additional throughput units by filing a support request. Ver quotas de Hubs de eventos para obter mais informações.See Event Hubs quotas for more information. Tal como acontece com todas as métricas de desempenho, vários fatores podem afetar o desempenho, como o tamanho de payload de mensagem, pelo que não interpretar esses números como um parâmetro de comparação.As with all performance metrics, many factors can affect performance, such as message payload size, so don't interpret these numbers as a benchmark. Se necessitar de mais débito, o serviço de ingestão pode partições horizontais em mais do que um hub de eventos.If more throughput is needed, the Ingestion service can shard across more than one event hub. Para ainda mais elevadas taxas de transferência, Hubs de eventos dedicados oferece implementações de inquilino único, que podem receber mais de 2 milhões de eventos por segundo.For even higher throughput rates, Event Hubs Dedicated offers single-tenant deployments that can ingress over 2 million events per second.

É importante compreender como os Hubs de eventos pode alcançar tal débito elevado, uma vez que isso afeta como um cliente deve consumir mensagens dos Hubs de eventos.It's important to understand how Event Hubs can achieve such high throughput, because that affects how a client should consume messages from Event Hubs. Os Event Hubs não implementam um fila.Event Hubs does not implement a queue. Em vez disso, ele implementa uma fluxo de eventos.Rather, it implements an event stream.

Com uma fila, um consumidor individual pode remover uma mensagem da fila e o consumidor seguinte não verá essa mensagem.With a queue, an individual consumer can remove a message from the queue, and the next consumer won't see that message. Filas, pelo que permite a utilização de um padrão de consumidores concorrentes para processar mensagens em paralelo e melhorar a escalabilidade.Queues therefore allow you to use a Competing Consumers pattern to process messages in parallel and improve scalability. Para maior resiliência, o consumidor mantém um bloqueio na mensagem e libera o bloqueio quando tiver concluído a processar a mensagem.For greater resiliency, the consumer holds a lock on the message and releases the lock when it's done processing the message. Se o consumidor falhar — por exemplo, o nó que ele é executado em falhas — o bloqueio de tempo limite e a mensagem de volta para a fila.If the consumer fails — for example, the node it's running on crashes — the lock times out and the message goes back onto the queue.

Diagrama de semântica de fila

Os Hubs de eventos, por outro lado, usa a semântica de transmissão em fluxo.Event Hubs, on the other hand, uses streaming semantics. Os consumidores de ler o fluxo de forma independente em seu próprio ritmo.Consumers read the stream independently at their own pace. Cada consumidor é responsável por manter o controle de sua posição atual no fluxo.Each consumer is responsible for keeping track of its current position in the stream. Um consumidor deve gravar sua posição atual no armazenamento persistente algumas intervalo predefinido.A consumer should write its current position to persistent storage at some predefined interval. Dessa forma, se o consumidor sofrer uma falha (por exemplo, as falhas de consumidor ou a falha de anfitrião), em seguida, uma nova instância pode retomar a ler o fluxo da última posição gravada.That way, if the consumer experiences a fault (for example, the consumer crashes, or the host fails), then a new instance can resume reading the stream from the last recorded position. Este processo é denominado ponto de verificação.This process is called checkpointing.

Por motivos de desempenho, um consumidor não geralmente o ponto de verificação após cada mensagem.For performance reasons, a consumer generally doesn't checkpoint after each message. Em vez disso, ele pontos de verificação em algum intervalo fixo, por exemplo após o processamento n mensagens, ou cada n segundos.Instead, it checkpoints at some fixed interval, for example after processing n messages, or every n seconds. Como conseqüência, se um consumidor falhar, alguns eventos podem obter processados duas vezes, uma vez que uma nova instância sempre seleciona do último ponto de verificação.As a consequence, if a consumer fails, some events may get processed twice, because a new instance always picks up from the last checkpoint. Há uma compensação: Pontos de verificação freqüentes podem prejudicar o desempenho, mas os pontos de verificação dispersos significa que irá reproduzir mais eventos após uma falha.There is a tradeoff: Frequent checkpoints can hurt performance, but sparse checkpoints mean you will replay more events after a failure.

Diagrama de semântica de fluxo

Os Hubs de eventos não foi concebido para consumidores concorrentes.Event Hubs is not designed for competing consumers. Embora vários consumidores podem ler um fluxo, cada atravessa independentemente o fluxo.Although multiple consumers can read a stream, each traverses the stream independently. Em vez disso, os Hubs de eventos utiliza um padrão de consumidor particionado.Instead, Event Hubs uses a partitioned consumer pattern. Um hub de eventos tem até 32 partições.An event hub has up to 32 partitions. Dimensionamento horizontal é obtido através da atribuição de um consumidor separado para cada partição.Horizontal scale is achieved by assigning a separate consumer to each partition.

O que isso significa para o fluxo de trabalho de entrega por drone?What does this mean for the drone delivery workflow? Para obter todos os benefícios dos Hubs de eventos, o agendador de entrega não pode esperar por cada mensagem a ser processada antes de passar para a próxima.To get the full benefit of Event Hubs, the Delivery Scheduler cannot wait for each message to be processed before moving onto the next. Se ele faz isso, ele será passam a maior parte de seu tempo a aguardar a conclusão de chamadas de rede.If it does that, it will spend most of its time waiting for network calls to complete. Em vez disso, ele precisa processar lotes de mensagens em paralelo, chamadas assíncronas para os serviços de back-end.Instead, it needs to process batches of messages in parallel, using asynchronous calls to the backend services. Como verá, escolher a estratégia de ponto de verificação correto também é importante.As we'll see, choosing the right checkpointing strategy is also important.

Fluxo de trabalhoWorkflow

Demos uma olhada três opções para ler e processar as mensagens: Anfitrião do processador de eventos, filas do Service Bus e a biblioteca de reagir de IoTHub.We looked at three options for reading and processing the messages: Event Processor Host, Service Bus queues, and the IoTHub React library. Escolhemos IoTHub reagir, mas para compreender o motivo, ajuda para começar com o anfitrião do processador de eventos.We chose IoTHub React, but to understand why, it helps to start with Event Processor Host.

Anfitrião do Processador de EventosEvent Processor Host

Anfitrião do processador de eventos foi concebido para a criação de batches de mensagem.Event Processor Host is designed for message batching. A aplicação implementa o IEventProcessor interface e o anfitrião do processador cria uma instância de processador de eventos para cada partição no hub de eventos.The application implements the IEventProcessor interface, and the Processor Host creates one event processor instance for each partition in the event hub. O anfitrião do processador de eventos, em seguida, chama cada processador de eventos ProcessEventsAsync método com lotes de mensagens de eventos.The Event Processor Host then calls each event processor's ProcessEventsAsync method with batches of event messages. Os controlos de aplicação quando a ponto de verificação dentro do ProcessEventsAsync método e o anfitrião do processador de eventos escreve os pontos de verificação para o armazenamento do Azure.The application controls when to checkpoint inside the ProcessEventsAsync method, and the Event Processor Host writes the checkpoints to Azure storage.

Dentro de uma partição, anfitrião do processador de eventos aguarda ProcessEventsAsync para retornar antes de chamar novamente com o lote seguinte.Within a partition, Event Processor Host waits for ProcessEventsAsync to return before calling again with the next batch. Essa abordagem simplifica o modelo de programação, porque seu código de processamento do evento não precisa ser reentrância.This approach simplifies the programming model, because your event processing code doesn't need to be reentrant. No entanto, isso também significa que o processador de eventos processa um lote a cada vez e isso gates a velocidade a que o anfitrião do processador pode bombardeia mensagens.However, it also means that the event processor handles one batch at a time, and this gates the speed at which the Processor Host can pump messages.

Nota

O anfitrião do processador não realmente aguarde no sentido de bloquear um thread.The Processor Host doesn't actually wait in the sense of blocking a thread. O ProcessEventsAsync método é assíncrono, para que o anfitrião do processador pode efetuar outros trabalhos enquanto o método está a concluir.The ProcessEventsAsync method is asynchronous, so the Processor Host can do other work while the method is completing. Mas ele não oferece outro lote de mensagens para essa partição até que o método retorna.But it won't deliver another batch of messages for that partition until the method returns.

No aplicativo de drones, um lote de mensagens pode ser processado em paralelo.In the drone application, a batch of messages can be processed in parallel. Mas a aguardar que o lote inteiro concluir pode provocar um estrangulamento.But waiting for the whole batch to complete can still cause a bottleneck. Processamento só pode ser o mais rápido a mensagem mais lenta num lote.Processing can only be as fast as the slowest message within a batch. Qualquer variação nos tempos de resposta pode criar uma "cauda longa," onde algumas respostas lentas arraste para baixo a todo o sistema.Any variation in response times can create a "long tail," where a few slow responses drag down the entire system. Nossos testes de desempenho mostraram que não conseguimos nossa taxa de transferência de destino usando essa abordagem.Our performance tests showed that we did not achieve our target throughput using this approach. Ele faz não significa que deve evitar usar o anfitrião do processador de eventos.This does not mean that you should avoid using Event Processor Host. Mas para um débito elevado, evitar fazer quaisquer tarefas de longa execução dentro do ProcesssEventsAsync método.But for high throughput, avoid doing any long-running tasks inside the ProcesssEventsAsync method. Processe cada lote rapidamente.Process each batch quickly.

IotHub ReactIotHub React

Reagir IotHub está disponível uma biblioteca Akka fluxos para a leitura de eventos do Hub de eventos.IotHub React is an Akka Streams library for reading events from Event Hub. Fluxos de Akka é uma estrutura de programação baseada em fluxo que implementa a fluxos Reativos especificação.Akka Streams is a stream-based programming framework that implements the Reactive Streams specification. Ele fornece uma forma de criar eficientes pipelines de transmissão em fluxo, em que todas as operações de transmissão em fluxo são executadas de forma assíncrona, e o pipeline manipula corretamente backpressure.It provides a way to build efficient streaming pipelines, where all streaming operations are performed asynchronously, and the pipeline gracefully handles backpressure. Backpressure ocorre quando uma origem de evento produz eventos a uma taxa mais rápida do que os consumidores de downstream podem recebê-las — que é exatamente a situação quando o sistema de entrega por drone tem um pico de tráfego.Backpressure occurs when an event source produces events at a faster rate than the downstream consumers can receive them — which is exactly the situation when the drone delivery system has a spike in traffic. Se os serviços de back-end mais lentamente, IoTHub reagir retardará.If backend services go slower, IoTHub React will slow down. Se for aumentada a capacidade, IoTHub reagir irá enviar mais mensagens através do pipeline.If capacity is increased, IoTHub React will push more messages through the pipeline.

Akka fluxos também é um modelo de programação muito natural para transmissão em fluxo de eventos provenientes dos Hubs de eventos.Akka Streams is also a very natural programming model for streaming events from Event Hubs. Em vez de um loop por meio de um lote de eventos, define um conjunto de operações que serão aplicadas a cada evento e permitir que os fluxos de Akka lidar com a transmissão em fluxo.Instead of looping through a batch of events, you define a set of operations that will be applied to each event, and let Akka Streams handle the streaming. Fluxos de Akka define um pipeline de transmissão em fluxo em termos de origens, fluxos, e coletores.Akka Streams defines a streaming pipeline in terms of Sources, Flows, and Sinks. Uma origem gera um fluxo de saída, um fluxo processa um fluxo de entrada e produz um fluxo de saída e um coletor consome um fluxo sem a produzir qualquer saída.A source generates an output stream, a flow processes an input stream and produces an output stream, and a sink consumes a stream without producing any output.

Aqui está o código no serviço de agendador que configura o pipeline de Akka fluxos:Here is the code in the Scheduler service that sets up the Akka Streams pipeline:

IoTHub iotHub = new IoTHub();
Source<MessageFromDevice, NotUsed> messages = iotHub.source(options);

messages.map(msg -> DeliveryRequestEventProcessor.parseDeliveryRequest(msg))
        .filter(ad -> ad.getDelivery() != null).via(deliveryProcessor()).to(iotHub.checkpointSink())
        .run(streamMaterializer);

Esse código configura os Hubs de eventos como uma origem.This code configures Event Hubs as a source. O map instrução desserializa cada mensagem de evento numa classe de Java que representa um pedido de entrega.The map statement deserializes each event message into a Java class that represents a delivery request. O filter instrução remove todos os null objetos do fluxo; essa proteção contra o caso em que uma mensagem não é possível anular a serialização.The filter statement removes any null objects from the stream; this guards against the case where a message can't be deserialized. O via instrução a origem é associado a um fluxo que processa cada pedido de entrega.The via statement joins the source to a flow that processes each delivery request. O to método associa o fluxo para o coletor de ponto de verificação, o que está incorporado ao reagir IoTHub.The to method joins the flow to the checkpoint sink, which is built into IoTHub React.

Reagir IoTHub utiliza uma estratégia de ponto de verificação diferente do que o processador de anfitrião do evento.IoTHub React uses a different checkpointing strategy than Event Host Processor. Pontos de verificação são escritos pelo coletor de ponto de verificação, o que é a terminação fase no pipeline.Checkpoints are written by the checkpoint sink, which is the terminating stage in the pipeline. O design de fluxos de Akka permite que o pipeline continuar a transmissão em fluxo de dados enquanto o sink é escrever o ponto de verificação.The design of Akka Streams allows the pipeline to continue streaming data while the sink is writing the checkpoint. Isso significa que os estágios de processamento a montante não precisam de esperar para o ponto de verificação acontecer.That means the upstream processing stages don't need to wait for checkpointing to happen. Pode configurar o ponto de verificação para ocorrer após um tempo limite ou de um determinado número de mensagens foram processado.You can configure checkpointing to occur after a timeout or after a certain number of messages have been processed.

O deliveryProcessor método cria o fluxo de Akka fluxos:The deliveryProcessor method creates the Akka Streams flow:

private static Flow<AkkaDelivery, MessageFromDevice, NotUsed> deliveryProcessor() {
    return Flow.of(AkkaDelivery.class).map(delivery -> {
        CompletableFuture<DeliverySchedule> completableSchedule = DeliveryRequestEventProcessor
                .processDeliveryRequestAsync(delivery.getDelivery(),
                        delivery.getMessageFromDevice().properties());

        completableSchedule.whenComplete((deliverySchedule,error) -> {
            if (error!=null){
                Log.info("failed delivery" + error.getStackTrace());
            }
            else{
                Log.info("Completed Delivery",deliverySchedule.toString());
            }

        });
        completableSchedule = null;
        return delivery.getMessageFromDevice();
    });
}

O fluxo chama um estático processDeliveryRequestAsync método que faz o trabalho real de processamento de cada mensagem.The flow calls a static processDeliveryRequestAsync method that does the actual work of processing each message.

Dimensionamento com o IoTHub ReactScaling with IoTHub React

O serviço de Scheduler foi concebido para que cada instância de contentor lê a partir de uma única partição.The Scheduler service is designed so that each container instance reads from a single partition. Por exemplo, se o Hub de eventos tem 32 partições, o serviço de agendador é implementado com 32 réplicas.For example, if the Event Hub has 32 partitions, the Scheduler service is deployed with 32 replicas. Isto permite uma grande flexibilidade em termos de dimensionamento horizontal.This allows for a lot of flexibility in terms of horizontal scaling.

Dependendo do tamanho do cluster, um nó do cluster pode ter mais do que um pod do serviço de agendador em execução no mesmo.Depending on the size of the cluster, a node in the cluster might have more than one Scheduler service pod running on it. Mas se o serviço de Scheduler necessita de mais recursos, o cluster pode ser aumentado horizontalmente, para distribuir os pods por mais nós.But if the Scheduler service needs more resources, the cluster can be scaled out, in order to distribute the pods across more nodes. Nossos testes de desempenho mostraram que o serviço de Scheduler está e thread-vinculadas à memória, para que o desempenho a depender bastante o tamanho da VM e o número de pods por nó.Our performance tests showed that the Scheduler service is memory- and thread-bound, so performance depended greatly on the VM size and the number of pods per node.

Cada uma delas precisa saber quais os Hubs de eventos para ler a partir de partição.Each instance needs to know which Event Hubs partition to read from. Para configurar o número de partição, aproveitamos o StatefulSet tipo de recurso no Kubernetes.To configure the partition number, we took advantage of the StatefulSet resource type in Kubernetes. Pods num StatefulSet têm um identificador persistente que inclui um índice numérico.Pods in a StatefulSet have a persistent identifier that includes a numeric index. Especificamente, é o nome de pod <statefulset name>-<index>, e este valor está disponível para o contentor através do Kubernetes API descendente.Specifically, the pod name is <statefulset name>-<index>, and this value is available to the container through the Kubernetes Downward API. Tempo de execução, os serviços de agendador lê o nome de pod e utiliza o índice de pod como o ID de partição.At run time, the Scheduler services reads the pod name and uses the pod index as the partition ID.

Se precisasse ampliar ainda mais o serviço de Scheduler, pode atribuir mais de um pod por partição do hub de eventos, para que vários pods estão lendo a cada partição.If you needed to scale out the Scheduler service even further, you could assign more than one pod per event hub partition, so that multiple pods are reading each partition. No entanto, nesse caso, cada instância seria leia todos os eventos na partição atribuído.However, in that case, each instance would read all of the events in the assigned partition. Para evitar o processamento duplicado, precisaria usar um algoritmo de hash, para que cada instância ignora uma parte das mensagens.To avoid duplicate processing, you would need to use a hashing algorithm, so that each instance skips over a portion of the messages. Dessa forma, vários leitores podem consumir o fluxo, mas cada mensagem é processada por apenas uma instância.That way, multiple readers can consume the stream, but every message is processed by only one instance.

Diagrama de hash do hub de eventos

Filas do Service BusService Bus queues

Uma terceira opção, que consideramos era copiar as mensagens dos Hubs de eventos numa fila do Service Bus e, em seguida, o serviço de agendador leu as mensagens do Service Bus.A third option that we considered was to copy messages from Event Hubs into a Service Bus queue, and then have the Scheduler service read the messages from Service Bus. Pode parecer estranho para escrever os pedidos de entrada nos Hubs de eventos apenas para copiá-los no Service Bus.It might seem strange to writing the incoming requests into Event Hubs only to copy them in Service Bus. No entanto, a idéia era para aproveitar a diferentes pontos fortes de cada serviço: Utilize os Hubs de eventos que para visam absorver picos de tráfego intenso, aproveitando a semântica de fila do Service Bus para processar a carga de trabalho com um padrão de consumidores concorrentes.However, the idea was to leverage the different strengths of each service: Use Event Hubs to absorb spikes of heavy traffic, while taking advantage of the queue semantics in Service Bus to process the workload with a competing consumers pattern. Lembre-se de que nosso destino para débito constante for inferior a nossa carga máxima esperada, então, processamento fila do Service Bus não precisariam ser mais rápido a ingestão de mensagens.Remember that our target for sustained throughput is less than our expected peak load, so processing the Service Bus queue would not need to be as fast the message ingestion.

Com esta abordagem, nossa implementação de uma prova de conceito, obtido cerca de 4 mil operações por segundo.With this approach, our proof-of-concept implementation achieved about 4K operations per second. Esses testes utilizaram os serviços de back-end fictício que não o tiver feito qualquer trabalho real, mas simplesmente adicionado uma quantidade fixa de latência por serviço.These tests used mock backend services that did not do any real work, but simply added a fixed amount of latency per service. Tenha em atenção de que nosso números de desempenho foram muito menor do que o máximo teórico de Service Bus.Note that our performance numbers were much less than the theoretical maximum for Service Bus. Motivos possíveis para as discrepâncias incluem:Possible reasons for the discrepancy include:

  • Não ter valores ideal para os vários parâmetros de cliente, como o limite do conjunto de ligação, o grau de paralelização, a contagem de obtenção prévia e o tamanho do lote.Not having optimal values for various client parameters, such as the connection pool limit, the degree of parallelization, the prefetch count, and the batch size.

  • Afunilamentos de e/s de rede.Network I/O bottlenecks.

  • Usar PeekLock modo vez ReceiveAndDelete, que era necessário para garantir que pelo-menos-uma vez entrega de mensagens.Use of PeekLock mode rather than ReceiveAndDelete, which was needed to ensure at-least-once delivery of messages.

Testes de desempenho mais poderão ter detetado a causa raiz e permitiu-nos resolver estes problemas.Further performance tests might have discovered the root cause and allowed us to resolve these issues. No entanto, IotHub reagir cumprido nosso destino de desempenho, portanto, escolhemos essa opção.However, IotHub React met our performance target, so we chose that option. Dito isso, o Service Bus é uma opção viável para este cenário.That said, Service Bus is a viable option for this scenario.

Processamento de falhasHandling failures

Existem três categorias gerais de falha a serem considerados.There are three general classes of failure to consider.

  1. Um serviço downstream pode ter uma falha não transitórias, que é qualquer falha que provavelmente não desaparecerá por si só.A downstream service may have a non-transient failure, which is any failure that's unlikely to go away by itself. Falhas não transitórias incluem as condições de erro normal, como entrada inválida para um método.Non-transient failures include normal error conditions, such as invalid input to a method. Eles também incluem exceções sem tratamento no código da aplicação ou um processo a falhar.They also include unhandled exceptions in application code or a process crashing. Se este tipo de erro ocorrer, a transação de toda a empresa tem de ser marcada como uma falha.If this type of error occurs, the entire business transaction must be marked as a failure. Poderá ser necessário anular outros passos na mesma transação que já foi concluída com êxito.It may be necessary to undo other steps in the same transaction that already succeeded. (Veja a transações de compensação, abaixo).(See Compensating Transactions, below.)

  2. Um serviço downstream pode ocorrer uma falha transitória, como o tempo limite da rede.A downstream service may experience a transient failure such as a network timeout. Estes erros, muitas vezes, podem ser resolvidos bastando repetir a chamada.These errors can often be resolved simply by retrying the call. Se a operação continuar a falhar após um determinado número de tentativas, ele é considerado uma falha não transitórias.If the operation still fails after a certain number of attempts, it's considered a non-transient failure.

  3. O próprio serviço de Scheduler pode de falha (por exemplo, uma vez que um nó falha).The Scheduler service itself might fault (for example, because a node crashes). Nesse caso, Kubernetes será apresentada uma nova instância do serviço.In that case, Kubernetes will bring up a new instance of the service. No entanto, é necessário recomeçar todas as transações que foram já em curso.However, any transactions that were already in progress must be resumed.

Transações de compensaçãoCompensating transactions

Se ocorrer uma falha de não transitórias, a transação atual pode estar num Falha parcial Estado, em que um ou mais passos já foi concluídos com êxito.If a non-transient failure happens, the current transaction might be in a partially failed state, where one or more steps already completed successfully. Por exemplo, se o serviço de Drones agendado já um drone, drone deve ser cancelado.For example, if the Drone service already scheduled a drone, the drone must be canceled. Nesse caso, a aplicação tem de anular os passos que teve êxito, com um transação de compensação.In that case, the application needs to undo the steps that succeeded, by using a Compensating Transaction. Em alguns casos, isso deve ser feito por um sistema externo ou até mesmo por um processo manual.In some cases, this must be done by an external system or even by a manual process.

Se a lógica para transações de compensação é complexa, considere a criação de um serviço separado que é responsável por esse processo.If the logic for compensating transactions is complex, consider creating a separate service that is responsible for this process. No aplicativo de entrega por Drone, o serviço de agendador coloca as operações com falhas para uma fila dedicada.In the Drone Delivery application, the Scheduler service puts failed operations onto a dedicated queue. Um microsserviço separado, chamado o Supervisor lê desta fila e chama um API de cancelamento nos serviços de que precisam para compensar.A separate microservice, called the Supervisor, reads from this queue and calls a cancellation API on the services that need to compensate. Esta é uma variação do padrão do Supervisor de agente do Scheduler.This is a variation of the Scheduler Agent Supervisor pattern. O serviço de Supervisor poderá efetuar outras ações também, como notificar o utilizador por e-mail ou de texto ou enviar um alerta para um dashboard de operações.The Supervisor service might take other actions as well, such as notify the user by text or email, or send an alert to an operations dashboard.

Diagrama que mostra os microsserviços de Supervisor

Operações de não idempotentes vs IdempotentesIdempotent vs non-idempotent operations

Para evitar perder quaisquer pedidos, o serviço de Scheduler tem de garantir que todas as mensagens são processadas pelo menos uma vez.To avoid losing any requests, the Scheduler service must guarantee that all messages are processed at least once. Os Hubs de eventos podem garantir a entrega pelo-menos-uma vez, se os pontos de verificação do cliente corretamente.Event Hubs can guarantee at-least-once delivery if the client checkpoints correctly.

Se o serviço de agendador falhar, poderá ser no meio do processamento de pedidos de cliente de um ou mais.If the Scheduler service crashes, it may be in the middle of processing one or more client requests. Essas mensagens serão captadas por outra instância do Scheduler e sejam reprocessadas.Those messages will be picked up by another instance of the Scheduler and reprocessed. O que acontece se uma solicitação é processada duas vezes?What happens if a request is processed twice? É importante evitar a duplicação de qualquer trabalho.It's important to avoid duplicating any work. Afinal de contas, não queremos o sistema para enviar dois drones para o mesmo pacote.After all, we don't want the system to send two drones for the same package.

Uma abordagem é criar todas as operações sejam idempotentes.One approach is to design all operations to be idempotent. Uma operação é idempotente se ele pode chamado várias vezes sem produzir efeitos colaterais adicionais após a primeira chamada.An operation is idempotent if it can be called multiple times without producing additional side-effects after the first call. Em outras palavras, um cliente pode chamar a operação uma vez, duas vezes ou muitas vezes, e o resultado será o mesmo.In other words, a client can invoke the operation once, twice, or many times, and the result will be the same. Essencialmente, o serviço deve ignorar chamadas duplicadas.Essentially, the service should ignore duplicate calls. Para um método com efeitos secundários sejam idempotentes, o serviço tem de ser capaz de detetar chamadas duplicadas.For a method with side effects to be idempotent, the service must be able to detect duplicate calls. Por exemplo, pode ter atribuir o ID do autor da chamada, em vez de ter o serviço de gerar um novo ID.For example, you can have the caller assign the ID, rather than having the service generate a new ID. O serviço, em seguida, pode verificar os IDs duplicados.The service can then check for duplicate IDs.

Nota

A especificação de HTTP determina que os métodos GET, PUT e DELETE devem ser idempotentes.The HTTP specification states that GET, PUT, and DELETE methods must be idempotent. Não são garantidos que métodos POST ser idempotentes.POST methods are not guaranteed to be idempotent. Se um método POST criar um novo recurso, geralmente não há nenhuma garantia de que esta operação é idempotente.If a POST method creates a new resource, there is generally no guarantee that this operation is idempotent.

Nem sempre é simples escrever o método idempotentes.It's not always straightforward to write idempotent method. Outra opção é para o Scheduler controlar o progresso de todas as transações num local durável.Another option is for the Scheduler to track the progress of every transaction in a durable store. Sempre que processa uma mensagem, ele poderia procurar o estado no armazenamento durável.Whenever it processes a message, it would look up the state in the durable store. Após cada passo, ele escreveria o resultado para o arquivo.After each step, it would write the result to the store. Pode haver implicações de desempenho para essa abordagem.There may be performance implications to this approach.

Exemplo: Operações IdempotentesExample: Idempotent operations

A especificação de HTTP indica que PUT métodos devem ser idempotentes.The HTTP specification states that PUT methods must be idempotent. A especificação define idempotentes, desta forma:The specification defines idempotent this way:

Um método de pedido é considerado "idempotente" se o efeito pretendido no servidor de vários pedidos idênticos com esse método é o mesmo que o efeito para um único esse pedido.A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. (RFC 7231)(RFC 7231)

É importante compreender a diferença entre a semântica PUT e POST ao criar uma nova entidade.It's important to understand the difference between PUT and POST semantics when creating a new entity. Em ambos os casos, o cliente envia uma representação de uma entidade no corpo do pedido.In both cases, the client sends a representation of an entity in the request body. Mas o significado do URI é diferente.But the meaning of the URI is different.

  • Para um método POST, o URI representa um recurso principal da nova entidade, por exemplo, uma coleção.For a POST method, the URI represents a parent resource of the new entity, such as a collection. Por exemplo, para criar uma nova entrega, o URI pode ser /api/deliveries.For example, to create a new delivery, the URI might be /api/deliveries. O servidor cria a entidade e o atribui um novo URI, como /api/deliveries/39660.The server creates the entity and assigns it a new URI, such as /api/deliveries/39660. Este URI é retornado no cabeçalho Location da resposta.This URI is returned in the Location header of the response. Sempre que o cliente envia um pedido, o servidor irá criar uma nova entidade com um novo URI.Each time the client sends a request, the server will create a new entity with a new URI.

  • Para um método PUT, o URI identifica a entidade.For a PUT method, the URI identifies the entity. Se já existe uma entidade com esse URI, o servidor de substituir a entidade existente com a versão no pedido.If there already exists an entity with that URI, the server replaces the existing entity with the version in the request. Se não existe nenhuma entidade com esse URI, o servidor cria um.If no entity exists with that URI, the server creates one. Por exemplo, suponha que o cliente envia um pedido PUT para api/deliveries/39660.For example, suppose the client sends a PUT request to api/deliveries/39660. Partindo do princípio de que não existe nenhum entrega com esse URI, o servidor cria uma nova.Assuming there is no delivery with that URI, the server creates a new one. Agora, se o cliente envia o pedido mesmo novamente, o servidor irá substituir a entidade existente.Now if the client sends the same request again, the server will replace the existing entity.

Eis a implementação do serviço de entrega do método PUT.Here is the Delivery service's implementation of the PUT method.

[HttpPut("{id}")]
[ProducesResponseType(typeof(Delivery), 201)]
[ProducesResponseType(typeof(void), 204)]
public async Task<IActionResult> Put([FromBody]Delivery delivery, string id)
{
    logger.LogInformation("In Put action with delivery {Id}: {@DeliveryInfo}", id, delivery.ToLogInfo());
    try
    {
        var internalDelivery = delivery.ToInternal();

        // Create the new delivery entity.
        await deliveryRepository.CreateAsync(internalDelivery);

        // Create a delivery status event.
        var deliveryStatusEvent = new DeliveryStatusEvent { DeliveryId = delivery.Id, Stage = DeliveryEventType.Created };
        await deliveryStatusEventRepository.AddAsync(deliveryStatusEvent);

        // Return HTTP 201 (Created)
        return CreatedAtRoute("GetDelivery", new { id= delivery.Id }, delivery);
    }
    catch (DuplicateResourceException)
    {
        // This method is mainly used to create deliveries. If the delivery already exists then update it.
        logger.LogInformation("Updating resource with delivery id: {DeliveryId}", id);

        var internalDelivery = delivery.ToInternal();
        await deliveryRepository.UpdateAsync(id, internalDelivery);

        // Return HTTP 204 (No Content)
        return NoContent();
    }
}

Espera-se que o maior número de pedidos irá criar uma nova entidade, para que o método optimistically chama CreateAsync no objeto de repositório e, em seguida, processa todas as exceções recurso duplicado por atualizar o recurso em vez disso.It's expected that most requests will create a new entity, so the method optimistically calls CreateAsync on the repository object, and then handles any duplicate-resource exceptions by updating the resource instead.