Halo, Habr! Ini adalah posting pertama di blog kami. Banyak orang mengenal kami sebagai obrolan untuk situs itu, dengan dia kami memulai, dan sekarang kami menempati posisi terdepan di bidang utusan bisnis. Kami secara bertahap berkembang menjadi solusi bisnis komprehensif yang memberikan banyak peluang bagi pelanggan: panggilan balik, komunikasi dengan pelanggan melalui pesan instan, jejaring sosial, aplikasi seluler, PBX virtual, fungsi CRM, dan banyak lagi.
Selama beberapa tahun, kami berhasil memecahkan banyak masalah teknis, mengumpulkan banyak hal menarik, dan di beberapa tempat pengalaman unik, tentu saja, menulis kruk dan sepeda kami. Dengan posting ini, kami memulai serangkaian artikel di mana kami akan berbagi pengalaman kami dalam mengembangkan, membangun proses dalam tim yang sepenuhnya terpencil, menceritakan tentang arsitektur kami, solusi teknis yang memungkinkan kami untuk melayani ratusan ribu pelanggan di seluruh dunia secara efektif.
Jivosite hari ini adalah:- 250 ribu pelanggan di seluruh dunia;
- 150 juta tayangan widget per hari;
- 3,5 juta pesan per hari;
- 10 juta obrolan per bulan;
- 1M koneksi simultan;
- 250+ server sedang diproduksi.
Karena kebanyakan orang mengenal kami sebagai obrolan untuk suatu situs, kami mungkin akan memulainya. Dalam artikel ini, kami akan menunjukkan cara menghubungkan kode Anda ke situs pihak ketiga dan apa yang harus Anda perhatikan sebagai contoh pengalaman bertahun-tahun kami bekerja dengan obrolan. Artikel ini akan bermanfaat bagi mereka yang berencana atau sudah mengembangkan layanan plug-in, dan hanya untuk semua orang yang tertarik dengan topik ini.
Titik masuk
Teater dimulai dengan gantungan, dan layanan yang terhubung dengan kode sisipan. Ini adalah titik masuk untuk layanan atau modul apa pun di situs. Sebagai aturan, dapat ditemukan dalam instruksi instalasi, setelah itu perlu menambahkannya ke kode HTML situs, dan kemudian ada "sihir", yang memuat dan menginisialisasi skrip dengan cara tertentu.
<script src="https://site.com/file.js"></script>
Tampaknya akan lebih mudah untuk menghubungkan skrip ke situs?
Secara standar, Anda hanya perlu menambahkan tag skrip ke kode HTML halaman. Tetapi pada kenyataannya, ini adalah tahap penting, menyembunyikan banyak jebakan. Misalnya, identifikasi pengguna, penerapan saluran pemuatan skrip cadangan, penyesuaian tampilan atau logika, kecepatan pemuatan halaman, dan sebagainya. Tapi mari kita bicara tentang semuanya secara berurutan.
Identifikasi
Hanya karena itu tidak terlalu menarik bagi siapa pun untuk menghubungkan skrip, pastinya skrip melakukan beberapa jenis logika, dan logika ini terkait dengan pengguna. Misalnya, ID penghitung, APP_ID dari jaringan sosial, dalam kasus kami, ini adalah ID dari saluran komunikasi yang dibuat. Artinya, skrip harus mengidentifikasi pengguna dalam permintaan ke server. Untuk mengidentifikasi klien melalui kode sisipan, ada tiga opsi implementasi.
Opsi # 1 <script async src="https://site.com/file.js?id=123"></script>
Masukkan ID secara langsung di tautan ke file dan di sisi server dengan cara lain, masukkan ke dalam skrip. Dalam hal ini, server harus menulis ID ke file dengan cepat atau membentuk string JS dengan ID yang akan memuat file.js. Logika ini mirip dengan implementasi permintaan JSONP.

