Evolusi pemrosesan webhook Facebook: dari nol menjadi 25.000 per detik

Kemungkinan besar, tidak ada yang perlu tahu apa itu webhooks. Tapi untuk berjaga-jaga: webhooks adalah mekanisme untuk melaporkan peristiwa dalam sistem eksternal. Misalnya, tentang membeli di toko online melalui kasir online, mengirim kode ke repositori GitHub, atau tindakan pengguna dalam obrolan. Dalam API biasa, Anda harus terus-menerus meminta server jika pengguna menulis sesuatu di obrolan. Menggunakan mekanisme webhook, Anda dapat "berlangganan" pemberitahuan, dan server itu sendiri akan mengirim permintaan HTTP ketika suatu peristiwa terjadi. Ini lebih nyaman dan lebih cepat daripada terus-menerus meminta data baru di server.



ManyChat adalah platform yang membantu bisnis berkomunikasi dengan pelanggan mereka melalui obrolan dalam pesan instan. Webhooks adalah salah satu bagian penting dari ManyChat, karena melalui merekalah bisnis berkomunikasi dengan pelanggan. Dan mereka banyak berkomunikasi - misalnya, melalui sistem, bisnis mengirim miliaran pesan sebulan ke pelanggan mereka.

Sebagian besar pesan dikirim melalui Facebook Messenger. Ini memiliki fitur - API lambat. Ketika seorang pelanggan menulis pesan untuk memesan pizza, Facebook mengirimkan webhook ke ManyChat. Platform memprosesnya, mengirimkan permintaan kembali dan pengguna menerima pesan. Karena API lambat, beberapa permintaan berlangsung beberapa detik. Tetapi ketika platform tidak merespon untuk waktu yang lama, bisnis kehilangan klien, dan Facebook dapat memutuskan aplikasi dari webhooks.

Oleh karena itu, memproses webhook adalah salah satu tugas rekayasa utama platform. Untuk mengatasi masalah tersebut, ManyChat mengubah arsitektur pemrosesan beberapa kali selama tiga tahun dari pengontrol sederhana di Yii menjadi sistem terdistribusi dengan Galaksi. Baca lebih lanjut tentang ini di bawah cut Dmitry Kushnikov (cancellarius)

Dmitry Kushnikov memimpin pengembangan di ManyChat dan telah memprogram secara profesional dalam PHP sejak tahun 2001. Dmitry akan memberi tahu Anda bagaimana arsitektur telah berubah seiring dengan pertumbuhan layanan dan beban, solusi dan teknologi apa yang diterapkan pada tahap yang berbeda, bagaimana pemrosesan webhook telah berkembang dan bagaimana platform mengelola untuk mengatasi beban yang sangat besar menggunakan sumber daya sederhana di PHP.

Catatan. Artikel ini didasarkan pada laporan Dmitry "Evolusi pemrosesan webhook Facebook: dari nol menjadi 12.500 per detik" di PHP Russia 2019 . Tetapi ketika dia sedang bersiap, indikator naik menjadi 25.000.


Apa itu ManyChat


Pertama, saya akan memperkenalkan Anda ke dalam konteks tugas kami. ManyChat adalah layanan yang membantu bisnis menggunakan pesan instan untuk pemasaran, penjualan, dan dukungan. Produk utama adalah platform untuk Pemasaran Messenger di Facebook Messenger . Selama tiga tahun, lebih dari 1 juta bisnis dari 100 negara di dunia menggunakan layanan ini untuk berkomunikasi dengan 700 juta pelanggan mereka.

Di sisi klien, sepertinya ini.


Tombol, gambar, dan galeri dalam dialog di Facebook Messenger.

Ini adalah antarmuka Facebook Messenger. Selain pesan teks, Anda dapat mengirim elemen interaktif di dalamnya untuk berinteraksi dengan pelanggan, terlibat dalam dialog, meningkatkan minat pada produk Anda dan menjual.

Dari sisi bisnissemuanya terlihat berbeda. Ini adalah antarmuka aplikasi web kami di mana, menggunakan antarmuka visual, perwakilan bisnis membuat dan skrip dialog program. Gambar adalah salah satu contoh skenario.


