كيف يمكنني خنق مستخدمي واجهة برمجة التطبيقات في موقعي؟

StackOverflow https://stackoverflow.com/questions/1375501

  •  21-09-2019
  •  | 
  •  

سؤال

يطرح المستخدمون المشروعون لموقعي من حين لآخر الخادم مع طلبات API التي تسبب نتائج غير مرغوب فيها. أرغب في تحديد حد لا يوجد أكثر من قول مكالمة API واحدة كل 5 ثوانٍ أو مكالمات ن في الدقيقة (لم أحسب الحد الأدنى حتى الآن). من الواضح أن بإمكاني تسجيل كل استدعاء API في DB والقيام بالحساب على كل طلب لمعرفة ما إذا كانت قد تجاوزت الحد الأقصى ، ولكن كل هذا النفقات العامة الإضافية في كل طلب سيكون هزيمة الغرض. ما هي الأساليب الأخرى الأقل كثافة في الموارد التي يمكنني استخدامها لإقامة حد؟ أنا أستخدم PHP/Apache/Linux ، لما يستحق.

هل كانت مفيدة؟

المحلول

حسنًا ، لا توجد طريقة لفعل ما طلبته بدونه أي يكتب إلى الخادم ، لكن يمكنني على الأقل التخلص من تسجيل كل طلب واحد. تتمثل إحدى الطرق في استخدام طريقة الاختناق "المتسرب" ، حيث تتبع فقط الطلب الأخير ($last_api_request) ونسبة عدد الطلبات/الحد من الإطار الزمني ($minute_throttle). لا يعيد دلو المتسرب أبدًا إعادة تعيين عداده (على عكس خنق API على Twitter الذي يعيد ضبط كل ساعة) ، ولكن إذا أصبح الدلو ممتلئًا (وصل المستخدم إلى الحد) ، فيجب عليه الانتظار n ثواني لتفريغ الدلو قليلاً قبل أن يتمكنوا من تقديم طلب آخر. بمعنى آخر ، يشبه الحد الأقصى المتداول: إذا كانت هناك طلبات سابقة في الإطار الزمني ، فهي تتسرب ببطء من الجرافة ؛ إنه يقيدك فقط إذا قمت بملء الدلو.

سيحسب هذا المقتطف الرمز الجديد $minute_throttle القيمة على كل طلب. لقد حددت اللحظة في $minute_throttle لأنه يمكنك إضافة خانقات لأي فترة زمنية ، مثل الساعة ، يوميًا ، وما إلى ذلك ... على الرغم من أن أكثر من واحد سيبدأ بسرعة في جعلها مربكة للمستخدمين.

$minute = 60;
$minute_limit = 100; # users are limited to 100 requests/minute
$last_api_request = $this->get_last_api_request(); # get from the DB; in epoch seconds
$last_api_diff = time() - $last_api_request; # in seconds
$minute_throttle = $this->get_throttle_minute(); # get from the DB
if ( is_null( $minute_limit ) ) {
    $new_minute_throttle = 0;
} else {
    $new_minute_throttle = $minute_throttle - $last_api_diff;
    $new_minute_throttle = $new_minute_throttle < 0 ? 0 : $new_minute_throttle;
    $new_minute_throttle += $minute / $minute_limit;
    $minute_hits_remaining = floor( ( $minute - $new_minute_throttle ) * $minute_limit / $minute  );
    # can output this value with the request if desired:
    $minute_hits_remaining = $minute_hits_remaining >= 0 ? $minute_hits_remaining : 0;
}

if ( $new_minute_throttle > $minute ) {
    $wait = ceil( $new_minute_throttle - $minute );
    usleep( 250000 );
    throw new My_Exception ( 'The one-minute API limit of ' . $minute_limit 
        . ' requests has been exceeded. Please wait ' . $wait . ' seconds before attempting again.' );
}
# Save the values back to the database.
$this->save_last_api_request( time() );
$this->save_throttle_minute( $new_minute_throttle );

