Rumah >hujung hadapan web >tutorial js >Pendikitan Dijelaskan: Panduan Mengurus Had Permintaan API

Pendikitan Dijelaskan: Panduan Mengurus Had Permintaan API

DDD
DDDasal
2024-12-05 22:29:10510semak imbas

Bilakah Anda Harus Melaksanakan Pendikitan dalam Kod Anda?

Untuk projek besar, biasanya lebih baik menggunakan alatan seperti Pengehadan Kadar Cloudflare atau HAProxy. Ini berkuasa, boleh dipercayai dan menjaga angkat berat untuk anda.

Tetapi untuk projek yang lebih kecil—atau jika anda ingin mengetahui cara sesuatu berfungsi—anda boleh membuat pengehad kadar anda sendiri terus dalam kod anda. Kenapa?

  • Mudah: Anda akan membina sesuatu yang mudah difahami.
  • Mesra Bajet: Tiada kos tambahan selain daripada mengehos pelayan anda.
  • Ia Berfungsi untuk Projek Kecil: Selagi trafik rendah, ia memastikan keadaan cepat dan cekap.
  • Ia Boleh Digunakan Semula: Anda boleh menyalinnya ke projek lain tanpa menyediakan alatan atau perkhidmatan baharu.

Apa yang Anda Akan Pelajari

Menjelang penghujung panduan ini, anda akan tahu cara membina pendikit asas dalam TypeScript untuk melindungi API anda daripada tertekan. Inilah perkara yang akan kami bincangkan:

  • Had Masa Boleh Dikonfigurasikan: Setiap percubaan yang disekat meningkatkan tempoh kunci keluar untuk mengelakkan penyalahgunaan.
  • Hidup Permintaan: Tetapkan bilangan maksimum permintaan yang dibenarkan. Ini amat berguna untuk API yang melibatkan perkhidmatan berbayar, seperti OpenAI.
  • Storan Dalam Memori: Penyelesaian mudah yang berfungsi tanpa alat luaran seperti Redis—sesuai untuk projek kecil atau prototaip.
  • Had Setiap Pengguna: Jejaki permintaan berdasarkan setiap pengguna menggunakan alamat IPv4 mereka. Kami akan memanfaatkan SvelteKit untuk mendapatkan semula IP pelanggan dengan mudah dengan kaedah terbina dalamnya.

Panduan ini direka bentuk untuk menjadi titik permulaan yang praktikal, sesuai untuk pembangun yang ingin mempelajari asas tanpa kerumitan yang tidak perlu. Tetapi ia belum siap pengeluaran.

Sebelum bermula, saya ingin memberikan kredit yang betul kepada bahagian Had Kadar Lucia.


Pelaksanaan Pendikit

Mari kita tentukan kelas Throttler:

export class Throttler {
    private storage = new Map<string, ThrottlingCounter>();

    constructor(private timeoutSeconds: number[]) {}
}

Pembina Throttler menerima senarai tempoh tamat masa (timeoutSeconds). Setiap kali pengguna disekat, tempoh meningkat secara progresif berdasarkan senarai ini. Akhirnya, apabila tamat masa terakhir dicapai, anda malah boleh mencetuskan panggilan balik untuk melarang IP pengguna secara kekal—walaupun itu di luar skop panduan ini.

Berikut ialah contoh mencipta tika pendikit yang menyekat pengguna untuk meningkatkan selang:

const throttler = new Throttler([1, 2, 4, 8, 16]);

Acara ini akan menyekat pengguna buat kali pertama selama satu saat. Kali kedua untuk dua orang, dan seterusnya.

Kami menggunakan Peta untuk menyimpan alamat IP dan data yang sepadan. Peta sesuai kerana ia mengendalikan penambahan dan pemadaman yang kerap dengan cekap.

Petua Pro: Gunakan Peta untuk data dinamik yang kerap berubah. Untuk data statik, tidak berubah, objek adalah lebih baik. (Lubang arnab 1)


Apabila titik akhir anda menerima permintaan, ia mengekstrak alamat IP pengguna dan merujuk kepada Pendikit untuk menentukan sama ada permintaan itu harus dibenarkan.

Bagaimana ia Berfungsi

  • Kes A: Pengguna Baharu atau Tidak Aktif

    Jika IP tidak ditemui dalam Throttler, ini sama ada permintaan pertama pengguna atau mereka telah tidak aktif cukup lama. Dalam kes ini:

    • Benarkan tindakan.
    • Jejak pengguna dengan menyimpan IP mereka dengan tamat masa awal.
  • Kes B: Pengguna Aktif

    Jika IP ditemui, ia bermakna pengguna telah membuat permintaan sebelumnya. Di sini:

    • Semak sama ada masa menunggu yang diperlukan (berdasarkan tatasusunan timeoutSeconds) telah berlalu sejak blok terakhir mereka.
    • Jika cukup masa telah berlalu:
    • Kemas kini cap masa.
    • Naikkan indeks tamat masa (dihadkan kepada indeks terakhir untuk mengelakkan limpahan).
    • Jika tidak, tolak permintaan itu.

Dalam kes terakhir ini, kita perlu menyemak sama ada masa yang mencukupi telah berlalu sejak blok terakhir. Kita tahu mana antara timeoutSeconds yang harus kita rujuk terima kasih kepada indeks. Jika tidak, bangkit semula. Jika tidak, kemas kini cap masa.

export class Throttler {
    private storage = new Map<string, ThrottlingCounter>();

    constructor(private timeoutSeconds: number[]) {}
}

