UMA BREVE INTRODUÇÃO À PROGRAMAÇÃO ASSÍNCRONA COM PYTHON DEFERRED

Gabriel Weizenmann
Aurum Tech
4 min readSep 13, 2018

--

INTRODUÇÃO

Como você provavelmente sabe, a Aurum trabalha com software jurídico. Isso envolve a extração de dados das mais diversas fontes, tudo isso com uma certa frequência e com a garantia da integridade dos dados.

Na minha trajetória profissional na empresa (que começou ainda como estagiário) um dos maiores desafios foi, e continua sendo, trazer esses dados com a maior eficiência possível, tanto em velocidade quanto em qualidade. É aí que a programação assíncrona entra e é dela que eu vou falar nesse texto — apenas uma pincelada, pois o assunto é bem mais amplo e profundo que isso!

O PROBLEMA

DADOS. Muitos dados. Hoje na Aurum lidamos com um volume absurdo de dados (mais de 125 milhões de andamentos diariamente, por exemplo) e nossos clientes esperam que esses dados se mantenham atualizados regularmente.

Agora imagine um sistema síncrono, onde cada dado é atualizado numa fila e se um único deles falhar na atualização (digamos que o site do tribunal esteja fora do ar) todo o processo é interrompido e a execução é perdida. Extremamente ineficiente e suscetível a erros.

A SOLUÇÃO

E se dados puderem ser executados de forma assíncrona? E se a próxima linha de código puder começar a ser lida e interpretada antes que o resultado da anterior seja retornado?

Pense numa corrida de revezamento 4x100. Antes que o bastão seja passado, o corredor que vai recebê-lo começa a tomar posição e a correr antes de realmente tê-lo em mãos. É mais ou menos isso que se busca com a programação assíncrona: uma forma de executar o código sem que o programa fique preso em uma ou outra transação mais demorada, nesse caso o bastão seriam os dados.

A corredora que recebe o bastão (dados) já está em posição de corrida.

HANDS ON!

“Ok, você falou sobre atletismo, sobre fila, mas ainda não vi nenhuma linha de código no texto”. Como eu trabalho com Python aqui dentro, vou falar um pouco sobre o funcionamento do Deferred, uma das bases do funcionamento do Scrapy, o framework que é o melhor amigo daqueles que extraem dados na web.

DEFERRED

O Deferred é um mecanismo usado para controlar o fluxo de execução para tarefas assíncronas. Segue abaixo um exemplo curto do seu funcionamento utilizando o framework Twisted (esse pode ser o assunto para outro post, mas você pode conferir a documentação aqui).

from twisted.web.client import getPage

from twisted.internet import reactor

def printContents(contents):
print contents
reactor.stop()

d = getPage('http://twistedmatrix.com/')

d.addCallback(printContents)

reactor.run()

CALLBACK E ERRBACK

A partir da criação do deferred com a função getPage do Twisted é adicionado um callback a ele, ou seja, é feito um “acordo” para que a função printContents seja chamada assim que o objeto d tenha a resposta da primeira execução. Assim, permite que a próxima linha seja lida e interpretada sem que haja necessariamente o conteúdo da requisição anterior.

Outro tratamento que pode ser feito é a adição de um errback com addErrback(f) onde f é a função a ser chamada caso o deferred retorne um erro.

CADEIA DE CALLBACK E A PROGRAMAÇÃO ASSÍNCRONA

É possível adicionar vários callbacks a um deferred, criando o que chamamos de cadeia de callback (ou callback chain) dessa forma deixando-o responsável por gerenciar a ordem de execução do código, sem que uma única linha de código seja capaz de travar toda a execução, permitindo que a próxima seja lida e interpretada e tornando o sistema assíncrono.

from twisted.web.client import getPage

from twisted.internet import reactor
def toLowerCase(contents):
return contents.lower()
def printContents(contents):
print contents
reactor.stop()

d = getPage('http://twistedmatrix.com/')
print “42” # código que independe do resultado de getPaged.addCallback(printContents)d.addCallback(toLowerCase)

reactor.run()

Dessa forma a função toLowerCase é chamada e roda em cima do resultado de printContents, porém o print “42” não tem nenhuma dependência com esses métodos e é chamado sem que haja necessariamente o resultado de getPage.

“PARECE FÁCIL, POR QUE É UM DESAFIO?”

Quando se lida com um grande volume de dados e você quer pegá-los de forma assíncrona, a parte mais difícil envolve a integridade dos dados: como garantir que um certo dado x se refere ao processo y nesse oceano cheio de dados? E como juntar o resultado de várias requisições no final da execução do crawler?

Além disso, existem problemas com sessão de navegador, gerenciamento de cookies, entre outros.

Todos esses fatores vão bem além da proposta desse texto e podem ser assunto para um próximo post (ou você pode ler a documentação dos frameworks que eu citei). Até mais, e Obrigado pelos Peixes!

--

--