
HÀndelsedriven arkitektur ökar kostnadseffektiviteten för de resurser som anvÀnds, eftersom de anvÀnds endast nÀr de behövs. Det finns mÄnga sÀtt att implementera detta utan att skapa extra molnresurser som worker-applikationer. Idag kommer jag inte att prata om FaaS, utan om webhooks. Jag kommer att visa ett exempel pÄ hÀndelsebehandling med hjÀlp av webhooks frÄn objektlagring.
Ett par ord om objektlagring och webhooks. Objektlagring gör det möjligt att lagra vilken data som helst i molnet som objekt, som Àr tillgÀngliga via S3 eller annan API (beroende pÄ implementation) över HTTP/HTTPS. Webhooks Àr i allmÀnhet anvÀndardefinierade omvÀnda anrop via HTTP. De utlöses vanligtvis av en hÀndelse, till exempel nÀr kod skickas till ett repository eller en kommentar publiceras i en blogg. NÀr en hÀndelse intrÀffar, skickar den ursprungliga webbplatsen en HTTP-förfrÄgan till den URL som anges för webhook. Som ett resultat kan hÀndelser pÄ en webbplats utlösa ÄtgÀrder pÄ en annan. I det hÀr fallet, nÀr objektlagring fungerar som den ursprungliga webbplatsen, Àr förÀndringar i dess innehÄll hÀndelser.). NÀr kÀllwebbplatsen Àr ett objektlagringssystem, utgör Àndringar i dess innehÄll hÀndelser.
Exempel pÄ enklare fall dÀr sÄdan automatisering kan anvÀndas:
- Skapa kopior av alla objekt i en annan molnlagring. Kopiorna ska skapas "pÄ flygande fot", vid varje tillÀgg eller Àndring av filer.
- Automatisk skapelse av serier av miniatyrbilder av grafiska filer, tillÀgg av vattenstÀmplar pÄ foton, andra modifieringar av bilder.
- Notifiering om ankomsten av nya dokument (till exempel, en distribuerad bokföringsavdelning laddar upp rapporter till molnet, och finansiell övervakning fÄr aviseringar om nya rapporter, kontrollerar och analyserar dem).
- Lite mer komplexa fall innebÀr exempelvis att generera en förfrÄgan till Kubernetes, som skapar en pod med nödvÀndiga containrar, överför uppgiftens parametrar och sedan avvecklar containern efter bearbetningen.
Som ett exempel kommer vi att göra en variant av uppgift 1 dÀr Àndringar i objektlagringen Mail.ru Cloud Solutions (MCS) synkroniseras till AWS objektlagring med hjÀlp av webhooks. I verkliga tunga fall bör man tÀnka pÄ asynkron drift genom att registrera webhooks i en kö, men för den hÀr övningsuppgiften gör vi en implementation utan det.
Arbetsdiagram
Protokollet för interaktion beskrivs i detalj i . Arbetsdiagrammet innehÄller följande element:
- PubliseringstjÀnst, som finns pÄ sidan av S3-lagringen och publicerar HTTP-förfrÄgningar nÀr webhooken utlöses.
- Webhook-mottagarserver, som lyssnar pÄ förfrÄgningar frÄn publiseringstjÀnsten via HTTP och utför lÀmpliga ÄtgÀrder. Servern kan skrivas pÄ vilket sprÄk som helst, i vÄrt exempel kommer vi att skriva servern pÄ Go.
En specifik funktion av implementeringen av webhooks i S3 API Àr registreringen av webhook-mottagarservern pÄ publiseringstjÀnsten. Specifikt mÄste webhook-mottagarservern bekrÀfta prenumerationen pÄ meddelanden frÄn publiseringstjÀnsten (i andra implementationer av webhooks krÀvs vanligtvis inte prenumerationsbekrÀftelse).
DÀrför mÄste webhook-mottagarservern stödja tvÄ grundlÀggande operationer:
- bekrÀfta publikationsservicens begÀran om registreringsbekrÀftelse,
- behandla inkommande hÀndelser.
Installation av webhook-mottagarserver
För att köra webhook-mottagarservern behöver vi en Linux-server. I denna artikel anvÀnder vi för exempel en virtuell instans som vi implementerar pÄ MCS.
LÄt oss installera nödvÀndig programvara och starta webhook-mottagarservern.
ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install git
LĂ€ser paketlistor... Klar
Bygger beroendetrÀd
LÀser tillstÄndsinformation... Klar
Följande paket har installerats automatiskt och krÀvs inte lÀngre:
bc dns-root-data dnsmasq-base ebtables landscape-common liblxc-common
liblxc1 libuv1 lxcfs lxd lxd-client python3-attr python3-automat
python3-click python3-constantly python3-hyperlink
python3-incremental python3-pam python3-pyasn1-modules
python3-service-identity python3-twisted python3-twisted-bin
python3-zope.interface uidmap xdelta3
AnvÀnd 'sudo apt autoremove' för att ta bort dem.
Föreslagna paket:
git-daemon-run | git-daemon-sysvinit git-doc git-el git-email git-gui
gitk gitweb git-cvs git-mediawiki git-svn
Följande NYA paket kommer att installeras:
git
0 uppgraderade, 1 nyinstallerad, 0 att ta bort och 46 inte uppgraderade.
Behöver hÀmta 3915 kB av arkiv.
Efter den hÀr ÄtgÀrden kommer 32.3 MB extra diskutrymme att anvÀndas.
HĂ€mtar:1 http://MS1.clouds.archive.ubuntu.com/ubuntu bionic-updates/main
amd64 git amd64 1:2.17.1-1ubuntu0.7 [3915 kB]
HÀmtade 3915 kB pÄ 1s (5639 kB/s)
VĂ€ljer tidigare ickevalt paket git.
(LÀser databasen ... 53932 filer och mappar Àr för nÀrvarande installerade.)
Förbereder att packa upp .../git_12.17.1-1ubuntu0.7_amd64.deb ...
Packar upp git (1:2.17.1-1ubuntu0.7) ...
StÀller in git (1:2.17.1-1ubuntu0.7) ...Klonar mappen med webhook-mottagarservern:
ubuntu@ubuntu-basic-1-2-10gb:~$ git clone
https://github.com/RomanenkoDenys/s3-webhook.git
Klonar in i 's3-webhook'...
remote: UpprÀkning av objekt: 48, klar.
remote: RĂ€kna objekt: 100% (48/48), klar.
remote: Komprimering av objekt: 100% (27/27), klar.
remote: Totalt 114 (delta 20), ÄteranvÀnd 45 (delta 18), packade om 66
Mottar objekt: 100% (114/114), 23.77 MiB | 20.25 MiB/s, klar.
Löser skillnader: 100% (49/49), klar.Vi startar servern:
ubuntu@ubuntu-basic-1-2-10gb:~$ cd s3-webhook/
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80Prenumeration av publiceringstjÀnst
Du kan registrera din webbhook-server antingen via API eller webgrÀnssnitt. För enkelhetens skull registrerar vi via webgrÀnssnittet:
- i kontrollpanelen.
- GÄ in i bucketen för vilken vi ska konfigurera webhooks, och klicka pÄ kugghjulet:

GÄ till fliken Webhooks och klicka pÄ LÀgg till:

Fyll i fÀlten:

ID - namnet pÄ webhooken.
Event - vilka hÀndelser som ska överföras. Vi har angett att överföra alla hÀndelser som intrÀffar vid hantering av filer (tillÀgg och borttagning).
URL - adressen till webbhook-mottagarservern.
Filter prefix/suffix - filter som gör att webhooks genereras endast för objekt vars namn följer vissa regler. Till exempel, för att webhooken ska utlösas endast för filer med filtillÀgget .png, behöver du i Filter suffix skriva "png".
För nÀrvarande stöds endast portar 80 och 443 för kommunikation med webbhook-mottagarservern.
Vi klickar pÄ LÀgg till hook och ser följande:

Hook har lagts till.
Webbhook-mottagarservern visar i loggarna att registreringen av hooken genomfördes:
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80
2020/06/15 12:01:14 [POST] inkommande HTTP-förfrÄgan frÄn
95.163.216.92:42530
2020/06/15 12:01:14 Fick tidsstÀmpel: 2020-06-15T15:01:13+03:00 TopicArn:
mcs5259999770|myfiles-ash|s3:ObjectCreated:*,s3:ObjectRemoved:* Token:
E2itMqAMUVVZc51pUhFWSp13DoxezvRxkUh5P7LEuk1dEe9y URL:
http://89.208.199.220/webhook
2020/06/15 12:01:14 Generera respons-signatur:
3754ce36636f80dfd606c5254d64ecb2fd8d555c27962b70b4f759f32c76b66dRegistreringen Àr avslutad. I nÀsta avsnitt kommer vi att titta mer detaljerat pÄ hur webbhook-mottagarservern fungerar.
Beskrivning av webbhook-mottagarservern
I vÄrt exempel Àr servern skriven i Go. LÄt oss gÄ igenom de grundlÀggande principerna för dess funktion.
package main
// Generera hmac_sha256_hex
func HmacSha256hex(message string, secret string) string {
}
// Generera hmac_sha256
func HmacSha256(message string, secret string) string {
}
// Skicka bekrÀftelse av prenumerationen
func SubscriptionConfirmation(w http.ResponseWriter, req *http.Request, body []byte) {
}
// Ta emot registreringar
func GotRecords(w http.ResponseWriter, req *http.Request, body []byte) {
}
// HĂ€lsokontroll
func Ping(w http.ResponseWriter, req *http.Request) {
// logga förfrÄgan
log.Printf("[%s] inkommande HTTP Ping-förfrÄgan frÄn %sn", req.Method, req.RemoteAddr)
fmt.Fprintf(w, "Pongn")
}
//Webhook
func Webhook(w http.ResponseWriter, req *http.Request) {
}
func main() {
// hÀmta kommandoradsargument
bindPort := flag.Int("port", 80, "nummer mellan 1-65535")
bindAddr := flag.String("address", "", "IP-adress i punktformat")
flag.StringVar(&actionScript, "script", "", "extern skript att köra")
flag.Parse()
http.HandleFunc("/ping", Ping)
http.HandleFunc("/webhook", Webhook)
log.Fatal(http.ListenAndServe(*bindAddr+":"+strconv.Itoa(*bindPort), nil))
}LÄt oss titta pÄ huvudfunktionerna:
- Ping() â en rutt som svarar pĂ„ URL/ping, en enkel implementering av liveness probe.
- Webhook() â huvudrutten, hanterare av URL/webhook:
- bekrÀftar registreringen pÄ publiceringstjÀnsten (gÄr över till funktionen SubscriptionConfirmation),
- hanterar inkommande webhooks (funktionen Gotrecords).
- Funktionerna HmacSha256 och HmacSha256hex â implementeringar av HMAC-SHA256- och HMAC-SHA256-algoritmerna som ger utdata i form av hexadecimalstrĂ€ngar för signaturberĂ€kning.
- main â huvudfunktionen, hanterar kommandoradsparametrar och registrerar URL-hanterare.
Kommandoradsparametrar som servern tar emot:
- -port â porten som servern kommer att lyssna pĂ„.
- -address â IP-adressen som servern kommer att lyssna pĂ„.
- -script â ett externt program som anropas för varje inkommande hook.
LÄt oss titta nÀrmare pÄ vissa funktioner:
//Webhook
func Webhook(w http.ResponseWriter, req *http.Request) {
// Read body
body, err := ioutil.ReadAll(req.Body)
defer req.Body.Close()
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// log request
log.Printf("[%s] incoming HTTP request from %sn", req.Method, req.RemoteAddr)
// check if we got subscription confirmation request
if strings.Contains(string(body),
""Type":"SubscriptionConfirmation"") {
SubscriptionConfirmation(w, req, body)
} else {
GotRecords(w, req, body)
}
}Denna funktion avgör vad som har kommit â en begĂ€ran om registreringsbekrĂ€ftelse eller en webhook. Som framgĂ„r av , i hĂ€ndelse av registreringsbekrĂ€ftelse kommer följande JSON-struktur i POST-begĂ€ran:
POST http://test.com HTTP/1.1
x-amz-sns-messages-type: SubscriptionConfirmation
content-type: application/json
{
"Timestamp":"2019-12-26T19:29:12+03:00",
"Type":"SubscriptionConfirmation",
"Message":"Du har valt att prenumerera pÄ Àmnet $topic. För att bekrÀfta prenumerationen behöver du svara med den berÀknade signaturen",
"TopicArn":"mcs2883541269|bucketA|s3:ObjectCreated:Put",
"SignatureVersion":1,
"Token":«RPE5UuG94rGgBH6kHXN9FUPugFxj1hs2aUQc99btJp3E49tA»
}Detta svarar pÄ begÀran:
content-type: application/json
{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}DÀr signaturen berÀknas som:
signature = hmac_sha256(url, hmac_sha256(TopicArn,
hmac_sha256(Timestamp, Token)))Om det kommer en webhook ser POST-begÀrans struktur ut sÄ hÀr:
POST HTTP/1.1
x-amz-sns-messages-type: SubscriptionConfirmation
{ "Records":
[
{
"s3": {
"object": {
"eTag":"aed563ecafb4bcc5654c597a421547b2",
"sequencer":1577453615,
"key":"some-file-to-bucket",
"size":100
},
"configurationId":"1",
"bucket": {
"name": "bucketA",
"ownerIdentity": {
"principalId":"mcs2883541269"}
},
"s3SchemaVersion":"1.0"
},
"eventVersion":"1.0",
"requestParameters":{
"sourceIPAddress":"185.6.245.156"
},
"userIdentity": {
"principalId":"2407013e-cbc1-415f-9102-16fb9bd6946b"
},
"eventName":"s3:ObjectCreated:Put",
"awsRegion":"ru-msk",
"eventSource":"aws:s3",
"responseElements": {
"x-amz-request-id":"VGJR5rtJ"
}
}
]
} DÀrmed, beroende pÄ begÀran, mÄste vi förstÄ hur vi ska bearbeta data. Jag valde att anvÀnda postningen "Type":"SubscriptionConfirmation", eftersom den finns i begÀran om bekrÀftelse av prenumerationen och inte finns i webhooken. Beroende pÄ nÀrvaron/frÄnvaron av denna post i POST-begÀran fortsÀtter programmets utförande antingen till funktionen SubscriptionConfirmation, eller till funktionen GotRecords.
Vi kommer inte att granska funktionen SubscriptionConfirmation i detalj, den Àr implementerad enligt de principer som beskrivs i . Den ursprungliga koden för denna funktion kan studeras i .
Funktionen GotRecords analyserar den inkommande begÀran och för varje objekt Record anropar ett externt skript (namnet pÄ vilket som överfördes i parametern -script) med parametrarna:
- bucket-namn
- objektnyckel
- ÄtgÀrd:
- copy â om EventName = ObjectCreated | PutObject | PutObjectCopy i den ursprungliga begĂ€ran
- delete â om EventName = ObjectRemoved | DeleteObject i den ursprungliga begĂ€ran
SÄledes, om en webhook har kommit med en POST-begÀran, som beskrivs , och parametern -script=script.sh, kommer skriptet att anropas pÄ följande sÀtt:
script.sh bucketA some-file-to-bucket copyDet Ă€r viktigt att förstĂ„ att den hĂ€r servern för mottagande av webhooks â inte Ă€r en fĂ€rdig produktionslösning, utan ett förenklat exempel pĂ„ en möjlig implementering.
Exempel pÄ arbete
Vi kommer att synkronisera filer frĂ„n huvud-bucket i MCS till en reserv-bucket i AWS. Huvud-bucket kallas myfiles-ash, reserv-bucket â myfiles-backup (konfiguration av bucketen i AWS ligger utanför denna artikel). DĂ€rför, nĂ€r en fil placeras i huvud-bucket, ska en kopia av den dyka upp i reserv-bucket, och nĂ€r den tas bort frĂ„n huvud-bucket â tas den bort i reserv-bucket.
Vi kommer att arbeta med bucketer genom verktyget awscli, som Àr kompatibelt med bÄde MCS molnlagring och AWS molnlagring.
ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install awscli
Reading package lists... Done
Building dependency tree
Reading state information... Done
After this operation, 34.4 MB of additional disk space will be used.
Unpacking awscli (1.14.44-1ubuntu1) ...
Setting up awscli (1.14.44-1ubuntu1) ...LÄt oss konfigurera Ätkomst till API S3 MCS:
ubuntu@ubuntu-basic-1-2-10gb:~$ aws configure --profile mcs
AWS Access Key ID [None]: hdywEPtuuJTExxxxxxxxxxxxxx
AWS Secret Access Key [None]: hDz3SgxKwXoxxxxxxxxxxxxxxxxxx
Default region name [None]:
Default output format [None]:LÄt oss konfigurera Ätkomst till API S3 AWS:
ubuntu@ubuntu-basic-1-2-10gb:~$ aws configure --profile aws
AWS Access Key ID [None]: AKIAJXXXXXXXXXXXX
AWS Secret Access Key [None]: dfuerphOLQwu0CreP5Z8l5fuXXXXXXXXXXXXXXXX
Default region name [None]:
Default output format [None]:LÄt oss kontrollera Ätkomster:
Till AWS:
ubuntu@ubuntu-basic-1-2-10gb:~$ aws s3 ls --profile aws
2020-07-06 08:44:11 myfiles-backupFör MCS, mĂ„ste kommandot lĂ€ggas till âendpoint-url:
ubuntu@ubuntu-basic-1-2-10gb:~$ aws s3 ls --profile mcs --endpoint-url
https://hb.bizmrg.com
2020-02-04 06:38:05 databasebackups-0cdaaa6402d4424e9676c75a720afa85
2020-05-27 10:08:33 myfiles-ashĂ tkomst beviljad.
Nu skriver vi ett skript för att hantera den inkommande hÀndelsen, vi kallar det s3_backup_mcs_aws.sh
#!/bin/bash
# Require aws cli
# if file added â copy it to backup bucket
# if file removed â remove it from backup bucket
# Variables
ENDPOINT_MCS="https://hb.bizmrg.com"
AWSCLI_MCS=`which aws`" --endpoint-url ${ENDPOINT_MCS} --profile mcs s3"
AWSCLI_AWS=`which aws`" --profile aws s3"
BACKUP_BUCKET="myfiles-backup"
SOURCE_BUCKET="${1}"
SOURCE_FILE="${2}"
ACTION="${3}"
SOURCE="s3://${SOURCE_BUCKET}/${SOURCE_FILE}"
TARGET="s3://${BACKUP_BUCKET}/${SOURCE_FILE}"
TEMP="/tmp/${SOURCE_BUCKET}/${SOURCE_FILE}"
case ${ACTION} in
"copy")
${AWSCLI_MCS} cp "${SOURCE}" "${TEMP}"
${AWSCLI_AWS} cp "${TEMP}" "${TARGET}"
rm ${TEMP}
;;
"delete")
${AWSCLI_AWS} rm ${TARGET}
;;
*)
echo "Usage: ${0} sourcebucket sourcefile copy/delete"
exit 1
;;
esacStartar servern:
ubuntu@ubuntu-basic-1-2-10gb:~\/s3-webhook$ sudo .\/s3-webhook -port 80 -
skript scripts\/s3_backup_mcs_aws.shVi kontrollerar hur detta fungerar. Genom lÀgger vi till filen test.txt i bucket myfiles-ash. I loggarna i konsolen syns det att en begÀran har gjorts till webhook-servern:
2020\/07\/06 09:43:08 [POST] inkommande HTTP-begÀran frÄn
95.163.216.92:56612
download: s3:\/\/myfiles-ash\/test.txt till ..\/..\/..\/tmp\/myfiles-ash\/test.txt
upload: ..\/..\/..\/tmp\/myfiles-ash\/test.txt till
s3:\/\/myfiles-backup\/test.txtLÄt oss kontrollera innehÄllet i bucket myfiles-backup i AWS:
ubuntu@ubuntu-basic-1-2-10gb:~\/s3-webhook$ aws s3 --profile aws ls
myfiles-backup
2020-07-06 09:43:10 1104 test.txtNu tar vi bort filen frÄn bucket myfiles-ash genom webbgrÀnssnittet.
Serverloggar:
2020\/07\/06 09:44:46 [POST] inkommande HTTP-begÀran frÄn
95.163.216.92:58224
delete: s3:\/\/myfiles-backup\/test.txtInnehÄllet i bucketen:
ubuntu@ubuntu-basic-1-2-10gb:~\/s3-webhook$ aws s3 --profile aws ls
myfiles-backup
ubuntu@ubuntu-basic-1-2-10gb:~$Filen har tagits bort, problemet löst.
Slutsats och ToDo
All kod som anvÀnds i denna artikel ligger hÀr . DÀr finns ocksÄ exempel pÄ skript och exempel pÄ berÀkning av signaturer för registrering av webhooks.
Denna kod Àr inget mer Àn ett exempel pÄ hur man kan anvÀnda S3-webhooks i sin verksamhet. Som jag sa i början, om man planerar att anvÀnda en sÄdan server i produktion, bör man Ätminstone skriva om servern för asynkron drift: inkommande webhooks registreras i en kö (RabbitMQ eller NATS), och dÀrifrÄn bearbetas de av arbetande applikationer. Annars kan man vid massiv ankomst av webhooks stöta pÄ brist pÄ serverresurser för att utföra uppgifter. Att ha köer tillÄter att sprida servern och arbetarna, samt att lösa problem med upprepning av uppgifter vid fel. Det Àr ocksÄ önskvÀrt att förÀndra loggningen till mer detaljerad och mer standardiserad.
Lycka till!
LÀs mer om Àmnet:
KĂ€lla: habr.com
