Preskoči na sadržaj

Upload direktno na S3 s potpisanim URL-om, umjesto preko backenda

Pedeset istovremenih uploada od 4 MB preko servera drži 200 MB RAM-a i dvaput prelazi mrežu. Kako potpisati petominutni URL za upload na S3 i provjeriti fajl.

3 min čitanja5 sekcija

Kad sam počeo raditi na državnom sistemu za lične karte i pasoše, prvo sam pročitao račun za cloud. Bio je osam puta veći nego što je trebalo, iz tri odvojena razloga. Ovdje pišem o trećem, jer u njemu griješe skoro svi i jer najlošije podnosi rast.

Svaki zahtjev za ličnu kartu ili pasoš u tom sistemu bio je upload: fotografije lica, fotografije dokumenata i biometrijski podaci snimljeni na terenu. Svaki od tih fajlova putovao je do skladišta preko backenda.

01

Koliko košta upload preko backenda

Obrazac izgleda bezazleno. Fajl stigne na vaš server, middleware ga baferuje, kod ga validira, a onda ga pošaljete u object storage:

snippet.js
JavaScript · 8 linija
app.post('/upload', upload.single('file'), async (req, res) => {  await s3.putObject({    Bucket: BUCKET,    Key: key(req),    Body: req.file.buffer,  })  res.json({ ok: true })})

Pritom se dešavaju tri stvari koje na malom deploymentu ne primijetite.

Svaki bajt zauzima memoriju instance. Fotografija od 4 MB drži 4 MB RAM-a dok traje zahtjev, pa pedeset istovremenih uploada drži 200 MB koji ne rade ništa osim što čekaju. Na kraju instancu dimenzionišete prema najvećem broju istovremenih uploada umjesto prema poslu koji aplikacija radi.

Svaki bajt uz to dvaput prelazi mrežu, od klijenta do vašeg servera i od servera do skladišta. Plaćate prenos koji nije morao postojati, a plaćate ga još jednom latencijom, jer upload klijenta nije gotov dok vaš server ne završi svoj.

Granicu propusnosti tada određuje instanca, a ne skladište. Object storage prima daleko više istovremenih upisa nego bilo koji pojedinačni aplikacioni server. Kad server stavite u sredinu, servis napravljen za takvo opterećenje zamijenite onim koji za njega nije napravljen.

Ništa od ovoga ne izbacuje grešku. Vidite samo veću instancu, sporije uploade i račun za koji mislite da je normalna cijena rada sistema.

02

Fajl ide direktno u skladište

Klijent uploaduje direktno u skladište, a vaš server bajtove nikad ne vidi. Server samo odlučuje smije li se upload desiti i potpisuje URL koji dopušta taj jedan upload i ništa više. AWS takav URL zove presigned URL.

server: authorize, then sign
JavaScript · 16 linija
// server: authorize, then signapp.post('/upload-url', requireAuth, async (req, res) => {  const key = `applications/${req.user.id}/${randomUUID()}`   const url = await getSignedUrl(    s3,    new PutObjectCommand({      Bucket: BUCKET,      Key: key,      ContentType: req.body.contentType,    }),    { expiresIn: 300 },  )   res.json({ url, key })})
client: upload straight to storage
JavaScript · 12 linija
// client: upload straight to storageconst { url, key } = await api.post('/upload-url', {  contentType: file.type,}) await fetch(url, {  method: 'PUT',  body: file,  headers: { 'Content-Type': file.type },}) await api.post('/applications', { photoKey: key })

Server ovdje provjerava ko traži, odlučuje gdje fajl smije završiti i postavlja kratak rok isteka, a da pritom ne dotakne nijedan bajt fajla.

03

Gdje se kod direktnog uploada griješi

Fajl više ne možete pregledati prije nego što stigne, pa prije potpisivanja provjerite ono što možete: content type, prijavljenu veličinu i ko traži. Ostalo provjerite kad objekt već postoji. U praksi je to korak koji pročita sačuvani objekt i odbaci ga ako nije ono za šta se izdavao.

Server uz to više ne zna kad je upload završen. To mu javlja klijent, a klijenti lažu ili nestanu. Zato ili provjerite da objekt postoji prije nego što zapis smatrate potpunim, ili se pretplatite na događaje iz skladišta i pustite da se zapis sam dovrši.

Potpisani URL koji je iskorišten, a nikad potvrđen, iza sebe ostavi objekt na koji ne pokazuje nijedan zapis. Lifecycle pravilo koje u jednom prefiksu briše takve objekte ne košta ništa, a poštedi vas toga da za njih saznate tek godinu dana kasnije.

Pet minuta je obično dobar rok isteka: dovoljno dugo za sporu vezu, a dovoljno kratko da URL koji procuri ne vrijedi ništa do trenutka kad ga neko pronađe.

Dozvolu dajte što užu. Potpisani URL je ovlaštenje, pa ga potpišite za jedan ključ, jednu metodu i jedan content type.

04

Šta je ova izmjena promijenila na tom projektu

Na tom projektu ovo je bila jedna od tri izmjene, a zajedno su spustile mjesečni račun s 24.000 $ na 2.000 $. Ova je bila najvažnija zbog prirode proizvoda. Svaki zahtjev u njemu bio je upload, pa je većina posla sistema išla putanjom uploada.

Instance su postale manje jer više nisu držale fajlove, a uploadi brži jer je svaki fajl putovao jednom umjesto dvaput. Granica propusnosti preselila se sa servera koje smo vodili mi na skladište koje vodi Amazon.

05

Kad upload ipak treba ići preko servera

Ponekad treba. Ako fajl morate obraditi prije pohranjivanja, na primjer promijeniti mu veličinu, transkodirati ga, ukloniti metapodatke ili ga skenirati na malver prije nego što bude sačuvan bilo gdje, bajtovi moraju stići do nečega što taj posao može obaviti.

I tada je bolje da fajl naknadno iz skladišta povuče worker nego da svaki bajt prolazi kroz vaš API server. Provjerite i treba li vam išta od ovoga, ili fajlovi idu preko servera samo zato što je tako bilo u tutorijalu.

Kako je nastao ovaj tekstIzrada državnog sistema za izdavanje ličnih karata i pasoša, čiji je račun za cloud imao još dva problema: predimenzionisanu infrastrukturu i beskonačne petlje.Pročitajte više o projektu →Povezana uslugaPregled troškova cloudaPogledajte uslugu →Sljedeći tekstAWS račun s 24.000 $ na 2.000 $ mjesečno: tri uzroka koja sam našaoPročitajte →

Blog

Ako radite na nečemu sličnom, javite nam se s detaljima.

Prvi poziv traje 30 minuta i ni na šta vas ne obavezuje.

Opišite nam svoj projekatcontact@microtech.consulting