top of page

My Items

I'm a title. ​Click here to edit me.

Guia rápido sobre Apache Kafka: O poder da arquitetura Event-Driven

Guia rápido sobre Apache Kafka: O poder da arquitetura Event-Driven

Introdução No mundo orientado por dados de hoje, a capacidade de processar e analisar eficientemente grandes volumes de dados em tempo real tornou-se um fator determinante para empresas e organizações de todos os tamanhos. Desde plataformas de comércio eletrônico e redes sociais até instituições financeiras e dispositivos IoT, a demanda por lidar com fluxos de dados em escala está em constante crescimento. É aí que o Apache Kafka entra como uma ferramenta fundamental no mundo da arquitetura orientada por eventos. Imagine uma tecnologia que pode conectar, processar e entregar dados de forma contínua entre inúmeros sistemas e aplicativos em tempo real. O Apache Kafka, frequentemente chamado de plataforma de streaming distribuído, é exatamente essa tecnologia. É o herói desconhecido nos bastidores, permitindo o fluxo de dados em tempo real e fornecendo a base para uma infinidade de aplicativos modernos orientados por dados. Neste guia rápido sobre Apache Kafka, vamos mostrar os principais componentes da arquitetura que compõem o Apache Kafka, desvendando seus conceitos essenciais, arquitetura, casos de uso e por último, um tutorial prático onde você será capaz de entender a dinâmica e funcionamento de como um produtor e consumidor de eventos se comunicam através de um tópico. Portanto, embarquemos nessa aventura e descubramos como o Apache Kafka está revolucionando a forma como lidamos com dados no século 21. Conceitos chave do Kafka 1. Tópicos O que são Tópicos no Kafka? No Kafka, um tópico é um canal ou categoria lógica para dados. Ele atua como um canal onde são mantidos os registros enviados, permitindo que os produtores escrevam dados em tópicos específicos e que os consumidores leiam deles. Pense nos tópicos como uma maneira de categorizar e separar os fluxos de dados. Por exemplo, em uma plataforma de comércio eletrônico, você pode ter tópicos como "Atualizações de Pedidos", "Alterações de Estoque" e "Feedback do Cliente", cada um dedicado a um tipo específico de dados. Particionamento dos Tópicos Uma das características poderosas dos tópicos do Kafka é o particionamento. Quando um tópico é dividido em partições, isso aprimora a capacidade do Kafka de lidar com grandes volumes de dados e distribuir a carga entre vários corretores. As partições são as unidades de paralelismo no Kafka e fornecem tolerância a falhas, escalabilidade e capacidades de processamento paralelo. Cada partição é ordenada e imutável, e os registros dentro de uma partição são atribuídos a um deslocamento único, que é um identificador numérico que representa a posição de um registro dentro da partição. Esse deslocamento é usado pelos consumidores para controlar os dados consumidos, permitindo que eles retomem de onde pararam em caso de falha ou ao processar dados em tempo real. Organização de Dados Tópicos possibilitam uma organização dos dados. Conforme mostrado nas imagens acima, os tópicos funcionam como uma camada de armazenamento dentro do contexto do Kafka onde todos os dados que são enviados pelo produtores são organizados em tópicos e partições. Modelo de Publicação e Assinatura O modelo Publicação e Assinatura ou pub/sub é um modelo no qual os produtores publicam dados em um tópico e os consumidores se inscrevem nos tópicos de interesse para receber os dados. Uma analogia do mundo real é quando nos cadastramos para receber newsletter de um determinado site, ou seja, o editor daquele site publica notícias e nós como consumidores, recebemos tudo que for publicado. Escalabilidade Os tópicos podem ser divididos em partições, permitindo ao Kafka distribuir dados em vários brokers possibilitando escalabilidade e processamento paralelo. Retenção dos dados Cada tópico pode ter sua própria política de retenção de dados, definindo por quanto tempo os dados permanecem no tópico. Regras como estas possibilitam administrar melhor o volume de dados armazenados nos tópicos podendo ou não liberar espaço em disco. 2. Produtores No Kafka, um produtor é um componente crucial responsável por enviar dados aos tópicos do Kafka. Pense nos produtores como os originadores de informações - aplicativos ou sistemas que geram e publicam registros em tópicos específicos dentro do cluster do Kafka. Esses registros podem representar desde eventos de usuário em um site até logs do sistema ou transações financeiras. Os produtores são a fonte da verdade para dados no Kafka. Eles geram registros e os enviam para tópicos designados para processamento posterior. Eles também decidem para qual tópico um registro deve ser enviado, com base na natureza dos dados. Isso garante que os dados sejam categorizados adequadamente dentro do ecossistema do Kafka. Tipo do dado Geralmente utiliza-se JSON como formato de dado para o envio, dessa forma facilita a transferência eficiente e o armazenamento de dados. Confirmação de entrega Os produtores podem lidar com confirmações do broker do Kafka, garantindo que os dados sejam recebidos e persistidos com sucesso. Esse mecanismo de confirmação contribui para a confiabilidade dos dados e que estão sendo entregues ao tópico sem perda dos dados. Envio para partições específicas Conforme falado mais cedo, produtores enviam dados para tópicos, porém é possível configurar o envio para uma partição específica dentro de um tópico. 3. Consumidores Consumidores são componentes importantes dentro do contexto Kafka, são responsáveis por consumir e fornecer os dados de origem. Tecnicamente, consumidores se inscrevem em tópicos do Kafka e qualquer informação ali produzida são recebidas pelos consumidores, representando a abordagem pub /sub. Inscrição em Tópicos Como citado anteriormente, consumidores são um dos responsáveis por fornecer dados direto da origem. Consumidores possuem a flexibilidade de se inscrever em qualquer tópico de acordo com seus interesses possibilitando receber dados relevantes para o negócio. Processamento dos dados Consumidores sempre vão receber novos dados vindo dos tópicos, ou seja, cada consumidor é responsável processar estes dados de acordo suas necessidades. Pense em um microserviço que funciona como um consumidor, este pode consumir dados de um tópico responsável por armazenar logs de aplicações e executar qualquer tratamento antes de entrega-lo ao usuário ou para outras aplicações terceiras. Integração entre aplicações Como citado no item anterior, Kafka possibilita aplicações de integrarem seus serviços facilmente através de tópicos e consumidores variados. Um dos casos de usos mais comuns é a integração entre aplicações. Antigamente, aplicações precisavam conectar em diferentes bancos de dados para acessar dados de outras aplicações, isso criava vulnerabilidades e feria princípios de responsabilidades entre as aplicações. Soluções como Kafka possibilita integrar diferentes serviços usando o padrão pub /sub onde diferentes consumidores representados por aplicações podem acessar os mesmos tópicos e processar estes dados em tempo real sem a necessidade de acessar bancos de dados de terceiros ou qualquer outra fonte de dado evitando qualquer risco de segurança e adicionado agilidade ao processo de entrega dos dados. 4. Brokers Brokers são peças fundamentais na arquitetura do Kafka, são eles os responsáveis por mediar e gerenciar a troca de mensagens entre produtores e consumidores, ou seja, Brokers gerenciam o armazenamento dos dados produzidos pelos produtores e garantem uma transmissão confiável dos dados dentro de um cluster Kafka. Na prática, Brokers possuem um papel transparente dentro de um cluster Kafka, mas a seguir irei pontuar algumas de suas responsabilidades que fazem toda a diferença para o funcionamento do Kafka. Recepção dos dados Brokers são responsáveis pelo recebimento dos dados, eles funcionam como um entry-point ou porta de entrada pelo dados produzidos e em seguida gerenciam todo o armazenamento para que sejam consumidos por qualquer consumidor. Tolerância a falhas Como toda arquitetura de dados, precisamos pensar na tolerância a falhas. No contexto do Kafka são os Brokers responsáveis por garantir que mesmo em falhas os dados sejam duráveis e que mantenham uma alta disponibilidade. Brokers são responsáveis por gerenciar as partições dentro dos tópicos capazes de replicar os dados prevendo qualquer falha e diminuindo a possibilidade da perda de dados. Replicação de dados Como citado no item anterior, a replicação de dados é uma forma de diminuir a perda de dados em casos de falha. A replicação de dados é feita a partir de múltiplas replicas de partições armazenados em diferentes Brokers, isso permite que mesmo se um Broker falhe, haverá dados replicados em vários outros. Responsável por gerenciar partições Citamos recente sobre partições dentro de tópicos mas não citamos quem as gerenciam. Partições são gerenciadas por um Broker que trabalha coordenando a leitura e escrita naquela partição e também distribuindo o carregamento dos dados pelo cluster. Resumindo, os Brokers desempenham um trabalho de orquestração dentro um cluster Kafka, gerenciando a leitura e escrita feita pelos produtores e consumidores, garantindo que a troca de mensagens sejam realizadas e que não haverá perda dos dados em caso de falhas em alguns dos seus componentes através da replicação dos dados também gerenciada por eles. Conclusão Apache Kafka se destaca como uma solução robusta e poderosa, atendendo às demandas complexas de ambientes modernos baseados em dados. Seus recursos escaláveis, tolerantes a falhas e processamento em tempo real o tornam parte integrante das arquiteturas que lidam com fluxos de dados dinâmicos e em grande escala. Como mostrado neste post, Kafka atende diversos cenários, desde processamento de dados em tempo real em quer requer uma versão fresca do dado como em qualquer arquitetura de dados atualmente, e até como uma solução para integrar aplicações oferecendo o uso do padrão publish/subscribe. Kafka tem sido adotado por grandes empresas como: Linkedin, onde originalmente foi criado. Netflix, Uber, Airbnb, Wallmart , Goldman Sachs, Twitter e várias outras.