Untuk waktu yang lama kami bekerja pada prinsip ini, tetapi kelemahan dari pendekatan ini adalah bahwa beban "idle" di server dan kebutuhan untuk mengimplementasikan caching server ditambahkan.
Opsi # 2 <script src="https://site.com/file.js" [async]></script> <script type=”text/javascript”> window.serviceNameId = “123”; </script>
Atribut Async - memberi tahu browser bahwa tidak perlu menunggu skrip dimuat untuk membangun DOM, skrip harus dijalankan segera setelah memuat. Ini mengurangi waktu buka halaman, tetapi ada juga sisi lain dari koin: skrip dapat dieksekusi sebelum DOM siap untuk bekerja.Salah satu implementasi paling populer, termasuk layanan besar, tidak hanya itu, hanya sintaks yang berbeda, tetapi esensi semua adalah sama.

Pendekatan ini memiliki dua kelemahan utama, yang pertama - kode embed rumit, dan yang kedua - urutan pelaksanaan kode ini sangat penting, jika tidak tidak ada yang akan berhasil. Selain itu, Anda perlu membuat pilihan antara kecepatan (async) dan stabilitas (tanpa async), sebagian besar memilih opsi ke-2.
Opsi # 3 <script async src="https://site.com/file.js?id=123"></script>
Demikian pula dengan opsi pertama, transfer ID dalam tautan ke file, tetapi ambil di browser, dan bukan di server. Memang tidak sesederhana kelihatannya, tetapi itu mungkin. API browser memiliki properti document.currentScript, itu mengembalikan tautan ke skrip yang dimuat dan saat ini berjalan di browser. Mengetahui hal ini, Anda dapat menghitung ID, untuk ini Anda perlu mendapatkan properti document.currentScript.src dan secara teratur mengekstrak ID dari itu.

Ada satu hal tetapi: document.currentScript tidak didukung oleh semua browser. Untuk peramban yang tidak mendukung properti ini, kami membuat peretasan yang menarik. Dalam kode file.js, Anda dapat melempar pengecualian "palsu" khusus yang dibungkus dengan try / catch, setelah itu URL skrip tempat kesalahan terjadi dalam tumpukan kesalahan akan dibuang. URL akan berisi ID yang kami dapatkan dengan keteraturan yang sama.
Jenis sihir ini diperoleh, tetapi berhasil. Tidak ada masalah dengan urutan eksekusi, kode yang dimasukkan terlihat sederhana dan tidak ada overhead pada server. Selama dua tahun terakhir, kami telah menggunakan pendekatan semacam itu, meskipun kode sisipan itu sendiri berbeda, tetapi prinsipnya sama.
Pengaturan
Dalam kebanyakan kasus, skrip plug-in memiliki pengaturan yang bertanggung jawab atas penampilan atau logika kerja. Pengaturan ini harus "dilempar" ke skrip plug-in, karena ini ada dua pendekatan yang berbeda secara mendasar.
Pendekatan # 1 <script async src="https://site.com/file.js"></script> <script type=”text/javascript”> window.serviceName = {color: “red”, title: “”, ...}; </script>
Pendekatan ini juga termasuk meneruskan pengaturan dalam parameter GET ke url skrip, mirip dengan opsi # 1 dari bagian "Identifikasi". Pendekatannya adalah jika klien ingin mengubah pengaturan, maka ia perlu mengedit kode sematan dan memperbaruinya di situs.

Ini bagus karena semua pengaturan disimpan di klien dan mereka tidak perlu disimpan di server, kembangkan dan pertahankan semua logika bisnis yang terkait dengan ini. Kerugian utama dari pendekatan ini adalah ketidaknyamanan untuk klien, ia harus melakukan semuanya secara manual, dan jika ada banyak pengaturan, maka kode embed berubah menjadi sulit untuk mempertahankan sheet, di mana mudah untuk membuat kesalahan. Dan agar pembaruan berlaku, Anda perlu memperbarui situs, ini adalah gerakan ekstra dari pengembang dan admin.
Pendekatan # 2 <script async src="https://site.com/file.js?id=123"></script>
Pendekatan kedua adalah bahwa jika perlu untuk mengubah pengaturan, klien tidak perlu memodifikasi kode penyisipan, semua pengaturan disimpan di server. Untuk mengubah pengaturan, buka panel grafik, ubah parameter yang diperlukan dan klik tombol "Simpan". Setelah itu, pengaturan akan secara otomatis diterapkan ke situsnya!