Jantung dari sistem kami adalah komponen Flow Builder.

Rangkaian skrip dan aturan otomatisasi yang kami sebut bot . Oleh karena itu, untuk mempermudah, kita dapat mengatakan bahwa ManyChat adalah desainer bot.


Contoh bot.

Klien bisnis yang berpartisipasi dalam dialog disebut pelanggan , karena untuk interaksi klien berlangganan bot .

Mengapa Facebook


Mengapa Facebook Messenger, kami adalah negara Telegram yang masih hidup? Ada alasan untuk ini.

  • Telegram , №1 Facebook. 1,5 , Telegram 200-300 .
  • Facebook , . , Facebook - .
  • Facebook F8 - 300 . Facebook Messenger. 20 . ManyChat 40%.

Facebook


Interaksi dengan Facebook diatur seperti ini:



Bisnis menggunakan aplikasi web untuk mengonfigurasi logika bot. Ketika klien berinteraksi dengan bot melalui telepon, Facebook menerima informasi tentang ini dan mengirimkan kepada kami webhook. ManyChat memprosesnya, tergantung pada logika yang diprogram oleh bisnis, dan mengirimkan permintaan kembali. Kemudian Facebook mengirimkan pesan ke ponsel pengguna.

Tumpukan teknologi


Kami melakukan semua ini pada tumpukan sederhana. Intinya, tentu saja, adalah PHP. Server web menjalankan Nginx, basis data utamanya adalah PostgreSQL, dan ada juga Redis dan Elasticsearch. Semuanya berputar di awan Amazon Web Services.

Menangani Facebook Webhook


Seperti inilah tampilan webcam Facebook: ini adalah permintaan dengan payload dalam format JSON.

{
    "object":"page",
    "entry":[
        }
            "id":"<PAGE_ID>",
            "time":1458692752478,
            "messaging":[
                {
                    "sender":{
                        "id":"<PSID>"
                    },
                    "recipient":{
                        "id":"<PAGE_ID>"
                    },

                    ...
                }
            ]  
        }
    ]
}

Webhook hanya 10% dari beban kami, tetapi merupakan bagian terpenting dari sistem. Melalui mereka, bisnis berkomunikasi dengan pengguna. Jika pesan melambat atau tidak terkirim, maka pengguna menolak untuk berinteraksi dengan bot, dan bisnis kehilangan klien.

Mari kita lihat evolusi arsitektur kita sejak peluncuran produk.

Mei 2016 . Kami baru saja meluncurkan layanan kami: 20 bot, 10 di antaranya adalah yang uji, dan 20 pelanggan. Muatannya adalah 0 RPS.

Skema interaksi terlihat seperti ini:



  • Permintaan masuk ke nginx.
  • Nginx mengakses PHP-FPM.
  • PHP-FPM membawa aplikasi hingga Yii.
  • Kontroler webhook memproses logika dan mengirimkan permintaan ke Facebook sesuai dengannya.


Sekelompok Nginx dan PHP-FPM


Juni 2016. Sebulan kemudian, kami mengumumkan ManyChat di ProductHunt dan jumlah bot meningkat menjadi 2 ribu. Jumlah pelanggan telah meningkat menjadi 7 ribu.

Pada saat ini, masalah pertama muncul di sistem. API Facebook tidak terlalu cepat: beberapa permintaan bisa memakan waktu beberapa detik, dan beberapa permintaan bisa memakan waktu puluhan detik. Tetapi server webhook ingin kita merespons dengan cepat. Karena API lambat, kami tidak merespons untuk waktu yang lama: server pertama bersumpah, dan kemudian itu benar-benar dapat memutuskan aplikasi dari webhooks.

Ada beberapa pengguna, kami masih mengembangkan aplikasi, kami mencari pasar kami, audiens, dan masalah pemuatan telah muncul. Tetapi kami diselamatkan oleh solusi sederhana: pada saat pengontrol dimulai, kami mengganggu akses ke Facebook. Kami memberi tahu Facebook bahwa semuanya baik-baik saja, tetapi di latar belakang kami memproses permintaan dan webhook.



Antrian di PostgreSQL