Quick guide about Apache Kafka: Powering Event-Driven architecture

Quick guide about Apache Kafka: Powering Event-Driven architecture

Introduction In today's data-driven world, the ability to efficiently process and analyze vast amounts of data in real-time has become a game-changer for businesses and organizations of all sizes. From e-commerce platforms and social media to financial institutions and IoT devices, the demand for handling data streams at scale is ever-increasing. This is where Apache Kafka steps in as a pivotal tool in the world of event-driven architecture. Imagine a technology that can seamlessly connect, process, and deliver data between countless systems and applications in real-time. Apache Kafka, often referred to as a distributed streaming platform, is precisely that technology. It's the unsung hero behind the scenes, enabling real-time data flow and providing a foundation for a multitude of modern data-driven applications. In this quick guide about Apache Kafka, we'll take a deep dive into Apache Kafka, unraveling its core concepts, architecture, and use cases. Whether you're new to Kafka or looking to deepen your understanding, this guide will serve as your compass on a journey through the exciting world of real-time data streaming. We'll explore the fundamental principles of Kafka, share real-world examples of its applications, and provide practical insights for setting up your own Kafka environment. So, let's embark on this adventure and discover how Apache Kafka is revolutionizing the way we handle data in the 21st century. Key Concepts of Kafka 1. Topics What Are Kafka Topics? In Kafka, a topic is a logical channel or category for data. It acts as a named conduit for records, allowing producers to write data to specific topics and consumers to read from them. Think of topics as a way to categorize and segregate data streams. For example, in an e-commerce platform, you might have topics like "OrderUpdates," "InventoryChanges," and "CustomerFeedback," each dedicated to a specific type of data. Partitioning within Topics One of the powerful features of Kafka topics is partitioning. When a topic is divided into partitions, it enhances Kafka's ability to handle large volumes of data and distribute the load across multiple brokers. Partitions are the unit of parallelism in Kafka, and they provide fault tolerance, scalability, and parallel processing capabilities. Each partition is ordered and immutable, and records within a partition are assigned a unique offset, which is a numeric identifier representing the position of a record within the partition. This offset is used by consumers to keep track of the data they have consumed, allowing them to resume from where they left off in case of failure or when processing real-time data. Data organization Topics provide a structured way to organize data. They are particularly useful when dealing with multiple data sources and data types. Topics works as a storage within Kafka context where data sent by producers is organized into topics and partitions. Publish-Subscribe Model Kafka topics implement a publish-subscribe model, where producers publish data to a topic, and consumers subscribe to topics of interest to receive the data. An analogy that we can do is when we subscribe to a newsletter to receive some news or articles. When some news is posted, you as a subscriber will receive it. Scalability Topics can be split into partitions, allowing Kafka to distribute data across multiple brokers for scalability and parallel processing. Data Retention Each topic can have its own data retention policy, defining how long data remains in the topic. This makes easier to manage the data volume wheter or not frees up space. 2. Producers In Kafka, a producer is a crucial component responsible for sending data to Kafka topics. Think of producers as information originators—applications or systems that generate and publish records to specific topics within the Kafka cluster. These records could represent anything from user events on a website to system logs or financial transactions. Producers are the source of truth for data in Kafka. They generate records and push them to designated topics for further processing. Also decide which Topic the message will be send based on the nature of the data. This ensures that data is appropriately categorized within the Kafka ecosystem. Data Type Usually producers send messages based on JSON format that makes easier the data transferring into the storage. Acknowledgment Handling Producers can handle acknowledgments from the Kafka broker, ensuring that data is successfully received and persisted. This acknowledgment mechanism contributes to data reliability. Sending data to specific partitions Producers can send messages directly to a specific partition within a Topic. 3. Consumers Consumers are important components in the Kafka context, they are responsible for consuming and providing data from the source. Basically, consumers subscribe to Kafa Topics and any data produced there will be received by consumers representing the pub/sub approach. Subscribing to Topics Consumers actively subscribe to Kafka topics, indicating their interest in specific streams of data. This subscription model enables consumers to receive relevant information aligned with their use case. Data Processing Consumers will always receive new data from topics, each consumer is responsible for processing this data according to their needs. A microservice that works as a consumer for example, it can consume data from a topic responsible for storing application logs and performing any processing before delivering it to the user or to other third-party applications. Integration between apps As mentioned previously, Kafka enables applications to easily integrate their services across varied topics and consumers. One of the most common use cases is integration between applications. In the past, applications needed to connect to different databases to access data from other applications, this created vulnerabilities and violated principles of responsibilities between applications. Technologies like Kafka make it possible to integrate different services using the pub/sub pattern where different consumers represented by applications can access the same topics and process this data in real time without the need to access third-party databases or any other data source, avoiding any security risk and added agility to the data delivery process. 4. Brokers Brokers are fundamental pieces in Kafka's architecture, they are responsible for mediating and managing the exchange of messages between producers and consumers. Brokers manage the storage of data produced by producers and guarantee reliable transmission of data within a Kafka cluster. In practice, Brokers have a transparent role within a Kafka cluster, but below I will highlight some of their responsibilities that make all the difference to the functioning of Kafka. Data reception Brokers are responsible for receiving the data, they function as an entry-point or proxy for the data produced and then manage all storage so that it can be consumed by any consumer. Fault tolerance Like all data architecture, we need to think about fault tolerance. In the context of Kafka, Brokers are responsible for ensuring that even in the event of failures, data is durable and maintains high availability. Brokers are responsible for managing the partitions within the topics capable of replicating the data, predicting any failure and reducing the possibility of data loss. Data replication As mentioned in the previous item, data replication is a way to reduce data loss in cases of failure. Data replication is done from multiple replicas of partitions stored in different Brokers, this allows that even if one Broker fails, there is data replicated in several others. Responsible for managing partitions We mentioned a recent article about partitions within topics but we did not mention who manages them. Partitions are managed by a Broker that works by coordinating reading and writing to that partition and also distributing data loading across the cluster. In short, Brokers perform orchestration work within a Kafka cluster, managing the reading and writing done by producers and consumers, ensuring that message exchanges are carried out and that there will be no loss of data in the event of failures in some of its components through data replication also managed by them. Conclusion Apache Kafka stands as a versatile and powerful solution, addressing the complex demands of modern data-driven environments. Its scalable, fault-tolerant, and real-time capabilities make it an integral part of architectures handling large-scale, dynamic data streams. Kafka has been adopted by different companies and business sectors such as Linkedin, where Kafka was developed by the way, Netflix, Uber, Airbnb, Wallmart, Goldman Sachs, Twitter and more.