Tidak perlu memahami kode dan membuat penyebaran untuk ini, ini dapat dilakukan oleh orang yang jauh dari JavaScript, misalnya, seorang manajer. Tentu saja, opsi ini jauh lebih nyaman dan sederhana bagi pengguna, itulah sebabnya kami menggunakannya. Tetapi Anda harus membayar untuk kenyamanan, pendekatan ini membutuhkan pengembangan dan dukungan logika pada server dan menyiratkan beban tambahan di atasnya. Dalam artikel berikut, kami pasti akan memberi tahu Anda bagaimana kami memproses 150 juta permintaan seperti itu setiap hari.
Kompatibilitas mundur
Sangat penting untuk mendapatkan versi dewasa dari kode sematan secepat mungkin. Karena memperbarui kode sisipan yang sudah terpasang akan sangat sulit. Contoh dari praktik kami: dalam versi pertama kami menggunakan ID numerik, tetapi karena alasan keamanan kami menggantinya dengan yang alfanumerik. Ternyata sangat sulit untuk mencapai perubahan dalam kode embed yang sudah diinstal. Banyak orang bahkan tidak tahu apa itu HTML dan bagaimana situs web dirancang. Misalnya, situs web dibuat oleh freelancer, studio, atau situs web dibuat melalui CMS / konstruktor, dll. Dalam kebanyakan kasus, klien kami hanya bekerja dengan panel pengaturan widget. Sejak itu, kami masih memiliki dalam peta nginx menulis ulang ID lama ke yang baru, yang memiliki sekitar 40K catatan.
.... /script/widget/config/15**90 /script/widget/config/bqZB**rjW5; /script/widget/config/15**94 /script/widget/config/qtfx**xnTi; /script/widget/config/15**95 /script/widget/config/fqmpa**4YX; /script/widget/config/15**97 /script/widget/config/Vr21g**nuT; /script/widget/config/15**98 /script/widget/config/8NXL5**F8E; /script/widget/config/15**00 /script/widget/config/Th2HN**6RJ; ....
Karena fitur ini, kami terpaksa mempertahankan kompatibilitas kode embed untuk semua refactoring, yang ada sekitar 5 di memori kami.
Isolasi kode
Karena skrip terhubung ke situs pihak ketiga yang sudah memiliki kode JavaScript dan CSS untuk situs dan layanan lainnya, tujuan utama bukanlah untuk merusak situs sehingga kode kami tidak mengubah logika, apalagi memecahnya. Ini bisa jadi kesalahan JavaScript yang menghentikan aliran eksekusi, atau gaya yang mengesampingkan gaya situs. Tetapi kode situs juga dapat memengaruhi skrip yang terhubung, misalnya, pustaka digunakan yang memodifikasi API peramban, setelah itu kode berhenti bekerja atau tidak berfungsi seperti yang kita harapkan.
<script type="text/javascript"> </script>
<style type="text/css"> // body * { padding: 20px; } form input { display: block; border: 2px solid red; } </style>
Ada beberapa opsi untuk mengisolasi kode. Misalnya, Anda bisa menggunakan awalan dalam variabel JS, closure, agar tidak menyumbat konteks global, gunakan sesuatu seperti BEM untuk gaya. Tetapi cara termudah adalah dengan mengeksekusi kode dalam iframe, itu memecahkan sebagian besar masalah isolasi, tetapi memberlakukan batasan tertentu. Kami menggunakan versi hybrid, kami akan memberi tahu Anda lebih banyak tentang isolasi kode di artikel berikut.
Blokir situs pemuatan