Apabila mengemas kini indeks, ia dihadkan kepada indeks terakhir masa tamatSekon. Tanpanya, counter.index 1 akan melimpahinya dan seterusnya akses this.timeoutSeconds[counter.index] akan mengakibatkan ralat masa jalan.

Contoh titik akhir

Contoh ini menunjukkan cara menggunakan Throttler untuk mengehadkan kekerapan pengguna boleh menghubungi API anda. Jika pengguna membuat terlalu banyak permintaan, mereka akan mendapat ralat dan bukannya menjalankan logik utama.

const throttler = new Throttler([1, 2, 4, 8, 16]);

Throttling Explained: A Guide to Managing API Request Limits

Nota untuk Pengesahan

Apabila menggunakan pengehadan kadar dengan sistem log masuk, anda mungkin menghadapi isu ini:

  1. Seorang pengguna log masuk, mencetuskan Throttler untuk mengaitkan tamat masa dengan IP mereka.
  2. Pengguna log keluar atau sesi mereka tamat (cth., log keluar serta-merta, kuki tamat tempoh dengan sesi dan penyemak imbas ranap, dsb.).
  3. Apabila mereka cuba log masuk semula sejurus selepas itu, Pendikit mungkin masih menyekat mereka, mengembalikan ralat 429 Too Many Requests.

Untuk mengelakkan ini, gunakan ID pengguna unik pengguna dan bukannya IP mereka untuk mengehadkan kadar. Selain itu, anda mesti menetapkan semula keadaan pendikit selepas log masuk yang berjaya untuk mengelakkan sekatan yang tidak perlu.

Tambah kaedah tetapan semula pada kelas Throttler:

export class Throttler {
    // ...

    public consume(key: string): boolean {
        const counter = this.storage.get(key) ?? null;
        const now = Date.now();

        // Case A
        if (counter === null) {
            // At next request, will be found.
            // The index 0 of [1, 2, 4, 8, 16] returns 1.
            // That's the amount of seconds it will have to wait.
            this.storage.set(key, {
                index: 0,
                updatedAt: now
            });
            return true; // allowed
        }

        // Case B
        const timeoutMs = this.timeoutSeconds[counter.index] * 1000;
        const allowed = now - counter.updatedAt >= timeoutMs;
        if (!allowed) {
            return false; // denied
        }

        // Allow the call, but increment timeout for following requests.
        counter.updatedAt = now;
        counter.index = Math.min(counter.index + 1, this.timeoutSeconds.length - 1);
        this.storage.set(key, counter);

        return true; // allowed
    }
}

Dan gunakannya selepas log masuk berjaya:

export class Throttler {
    private storage = new Map<string, ThrottlingCounter>();

    constructor(private timeoutSeconds: number[]) {}
}

Mengurus Rekod IP Lapuk dengan Pembersihan Berkala

Apabila pendikit anda menjejaki IP dan had kadar, adalah penting untuk memikirkan cara dan bila untuk mengalih keluar rekod IP yang tidak diperlukan lagi. Tanpa mekanisme pembersihan, pendikit anda akan terus menyimpan rekod dalam ingatan, yang berpotensi membawa kepada isu prestasi dari semasa ke semasa apabila data berkembang.

Untuk mengelakkan ini, anda boleh melaksanakan fungsi pembersihan yang secara berkala mengalih keluar rekod lama selepas tempoh tertentu tidak aktif. Berikut ialah contoh cara menambah kaedah pembersihan mudah untuk mengalih keluar masukan lapuk daripada pendikit.

const throttler = new Throttler([1, 2, 4, 8, 16]);

Cara yang sangat mudah (tetapi mungkin bukan yang terbaik) untuk menjadualkan pembersihan adalah dengan setInterval:

export class Throttler {
    // ...

    public consume(key: string): boolean {
        const counter = this.storage.get(key) ?? null;
        const now = Date.now();

        // Case A
        if (counter === null) {
            // At next request, will be found.
            // The index 0 of [1, 2, 4, 8, 16] returns 1.
            // That's the amount of seconds it will have to wait.
            this.storage.set(key, {
                index: 0,
                updatedAt: now
            });
            return true; // allowed
        }

        // Case B
        const timeoutMs = this.timeoutSeconds[counter.index] * 1000;
        const allowed = now - counter.updatedAt >= timeoutMs;
        if (!allowed) {
            return false; // denied
        }

        // Allow the call, but increment timeout for following requests.
        counter.updatedAt = now;
        counter.index = Math.min(counter.index + 1, this.timeoutSeconds.length - 1);
        this.storage.set(key, counter);

        return true; // allowed
    }
}

Mekanisme pembersihan ini membantu memastikan pendikit anda tidak menyimpan rekod lama selama-lamanya, memastikan aplikasi anda cekap. Walaupun pendekatan ini mudah dan mudah dilaksanakan, pendekatan ini mungkin memerlukan penghalusan lanjut untuk kes penggunaan yang lebih kompleks (cth., menggunakan penjadualan yang lebih maju atau mengendalikan keselarasan tinggi).

Dengan pembersihan berkala, anda menghalang memori memori dan memastikan bahawa pengguna yang tidak cuba membuat permintaan untuk seketika tidak lagi dijejaki - ini adalah langkah pertama ke arah menjadikan sistem pengehad kadar anda berskala dan cekap sumber.


  1. Jika anda berasa mencabar, anda mungkin berminat untuk membaca cara harta tanah diperuntukkan dan cara ia berubah. Juga, mengapa tidak, tentang pengoptimuman VM seperti cache sebaris, yang sangat disukai oleh monomorfisme. Nikmati. ↩

Atas ialah kandungan terperinci Pendikitan Dijelaskan: Panduan Mengurus Had Permintaan API. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn