Introdução
Nos posts anteriores da nossa série sobre microsserviços, discutimos as features e características necessárias às aplicações de próxima geração. À medida que infraestruturas 5G e de Edge Computing são construídas, aplicações e serviços confiáveis, de latência ultrabaixa, se tornarão cada vez mais comuns, acelerando as já elevadas expectativas do usuário por desempenho. Além disso, com mais pessoas do que nunca trabalhando, fazendo compras e aprendendo online, medidas severas de segurança serão necessárias para atender a exigências de compliance e garantir privacidade dos usuários.
Assim, provedores serverless como a Azion são responsáveis por entregar uma plataforma projetada para oferecer o máximo de desempenho, confiança e segurança possível. É por isso que a Azion escolheu a linguagem de programação Rust como a base da nossa tecnologia, o Azion Cells - usada no Edge Functions e em outros produtos Azion. Este post dará uma visão aprofundada do Rust, examinando suas features e vantagens, bem como discutirá nossa implementação de Rust em Azion Cells.
Origem
Rust é uma linguagem de programação de sistemas que executa códigos seguros, confiáveis e de alto desempenho. Foi criada pelo programador da Mozilla Graydon Moare, que começou a trabalhar no Rust em 2006. Em 2009, o Mozilla apoiou o Rust como parte do projeto Servo Parallel Browser, uma iniciativa a longo prazo para reconstruir o navegador usando tecnologia concorrente mais segura do que sua implementação anterior em C++.
O primeiro lançamento pré-alpha de Rust aconteceu em 2012 e sua versão estável 1.0 foi lançada em 2015. Desde então, chamou atenção e ganhou popularidade da comunidade tech por sua confiabilidade, segurança e desenvolvimento. O Rust foi desenvolvido inicialmente como uma alternativa de C e C++ que poderia ser usada em aplicações de alto desempenho ao mesmo tempo evitando questões de memória que geralmente ocorrem ao programar em C e C++.
Em uma entrevista para a InfoQ, Hoare explicou qual foi sua motivação para criar o Rust:
Muitas boas ideias óbvias, conhecidas e amadas em outras linguagens, não chegaram às linguagens de sistema mais usadas, ou são implementadas em linguagens com modelos de memória pobres - inseguras, hostis à concorrência. (…) Eu queria reviver algumas dessas ideias e dar uma nova chance para elas, pensando que as circunstâncias mudaram: a internet tem muita concorrência e é muito consciente de segurança, então as trocas de design que sempre favoreceram C e C++, por exemplo, têm mudado.
Princípios de design
Hoje, em seu site oficial o Rust é descrito como “uma linguagem capacitando todos a construir softwares confiáveis e eficientes”, tendo elogiado desempenho, confiabilidade e ferramentas. Um post de blog de 2020 do GitLab lista os princípios chave de design do Rust:
- fazer cumprir rigorosamente o empréstimo seguro de dados;
- funções, métodos e encerramento para operar com dados;
- tuples, structs e enums para agregar dados;
- padrões correspondentes para selecionar e desestruturar dados; e
- Características para definir o comportamento de dados.
O GitLab explica que essas características essencialmente criam “guarda-corpos” permitindo que desenvolvedores que usam Rust possam “criar códigos de movimentos rápidos com poucas coisas para desacelerá-los”.
Características
O blog do Stack Overflow elogia as características do Rust que melhoram tanto sua velocidade quanto segurança. Usa tipos fortes e estáticos, o que quer dizer que muitas regras de tipagem são restringidas para garantir exatidão e esse tipo é checado durante o tempo de compilação em vez de no tempo de execução. Seu compilador tem um verificador de empréstimos que permite compatibilidade retrógrada que “garante que referências não durem mais que os dados a que se referem”.
Além disso, como o Mozilla nota em um vídeo de 2016, as mesmas verificações de segurança que evitam bugs de memória também detectam erros de multithread, permitindo paralelismo e concorrência seguros. Paralelismo permite que código seja executado em múltiplos núcleos ao mesmo tempo, enquanto concorrência permite que duas ou mais tarefas sejam executadas em períodos de tempo sobrepostos – ambos permitindo execuções mais rápidas e eficientes.
Além disso, fornece abstrações de custo zero e não usa um garbage collector (coletor de lixo) para gerenciar memórias. Finalmente, diferentemente de C, Rust é seguro por padrão – isso significa que, para escrever códigos inseguros, você opta por ativá-los com uma palavra-chave.
- tipagem forte e segura;
- verificador de empréstimos para compatibilidade retrógrada;
- concorrência e paralelismo seguros;
- abstrações de custo zero;
- sem coletor de lixo; e
- opção por modo inseguro.
Vantagens do Rust
O Rust pode não ser ideal para todas as situações, mas as vantagens para códigos de entrada e de desempenho-crítico são inegáveis, já que equilibra as necessidades por um código seguro, protegido e confiável, com a habilidade de executar de forma rápida e eficiente.
Confiabilidade e segurança
Talvez a maior vantagem do Rust seja sua habilidade de detectar e prevenir erros de memória. Como dito antes, o Rust é seguro por padrão - isso significa que a linguagem de programação cuida da segurança do início ao fim.
Como a New Stack explica, “para cada objeto, a quantidade apropriada de memória será alocada – ou reservada – de acordo. Quando acessamos aquele objeto, é impossível acidentalmente acessar o local de memória fora dos seus limites. E quando seu trabalho acaba, o objeto é automaticamente realocado pelo sistema”. Em outras palavras, gerir memória não requer que o desenvolvedor faça cálculos e, como resultado, não oferece oportunidade de equívocos nem induz a erros.
Isso é uma feature particularmente útil, já que bugs de memória podem ser difíceis de identificar e podem permanecer por anos sem serem detectados. Eliminar – ou melhor, prevenir – esses bugs é crucial, já que o escopo de suas consequências, que o Mozilla descreve em um post de blog de 2019 incluem:
- crash: fechar inesperadamente devido a acessos de memória inválida;
- vazamento de dados: expor dados não-públicos, como senhas; e
- arbitrary code execution (ACE): dar a habilidade de executar comandos arbitrários a uma máquina alvo para invasores, acidentalmente; ou remote code execution (RCE) quando isso ocorre em uma rede.
Em outras palavras, o Rust previne erros de memória que tornam programas menos confiáveis, além de torná-los muito menos seguros. Como colocado pela Mozilla, “O melhor dos casos, na maioria dos erros de memória causa crashes inofensivos – que também não é muito bom. No entanto, o pior dos casos é que um invasor possa controlar um programa por meio de uma vulnerabilidade - que pode levar a mais ataques”.
Desempenho
Apesar das possíveis falhas de segurança, código inseguro pode ser usado para atingir metas de desempenho – daí seu uso padrão em C e C++. E, se o Rust tem um modo inseguro pela qual você pode optar, o Rust seguro também foi desenvolvido para rapidez e eficiência de recursos.
Para evitar erros introduzidos pelo manejo manual de memória, como nos casos de C e C++, muitas linguagens usam o coletor de lixo para gerenciar a memória automaticamente. No entanto, essa medida de segurança tem um custo. Programas devem pausar durante a coleta de lixo, resultando em picos de latência intermitentes. Para evitar essa pausa, o Rust usa um modelo de posse, impondo regras que permitem que o compilador gerencie a memória sem desacelerar o desempenho em tempo de execução.
Além disso, o Rust fornece um jeito de entregar execução rápida via concorrência segura e programação paralela. Por décadas, os ganhos de performance de CPUs cresciam regularmente, mas atingiram um limite de otimização. Para acelerar o processamento, tarefas devem ser executadas em múltiplos núcleos ao mesmo tempo, necessitando programação concorrente (de sobreposição) ou paralela (simultânea).
No entanto, esse tipo de programação multithread é difícil e pode trazer erros numerosos que passam despercebidos por anos, forçando que desenvolvedores escolham entre rapidez e segurança – uma troca que não é necessária no Rust.
Como o Rust se compara com outras linguagens
O Rust foi claramente desenvolvido para resolver algumas das frustrações que os desenvolvedores expressaram em relação a C e C++ e tem sido bastante reconhecido como uma melhoria quanto a essas questões. Um artigo de opinião escrito por Jon Evans na TechCruch, muito compartilhado em 2015, colocava o Rust como alternativa ao C por “chegar nos ossos e funcionar a toda velocidade” sem trazer “bugs sutis que podem se tornar lacunas enormes de segurança”.
O Rust também chamou atenção por resolver problemas de outras linguagens de programação. Em 2016, o Sentry usou Rust para fazer um source map parsing mais rápido e de recursos mais eficientes do que conseguiu usando Python, devido ao object headers e ao coletor de lixo do Python. De maneira semelhante, o Discord passou a usar o Rust em 2020 para reduzir picos de latência que aconteciam durante as coletas de lixo forçadas do Go e conseguiu reduzir a latência de forma significativa, além de otimizar a memória e CPU.
Como resultado de seus próprios benefícios, o Rust foi eleito como linguagem “mais amada” pelos últimos cinco anos na pesquisa anual dos desenvolvedores do Stack Overflow. Além disso, está sendo usado com cada vez mais frequência, em projetos maiores, entrando na lista de top 20 linguagens de programação mais populares da Tiobe em junho do ano passado. No mais, a Microsoft passou a adotá-la para reduzir bugs e falhas de segurança depois de determinar que 70% de todos os pacotes de segurança são devidos a questões relacionadas à memória.
Implementando Rust nos Azion Cells
Diferentemente do AWS Lambda, que usa node.js em seu motor, nossa implementação foi escrita em Rust, permitindo maior confiabilidade e velocidade. Também permite melhor desempenho usando menos recursos, já que consegue executar JavaScript sem rodar um processo inteiro de node.js.
Além disso, Azion Cells usa V8 Isolate sobre um ambiente multitenant, que isola e assegura cada função sem a necessidade de contêineres, que pode degradar o desempenho ainda mais com latência alta e imprevista, devido a cold starts.
Azion Cells são como blocos de construção para o Edge Functions, viabilizando a execução rápida, confiável e segura de funções serverless em nossa edge network. O Edge Functions é ideal para aplicações modernas, particularmente 5G, Edge Computing, IoT e outras tecnologias emergentes centradas em dados que elevam o padrão de desempenho, segurança e confiabilidade.