Desember 2016. Layanan tumbuh 5-10 kali: 10 ribu bot dan 700 ribu pelanggan.

Pada saat yang sama, kami mengerjakan tugas-tugas baru: menampilkan statistik, pengiriman pesan, konversi tayangan, dan transisi. Juga menerapkan Obrolan Langsung. Selain mengotomatisasi interaksi, itu memberi bisnis kemampuan untuk menulis pesan langsung ke pelanggan mereka.

Solusi untuk masalah ini meningkatkan jumlah kait dilacak sebanyak 4 kali. Untuk setiap pesan yang kami kirim, kami menerima 3 kait web tambahan. Sistem pemrosesan perlu ditingkatkan lagi. Kami adalah platform kecil, hanya dua orang yang bekerja di backend, jadi kami memilih solusi paling sederhana - antrian di PostgreSQL.

Kami belum ingin menerapkan sistem yang kompleks, jadi kami cukup membagikan alur pemrosesan. Webhook yang perlu diproses dengan cepat sehingga pengguna menerima respons diproses secara sinkron. Semua sisanya dikirim dalam antrian untuk permintaan asinkron.



Antrian di Redis


Juni 2017. Layanan tumbuh: 75 ribu bot, 7 juta pelanggan.

Kami sedang mengimplementasikan fitur baru lainnya. Semua webhook yang kami proses hanya menyangkut komunikasi di messenger. Tetapi sekarang kami memutuskan untuk memberikan peluang bisnis kepada bisnis untuk berkomunikasi dengan pelanggan halaman bisnis dan mulai memproses jenis-jenis webhook baru - yang terkait dengan umpan halaman itu sendiri.

Umpan halaman bisnis jarang diperbarui. Pemasar sering memposting sesuatu, kemudian mereka mengikuti setiap suka dan menghitungnya. Tidak ada lalu lintas besar di halaman bisnis. Tetapi ada situasi sebaliknya, misalnya, Hari Katy Perry .

Katy Perry adalah penyanyi Amerika terkenal dengan sejumlah besar penggemar di seluruh dunia. Ada 64 juta pelanggan di grup Facebook-nya saja. Pada titik tertentu, pemasar penyanyi memutuskan untuk membuat bot di Facebook Messenger dan memilih platform kami. Pada saat itu, ketika mereka menerbitkan pesan panggilan untuk berlangganan bot, beban kami meningkat 3-4 kali.

Situasi ini membantu kami memahami bahwa tanpa implementasi antrian yang normal, kami tidak dapat melakukan apa pun. Sebagai solusi, mereka memilih Redis.
Memilih Redis untuk antrian adalah keputusan yang sangat bagus.
Dia membantu memecahkan sejumlah besar masalah. Sekarang setiap detik melalui Redis-cluster kami melewati 1 juta permintaan berbeda. Kami menggunakannya tidak hanya untuk semua antrian kaskade, tetapi juga untuk tugas-tugas lain, misalnya, pemantauan.

Antrian di Redis tidak diterapkan pada percobaan pertama. Ketika kami mulai melipat kait web di Redis dan memprosesnya dalam satu proses, kami memperluas corong di atas: ada lebih banyak kait web masuk yang diproses juga, tetapi prosesnya sendiri masih membutuhkan waktu. Keputusan pertama ini tidak berhasil.



Ketika mereka mencoba mengukur jumlah permintaan ini, ada sedikit keruntuhan. Antrian dapat mengakumulasi permintaan dari halaman yang berbeda, tetapi permintaan dari satu halaman dapat berjalan berurutan. Jika satu handler lambat, maka permintaan dari satu pelanggan dan dari satu bot akan diproses dalam urutan yang salah. Pengguna mengirim pesan, melakukan beberapa tindakan dengan bot, tetapi menerima respons secara acak.



Ini sepertinya kasus yang jarang, tetapi pengujian pada beban kerja kami telah menunjukkan bahwa ini akan sering terjadi.

Kami mulai mencari solusi lain. Di sini kesederhanaan dan kekuatan Redis datang untuk menyelamatkan - kami memutuskan untuk membuat antrian untuk setiap bot .