Accessing APIs and Extracting Data with Airflow

Accessing APIs and Extracting Data with Airflow

Intro Airflow provides different ways of working with automated flows and one of the ways is the possibility of accessing external APIs using HTTP operators and extracting the necessary data. hands-on In this tutorial we will create a DAG which will access an external API and extract the data directly to a local file. If this is your first time using Airflow, I recommend accessing this link to understand more about Airflow and how to set up an environment. Creating the DAG For this tutorial, we will create a DAG that will trigger every 1 hour (schedule_interval="0 * * * *") and access an external API by extracting some data directly to a local JSON file. In this scenario we will use the SimpleHttpOperator operator which provides an API capable of executing requests to external APIs. Note that we use two operators within the same DAG. The SimpleHttpOperator operator that provides ways of accessing external APIs that through the method field we define HTTPs methods (GET, POST, PUT, DELETE). The endpoint field allows specifying the endpoint of the API, which in this case is products and finally, the http_conn_id parameter, where it's necessary to pass the identifier of the connection that will be defined next through the Airflow UI. As shown below, access the menu Admin > Connections Fill in the data as shown in the image below and then save. About the PythonOperator operator, we are only using it to execute a Python function called _write_response using XComs where through the task_id of the write_response task, it is possible to retrieve the result of the response and use it in any part of the code. In this scenario we are using the result retrieved from the API to write to the file. XCom is a communication mechanism between different tasks that makes Airflow very flexible. Tasks can often be executed on different machines and with the use of XComs, communication and information exchange between Tasks is possible. Finally, we define the execution of the tasks and their dependencies, see that we use the >> operator, which is basically to define the order of execution between the tasks. In our case, API access and extraction must be performed before writing to the file extract_data >> write_response . After executing the DAG, it is possible to access the file that was generated with the result of the extraction, just access one of the workers via the terminal, which in this case will only have one. Run the following command below to list the containers: docker ps A listing similar to the one below will be displayed. Notice that one of the lines in the NAMES column refers to the worker, in this case coffee_and_tips_airflow-worker_1 . Continuing in the terminal, type the following command to access the Airflow directory where the extract_data.json file is located. docker exec -it coffee_and_tips_airflow-worker_1 /bin/bash It's done, now just open the file and check the content. Conclusion Once again we saw the power of Airflow for automated processes that require easy access and integration of external APIs with few lines of code. In this example, we explore the use of XComs, which aims to make the exchange of messages between tasks that can be executed on different machines in a distributed environment more flexible. Hope you enjoyed!

Acessando APIs e extraindo dados com Airflow

Acessando APIs e extraindo dados com Airflow

Introdução Airflow provê diferentes formas de se trabalhar com fluxos automatizados e uma das formas é a possibilidade de acesso a APIs externas utilizando operadores HTTP e extraindo os dados necessários. Mãos na massa Neste tutorial iremos criar uma DAG na qual acessará uma API externa e fará a extração dos dados diretamente para um arquivo local. Se for sua primeira vez utilizando Airflow, recomendo acessar este link para entender mais sobre o Airflow e como montar um ambiente. Criando a DAG Para este tutorial, criaremos uma DAG que será trigada a cada 1 hora (schedule_interval=" 0 * * * *" ) e acessará uma API externa extraindo alguns dados diretamente para um arquivo JSON local. Neste cenário usaremos o operador SimpleHttpOperator onde provê uma API com capacidade de executar requisições para APIs externas. Perceba que utilizamos dois operadores dentro de uma mesma DAG. O operador SimpleHttpOperator que provê formas de acesso a APIs externas que através do campo method definimos métodos HTTPs (GET, POST, PUT, DELETE). O campo endpoint permite especificar o endpoint da API, que no caso é products e por fim, o parâmetro http_conn_id , onde é necessário passar o identificador da conexão que será definida a seguir através da UI do Airflow. Conforme imagem abaixo, acesse o menu Admin > Connections Preencha os dados conforme imagem abaixo e salve em seguida. Sobre o operador PythonOperator , estamos utilizando apenas para executar uma função Python chamada _write_response utilizando XComs onde através da task_id da task write_response , é possível recuperar o resultado do response e utilizar em qualquer parte do código. Neste cenário estamos usando o resultado recuperado da API para escrever no arquivo. XCom é um mecanismo de comunicação entre diferente tasks que torna o Airflow bastante flexível. Muitas das vezes as tasks podem ser executadas em diferentes máquinas e com o uso de XComs, possibilita a comunicação e a troca de informações entre Tasks. Por fim, definimos a execução das tasks e suas dependências, veja que usamos o operador >> , que é basicamente definir a ordem da execução entre as tasks. No nosso caso, o acesso a API e extração deve ser executado antes da escrita no arquivo extract_data >> write_response . Após a execução da DAG, é possível acessar o arquivo que foi gerado com o resultado da extração, basta acessar via terminal um dos workers que no caso vamos ter somente um. Execute o seguinte comando abaixo para listar os containers: docker ps Uma listagem similar a esta abaixo será mostrada. Perceba que uma das linhas na coluna NAMES refere-se ao worker, no caso coffee_and_tips_airflow-worker_1 . Continuando no terminal, digite o seguinte comando para ter acesso ao diretório do Airflow onde arquivo extract_data.json se encontra. docker exec -it coffee_and_tips_airflow-worker_1 /bin/bash Pronto, agora é só abrir o arquivo e conferir o conteúdo. Conclusão Mais uma vez vimos o poder do Airflow para processos automatizados que requer acessos e integrações de APIs externas de forma fácil e com poucas linhas de código. Neste exemplo exploramos o uso de XComs que visa flexibilizar a troca de mensagens entre tasks que podem ser executadas em diferentes máquinas em um ambiente distribuído. Espero que tenha curtido!

Creating Asynchronous Java Code with Future

Creating Asynchronous Java Code with Future

