O documento descreve os desafios técnicos de um serviço de armazenamento de arquivos feito em GO, incluindo extração de metadados, feedback de progresso para o usuário, complexidade da arquitetura e transferência segura de arquivos grandes entre camadas. Soluções como streaming, criptografia e divisão de arquivos são discutidas.
2. There is More to Know | Neoway
Marcelo Aymone
marcelo.aymone@neoway.com.br
Desenvolvedor de software na Neoway há quase 2 anos, atua na
manutenção e evolução da arquitetura de indexação e pesquisas
do motor de buscas. Além de produzir software ama cozinhar e
jogar RPG online.
Diogo Giassi
diogo.giassi@neoway.com.br
Desenvolvedor de software na Neoway a mais de cinco anos onde
atualmente conduz um Squad com foco em solucionar desafios
que envolvem integrações automatizadas entre plataformas para
ingestão e extração de dados. Aficionado por solucionar
problemas, encontrou a mais de dez anos no desenvolvimento de
software uma forma de unir trabalho e paixão.
A Neoway
Resolvemos problemas reais de
empresas que precisam vender
mais ou perder menos.
Usamos o Big Data como uma
ferramenta de descoberta de
oportunidades mercadológicas, tornando
o grande volume de dados facilmente
navegável, as análises intuitivas e as
decisões mais seguras.
https://www.neoway.com.br
4. There is More to Know | Neoway
Neoway Drive
Lançado em Fevereiro de 2018, o
Neoway Drive, tem o propósito
de servir como um repositório
persistente para arquivos
importados/exportados pelo
usuário a partir dos diversos
outros apps. Organizado por um
diretório raíz e sub-diretórios, os
arquivos são armazenados
durante um determinado
período de tempo, até que
sejam finalmente expirados.
Aplicação
Componente
5. There is More to Know | NeowayThere is More to Know | Neoway
Requisitos Técnicos
● Possível instalação em ambientes Cloud ou
OnPremises;
● Transferência e armazenamento dos arquivos de
forma criptografada;
● Possibilitar o armazenamento de arquivos com
até 5 Gigas de tamanho;
● Extração de Metadados durante o processo de
Upload (Ex.: Contagem de linhas, Checksum);
● Upload através do Browser com barra de
progresso;
● Centralizar os pontos de recebimento e entrega
de arquivos aos usuários da plataforma.
6. There is More to Know | Neoway
Application Cluster
Arquitetura / Integrações
DRIVE
WebApp
DRIVE
API - Gateway
DRIVE
Service
Mongo
DB
AWS
S3
HUB
WebApp
HUB
API - Gateway
MATCHING
Service
PV
BIN
META
DRIVE
SEARCHBAR
WebApp
SEARCHBAR
API - Gateway
EXPORT
Service
PV
PV
DriveLayer
BIN
META
Front Layer Gateway Layer
Service Layer StoragesComponents
Legenda
Temporary Storage
Mongo
DB
Mongo
DB
8. There is More to Know | NeowayThere is More to Know | Neoway
Desafios
● Extração de metadados baseados no tipo e
conteúdo do arquivo;
● Feedback de progresso para o usuário nos
processos de Upload e Download;
● Complexidade da arquitetura da Neoway;
● Trafegar arquivos de até 5GB entre as camadas
garantindo:
○ Ausência de timeouts;
○ Baixo consumo de recursos;
○ Segurança no tráfego e armazenamento de
dados;
● Calcular o Checksum do arquivo.
9. There is More to Know | Neoway
Application Cluster
Baseada em Microsserviços
Gatekeepers Cluster
Node 1
Node 2Node 2
Node 1
NGINX
Reverse-Proxy
Arquitetura - Neoway
Mongo
DB
AWS
S3
BIN
META
BIN
META
DRIVE
Service
PV
DRIVE
API - Gateway
HEIMDALL
Vulcand-Proxy
LOKI
Authorization
Server
NGINX
Reverse-Proxy
AZURE-LOADBALANCER
Load-Balancer
AZURE-LOADBALANCER
Load-Balancer
NGINX
Reverse-Proxy
DRIVE
Service
PV
DRIVE
API - Gateway
NGINX
Reverse-Proxy
HEIMDALL
Vulcand-Proxy
LOKI
Authorization
Server
DriveServiceLayer
CLOUDFLARE
ServidorDNS
10. There is More to Know | Neoway
Application Cluster
Redundância
Gatekeepers Cluster
Node 1
Node 2Node 2
Node 1
NGINX
Reverse-Proxy
Arquitetura - Neoway
Mongo
DB
AWS
S3
BIN
META
BIN
META
DRIVE
API - Gateway
HEIMDALL
Vulcand-Proxy
LOKI
Authorization
Server
NGINX
Reverse-Proxy
NGINX
Reverse-Proxy
DRIVE
API - Gateway
NGINX
Reverse-Proxy
HEIMDALL
Vulcand-Proxy
LOKI
Authorization
Server
DriveServiceLayer
CLOUDFLARE
ServidorDNS
AZURE-LOADBALANCER
Load-Balancer
AZURE-LOADBALANCER
Load-Balancer
Redundância de serviços
DRIVE
Service
DRIVE
Service
PV
PV
11. There is More to Know | Neoway
Application Cluster
Timeouts
Gatekeepers Cluster
Node 1
Node 2Node 2
Node 1
NGINX
Reverse-Proxy
Arquitetura - Neoway
Mongo
DB
AWS
S3
BIN
META
BIN
META
DRIVE
Service
PV
HEIMDALL
Vulcand-Proxy
LOKI
Authorization
Server
NGINX
Reverse-Proxy
NGINX
Reverse-Proxy
DRIVE
Service
PV
NGINX
Reverse-Proxy
HEIMDALL
Vulcand-Proxy
LOKI
Authorization
Server
DriveServiceLayer
CLOUDFLARE
ServidorDNS
Timeout de 60 segundos em várias
camadas e a sessão do usuário
AZURE-LOADBALANCER
Load-Balancer
AZURE-LOADBALANCER
Load-Balancer
DRIVE
API - Gateway
DRIVE
API - Gateway
12. There is More to Know | Neoway
Application Cluster
Consumo de Memória
Gatekeepers Cluster
Node 1
Node 2Node 2
Node 1
NGINX
Reverse-Proxy
Arquitetura - Neoway
Mongo
DB
AWS
S3
BIN
META
BIN
META
DRIVE
Service
PV
DRIVE
API - Gateway
HEIMDALL
Vulcand-Proxy
LOKI
Authorization
Server
NGINX
Reverse-Proxy
NGINX
Reverse-Proxy
DRIVE
Service
PV
DRIVE
API - Gateway
NGINX
Reverse-Proxy
HEIMDALL
Vulcand-Proxy
LOKI
Authorization
Server
DriveServiceLayer
CLOUDFLARE
ServidorDNS
Middlewares sem suporte a stream
levando a um alto consumo de memória
AZURE-LOADBALANCER
Load-Balancer
AZURE-LOADBALANCER
Load-Balancer
14. There is More to Know | Neoway
Leitura e Escrita
Source io.Reader
Targetio.Writer
Transfer Buffer []byte
io.Reader
● É a interface que envolve o método básico de leitura: Read(p []byte) (n int, err error).
io.Writer
● É a interface que envolve o método básico de escrita: Write(p []byte) (n int, err error).
Transfer Buffer []byte
15. There is More to Know | Neoway
io.Copy
Stream
Targetio.Writer
Transfer Buffer []byte
Source io.Reader
io.Copy
● O método io.Copy realiza a cópia da origem para o destino;
● Retorna o número de bytes copiados e o primeiro erro encontrado durante a cópia, se
houver;
● Possui um tamanho padrão de buffer de 32 KB. Caso precise de um buffer de tamanho
diferente utilizar io.CopyBuffer.
16. There is More to Know | Neoway
io.Copy
Stream - Analisando Dados
Targetio.Writer
Transfer Buffer []byte
Source io.Reader io.TeeReader
io.Writer Target
io.TeeReader
● O método io.TeeReader retorna um io.Reader que escreve para um Destino o que lê de uma
Origem. Todas as leituras realizadas através dele são correspondidas com as gravações
correspondentes para o novo Destino informado;
● Não há buffer interno - a gravação deve ser concluída antes da leitura ser concluída.
17. There is More to Know | Neoway
io.Copy
Stream - Criptografando Dados
Targetio.Writer
Transfer Buffer []byte
Source io.Reader cipher.StreamReader
cipher.Stream
● Representa uma cifra de fluxo;
cipher.StreamReader
● Agrupa um fluxo em um io.Reader. Ele chama XORKeyStream da interface cipher.Stream para
processar cada fatia de dados que passa.
18. There is More to Know | Neoway
Stream - Criptografando Dados
aes.NewCipher
● Retorna um novo cifrador de blocos (cipher.Block), baseado em uma chave AES de 16, 24 ou
32 Bytes para selecionar o algoritmo AES-128, AES-192 ou AES-256.
cipher.NewOFB
● Retorna um fluxo que pode criptografar ou descriptografar usando uma cifrador de bloco no
modo Output Feedback (OFB).
func BuildOFBStreamReader(key string, reader io.Reader) (io.Reader, error) {
block, err := aes.NewCipher([]byte(key))
if err != nil {
return nil, err
}
var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
return &cipher.StreamReader{S: stream, R: reader}, nil
}
19. There is More to Know | Neoway
Download
io.Copy AWS SDK
Upload / Download
AWS
S3
http.ResponseWriter
io.Writer
cipher.OFB
cipher.StreamReader
Buffer
io.ReaderUser
Storage
Upload
AWS SDK
AWS
S3
io.TeeReader
cipher.OFB
cipher.StreamReader
Metadata
Extractor
io.Writer
sha256.Hash
io.Writer
io.TeeReader
Size
Rows
SHA-256
META
Buffer
http.Request.Body
io.Reader
User
Storage
20. There is More to Know | NeowayThere is More to Know | Neoway
Desafios (Solucionados)
● ✔ Extração de metadados baseados no tipo e
conteúdo do arquivo;
● Feedback de progresso para o usuário nos
processos de Upload e Download;
● Complexidade da arquitetura da Neoway;
● Trafegar arquivos de até 5 GB entre as camadas
garantindo:
○ Ausência de timeouts;
○ ✔ Baixo consumo de recursos;
○ ✔ Segurança no tráfego e armazenamento
de dados;
● ✔ Calcular o Checksum do arquivo.
21. There is More to Know | Neoway
Dividindo Arquivos
Chunked Transfer Encoding (HTTP 1.1)
● Conexão TCP persistente
● Cabeçalho “Transfer-Encoding: chunked”
● Tela parada até finalizar
● Buffer em camadas de roteamento
Técnica Multipart
● Múltiplas requests
● Envio paralelo de chunks
● Suporta barra de progresso
● Suporta pausar e continuar
● Controle de requests concorrentes
● Precisa codificar
Multipart
client server
Open
Close
Chunked Transfer
client server
Open
Close
Open
Open
Close
Close
22. There is More to Know | Neoway
Dividindo Arquivos
Blob
● Um objeto Blob representa um objeto do tipo arquivo, com dados brutos imutáveis.
hello world = 68 65 6c 6c 6f 20 77 6f 72 6c 64
string (11) = []byte
Blob Slice
● Novo objeto Blob contendo os dados do intervalo especificado de bytes(3) da fonte Blob.
hello world = 68 65 6c 6c 6f 20 77 6f 72 6c 64
3 bytes
23. There is More to Know | Neoway
Dividindo Arquivos - Frontend
Browser Upload
● Dividindo arquivos em múltiplas requisições.
Definições para multipart
● Tamanho dos chunks (bytes)
○ 1mb = 1.000.000 bytes
● Quantidade de chunks
○ filesize / chunksize
○ ceil(54234543 / 1000000) = 55
● Cabeçalho do envio
○ “Content-Range: bytes 1000-2000/10000”
● Controle de progresso
○ (1 / 55) * 100 = 1.8%
// set mimetype
let contentType = blob.type;
// set header from params
let rangeheader = `bytes ${start}-${end}/${size}`;
// create chunk from slice
let chunk = blob.slice(start, end, contentType);
// send request
const xhr = new XMLHttpRequest();
xhr.open('PUT', UPLOAD_URL, true);
xhr.setRequestHeader('Content-
Range',rangeheader);
xhr.setRequestHeader('Content-Type', contentType);
xhr.send(chunk);
24. There is More to Know | Neoway
Dividindo Arquivos - Frontend
25. There is More to Know | Neoway
Backend
● Endpoints:
○ Registrar o upload;
○ Enviar os chunks;
○ Finalizar o envio;
● Recombinar os chunks:
○ Ordenar (Chunk Id);
○ Agrupar;
○ Enviar (S3);
○ Limpar temporários;
Dividindo Arquivos - Backend
----------------------------------------
POST /v1/files
header Content-Type: "text/csv"
Response 201 Created
{ fileId: “5d74027c79f40700077c1950” }
----------------------------------------
PUT /v1/files/:fileId/chunked-upload
header Content-Range: "bytes 0-1000/10000"
Response 206 Partial Content
----------------------------------------
PUT /v1/files/:fileId/finish-upload
Response 202 Accepted
26. There is More to Know | Neoway
Backend
● Endpoints:
○ Registrar o upload;
○ Enviar os chunks;
○ Finalizar o envio;
● Recombinar os chunks:
○ Ordenar (Chunk Id);
○ Agrupar;
○ Enviar (S3);
○ Limpar temporários;
Dividindo Arquivos - Backend
type chunk struct {
name string // 0000-1000
start int64 // 0000
end int64 // 1000
}
type chunkSorter []*chunk
func (s chunkSorter) Len() int {
return len(s)
}
func (s chunkSorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s chunkSorter) Less(i, j int) bool {
a := s[i].start + s[i].end // 0000 + 1000 = 1000
b := s[j].start + s[j].end // 2000 + 3000 = 5000
return a < b
}
infos, err := ioutil.ReadDir(dirname) // []os.FileInfo
chunks := getChunks(infos) // parse chunk data from
os.fileInfo
sort.Sort(chunkSorter(chunks)) // sort after parse chunk metadata
Ordenação quicksort
27. There is More to Know | Neoway
Backend
● Endpoints:
○ Registrar o upload;
○ Enviar os chunks;
○ Finalizar o envio;
● Recombinar os chunks:
○ Ordenar (Chunk Id);
○ Agrupar;
○ Enviar (S3);
○ Limpar temporários;
Dividindo Arquivos - Backend
// open fileout to append chunks from list
fileOut, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
for _, chunk := range chunks {
// open chunk buffer
chunkBuffer, err := os.Open(dirName + “/” + chunk.name))
if err != nil {
return nil, err
}
// copy chunk buffer to fileOut in append mode
if _, err := io.Copy(fileOut, chunkBuffer); err != nil {
return nil, err
}
err = chunkBuffer.Close()
if err != nil {
return nil, err
}
//flush fileout buffer direct to disk
err = fileOut.Sync()
if err != nil {
return nil, err
}
28. There is More to Know | Neoway
Merging
Chunks
Chunk
Upload
io.Copy
io.Copy
Finish
Upload
AWS SDK
Dividindo Arquivos - Upload
os.File
io.Writer
Buffer
http.Request.Body
io.Reader
Local
Storage
AWS
S3
User
Storage
Local
Storage
os.File
io.Reader
io.TeeReader
cipher.OFB
cipher.StreamReader
Metadata
Extractor
io.Writer
sha256.Hash
io.Writer
io.TeeReader
Size
Rows
SHA-256
META
os.File
io.ReaderLocal
Storage
os.File
io.Writer
Local
Storage
Buffer
Buffer
os.File
io.Reader
os.File
io.Reader
29. There is More to Know | NeowayThere is More to Know | Neoway
Desafios (Solucionados)
● ✔ Extração de metadados baseados no tipo e
conteúdo do arquivo;
● ✔ Feedback de progresso para o usuário nos
processos de Upload e Download;
● ✔ Complexidade da arquitetura da Neoway;
● ✔ Trafegar arquivos de até 5 GB entre as
camadas garantindo:
○ ✔ Ausência de timeouts;
○ ✔ Baixo consumo de recursos;
○ ✔ Segurança no tráfego e armazenamento
de dados;
● ✔ Calcular o Checksum do arquivo.
31. There is More to Know | Neoway
Resultados
Após um ano e meio do
lançamento do serviço em
produção estes são as
integrações e formas de uso do
serviço.
Utilização
Integrações
● Componente de Upload utilizado em 3 aplicativos
desenvolvidos em AngularJS e VueJS;
● Integrado com 7 serviços desenvolvidos em NodeJS e
GO que utilizam para:
○ Armazenamento temporário de dados exportados
em arquivos CSV, XLSX e ZIP.
○ Armazenamento de dossiês em arquivos PDF e
HTML por períodos de até 5 anos;
○ Área temporária para importação de dados
através de arquivos CSV.
32. There is More to Know | Neoway
Resultados
Utilização
Volume
● Mais de 168 mil arquivos armazenados dos seguintes
formatos: XLSX, PDF, CSV, HTML e ZIP;
● Em torno de 100 GB em arquivos armazenados;
● Crescimento mensal em 2019 na média de 15 mil
arquivos e 9 GB em espaço de armazenamento.
Custo S3 (AWS)
● Total de $ 3,05 dólares no mês de Agosto de 2019.
Após um ano e meio do
lançamento do serviço em
produção estes são os dados de
utilização e consumo de
recursos.
33. There is More to Know | Neoway
Consumo de Recursos
CPU
● Média de 0.2% em uma máquina Azure -
Standard D4s (v3) equipada com 4
processadores Intel Xeon® E5-2673 de 2,4 GHz.
Memória
● Média de 16 MB constantes indiferente do
tamanho dos arquivos trafegados.
35. There is More to Know | NeowayThere is More to Know | Neoway
Melhorias Futuras
● Não trabalhar com discos baseados em rede
como storage local;
● Expirar os arquivos para o Amazon S3 Glacier
visando a redução do custo;
● Permitir reiniciar da onde parou Uploads
interrompidos por problemas de comunicação;
● Não precisar informar de forma diferente ao
servidor que o Upload acabou, o mesmo saber por
entender que todas as partes chegaram;
● Melhorar o algoritmo de junção de partes para já
ir juntando as partes no decorrer do Upload.