Bagaimana itu bekerja? Pesan yang berhubungan dengan setiap bot ditambahkan ke antrian. Agar tidak menaikkan handler ke setiap antrian, kami membuat antrian kontrol . Dia bekerja seperti itu. Setiap kali permintaan datang dari bot, dua pesan diterbitkan di Redis: satu di antrian bot, yang kedua di kontrol. Pawang memonitor kontrol dan setiap kali memulai daemon ketika ada tugas untuk memproses bot. Iblis itu membuat antrian bot yang sesuai.

Selain tugas utama, kami memecahkan masalah "tetangga yang bising". Ini adalah ketika satu bot menghasilkan sejumlah besar webhooks dan memperlambat sistem, karena halaman lain sedang menunggu untuk diproses. Untuk mengatasi masalah, cukup dengan skala : ketika antrian kontrol penuh, kami menambahkan penangan baru.

Selain itu, antriannya virtual . Ini hanya sel dalam memori Redis. Ketika tidak ada apa pun dalam antrian, itu tidak ada, itu tidak menempati apa pun.

ReactPHP


Januari 2018 . Kami telah mencapai 1 miliar posting per bulan.

Bebannya 5 ribu RPS per sistem. Ini bukan beban puncak, tetapi standar. Ketika bot penyanyi terkenal muncul, semuanya tumbuh beberapa kali dari figur ini. Tapi itu bukan masalah. Masalahnya adalah di PHP-FPM: tidak bisa lagi menahan beban 5 ribu RPS.

Semua orang pada waktu itu berbicara tentang pemrosesan asinkron yang modis. Kami melihat lebih dekat, melihat ReactPHP, melakukan tes cepat, menggantinya dengan PHP-FPM dan langsung mendapat peningkatan 4 kali lipat.



Kami tidak menulis ulang pemrosesan pemrosesan kami - ReactPHP mengangkat kerangka Yii. Pertama, kami meningkatkan 4 layanan ReactPHP, dan kemudian kami mencapai 30. Untuk waktu yang lama kami menggunakannya, dan kerangka kerjanya mengatasi beban.

Segera setelah kami memperluas corong, keruntuhan lainnya terjadi: setelah memulai corong di resepsi, pemrosesan mulai menderita lagi. Untuk mengatasi masalah ini, kami memutuskan untuk memisahkan pemrosesan menjadi beberapa kluster.

Cluster


Mereka mengambil bot, membagikannya ke dalam kelompok dan membangun rantai logis dari Redis, Postgres dan seorang pawang.



Sebagai hasilnya, kami telah membentuk konsep "Galaxy" - abstraksi fisik logis atas pemrosesan . Ini terdiri dari contoh: Redis, PostgreSQL dan satu set layanan PHP. Setiap bot milik cluster tertentu, dan ReactPHP tahu di mana cluster pesan untuk bot ini harus ditempatkan. Skema di atas bekerja lebih jauh.


Semesta berkembang, Semesta sistem kami juga, dan kami menambahkan "Galaxy" baru ketika ini terjadi.
Galaksi adalah cara penskalaan kita.

Mengganti ReactPHP dengan sekelompok Nginx dan Lua


Selama enam bulan ke depan, kami terus tumbuh: 200 juta pelanggan dan 3 miliar pesan per bulan. Bayangkan sebuah situs untuk 200 juta pengguna terdaftar - beban yang sama.

Masalah baru telah muncul. Webhook adalah tugas kecil dengan tipe yang sama, dan PHP tidak cocok untuk menyelesaikannya. Bahkan ReactPHP tidak membantu lagi.

  • Dia tidak bisa mengatasi beban 10 ribu RPS - sejak diperkenalkannya ReactPHP, beban telah meningkat.
  • Itu perlu untuk me-restart bahkan dengan penyebaran, apalagi, secara berurutan, karena Anda tidak dapat mengganggu pemrosesan webhook yang masuk. Facebook menonaktifkan aplikasi ketika menyadari bahwa ia memiliki masalah. Bagi ManyChat, ini adalah bencana - 650 ribu bisnis yang aktif tidak akan memaafkan kami.

Oleh karena itu, kami secara bertahap menggigit logika yang berbeda dari ReactPHP, meneruskannya ke prosesor, dan mengisolasi antrian baru. Dalam prosesnya, mereka memperhatikan bahwa ReactPHP melakukan satu tugas sederhana - dibutuhkan webhook dan menempatkannya dalam antrian . Semua sisanya dilakukan oleh prosesi. Apakah ada analog untuk tugas sesederhana itu?

Kami ingat bahwa Nginx memiliki modul dan memperhatikan perpustakaan OpenResty . Selain mendukung bahasa pemrograman Lua, ia memiliki modul untuk bekerja dengan Redis. Tes yang ditulis dalam 3 jam menunjukkan bahwa semua pekerjaan dari 30 layanan di ReactPHP dapat dilakukan langsung di sisi nginx.



Begini hasilnya: kami memproses semacam titik akhir, mengambil badan permintaan dan menambahkannya langsung ke Redis.

location / {
    error_log /var/log/nginx/error.log;

    resolver ###resolver###;

    content_by_lua '

        ngx.req.read_body()
        local mybody = ngx.req.get_body_data()

        if not mybody then
            return ngx.exit(400)
        end

        local hash = ngx.crc32_long(mybody)
        local cluster = hash % ###wh_inbound_shards### + 1

        local redis = require "resty.redis";
        local red = radis.new()
        red:set_timeout(3000)

        local ok, err = red:connect("###redisConnectionWh2.server.host###", 6379)
		
        if not ok then
            ngx.log(ngx.ERR, err, "Redis failed to connect")
            return ngx.exit(403)
        end

        local ok, err = red:rpush("###wh_inbound_queue###" .. queuesuffix .. cluster, mybody)
        
        if not ok then
            ngx.log(ngx.ERR, err, "Failed to write data", mybody)
            return ngx.exit(500)
        end

        local ok, err = red:set_keepalive(10000, 100)

        ngn.say("ok")
    ';
}

OpenResty dan Lua telah membantu meningkatkan throughput. Kami terus mengatasi beban kerja kami, layanan tetap hidup, semua orang senang.

Memperbaiki solusi pada Lua


Tahap terakhir ( catatan: pada saat laporan ) adalah Februari 2019 . 500 juta pelanggan mengirim dan menerima 7 juta pesan dari sejuta bot setiap bulan.

Ini adalah langkah untuk meningkatkan solusi kami pada Lua. Secara bertahap gigit beberapa logika dari antrian, dan transfer pemrosesan utama mendistribusikan webhooks antara sistem ke Lua. Sekarang sistem kami lebih produktif dan kurang tergantung.



Kami mempertahankan pemrosesan terpisah dan pemrosesan asinkron . Memproses masalah statistik dan hal lainnya - sekarang ini adalah sistem yang sama sekali berbeda.

Sistemnya tampak sederhana, tetapi ternyata tidak. Di bawah tenda, ada 500 layanan yang memproses permintaan mereka. Seluruh sistem berjalan pada 50 instance Amazon: Redis, PostgreSQL, dan PHP handler itu sendiri.

Memproses Evolusi


Highload bisa keren dilakukan di PHP.

Ingat secara singkat bagaimana kami melakukan ini dalam proses pengembangan sistem.

  • Dimulai dengan Nginx dan PHP-FPM biasa.
  • Menambahkan antrian ke PostgreSQL, dan kemudian ke Redis.
  • Menambahkan pengelompokan.
  • Diimplementasikan ReactPHP.
  • Kami mengganti ReactPHP dengan sekelompok Nginx dan Lua, dan kemudian memindahkan logika ke kelompok itu.



Dari pengalaman kami, kami menemukan bahwa adalah mungkin untuk menumbuhkan dan membangun arsitektur dengan berturut-turut mengubah bagian-bagian yang rentan, menggunakan pendekatan sederhana yang terkenal dan pada saat yang sama tidak memperluas tumpukan.

, , 11 TeamLead Conf. , LeSS, .

PHP Russia Saint HighLoad++, . PHP , — PHP Russia 13 . highload PHP, Saint HighLoad++ .

Source: https://habr.com/ru/post/undefined/


All Articles