Back-End

27 fev, 2019

Orquestrando microsserviços com Zeebe e Golang

Publicidade

Se você é de tecnologia e já fez seu TCC, talvez já deve ter visto ou modelado o fluxo do seu projeto. Isso clarifica aos professores como cada componente se comunica com outro, e por onde o dado trafega até chegar ao seu destino final.

Mostrarei de forma prática como você pode juntar os dois processos – desenvolvimento e modelagem de fluxo, para construir uma aplicação de alta performance utilizando Zeebe.

Sobre o Zeebe

O Zeebe é um orquestrador de microsserviços baseado em fluxo de trabalho. Os fluxos são modelados em BPMN (Business Process Model and Notation). Acaba que nada foge muito do que você já deve ter visto em relação à modelagem de fluxo.

Apesar de se utilizar de BPMN para modelar seu fluxo, para modelar você precisa utilizar o Zeebe Modeler, um projeto open source escrito em Nodejs.

Se você usa mac e brew, basta copiar o comando abaixo. No GitHub do projeto você encontra todo o processo para você realizar a instalação em outras plataformas.

brew cask install zeebe-modeler

Esse é o visual que você vai encontrar. O flow no print é o que eu pretendo esmiuçar no artigo.

Construindo um flow com Zeebe modeler

Esse foi o primeiro flow que eu escrevi utilizado o Zeebe. No dia a dia eu utilizo o Camunda com Camunda modeler. Zeebe pertence ao Camunda, então existe uma grande similaridade no processo.

Você poderia me perguntar: “Por que não escreve utilizando Camunda?”. O Camunda não possui um cliente oficial para Golang, apenas para Java, PHP, Nodejs e C#.

O código está disponível no meu GitHub – não vou me ater tanto à forma como organizei o código, mas para deixar um pouco mais fácil para fazer o deploy do bpmn para o Zeebe e ver o flow rodando, eu construí tudo utilizando docker.

A única coisa que você precisa ter na sua máquina é o docker e docker-compose, e na raiz do projeto basta rodar o comando abaixo:

make up

Vamos lá!

Na documentação cliente Go para o Zeebe, você vai encontrar um passo a passo até chegar no processo de estar utilizando uma task em go para realizar o processamento de uma tarefa.

No flow acima eu estou verificando a disponibilidade de um item. Caso ele esteja disponível, o flow vai tomar um fluxo, caso não esteja, tomará outro. Esse é um dos primeiros benefícios que o Zeebe traz: visibilidade.

Para quem é de negócios, isso é um ganho muito bom, pois ele pode acompanhar o tráfego do dado e quais foram a decisões tomadas.

Esse é um fluxo simples, mas em fluxos mais complexos é possível implementar decisões de rollback que acabam diminuindo muito o que precisa ser feito à nível de código.

Abaixo você pode verificar o código escrito go para processar a task CheckAvailability e logo a baixo um trecho do bpmn na qual é definido o tipo de task, que no meu caso é check-availability. Pelo Zeebe modeler você pode fazer isso pela aba Properties Panel, do lado direito do seu editor.

package tasks

import (
	"log"
	"math/rand"

	"github.com/zeebe-io/zeebe/clients/go/entities"
	"github.com/zeebe-io/zeebe/clients/go/worker"
)

//CheckAvailability -
func CheckAvailability(client worker.JobClient, job entities.Job) {
	jobKey := job.GetKey()

	payload, err := job.GetPayloadAsMap()
	if err != nil {
		// failed to handle job as we require the payload
		failJob(client, job)
		return
	}

	payload["available"] = rand.Int()%2 == 0
	request, err := client.NewCompleteJobCommand().JobKey(jobKey).PayloadFromMap(payload)
	if err != nil {
		// failed to set the updated payload
		failJob(client, job)
		return
	}

	log.Println("Complete job", jobKey, "of type", job.Type)
	request.Send()
}
<bpmn:serviceTask id="ServiceTask_009ozt4" name="CheckAvailability">
      <bpmn:extensionElements>
        <zeebe:taskDefinition type="check-availability" />
      </bpmn:extensionElements>
      <bpmn:incoming>SequenceFlow_1h63827</bpmn:incoming>
      <bpmn:outgoing>SequenceFlow_0u858r4</bpmn:outgoing>
    </bpmn:serviceTask>

Como a minha loja é fictícia, eu não tenho uma lógica para verificar a disponibilidade – eu gero um inteiro randômico e vejo se o resto da divisão dele por 2 é 0. Loja maneira essa.

Quando o meu processo termina eu pego payload, altero e submeto novamente para o flow. Se tudo foi enviado com sucesso eu libero o processo para a próximo task. No meu código eu usei o mesmo exemplo usado pelo Zeebe, inclusive a função failJob que é utilizada em caso de erro.

Um outro ponto legal são os Retries. Você pode definir a quantidade de vezes que a task deve ser executada em caso de erro. No print acima você pode ver o campo Retries, onde você pode definir essa configuração.

func failJob(client worker.JobClient, job entities.Job) {
	log.Println("Failed to complete job", job.GetKey())
	client.NewFailJobCommand().JobKey(job.GetKey()).Retries(job.Retries - 1).Send()
}

Gateways

O Zeebe possui três tipos de gateways: Exclusive Gateways, Parallel Gateways e Event-Based Gateways. Cada um pode ser utilizado de acordo com a modelagem do seu flow. No meu caso, como eu precisava apenas desviar o fluxo em caso de indisponibilidade, eu utilizei o Exclusive.

Para definir a condição do seu if/else, basta clicar na linha e definir no campo Condition expression. A sintaxe é bem simples. A propriedade $.nome_da_propriedade foi submetida pela task check-availability.

Todo o fluxo começa com um StartEvent que é representado pelo circulo com linha mais fina e o EndEvent, que é representado pelo circulo com a linha mas grossa.

Sempre que estiver modelando um fluxo você precisa utilizar esses dois eventos. Você pode dar um nome para esses eventos – eu dei Start e Finish. No fluxo dado como exemplo, quando ele chega ao Finish, o Zeebe encerra o flow.

Pontos interessantes do Zeebe

Se você acessar what’s zeebe, você vai encontrar vários pontos que podem te auxiliar na decisão de utilizar o Zeebe ou não. Mais vou destacar aqui os que eu achei interessantes:

  • Escalabilidade horizontal: o Zeebe não tem dependência de banco externo, ele escreve os dados direto no file system da máquina onde ele está deployado e pode facilmente distribuir o processamento entre cluster
  • Tolerância a falhas e alta disponibilidade: com fácil configuração, o Zeebe pode se recuperar de falhas de máquina ou software, sem perda de dados e com menor downtime
  • Modelagem de fluxo: técnicos e não técnicos podem colaborar com o workflow na mesma linguagem.
  • Agnóstico de linguagem: atualmente o Zeebe possui dois clientes para Java e Golang. O cliente Zeebe é baseado em grpc. Logo, o cliente pode ser facilmente gerado para outras linguagens. No site do Grpc você pode ver as linguagens suportadas