Intro Java Future is one of several ways to work with the language asynchronously, providing a multi-thread context in which it is possible to execute tasks in parallel without blocking the process. In the example below, we will simulate sending a fictitious email in which, even during sending, the process will not be blocked, that is, it will not be necessary to wait for the sending to finish for the other functionalities or mechanisms to operate again. EmailService class Understanding the EmailService class The class above represents the sending emails in a fictitious way, the idea of ​​using the loop is to simulate the sending is precisely to delay the process itself. Finally, at the end of sending, the method sendEmailBatch(int numberOfEmailsToBeSent) returns a String containing a message referring to the end of the process. EmailServiceAsync class Understanding the EmailServiceAsync class The EmailServiceAsync class represents the asynchronous mechanism itself, in it we have the method sendEmailBatchAsync(int numberOfEmailsToBeSent) which will be responsible for making the process of sending dummy e-mails asynchronous. The asynchronous process is managed by using the ExecutorService instance which facilitates the management of tasks asynchronously which are assigned to a pool of threads. In this case, the call to the sendEmailBatch(int numberOfEmailsToBeSent) method boils down to a task (task) which will be assigned to a Thread defined in Executors.newFixedThreadPool(1) . Finally, the method returns a Future that is literally a promise that task will be completed at some point, representing an asynchronous process. EmailServiceAsyncRun class Understanding the EmailServiceAsyncRun class It is in this class where we will test the asynchronous process using Future . Let's recap, in the EmailService class, we've created a method called sendEmailBatch(int numberOfEmailsToBeSent) in which we're simulating through the for the sending of dummy email and printing a sending message that we'll use to test the concurrency. In the EmailServiceAsync class, the sendEmailBatchAsync(int numberOfEmailsToBeSent) method creates an ExecutorService instance that will manage the tasks together with the thread pool, which in this case, we are creating just one Thread defined in Executors.newFixedThreadPool(1) and will return a Future . Now in the EmailServiceAsyncRun class, this is where we actually test the process, let's understand by parts: We instantiate an object of type EmailServiceAsync We create an object of type Future<String> and assign it to the return of the emailAsync.sendEmailBatchAsync(500) method. The idea of ​​argument 500 is just to control the iteration of the For , delaying the process to be finished. We could even use Thread.sleep() as an alternative and set a delay time which would also work fine. Note that we are using the futureReturn.isDone() method to control the while iteration control, that is, this method allows the process not to be blocked while the email flow is executed. In this case, any process that you want to implement to compete while sending is done, can be created inside the while , such as a flow of updating customer tables or any other process. On line 20, using the futureReturn.get() method, we're printing the result of sending the emails. And finally, we finish the executorService and its tasks through the executorService.shutdown() method. Running the process Notice clearly that there are two distinct processes running, the process of sending email "Sending email Nº 498.." and the process of updating a customer table. Finally the process is finished when the message "A total of 500 emails has been sent" is printed. Working with blocking processes The use of Future is widely used for use cases where we need to block a proces. The current Thread will be blocked until the process being executed by Future ends. To do so, simply invoke the futureReturn.get() method directly without using any iteration control as used in the previous example. An important point is that this type of approach can cause resources to be wasted due to the blocking of the current Thread. Conclusion The use of Future is very promising when we need to add asynchronous processes to our code in the simplest way or even use it to block processes. It's a lean API with a certain resource limitation but that works well for some scenarios. Hope you enjoyed!

Criando códigos Java assíncronos com Future

Criando códigos Java assíncronos com Future

Introdução Java Future é uma das várias formas de se trabalhar com a linguagem de forma assíncrona provendo um contexto multi-Thread em que é possível executar tarefas em paralelo sem gerar um bloqueio no processo. No exemplo abaixo faremos uma simulação de envio de e-mail fictício em que mesmo durante o envio, o processo não será bloqueado, ou seja, não será necessário esperar o termino do envio para que as demais funcionalidades ou mecanismos voltem a operar. Classe EmailService Entendendo a classe EmailService A classe acima representa de forma fictícia o envio de e-mails em massa, a ideia de se usar o loop simulando o envio é justamente para atrasar o processo em si. Por fim, ao final do envio, o método sendEmailBatch(int numberOfEmailsToBeSent) retorna uma String contendo uma mensagem referente ao fim do processo. Classe EmailServiceAsync Entendendo a classe EmailServiceAsync A classe EmailServiceAsync representa o mecanismo assíncrono em si, nela temos o método sendEmailBatchAsync(int numberOfEmailsToBeSent) no qual será o responsável por tornar o processo de envio de e-mail fictício assíncrono. O processo assíncrono é gerenciado pelo uso do ExecutorService no qual facilita o gerenciamento de tarefas de forma assíncrona nas quais são atribuídas a um pool de threads. No caso, a chamada ao método sendEmailBatch(int numberOfEmailsToBeSent) se resume a uma tarefa (task) no qual será atribuída a uma Thread definida em Executors.newFixedThreadPool(1). Por fim, o método retorna uma Future que é literalmente uma promessa de que aquela tarefa em alguma hora será finalizada, representando um processo assíncrono. Classe EmailServiceAsyncRun Entendendo a classe EmailServiceAsyncRun É nesta classe onde iremos testar o processo assíncrono usando Future . Vamos recapitular, na classe EmailService , criamos um método chamado sendEmailBatch(int numberOfEmailsToBeSent) no qual estamos simulando através do for o envio de e-mail fictício e printando uma mensagem de envio que usaremos para testar a concorrência. Na classe EmailServiceAsync , o método s endEmailBatchAsync(int numberOfEmailsToBeSent) cria um ExecutorService que fará o gerenciamento das tasks juntamente com o pool de threads, que neste caso, estamos criando só uma Thread definido em Executors.newFixedThreadPool(1) e retornará uma Future . Agora na classe EmailServiceAsyncRun , é onde de fato testamos o processo, vamos entender por partes: Instanciamos um objeto do tipo EmailServiceAsync Criamos um objeto do tipo Future<String> e atribuímos ao retorno do método emailAsync.sendEmailBatchAsync(500) . A ideia do argumento 500 é apenas para controlar a iteração do For , atrasando o processo para ser finalizado. Até poderíamos usar Thread.sleep() como alternativa e definir um tempo de delay que também funcionaria bem. Perceba que estamos utilizando para controlar o controle de iteração while , o método futureReturn.isDone() , ou seja, este método permite que o processo não seja bloqueado enquanto o fluxo de e-mail seja executado. Neste caso, qualquer processo em que deseja implementar para concorrer enquanto o envio é feito, pode ser criado dentro do while , como por exemplo, um fluxo de atualização de tabelas de clientes ou qualquer outro processo. Na linha 20, através do método futureReturn.get() , estamos imprimindo o resultado do envio dos e-mails. E por fim, finalizamos o e xecutorService e suas tasks através do método executorService .shutdown() . Executando o processo Perceba claramente que existem dois processos distintos sendo executados, o processo de envio de email " Sending email Nº 498.." e o processo de atualização de uma tabela de cliente. Trabalhando com processos blocantes O uso do Future é bastante utilizado para casos de uso onde precisamos bloquear um processo, ou seja, a Thread atual será bloqueada até que o processo sendo executado por Future termine. Para isso, basta invocar diretamente o método futureReturn.get() sem usar qualquer controle de iteração como foi usado no exemplo anterior. Um ponto importante é que este tipo de abordagem pode fazer com que recursos sejam desperdiçados devido ao bloqueio da Thread atual. Conclusão O uso de Future é bem promissor quando precisamos adicionar processos assíncronos em nosso código da maneira mais simples ou até mesmo utilizar para bloqueio de processos. É uma API enxuta com uma certa limitação de recursos mas que funciona bem para alguns cenários. Espero que tenha curtido!

Airflow para Iniciantes: Entenda Apache Airflow da maneira mais simples

Airflow para Iniciantes: Entenda Apache Airflow da maneira mais simples