Acara yang dibebani - terjadi setelah halaman web dimuat penuh, termasuk gambar, gaya dan skrip eksternal. Fitur penting adalah bahwa di sebagian besar situs, logika JS, skrip pihak ketiga, dan iklan mulai berfungsi pada terjadinya acara ini. Poin yang sangat penting untuk semua skrip yang terhubung adalah untuk mencegah dampak negatif pada acara ini.
Ini terjadi dalam kasus di mana server dari mana skrip dimuat jawaban untuk waktu yang lama atau tidak merespons sama sekali: maka acara onload tertunda dan pemuatan halaman lebih lanjut pada dasarnya diblokir. Dalam kasus ketika server tidak tersedia, peristiwa onload akan terjadi hanya setelah permintaan telah habis, yaitu lebih dari 60 detik. Dengan demikian, masalah pada server unggahan skrip pada dasarnya "memecah" situs, yang tidak dapat diterima.
Pengalaman pribadiDi masa lalu, saya bekerja untuk sebuah perusahaan yang memiliki situs web dengan online 100K simultan, kencan online. Pada masa itu, tombol "Bagikan di jejaring sosial" sangat populer. Agar mereka muncul di situs, Anda harus menghubungkan skrip (sdk) dari jejaring sosial yang diinginkan. Suatu hari, rekan-rekan berlari ke kami dan mengatakan bahwa situs kami tidak berfungsi! Kami melihat pemantauan, di mana semuanya normal, dan pada awalnya kami tidak mengerti apa masalahnya. Ketika mereka mulai menggali lebih dalam, mereka menyadari bahwa cdn-server Twitter sedang berbaring, dan SDK mereka tidak dapat memuat, ini menghalangi kami untuk memuat situs selama ~ 1,5 menit. Yaitu, setelah membuka situs, sedikit HTML dimuat (sisa SPA) dan hanya setelah 1,5 menit semuanya dimuat, batas waktu permintaan yang paling berhasil. Kami harus segera mengatur perbaikan terbaru dan menghapus skrip mereka dari situs. Setelah mengulangi situasi ini, kami memutuskan untuk menghapus blok Bagikan sama sekali.
Dalam versi pertama dari kode penyisipan, kami tidak memperhitungkannya, dan jika ada masalah teknis di pihak kami, untuk membuatnya lebih ringan, kami merepotkan pelanggan kami, tetapi seiring waktu kami memperbaikinya.
Solusi <script type='text/javascript'> (function(){ var initCode = function () { </script>
Solusinya sederhana, Anda harus berlangganan ke acara memuat penuh situs dan hanya kemudian memuat skrip, untuk ini Anda perlu menggunakan kode embed, dan bukan tag skrip.
Kecepatan halaman Google
Analisis versi seluler habr.comSebagian besar dari mereka memperhatikan kecepatan memuat situs, menurut banyak penelitian, ini secara langsung mempengaruhi laba, di samping itu, algoritma pencarian ketika peringkat mulai memperhitungkan waktu pemuatan halaman. Dalam hal ini, pemilik situs sering menggunakan alat serupa untuk mengevaluasi kinerja situs. Karena itu, sangat penting untuk menyambungkan kode secara optimal ke situs, karena secara langsung memengaruhi waktu pengunduhan.
Ini berarti Anda harus menggunakan teknik modern untuk mengoptimalkan pemuatan halaman. Misalnya, gunakan Gzip, cache file statis dan permintaan, gunakan pemuatan skrip asinkron, kompres statis dengan algoritma modern seperti WebP / Brotli / dll dan gunakan optimisasi lainnya. Kami secara rutin melakukan audit dan menanggapi peringatan dan rekomendasi untuk memenuhi persyaratan saat ini.
Cdn
Dalam versi pertama, kami mengunduh statika dari server aplikasi. Tetapi pendekatan ini memiliki kelemahan: lalu lintas mahal, keterpencilan dari pengunjung situs dan beban berlebihan pada saluran server. Anda dapat dengan mudah menyumbat saluran server aplikasi dengan efek habr situs, karena lalu lintas statis sangat "berat".
Untuk menghemat anggaran, stabilitas dan mengurangi latensi jaringan, adalah optimal untuk memuat statika dari server yang dirancang khusus untuk tujuan ini. Anda dapat menggunakan penyedia CDN yang sudah jadi, tetapi dalam skala besar itu tidak murah dan Anda harus dibatasi oleh kemampuan yang disediakan oleh penyedia ini atau itu.

Kami menerapkannya sederhana, memesan server murah di Rusia, Eropa dan Amerika dengan lalu lintas tanpa batas dan saluran yang luas. Itu murah, tidak memberlakukan batasan pada kami, kami dapat menyesuaikan semuanya untuk diri kami sendiri, dan toleransi kesalahan dijamin oleh mekanisme yang bekerja di browser. Saat ini, statika 1TB dimuat dari server CDN kami setiap hari.
Toleransi kesalahan
Sayangnya, dunia tidak sempurna, kebakaran terjadi, uplink jatuh, DC sepenuhnya tenggelam, ILV memblokir subnet, dan orang-orang membuat kesalahan. Meskipun demikian, perlu untuk dapat menangani situasi seperti itu dan terus bekerja.
PemantauanPertama, Anda perlu memahami bahwa ada yang salah. Anda bisa, tentu saja, menunggu sampai pengguna datang dan mengeluh, tetapi lebih baik untuk mengatur pemantauan dan peringatan, dan setelah rilis, periksa apakah semuanya beres. Kami memantau banyak parameter yang berbeda, baik server dan klien, dan jika terjadi kesalahan, kami segera melihatnya. Misalnya, jumlah unduhan widget atau lonjakan lalu lintas yang tidak wajar pada server CDN telah menurun.
Jumlah total unduhan widget untuk setiap versiKoleksi kesalahanJavaScript adalah bahasa yang sangat spesifik, dan mudah untuk membuat kesalahan. Selain itu, kebun binatang peramban di web modern sangat besar; apa yang berfungsi di Chrome terbaru bukan fakta yang akan berfungsi di Safari atau Firefox. Oleh karena itu, sangat penting untuk mengonfigurasi pengumpulan kesalahan dari browser dan merespons lonjakan waktu. Jika kode Anda berfungsi dalam iframe, Anda bisa melakukan ini dengan melacak global window.onerror handler dan, jika terjadi kesalahan, kirim data ke server. Jika kode berfungsi di luar iframe, maka sangat sulit untuk menerapkan pengumpulan kesalahan.
Jumlah total kesalahan dari semua situs dan browser
Informasi kesalahan spesifikCdn failoverSaya sudah menulis di atas bahwa semuanya memiliki properti jatuh, jadi penting untuk menangani situasi ini dan lebih baik - secara otomatis. Kami melewati beberapa tahap kemunduran server CDN, mulai dari manual, dan akhirnya menemukan cara untuk melakukan ini secara otomatis dan optimal untuk browser.
Dalam mode manual, ini hanya bekerja: SMS yang diterima administrator mengatakan bahwa CDN turun, mereka melakukan manipulasi tertentu, setelah itu widget mulai memuat dari server aplikasi. Ini bisa memakan waktu mulai dari 5 menit hingga 2 jam.
Untuk menerapkan fallback otomatis, Anda perlu mendeteksi entah bagaimana skrip telah mulai memuat, tetapi ini tidak semudah kelihatannya. Browser tidak menyediakan kemampuan untuk memantau keadaan perantara dari pemuatan tag script, seperti acara yang sedang berlangsung di XMLHttpRequest, tetapi hanya melaporkan peristiwa saat script dimuat dan dieksekusi. Juga tidak mungkin untuk waktu yang dapat diterima untuk mengetahui bahwa server saat ini tidak tersedia, satu-satunya peristiwa onerror kebakaran setelah batas waktu permintaan berakhir, lebih dari 1 menit. Dalam semenit, pengunjung mungkin sudah meninggalkan halaman, tetapi skrip tidak akan memuat.
Kami mencoba berbagai opsi, sederhana dan kompleks, tetapi pada akhirnya kami datang dengan permintaan ping untuk server CDN. Ini berfungsi seperti ini: pertama-tama kita melakukan ping ke server CDN, jika dijawab, maka kita memuat widget darinya. Untuk menerapkan skema ini secara optimal untuk browser dan server kami, kami menggunakan permintaan HEAD ringan (tanpa badan), dan selama unduhan berikutnya kami tidak melakukannya sampai versi widget diperbarui, karena widget sudah ada dalam cache browser.

Dengan demikian, kami menerima deteksi yang sangat cepat dan otomatis dari ketersediaan server statis, dan jika terjadi penurunan, kami beralih ke server cadangan hampir tanpa penundaan.
Loader
Untuk mengunggah skrip Anda ke situs pihak ketiga, Anda perlu memperhitungkan banyak poin, tetapi sulit untuk menerapkan logika ini dalam kode embed, karena itu hanya akan berubah menjadi "daging". Tetapi Anda masih perlu melakukan ini, untuk ini kami telah membuat modul kecil yang mengelola semua logika ini "di bawah tenda" dan memuat kode utama widget. Ini memuat pertama dan mengimplementasikan Failover CDN, caching, kompatibilitas ke belakang dengan kode embed lama, pengujian A / B, penataan bertahap widget versi baru, dan banyak fungsi lainnya.

Jadi, secara bertahap, kami sampai pada skema yang mencakup kasus-kasus utama memuat dan menginisialisasi widget. Dia telah membuktikan efektivitasnya selama bertahun-tahun digunakan di sejumlah besar situs yang berbeda. Pada saat yang sama, kode sisipan tetap sederhana dan universal, karena tidak ada logika di dalamnya dan kami dapat mengubahnya kapan saja, sementara tidak memaksa pengguna untuk mengubah kode sisipan.
Layanan Pihak Ketiga
Dan akhirnya, ada baiknya menyebutkan layanan pihak ketiga yang terhubung ke situs atau dalam beberapa cara berinteraksi dengan situs: bot pencarian, analisis, berbagai parser dan sebagainya. Layanan ini meninggalkan jejak di tempat kerja, Anda tidak boleh lupa tentang ini juga. Saya akan memberi tahu Anda beberapa kasus dari latihan kami.
GooglebotAplikasi operator kami memiliki fungsi "Pengunjung", di mana Anda dapat melihat pengunjung yang sedang melihat situs, dan berbagai informasi tentang mereka: waktu di situs, halaman, jumlah halaman yang dilihat, dan sebagainya. Di beberapa titik, pelanggan mulai mengeluh bahwa mereka "menggantung" pengunjung dari situs lain, yaitu, di situs yang menjual iPhone, klien yang diduga memiliki halaman yang disebut "Beli krim wajah". Ketika mereka mulai mengetahuinya, ternyata itu adalah GoogleBot, yang, ketika beralih dari situs ke situs, pertama-tama menembolok LocalStorage dan kemudian mentransfer data yang salah ke server.
Solusinya sederhana, server mulai mengabaikan data dari GoogleBot.
Yandex.MetricaAda fitur luar biasa dalam metrik - browser web, yang memungkinkan Anda melihat apa yang dilihat dan dilakukan pengguna dalam bentuk screencast. Untuk melakukan ini, metrik mencatat semua tindakan pengguna, dan setelah bot metrik khusus berjalan di sekitar situs, melakukan tindakan yang sama dan mencatat ini. Masalahnya adalah untuk mengemulasi browser seluler pengguna, menurut data kami, Firefox dihidupkan dalam mode emulasi seluler, tetapi userAgent dalam bot adalah desktop.
Ini mengarah pada fakta bahwa ketika melihat sesi pengguna seluler di browser web, versi desktop dari widget dibuka pada rekaman, meskipun pada kenyataannya, pengguna membuka versi mobile. Pelanggan kami mengira itu, dan membombardir kami dengan keluhan. , , , , .
, , , .
, . , . , , NodeJS , 270 - , .
, !