نصائح أخرى

يمكنك التحكم في المعدل مع خوارزمية دلو الرمز المميز, ، والتي تشبه خوارزمية دلو المتسرب. لاحظ أنه سيتعين عليك مشاركة حالة الدلو (أي مقدار الرموز) على العمليات (أو أي نطاق تريد التحكم فيه). لذلك قد ترغب في التفكير في القفل لتجنب ظروف السباق.

الأخبار السارة: لقد فعلت كل ذلك من أجلك: عرض النطاق الترددي/الرمز المميز

use bandwidthThrottle\tokenBucket\Rate;
use bandwidthThrottle\tokenBucket\TokenBucket;
use bandwidthThrottle\tokenBucket\storage\FileStorage;

$storage = new FileStorage(__DIR__ . "/api.bucket");
$rate    = new Rate(10, Rate::SECOND);
$bucket  = new TokenBucket(10, $rate, $storage);
$bucket->bootstrap(10);

if (!$bucket->consume(1, $seconds)) {
    http_response_code(429);
    header(sprintf("Retry-After: %d", floor($seconds)));
    exit();
}

لا أعرف ما إذا كان هذا الموضوع لا يزال على قيد الحياة أم لا ، لكنني أقترح الحفاظ على هذه الإحصائيات في ذاكرة التخزين المؤقت للذاكرة مثل memcached. سيؤدي ذلك إلى تقليل النفقات العامة لتسجيل الطلب إلى DB ولكن لا يزال يخدم الغرض.

سيكون الحل البسيط هو مجرد إعطاء كل مفتاح API عددًا محدودًا من الطلبات لكل 24 ساعة ، وإعادة ضبطها في بعض الوقت المعروف ، الثابت ،.

إذا استنفدوا طلبات API الخاصة بهم (أي. يصل العداد إلى الصفر ، أو الحد ، اعتمادًا على الاتجاه الذي تحسبه) ، توقف عن خدمة البيانات لهم حتى تقوم بإعادة تعيين عدادهم.

بهذه الطريقة ، سيكون في هُم مصلحة أفضل لعدم تأجيلك بالطلبات.

أنت تقول أن "كل THOS Extra Sevely على كل طلب سوف يهزم الغرض" ، لكنني لست متأكدًا من أن هذا صحيح. أليس الغرض لمنع تراجع الخادم الخاص بك؟ ربما تكون هذه هي الطريقة التي سأنفذها ، لأنها تتطلب حقًا قراءة/كتابة سريعة فقط. يمكنك حتى زراعة عمليات فحص خادم API إلى قرص/قرص مختلف إذا كنت قلقًا بشأن الأداء.

ومع ذلك ، إذا كنت تريد البدائل ، فيجب عليك التحقق mod_cband, ، وحدة Apache من طرف ثالث مصممة للمساعدة في اختناق النطاق الترددي. على الرغم من كونه في المقام الأول لتقييد النطاق الترددي ، إلا أنه يمكن أن يخنق بناءً على الطلبات في الثانية أيضًا. لم أستخدمها مطلقًا ، لذلك لست متأكدًا من نوع النتائج التي ستحصل عليها. كانت هناك وحدة أخرى تسمى mod-throttle أيضًا ، ولكن يبدو أن هذا المشروع قد تم إغلاقه الآن ، ولم يتم إصداره أبدًا لأي شيء فوق سلسلة Apache 1.3.

بالإضافة إلى التنفيذ من نقطة الصفر ، يمكنك أيضًا إلقاء نظرة على البنية التحتية لـ API مثل 3scale (http://www.3scale.net) الذي يحد من معدل وكذلك حفنة من الأشياء الأخرى (التحليلات وما إلى ذلك). هناك مكون إضافي لـ PHP: https://github.com/3scale/3scale_ws_api_for_php.

يمكنك أيضًا التمسك بشيء مثل الورنيش أمام واجهة برمجة التطبيقات والقيام بمعدل API مثل هذا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top