Introdução Airflow tem sido uma das principais ferramentas de orquestração do mercado e muito falada no mundo Modern Data Stack , por ser uma ferramenta capaz de orquestrar workloads de dados através de ETLs ou ELTs. Mas na verdade, o Airflow não se resume somente a isso, ele pode ser aplicado em diversos casos de usos do dia a dia de um Engenheiro de Dados ou Software. Neste Tutorial sobre Airflow para iniciantes, iremos apresentar o Airflow da maneira mais simples, sem a necessidade de saber ou criar ETLs. Mas o que é o Airflow de fato? O Apache Airflow é uma plataforma de orquestração de fluxo de trabalho amplamente utilizada para agendar, monitorar e gerenciar pipelines de dados. Ele possui vários componentes que trabalham juntos para fornecer suas funcionalidades. Componentes do Airflow DAG O DAG (Directed Acyclic Graph) é o principal componente e a representação de fluxo de trabalho no Airflow. É composto por tarefas (tasks) e dependências entre elas. As tarefas são definidas como operadores (operators), como PythonOperator , BashOperator , SQLOperator e entre outros. O DAG define a ordem de execução das tarefas e as relações de dependências. Webserver O componente do Webserver fornece uma interface da web para interagir com o Airflow. Ele permite que você visualize, gerencie e monitore seus fluxos de trabalho, tarefas, DAGs e logs. O Webserver também permite a autenticação de usuários e o controle de acesso com base em funções. Scheduler O Scheduler é responsável por agendar a execução das tarefas de acordo com a definição do DAG. Ele verifica periodicamente se há tarefas pendentes para execução e atribui recursos disponíveis para a execução das tarefas no momento apropriado. O Scheduler também lida com a recuperação de falhas e o agendamento de retries de tarefas. Executor O Executor é responsável por executar as tarefas definidas nos DAGs. Existem diferentes tipos de executores disponíveis no Airflow, como LocalExecutor , CeleryExecutor, KubernetesExecutor e etc. Cada executor tem suas próprias configurações e comportamentos de execução. Metadatabase O Metadatabase é um banco de dados onde o Airflow armazena metadados sobre as tarefas, DAGs, execuções, agendamentos, entre outros. Ele é usado para rastrear o status das tarefas, registrar o histórico de execução e fornecer informações para o monitoramento e a visualização do fluxo de trabalho. É possível utilizar diversos outros bancos de dados para registrar o histórico como MySQL, Postgres e dentre outros. Workers Os Workers são os nós de execução em um ambiente distribuído. Eles recebem tarefas atribuídas pelo Scheduler e as executam. Os Workers podem ser dimensionados horizontalmente para lidar com pipelines de dados maiores ou para distribuir a carga de trabalho para vários recursos. Plugins Os Plugins são extensões do Airflow que permitem adicionar novos recursos e funcionalidades ao sistema. Eles podem incluir novos operadores, hooks, sensores, conexões com sistemas externos e entre outros. Os Plugins fornecem uma maneira de personalizar e estender as capacidades do Airflow para atender às necessidades específicas de um fluxo de trabalho. Operadores Operadores são basicamente a composição de um DAG. Entenda um operador como um bloco de código com responsabilidade própria. Pelo Airflow ser um orquestrador e executar um fluxo de trabalho, podemos ter diferentes tarefas a serem executadas como por exemplo, acessar uma API, enviar um e-mail, acessar uma tabela em um banco de dados e efetuar uma operação, executar um código Python ou até um comando Bash. Para cada uma das tarefas acima, devemos usar um operador. A seguir, iremos abordar alguns dos principais operadores: BashOperator O BashOperator permite executar comandos Bash ou scripts diretamente no sistema operacional onde o Airflow está sendo executado. É útil para tarefas que envolvem a execução de scripts de shell, utilitários ou qualquer ação que possa ser realizada no terminal. Resumindo, quando precisamos abrir o terminal do nosso sistema e executar algum comando para manipular arquivos ou algo relacionado ao próprio sistema, porém dentro de um DAG, é este o operador a ser usado. PythonOperator O PythonOperator permite que você execute funções Python como tarefas no Airflow. Você pode escrever suas próprias funções Python personalizadas e usar o PythonOperator para chamar essas funções como parte do fluxo de trabalho. DummyOperator O DummyOperator é uma tarefa "falsa" que não realiza nenhuma ação. É útil para criar dependências e fluxos de trabalho complexos sem a necessidade de executar nenhuma ação real. Sensor Os Sensores são usados para aguardar a ocorrência de algum evento externo antes de continuar o fluxo de trabalho, pode funcionar como um listener (ouvinte). Por exemplo, o HttpSensor que é um tipo de Sensor, pode validar se uma API externa esteja ativa, caso esteja, o fluxo continua sendo executado. Não necessariamente é um operador HTTP que deveria retornar algo, mas somente um tipo de ouvinte. HttpOperator Diferente de um Sensor, o HttpOperator é usado para realizar solicitações HTTP, como GET, POST, PUT, DELETE e etc. Neste caso, permite interagir de forma mais completa com APIs internas ou externas. SqlOperator SqlOperator é o operador responsável por executar operações de DML e DDL em um banco de dados, ou seja, desde manipulações de dados como SELECTS, INSERTS, UPDATES até criação de tabelas, alteração e etc. Executores Os executores são responsáveis por executar as tarefas definidas em um fluxo de trabalho (DAG). Eles gerenciam a alocação e a execução das tarefas em tempo de execução, garantindo que cada tarefa seja executada de forma eficiente e confiável. O Airflow oferece diferentes tipos de executores, cada um com características e funcionalidades distintas, permitindo que você escolha o mais adequado para suas necessidades específicas. A seguir, abordaremos alguns dos principais executores: LocalExecutor O LocalExecutor é o executor padrão no Apache Airflow. Ele é projetado para ser usado em ambientes de desenvolvimento e teste onde a escalabilidade não é uma preocupação. O LocalExecutor executa as tarefas em threads separados no mesmo processo do Airflow. Essa abordagem é simples e eficiente para pipelines menores ou para execuções em um único nó. CeleryExecutor Se você precisa de um executor para ambientes distribuídos e com alta escala, o CeleryExecutor é uma excelente opção. Ele utiliza o Celery, uma biblioteca de tarefas em fila, para distribuir as tarefas em nós de execução separados. Essa abordagem torna o Airflow adequado para executar pipelines em clusters de servidores, permitindo dimensionar horizontalmente conforme a demanda. KubernetesExecutor Para ambientes que utilizam Kubernetes como plataforma de orquestração de containers, o KubernetesExecutor é uma escolha natural. Ele aproveita a capacidade de orquestração do Kubernetes para executar tarefas em pods separados, o que pode resultar em melhor isolamento de recursos e mais facilidade na execução de tarefas em containers. DaskExecutor Se o seu fluxo de trabalho requer processamento paralelo e distribuído, o DaskExecutor pode ser a opção certa. Ele utiliza a biblioteca Dask para realizar a computação paralela em um cluster de recursos. Essa abordagem é ideal para tarefas que podem ser divididas em sub-tasks independentes, permitindo maior aproveitamento dos recursos disponíveis. Linguagem de programação Airflow suporta unicamente como linguem de programação, o Python. Para ser sincero, não é um limitador para quem não conhece bem a linguagem. Na prática, o processo de criar DAGs é padrão, o que pode mudar de acordo com as suas necessidades, será lidar com diferentes tipos de operadores, podendo ou não usar Python. Mãos na massa Montando o ambiente Para este tutorial iremos usar o Docker que nos ajudará a provisionar o nosso ambiente sem a necessidade de instalar o Airflow. Caso não tenha o Docker instalado, recomendo seguir as recomendações deste link e após instalado, volte para seguirmos o tutorial. Baixando o projeto Para facilitar, clone o projeto do seguinte repositório e siga os passos para fazer o deploy do Airflow. Passos para o deploy Com o docker instalado e após ter baixado o projeto conforme o item anterior, acesse o diretório onde se encontra o projeto e abra o terminal, execute o seguinte comando docker: docker-compose up O comando acima irá iniciar os containers do docker onde estão presentes os serviços do próprio Airflow, postgres e entre outras que iremos utilizar. Se tiver curiosidade de como estes serviços estão mapeados, abra o arquivo docker-compose.yaml do projeto e lá você terá mais detalhes. Enfim, após a execução do comando acima e os containers já iniciados, acesse via browser o seguinte endereço http://localhost:8080/ Uma tela como abaixo será aberta, basta digitar airflow para o usuário e senha e acesse a UI do Airflow. Criando a DAG Criando um simples Hello World Para este exemplo, iremos criar uma simples DAG onde irá printar o clássico "Hello World". No projeto que você baixou, acesse a pasta /dags e crie o seguinte arquivo python chamado hello_world.py . O Código acima é um simples exemplo de uma DAG escrita em Python. Percebe-se que começamos importando algumas funções, incluindo a própria DAG, função relacionada a data e o operador Python. Em seguida, criamos uma função Python que irá printar no console "Hello World" chamada print_hello . Esta função será chamada pela DAG mais a frente. A declaração de uma DAG inicia-se com with DAG(..) passando alguns argumentos como: dag_id : Identificador da DAG no contexto do Airflow start_date : A data definida é apenas um ponto de referência e não necessariamente a data do início de execução e nem da criação da DAG. Normalmente as execuções são feitas a uma data posterior a definida neste parâmetro, e tem uma importância quando precisamos calcular execuções entre o início e o que foi definido no parâmetro schedule_interval. schedule_interval : Neste parâmetro definimos a periodicidade em que a DAG será executada. É possível definir diferentes formas de execuções através de expressões CRON ou através de Strings já definidas como @daily , @hourly , @once , @weekly e etc. No caso do exemplo, o fluxo será executado apenas uma vez. catchup : Este parâmetro controla execuções retroativas, ou seja, caso esteja definido como True , o Airflow irá executar o período retroativo a partir da data definida no start_date até a data atual. No exemplo anterior definimos como False por não ter a necessidade da execução retroativa. Após o preenchimento dos argumentos, criamos a task hello_task dentro da própria DAG através do operador PythonOperator no qual provê formas de executar funções python dentro de uma DAG. Perceba que declaramos um identificador através do task_id e no argumento python_callable no qual é nativa do operador PythonOperator, passamos a função python print_hello criada anteriormente. Por último, invoque a task hello_task . Dessa forma, a DAG entenderá que esta será a task a ser executada. Caso você já tenha feito o deploy, a DAG aparecerá no Airflow em pouco tempo para ser executada conforme na imagem abaixo: Após a DAG criada, ative-a e a execute clicando em Trigger DAG conforme a imagem acima. Clique na task hello_operator (centro) e em seguida uma janela será aberta conforme imagem abaixo: Clique no botão Log para ver mais detalhes da execução: Perceba como é simples a criação de uma DAG, basta pensar nas diferentes possibilidades e cenários de aplicabilidade. A seguir, faremos mais exemplos um pouco mais complexos explorando várias outros cenários. Conclusão Com base no simples exemplo mostrado, Airflow apresentou uma abordagem flexível e simples para o controle de fluxos automatizados, desde a criação de DAGs até a navegação do seu componente web. Como citei no início, a sua utilização não se limita somente na orquestração de ETLs como é feito no mercado em geral, mas também na possibilidade do seu uso em tarefas que requer qualquer necessidade em controlar fluxos que possuem dependências entre seus componentes dentro de um contexto escalável ou não. Repositório GitHub Espero que tenha curtido!

Tutorial : Apache Airflow for beginners

Tutorial : Apache Airflow for beginners

Intro Airflow has been one of the main orchestration tools on the market and much talked about in the Modern Data Stack world, as it is a tool capable of orchestrating data workloads through ETLs or ELTs. But in fact, Airflow is not just about that, it can be applied in several cases of day-to-day use of a Data or Software Engineer. In this Apache Airflow for Beginners Tutorial, we will introduce Airflow in the simplest way, without the need to know or create ETLs. But what is Airflow actually? Apache Airflow is a widely used workflow orchestration platform for scheduling, monitoring, and managing data pipelines. It has several components that work together to provide its functionalities. Airflow components DAG The DAG (Directed Acyclic Graph) is the main component and workflow representation in Airflow. It is composed of tasks (tasks) and dependencies between them. Tasks are defined as operators (operators), such as PythonOperator, BashOperator, SQLOperator and others. The DAG defines the task execution order and dependency relationships. Webserver The Webserver component provides a web interface for interacting with Airflow. It allows you to view, manage and monitor your workflows, tasks, DAGs and logs. The Webserver also allows user authentication and role-based access control. Scheduler The Scheduler is responsible for scheduling the execution of tasks according to the DAG definition. It periodically checks for pending tasks to run and allocates available resources to perform the tasks at the appropriate time. The Scheduler also handles crash recovery and scheduling task retries. Executor The Executor is responsible for executing the tasks defined in the DAGs. There are different types of executors available in Airflow such as LocalExecutor, CeleryExecutor, KubernetesExecutor and etc. Each executor has its own settings and execution behaviors. Metadatabase Metadatabase is a database where Airflow stores metadata about tasks, DAGs, executions, schedules, among others. It is used to track the status of tasks, record execution history, and provide information for workflow monitoring and visualization. It is possible to use several other databases to record the history such as MySQL, Postgres and among others. Workers Workers are the execution nodes in a distributed environment. They receive tasks assigned by the Scheduler and execute them. Workers can be scaled horizontally to handle larger data pipelines or to spread the workload across multiple resources. Plugins Plugins are Airflow extensions that allow you to add new features and functionality to the system. They can include new operators, hooks, sensors, connections to external systems, and more. Plugins provide a way to customize and extend Airflow's capabilities to meet the specific needs of a workflow. Operators Operators are basically the composition of a DAG. Understand an operator as a block of code with its own responsibility. Because Airflow is an orchestrator and executes a workflow, we can have different tasks to be performed, such as accessing an API, sending an email, accessing a table in a database and performing an operation, executing a Python code or even a Bash command. For each of the above tasks, we must use an operator. Next, we will discuss some of the main operators: BashOperator BashOperator allows you to run Bash commands or scripts directly on the operating system where Airflow is running. It is useful for tasks that involve running shell scripts, utilities, or any action that can be performed in the terminal. In short, when we need to open our system's terminal and execute some command to manipulate files or something related to the system itself, but within a DAG, this is the operator to be used. PythonOperator The PythonOperator allows you to run Python functions as tasks in Airflow. You can write your own custom Python functions and use the PythonOperator to call those functions as part of your workflow. DummyOperator The DummyOperator is a "dummy" task that takes no action. It is useful for creating complex dependencies and workflows without having to perform any real action. Sensor Sensors are used to wait for some external event to occur before continuing the workflow, it can work as a listener. For example, the HttpSensor, which is a type of Sensor, can validate if an external API is active, if so, the flow continues to run. It's not an HTTP operator that should return something, but a type of listener. HttpOperator Unlike a Sensor, the HttpOperator is used to perform HTTP requests such as GET, POST, PUT, DELETE end etc. In this case, it allows you to interact more fully with internal or external APIs. SqlOperator SqlOperator is the operator responsible for performing DML and DDL operations in a database, that is, from data manipulations such as SELECTS, INSERTS, UPDATES and so on. Executors Executors are responsible for executing the tasks defined in a workflow (DAG). They manage the allocation and execution of tasks at runtime, ensuring that each task runs efficiently and reliably. Airflow offers different types of executors, each with different characteristics and functionalities, allowing you to choose the most suitable one for your specific needs. Below, we’ll cover some of the top performers: LocalExecutor LocalExecutor is the default executor in Apache Airflow. It is designed to be used in development and test environments where scalability isn't a concern. LocalExecutor runs tasks on separate threads within the same Airflow process. This approach is simple and efficient for smaller pipelines or single-node runs. CeleryExecutor If you need an executor for distributed and high-scale environments, CeleryExecutor is an excellent choice. It uses Celery, a queued task library, to distribute tasks across separate execution nodes. This approach makes Airflow well-suited for running pipelines on clusters of servers, allowing you to scale horizontally on demand. KubernetesExecutor For environments that use Kubernetes as their container orchestration platform, KubernetesExecutor is a natural choice. It leverages Kubernetes' orchestration capability to run tasks in separate pods, which can result in better resource isolation and easier task execution in containers. DaskExecutor If your workflow requires parallel and distributed processing, DaskExecutor might be the right choice. It uses the Dask library to perform parallel computing on a cluster of resources. This approach is ideal for tasks that can be divided into independent sub-tasks, allowing better use of available resources. Programming language Airflow supports Python as programming language. To be honest, it's not a limiter for those who don't know the language well. In practice, the process of creating DAGs is standard, which can change according to your needs, it will deal with different types of operators, whether or not you can use Python. Hands-on Setting up the environment For this tutorial we will use Docker that will help us provision our environment without the need to install Airflow. If you don't have Docker installed, I recommend following the recommendations in this link and after installing it, come back to follow the tutorial. Downloading project To make it easier, clone the project from the following repository and follow the steps to deploy Airflow. Steps to deploy With docker installed and after downloading the project according to the previous item, access the directory where the project is located and open the terminal, run the following docker command: docker-compose up The above command will start the docker containers where the services of Airflow itself, postgres and more. If you're curious about how these services are mapped, open the project's docker-compose.yaml file and there you'll find more details. Anyway, after executing the above command and the containers already started, access the following address via browser http://localhost:8080/ A screen like below will open, just type airflow for the username and password and access the Airflow UI. Creating a DAG Creating a simple Hello World For this tutorial, we will create a simple DAG where the classic "Hello World" will be printed. In the project you downloaded, go to the /dags folder and create the following python file called hello_world.py . The code above is a simple example of a DAG written in Python. We noticed that we started import some functions, including the DAG itself, functions related to the datetime and the Python operator. Next, we create a Python function that will print to the console "Hello World" called by print_hello function. This function will be called by the DAG later on. The declaration of a DAG starts using the following syntax with DAG(..) passing some arguments like: dag_id : DAG identifier in Airflow context start_date : The defined date is only a point of reference and not necessarily the date of the beginning of the execution nor of the creation of the DAG. Usually the executions are carried out at a later date than the one defined in this parameter, and it is important when we need to calculate executions between the beginning and the one defined in the schedule_interval parameter. schedule_interval : In this parameter we define the periodicity in which the DAG will be executed. It is possible to define different forms of executions through CRON expressions or through Strings already defined as @daily , @hourly , @once , @weekly and etc. In the case of the example, the flow will run only once. catchup : This parameter controls retroactive executions, that is, if set to True , Airflow will execute the retroactive period from the date defined in start_date until the current date. In the previous example we defined it as False because there is no need for retroactive execution. After filling in the arguments, we create the hello_task within the DAG itself using the PythonOperator operator, which provides ways to execute python functions within a DAG. Note that we declared an identifier through the task_id and in the python_callable argument, which is native to the PythonOperator operator, we passed the python print_hello function created earlier. Finally, invoke the hello_task . This way, the DAG will understand that this will be the task to be performed. If you have already deployed it, the DAG will appear in Airflow in a short time to be executed as shown in the image below: After the DAG is created, activate and execute it by clicking on Trigger DAG as shown in the image above. Click on the hello_operator task (center) and then a window will open as shown in the image below: Click the Log button to see more execution details: Note how is simple it to create a DAG, just think about the different possibilities and applicability scenarios. For the next tutorials, we'll do more examples that are a bit more complex by exploring several other scenarios. Conclusion Based on the simple example shown, Airflow presented a flexible and simple approach to controlling automated flows, from creating DAGs to navigating your web component. As I mentioned at the beginning, its use is not limited only to the orchestration of ETLs, but also to the possibility of its use in tasks that require any need to control flows that have dependencies between their components within a context. scalable or not. GitHub Repository Hope you enjoyed!

Getting started with Java Reflection in 2 minutes

Getting started with Java Reflection in 2 minutes

Introduction Java Reflection is a powerful API that allows a Java program to examine and manipulate information about its own classes at runtime. With Reflection, you can get information about a class's fields, methods, and constructors, and access and modify those elements even if they're private. In this post we're going to write some Java codes exploring some of the facilities of using Reflection and when to apply it in your projects. Bank Class We'll create a simple class called Bank , where we'll create some fields, methods and constructors to be explored using Reflection. Accessing the fields of the Bank class With the Bank class created, let's explore via Reflection the listing of all fields of the class through the getDeclaredFields method of the Class class. Note that through the static method Class.forName , we pass a string with the name of the class we want to explore via Reflection as a parameter. Output Field name: code Field type: class java.lang.Integer ************ Field name: nameOfBank Field type: class java.lang.String ************ Field name: amountOfDepositedMoney Field type: class java.lang.Double ************ Field name: totalOfCustomers Field type: class java.lang.Integer ************ Accessing the methods of the Bank class Through the getDeclaredMethods method, we can retrieve all methods of the Bank class. Output Method name: doDeposit Method type: class java.lang.String ************ Method name: doWithDraw Method type: class java.lang.String ************ Method name: getReceipt Method type: class java.lang.String ************ Creating objects With the use of Reflection to create objects, it is necessary to create them through a constructor. In this case, we must first invoke a constructor to create the object. The detail is that to retrieve this constructor, we must pay attention to the types of parameters that make up the constructor and the order in which they are declared. This makes it flexible to retrieve different constructors with different parameter numbers and type in a class. Notice below that it was necessary to create an array of type Class assigning different types according to the composition of the constructor that we will use to create our object. In this scenario, it will be necessary to invoke the method class.getConstructor(argType) passing the previously created array as an argument. This way, we will have a constructor object that will be used in the creation of our object. Finally, we create a new array of type Object assigning the values ​​that will compose our object following the order defined in the constructor and then just invoke the method constructor.newInstance(argumentsValue) passing the array as a parameter returning the object we want to create. Output Bank{code=1, nameOfBank='Bank of America', amountOfDepositedMoney=1.5, totalOfCustomers=2500} Invoking methods To invoke a method through Reflection is quite simple as shown in the code below. Note that it is necessary to pass as a parameter in the method cls.getMethod("doDeposit", argumentsType) the explicit name of the method, in this case "doDeposit" and in the second parameter, an array representing the type of data used in the parameter of the method doDeposit( double amount) , in this case a parameter of type double . Finally, invoke the method method.invoke passing at the first parameter the object referencing the class, in this case an object of type Bank. And as the second parameter, the value that will be executed in the method. Output 145.85 of money has been deposited Conclusion Using Reflection is a good strategy when you need flexibility in exploring different classes and their methods without the need to instantiate objects. Normally, Reflection is used in specific components of an architecture, but it does not prevent it from being used in different scenarios. From the examples shown above, you can see infinite scenarios of its application and the advantages of its use. Hope you enjoyed!

Entendendo Java Reflection em 2 minutos

Entendendo Java Reflection em 2 minutos

Introdução A Java Reflection é uma poderosa API que permite a um programa Java examinar e manipular informações sobre suas próprias classes em tempo de execução. Com a Reflection, é possível obter informações sobre campos, métodos e construtores de uma classe, além de acessar e modificar esses elementos mesmo que eles sejam privados. Neste post vamos escrever alguns códigos Java explorando algumas das facilidades de se usar Reflection e quando aplicar em seus projetos. Classe Bank Criaremos uma simples classe chamada Bank , onde iremos criar alguns campos, métodos e construtores para que sejam explorados pelo uso do Reflection. Acessando os campos da classe Bank Com a classe Bank criada, vamos explorar via Reflection a listagem de todos campos da classe através do método getDeclaredFields da classe Class . Perceba que através do método estático Class.forName , passamos como parâmetro uma string com o nome da classe em que desejamos explorar via Reflection. Saída Field name: code Field type: class java.lang.Integer ************ Field name: nameOfBank Field type: class java.lang.String ************ Field name: amountOfDepositedMoney Field type: class java.lang.Double ************ Field name: totalOfCustomers Field type: class java.lang.Integer ************ Acessando os métodos da classe Bank Através do método getDeclaredMethods , podemos recuperar todos os métodos da classe Bank. Saída Method name: doDeposit Method type: class java.lang.String ************ Method name: doWithDraw Method type: class java.lang.String ************ Method name: getReceipt Method type: class java.lang.String ************ Criando objetos Com o uso do Reflection para criar objetos, é necessário cria-los através de um construtor. Neste caso, devemos primeiro invocar um construtor para que seja criado o objeto. O detalhe é que para recuperar este construtor, devemos nos atentar aos tipos dos parâmetros que compõem o construtor e a ordem que eles são declarados. Isso torna flexível recuperar diferentes construtores com quantidade de parâmetros e tipos diferentes. Perceba abaixo que foi necessário criar um array do tipo Class atribuindo diferentes tipos de acordo com a composição do construtor que iremos usar para criar o nosso objeto. Neste cenário, será necessário invocar o método class.getConstructor( argType ) passando o array criado anteriormente como argumento. Dessa forma, teremos um objeto construtor que será usado na criação do nosso objeto. Por fim, criamos um novo array do tipo Object atribuindo os valores que irão compor o nosso objeto obedecendo a ordem definida no construtor e em seguida, basta invocar o método constructor.newInstance(argumentsValue) passando o array como parâmetro retornando o objeto em que desejamos criar. Saída Bank{code=1, nameOfBank='Bank of America', amountOfDepositedMoney=1.5, totalOfCustomers=2500} Invocando métodos Para invocar um método através de Reflection é bem simples como é mostrado no código abaixo. Perceba que é necessário passar como parâmetro no método cls.getMethod("doDeposit", argumentsType) o nome explícito do método, no caso o "doDeposit" e no segundo parâmetro, um array representando o tipo do dado usado no parâmetro do método doDeposit( double amount ) , no caso um parâmetro do tipo double . Por fim, invocar o método method.invoke passando como primeiro parâmetro o objeto referenciando a classe, no caso um objeto do tipo Bank. E como segundo parâmetro, o próprio valor do parâmetro que será executado no método. Saída 145.85 of money has been deposited Conclusão O uso de Reflection é uma boa estratégia quando necessita-se de flexibilidade em explorar diferentes classes e seus métodos sem a necessidade de instanciar objetos. Normalmente usa-se Reflection em componentes específicos de uma arquitetura mas não impede de ser usado em cenários variados. Pelo exemplos mostrados acima, percebe-se cenários infinitos de sua aplicação e as vantagens do seu uso. Espero que tenha curtido!

Understanding Java Record Class in 2 minutes

Understanding Java Record Class in 2 minutes

Introduction Released in Java 14 as a preview, more specifically in JEP 395 , Record Class is an alternative to working with Classes in Java. Record Class was a very interesting approach designed to eliminate the verbosity when you need to create a class and its components, such as: Canonical constructors Public access methods Implement the equals and hashCode methods Implement the toString method Using Record Classes it is no longer necessary to declare the items above, helping the developer to be more focused on other tasks. Let's understand better in practice. Let's create a Java class called User and add some fields and methods. Note that for a simple class with 4 fields, we create a constructor, public access methods, implement the equals and hashCode methods and finally, the toString method. It works well, but we could avoid the complexity and create less verbose code. In that case, we can use Record Classes instead User class above. User Record Class The difference between Record and a traditional Java Class is remarkable. Note that it wasn't necessary to declare the fields, create the access methods or implement any other method. In a Record Class when created, implicitly the public access methods are created, the implementations of the equals , hashCode and toString methods are also created automatically and it is not necessary to implement them explicitly. And finally, the reference fields or components are created as private final with the same names. Output Disadvantages Record Class behaves like a common Java class, but the difference is that you can't work with inheritance. You can't extends another class, only implement one or more interfaces. Another point is that it's not possible to create non-static instance variables. Final conclusion Record Classes is a great approach for anyone looking for less verbose code or who needs agility in implementing models. Despite the limitation of not being able to extends other Record Classes, it's a limitation that doesn't affect its use in general. Hope you enjoyed!

Entendendo Record Class do Java em 2 minutos

Entendendo Record Class do Java em 2 minutos

Introdução Liberado no Java 14 como preview , mais especificamente na JEP 395 , Record Class é uma alternativa para se trabalhar com Classes em Java. Record Class foi uma sacada bem interessante desenhado para eliminar a verbosidade de quando se precisa criar uma classe e seus componentes, como por exemplo: Construtores canônicos Métodos de acesso público Implementar os métodos equals e hashCode Implementar o método toString Usando Record Classes não é mais necessário declarar os itens acima, ajudando o desenvolvedor a ser mais focado em outras frentes. Vamos entender melhor na prática. Vamos criar uma classe Java chamada User e adicionar alguns campos e métodos. Perceba que para uma simples classe com 4 campos, criamos um construtor, métodos de acesso público, implementamos os métodos equals e hashCode e por fim, o método toString . Funciona bem, porém poderíamos evitar a complexidade e criar um código menos verboso. Nesse caso, podemos utilizar Record Classes no lugar da classe User acima. User Record Class É notável a diferença entre Record e uma Classe Java tradicional. Perceba que não foi necessário declarar os campos, criar os métodos de acessos ou implementar nenhum outro método. Em uma Record Class quando criada, implicitamente os métodos de acesso público são criados, as implementações dos métodos equals , hashCode e toString também são criados de forma automática sendo não necessário implementa-los de forma explícita. E por fim, os campos de referências ou componentes, são criados como private final com o mesmos nomes. Saída Desvantagens Record Class se comporta como uma classe Java comum, mas a diferença é que não se pode trabalhar com herança, ou seja, não se pode estender ( extends ) outra classe, somente implementar uma ou mais interfaces. Outro ponto é que não é possível criar variáveis de instâncias não estáticas ( static ); Conclusão final Record Classes é uma ótima abordagem para quem procura um código menos verboso ou que precisa de agilidade em implementar modelos. Apesar da limitação de não poder estender outras Record Classes, é um limitador que não prejudica o seu uso em geral. Espero que tenha curtido!

bottom of page