
Arkitektura e drejtuar nga ngjarjet rrit efikasitetin e kostos së burimeve të përdorura, sepse ato aktivizohen vetëm kur janë të nevojshme. Ka shumë mënyra se si mund të implementohet kjo pa krijuar entitete të tjera në re si aplikacione punonjëse. Sot do flas jo për FaaS, por për webhook-et. Do tregoj një shembuj praktik për përpunimin e ngjarjeve përmes webhook-eve të sistemit të ruajtjes.
Disa fjalë për sistemin e ruajtjes dhe për webhook-et. Sistemet e ruajtjes lejojnë ruajtjen e çdo lloji të dhënash në re në formën e objekteve, të cilat janë të aksesueshme përmes S3 ose API-së tjetër (varësisht nga implementimi) përmes HTTP/HTTPS. Webhook-et në përgjithësi janë thirrje pasuese nga përdoruesi përmes HTTP. Zakonisht aktivizohen nga një ngjarje, si dërgimi i kodit në një depo ose një koment që publikohet në një blog. Kur ndodh një ngjarje, faqja burimore dërgon një kërkesë HTTP në URL-në e cila është caktuar për webhook-un. Si rezultat, ngjarjet në një faqe mund të shkaktojnë veprime në një tjetër. Në rastin kur faqja burimore është një sistem ruajtjeje, ngjarjet janë ndryshimet në përmbajtjen e saj.Në rastin kur faqja burimore është një sistem ruajtjeje, ngjarjet janë ndryshimet në përmbajtjen e saj.
Shembuj të rasteve të thjeshta kur mund të përdoret një automatizim i tillë:
- Krijimi i kopjeve të gjitha objekteve në një sistem tjetër ruajtjeje. Kopjet duhet të krijohen "në fluks", me çdo shtim ose ndryshim të skedarëve.
- Krijimi automatik i serive të miniaturave për skedarët grafikë, shtimi i ujëmarksave në fotografi, modifikime të tjera të imazheve.
- Njoftimi për ardhjen e dokumenteve të reja (p.sh., një shërbim të shpërndara të kontabilitetit ngarkon raporte në re, dhe monitorimi financiar merr njoftime për raportet e reja, i kontrollon dhe i analizon ato).
- Raste pak më komplekse përfshijnë, për shembull, formimin e një kërkese për Kubernetes, i cili krijon një pod me konteinerët e nevojshëm, i përcjell parametrat e detyrës dhe pas përpunimit mbyll konteinerin.
Si një shembull do të realizojmë versionin e detyrës 1, kur ndryshimet në bucket-in e sistemit të ruajtjes së Mail.ru Cloud Solutions (MCS) sinkronizohen përmes webhook-eve në sistemin e ruajtjes së AWS. Në një rast të ngarkuar real, duhet të parashikohet funksionimi asinkron duke regjistruar webhook-et në një radhë, por për detyrën praktike do ta realizojmë implementimin pa këtë.
Skema e funksionimit
Protokolli i ndërveprimit përshkruhet në detaje në . Në skemën e punës ka elementet e mëposhtme:
- Shërbimi i publikimit, i cili ndodhet nga ana e ruajtjes S3 dhe publikon kërkesa HTTP kur aktivizohet webhook-u.
- Serveri i pranimit të webhooks, i cili dëgjon thirrjet e shërbimit të publikimit përmes HTTP dhe kryen veprimet përkatëse. Serveri mund të jetë shkruar në çdo gjuhë, në shembullin tonë do të shkruajmë serverin në Go.
Veçoria e implementimit tĂ« webhooks nĂ« S3 API â regjistrimi i serverit tĂ« pranimit tĂ« webhooks nĂ« shĂ«rbimin e publikimit. SaktĂ«sisht, serveri i pranimit tĂ« webhooks duhet tĂ« konfirmojĂ« abonimin pĂ«r mesazhet e shĂ«rbimit tĂ« publikimit (nĂ« implementime tĂ« tjera tĂ« webhooks zakonisht nuk kĂ«rkohet tĂ« konfirmohet abonimi).
Për rrjedhojë, serveri i pranimit të webhooks duhet të mbështesë dy operacione kryesore:
- të përgjigjet në kërkesën e shërbimit të publikimit për konfirmimin e regjistrimit,
- të përpunojë ngjarjet që vijnë.
Instalimi i serverit të pranimit të webhooks
Për të nisur serverin e pranimit të webhooks, nevojitet një server Linux. Në këtë artikull do të përdorim si shembull një instance virtuale që e vendosim në MCS.
Do të instalojmë programet e nevojshme dhe do të nisim serverin e pranimit të webhooks.
ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install git
Duke listat e paketave... Përfunduar
Duke ndërtuar pemën e varësive
Duke lexuar informacionin e gjendjes... Përfunduar
Paketat e mëposhtme u instaluan automatikisht dhe nuk janë më të nevojshme:
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
Përdorni 'sudo apt autoremove' për t'i hequr ato.
Paketat e sugjeruara:
git-daemon-run | git-daemon-sysvinit git-doc git-el git-email git-gui
gitk gitweb git-cvs git-mediawiki git-svn
Paketet e REJA të mëposhtme do të instalohen:
git
0 e përmirësuar, 1 e instaluar të re, 0 për t'u hequr dhe 46 të pa përmirësuara.
Duhet të merrni 3915 kB arkivash.
Pas kësaj operacione, do të përdoren 32.3 MB hapësirë shtesë disku.
Merr:1 http://MS1.clouds.archive.ubuntu.com/ubuntu bionic-updates/main
amd64 git amd64 1:2.17.1-1ubuntu0.7 [3915 kB]
Më tërhiqen 3915 kB në 1s (5639 kB/s)
Duke zgjedhur paketën git të papërzgjedhur më parë.
(Duke lexuar databazën ... 53932 skedarë dhe drejtorë aktualisht të instaluar.)
Duke përgatitur për të shpërbërë .../git_12.17.1-1ubuntu0.7_amd64.deb ...
Duke shpërbërë git (1:2.17.1-1ubuntu0.7) ...
Duke e vendosur git (1:2.17.1-1ubuntu0.7) ...Klonojmë dosjen me serverin e pranimit të webhooks:
ubuntu@ubuntu-basic-1-2-10gb:~$ git clone
https://github.com/RomanenkoDenys/s3-webhook.git
Klonimi në 's3-webhook'...
remote: Enumerimi i objekteve: 48, kryer.
remote: Numërimi i objekteve: 100% (48/48), kryer.
remote: Kompresimi i objekteve: 100% (27/27), kryer.
remote: Totaali 114 (delta 20), e përsëritur 45 (delta 18), paketë e ripërdorur 66
Pranimi i objekteve: 100% (114/114), 23.77 MiB | 20.25 MiB/s, kryer.
Zgjidhja e daltave: 100% (49/49), kryer.Do të nisim serverin:
ubuntu@ubuntu-basic-1-2-10gb:~$ cd s3-webhook/
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80Abonimi për shërbimin e publikimit
Mund të regjistroni serverin tuaj të marrjes së webhooks përmes API-së ose ndërfaqes web. Për thjeshtësi, do ta regjistrojmë përmes ndërfaqes web:
- në panelin e menaxhimit.
- Hyny në kovën për të cilën do të konfigurojmë webhooks dhe klikoni në ingranazhin:

Kaloni në skedën Webhooks dhe klikoni Shto:

Plotësoni fushat:

ID â emri i webhooks.
Ngjarja â cili do tĂ« jetĂ« ngjarja qĂ« do tĂ« dĂ«rgohet. Ne kemi vendosur tĂ« dĂ«rgojmĂ« tĂ« gjitha ngjarjet qĂ« ndodhin gjatĂ« punĂ«s me skedarĂ«t (shtimi dhe fshirja).
URL â adresa e serverit tĂ« marrjes sĂ« webhooks.
Filtri pĂ«r parafyt/sufiks â njĂ« filtĂ«r qĂ« lejon gjenerimin e webhooks vetĂ«m pĂ«r objekte, emrat e tĂ« cilave pĂ«rputhen me rregulla tĂ« caktuara. PĂ«r shembull, nĂ«se dĂ«shironi qĂ« webhook tĂ« aktivizohet vetĂ«m pĂ«r skedarĂ«t me pĂ«rfundim .png, nĂ« Filtrimi i sufiksit duhet tĂ« shkruani "png".
Në këtë moment, vetëm portet 80 dhe 443 mbështeten për komunikimin me serverin e marrjes së webhooks.
Klikoni Shto hook dhe do të shihni të siguiente:

Hook i shtuar.
Serveri i marrjes së webhooks tregon në logët e tij procesin e regjistrimit të hook-ut:
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80
2020/06/15 12:01:14 [POST] HTTP kërkesë e ardhshme nga
95.163.216.92:42530
2020/06/15 12:01:14 Marr timestamp: 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 Generohet nënshkrimi i përgjigjes:
3754ce36636f80dfd606c5254d64ecb2fd8d555c27962b70b4f759f32c76b66dRegjistrimi përfundoi. Në seksionin tjetër do të shqyrtojmë më në detaje algoritmin e funksionimit të serverit të marrjes së webhooks.
Përshkrimi i serverit të marrjes së webhooks
Në shembullin tonë, serveri është shkruar në Go. Do të shqyrtojmë parimet kryesore të funksionit të tij.
package main
// Gjeneroni hmac_sha256_hex
func HmacSha256hex(message string, secret string) string {
}
// Gjeneroni hmac_sha256
func HmacSha256(message string, secret string) string {
}
// Dërgoni konfirmimin e abonimit
func SubscriptionConfirmation(w http.ResponseWriter, req *http.Request, body []byte) {
}
// Dërgoni konfirmimin e abonimit
func GotRecords(w http.ResponseWriter, req *http.Request, body []byte) {
}
// Prova e gjallërisë
func Ping(w http.ResponseWriter, req *http.Request) {
// regjistro kërkesën
log.Printf("[%s] kërkesë HTTP Ping e ardhshme nga %sn", req.Method, req.RemoteAddr)
fmt.Fprintf(w, "Pongn")
}
//Webhook
func Webhook(w http.ResponseWriter, req *http.Request) {
}
func main() {
// merrni argumentet e komandës
bindPort := flag.Int("port", 80, "numri midis 1-65535")
bindAddr := flag.String("address", "", "adresa IP në format pikësh")
flag.StringVar(&actionScript, "script", "", "shkruani skenarin për të ekzekutuar")
flag.Parse()
http.HandleFunc("/ping", Ping)
http.HandleFunc("/webhook", Webhook)
log.Fatal(http.ListenAndServe(*bindAddr+":"+strconv.Itoa(*bindPort), nil))
}Le të shohim funksionet kryesore:
- Ping() â rruga qĂ« pĂ«rgjigjet nĂ« URL/ping, implementimi mĂ« i thjeshtĂ« i liveness probe.
- Webhook() â rruga kryesore, pĂ«rpunuesi i URL/webhook:
- konfirmon regjistrimin në shërbimin e publikimit (kalimi në funksionin SubscriptionConfirmation),
- përpunon webhookët që vijnë (funksioni Gotrecords).
- Funksionet HmacSha256 dhe HmacSha256hex â implementimet e algoritmeve tĂ« enkriptimit HMAC-SHA256 dhe HMAC-SHA256 me daljen nĂ« formĂ«n e njĂ« varg 16-shifrorĂ«sh pĂ«r llogaritjen e nĂ«nshkrimit.
- main â funksioni kryesor, pĂ«rpunon parametrat e linjĂ«s sĂ« komandĂ«s dhe regjistron pĂ«rpunuesit e URL-sĂ«.
Parametrat e linjës së komandës që pranon serveri:
- -port â porta nĂ« tĂ« cilĂ«n serveri do tĂ« dĂ«gjojĂ«.
- -address â adresa IP qĂ« do tĂ« dĂ«gjojĂ« serveri.
- -script â programi i jashtĂ«m qĂ« thirret pĂ«r çdo webhook tĂ« ardhur.
Le të shqyrtojmë më në detaje disa funksione:
//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)
}
}Ky funksion pĂ«rcakton se çfarĂ« ka ardhur â njĂ« kĂ«rkesĂ« pĂ«r konfirmimin e regjistrimit apo njĂ« webhook. Siç del nga , nĂ« rastin e konfirmimit tĂ« regjistrimit vjen struktura e mĂ«poshtme Json nĂ« kĂ«rkesĂ«n Post:
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":"Keni zgjedhur të regjistroheni në temën $topic. Për të konfirmuar regjistrimin duhet të përgjigjeni me nënshkrimin e llogaritur",
"TopicArn":"mcs2883541269|bucketA|s3:ObjectCreated:Put",
"SignatureVersion":1,
"Token":«RPE5UuG94rGgBH6kHXN9FUPugFxj1hs2aUQc99btJp3E49tA»
}Për këtë kërkesë duhet të përgjigjeni:
content-type: application/json
{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}Ku nënshkrimi llogaritet si:
signature = hmac_sha256(url, hmac_sha256(TopicArn,
hmac_sha256(Timestamp, Token)))Nëse vjen një webhook, struktura e kërkesës Post duket kështu:
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"
}
}
]
} Sipas për këtij kërkese, duhet të kuptoni se si të përpunoni të dhënat. Kam zgjedhur si tregues regjistrimin "Type":"SubscriptionConfirmation", pasi ajo është e pranishme në kërkesën për konfirmimin e abonimit dhe nuk është e pranishme në webhook. Në bazë të pranisë/pasojës së kësaj regjistrimi në POST-requests, ekzekutimi i mëtejshëm i programit kalon ose në funksionin SubscriptionConfirmation, ose në funksionin GotRecords.
Nuk do ta shqyrtojmë funksionin SubscriptionConfirmation, ai është implementuar sipas parimeve të përshkruara në . Mund të studiohet kodi burimor i këtij funksioni në .
Funksioni GotRecords analizon kërkesën që arrin dhe për çdo objekt Record thërret një skenari të jashtëm (emri i të cilit është dhënë në parametër -script) me parametrat:
- emri i bucket-it
- çelësi i objektit
- veprimi:
- copy â nĂ«se nĂ« kĂ«rkesĂ«n fillestare EventName Ă«shtĂ« ObjectCreated | PutObject | PutObjectCopy
- delete â nĂ«se nĂ« kĂ«rkesĂ«n fillestare EventName Ă«shtĂ« ObjectRemoved | DeleteObject
Kështu, nëse arrin një hook cështja me një Post-requests, siç përshkruhet , dhe parametri -script=script.sh, atëherë skripti do të thërritet si më poshtë:
script.sh bucketA some-file-to-bucket copyDuhet të kuptojmë se ky server i pranimit të webhook-ëve nuk është një zgjidhje përfundimtare produksi, por një shembull i thjeshtuar i një realizimi të mundshëm.
Shembulli i punës
Do tĂ« kryejmĂ« sinkronizimin e skedareve nga bucket-i kryesor nĂ« MCS nĂ« bucket-in rezervĂ« nĂ« AWS. Bucket-i kryesor quhet myfiles-ash, rezervĂ« â myfiles-backup (konfigurimi i bucket-it nĂ« AWS kalon pĂ«rtej kĂ«tij artikulli). Pra, kur njĂ« skedar vendoset nĂ« bucket-in kryesor, njĂ« kopje e tij duhet tĂ« shfaqet nĂ« rezervĂ«, kur fshihet nga ai i parĂ« â tĂ« fshihet edhe nga ai rezervĂ«.
Do të punojmë me bucket-et me utilitarin awscli, me të cilin përputhen si ruajtja cloud e MCS, ashtu edhe ajo e AWS.
ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install awscli
Leximi i listave të paketave... Përfunduar
Krijimi i pemës së varësive
Leximi i informacionit rreth gjendjes... Përfunduar
Pas kësaj operacioni, 34.4 MB hapësirë shtesë disku do të përdoret.
Zhbllokimi i awscli (1.14.44-1ubuntu1) ...
Krijimi i awscli (1.14.44-1ubuntu1) ...Të konfigurojmë aksesin në API S3 MCS:
ubuntu@ubuntu-basic-1-2-10gb:~$ aws configure --profile mcs
AWS Access Key ID [Asnjë]: hdywEPtuuJTExxxxxxxxxxxxxx
AWS Secret Access Key [Asnjë]: hDz3SgxKwXoxxxxxxxxxxxxxxxxxx
Emri i rajonit fillestar [Asnjë]:
Formati i daljes fillestar [Asnjë]:Të konfigurojmë aksesin në API S3 AWS:
ubuntu@ubuntu-basic-1-2-10gb:~$ aws configure --profile aws
AWS Access Key ID [Asnjë]: AKIAJXXXXXXXXXXXX
AWS Secret Access Key [Asnjë]: dfuerphOLQwu0CreP5Z8l5fuXXXXXXXXXXXXXXXX
Emri i rajonit fillestar [Asnjë]:
Formati i daljes fillestar [Asnjë]:Të kontrollojmë akseset:
Tek AWS:
ubuntu@ubuntu-basic-1-2-10gb:~$ aws s3 ls --profile aws
2020-07-06 08:44:11 myfiles-backupPĂ«r MCS, kur ekzekutohet komanda duhet tĂ« shtohet â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-ashAkses është marrë.
Tani do të shkruajmë një skript për përpunimin e hook-ut që vjen, do ta quajmë 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
;;
esacNisni serverin:
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80 -
script scripts/s3_backup_mcs_aws.shTë kontrollojmë se si do të funksionojë. Përmes do ta shtojmë skedarin test.txt në bucket-in myfiles-ash. Në logët në konsolë shihet se është bërë një kërkesë për serverin e webhook-ëve:
2020/07/06 09:43:08 [POST] kërkesë HTTP e ardhur nga
95.163.216.92:56612
download: s3://myfiles-ash/test.txt to ../../../../tmp/myfiles-ash/test.txt
upload: ../../../../tmp/myfiles-ash/test.txt to
s3://myfiles-backup/test.txtTë kontrollojmë përmbajtjen e bucket-it myfiles-backup në AWS:
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ aws s3 --profile aws ls
myfiles-backup
2020-07-06 09:43:10 1104 test.txtTani përmes ndërfaqes së internetit do të fshijmë skedarin nga bucket-i myfiles-ash.
Logët e serverit:
2020/07/06 09:44:46 [POST] kërkesë HTTP e ardhur nga
95.163.216.92:58224
delete: s3://myfiles-backup/test.txtPërmbajtja e bucket-it:
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ aws s3 --profile aws ls
myfiles-backup
ubuntu@ubuntu-basic-1-2-10gb:~$Skedari është fshirë, detyra është zgjidhur.
Përfundimi dhe ToDo
Të gjithë kodin e përdorur në këtë artikull e gjen . Atje janë gjithashtu shembuj skripta dhe shembuj të llogaritjes së nënshkrimeve për regjistrimin e webhook-ëve.
Ky kod është vetëm një shembull se si mund të përdoren S3-webhook-ët në veprimtarinë tuaj. Siç e thashë në fillim, nëse planifikoni të përdorni një server të tillë në prodhim, duhet të riprogramoni të paktën serverin për punë asinkrone: regjistroni webhook-ët që vijnë në radhë (RabbitMQ ose NATS), dhe nga aty ata të analizohen dhe përpunohen nga aplikacione punëtor. Nëse jo, në rastin e një fluksi masiv të webhook-ëve, mund të përballeni me mungesë burimesh për serverin për të përfunduar detyrat. Prania e radhëve lejon ndarjen e serverit dhe punëtorëve, si dhe zgjidhjen e problemeve me përsëritjen e detyrave në rast dështimesh. Edhe e dëshirueshme është të ndryshoni logimin në diçka më të detajuar dhe më të standardizuar.
Suksese!
Lexoni më shumë rreth temës:
Burimi: habr.com
