हाँ, बेशक यह NGINX में संभव है!
आप निम्नलिखित डीएफए को लागू कर सकते हैं :
मूल्यों को सामान्य करने के लिए $http_referer
संभवतः कुछ रेगेक्स का उपयोग करके , के आधार पर , दर सीमित करना लागू करें map
। जब सीमा पार हो जाती है, तो एक आंतरिक त्रुटि पृष्ठ उठाया जाता है, जिसे आप संबंधित प्रश्न के अनुसारerror_page
हैंडलर के माध्यम से पकड़ सकते हैं , एक आंतरिक रीडायरेक्ट (क्लाइंट के लिए दृश्यमान नहीं) के रूप में एक नए आंतरिक स्थान पर जा सकते हैं।
अधिक सीमाओं के लिए उपरोक्त स्थान में, आप एक चेतावनी अनुरोध करते हैं, जिससे बाहरी तर्क सूचना को निष्पादित कर सकते हैं; इस अनुरोध को बाद में कैश किया जाता है, यह सुनिश्चित करते हुए कि आपको दिए गए समय विंडो के अनुसार केवल 1 अद्वितीय अनुरोध प्राप्त होगा।
पूर्व अनुरोध के HTTP स्थिति कोड को पकड़ो (एक स्थिति कोड using 300 लौटाकर और proxy_intercept_errors on
, या, वैकल्पिक रूप से, नहीं-निर्मित-बाय-डिफ़ॉल्ट का उपयोग करें auth_request
या add_after_body
"निशुल्क" सबरेक्वेस्ट) का उपयोग करें, और मूल अनुरोध को पूरा करें पहले चरण में शामिल नहीं था। ध्यान दें कि हमें काम करने के लिए पुनरावर्ती error_page
संचालन को सक्षम करने की आवश्यकता है ।
यहाँ मेरा PoC और एक MVP है, वो भी https://github.com/cnst/StackOverflow.cnst.nginx.conf/blob/master/sf.432636.detecting-slashdot-effect-in-nginx.conf पर :
limit_req_zone $http_referer zone=slash:10m rate=1r/m; # XXX: how many req/minute?
server {
listen 2636;
location / {
limit_req zone=slash nodelay;
#limit_req_status 429; #nginx 1.3.15
#error_page 429 = @dot;
error_page 503 = @dot;
proxy_pass http://localhost:2635;
# an outright `return 200` has a higher precedence over the limit
}
recursive_error_pages on;
location @dot {
proxy_pass http://127.0.0.1:2637/?ref=$http_referer;
# if you don't have `resolver`, no URI modification is allowed:
#proxy_pass http://localhost:2637;
proxy_intercept_errors on;
error_page 429 = @slash;
}
location @slash {
# XXX: placeholder for your content:
return 200 "$uri: we're too fast!\n";
}
}
server {
listen 2635;
# XXX: placeholder for your content:
return 200 "$uri: going steady\n";
}
proxy_cache_path /tmp/nginx/slashdotted inactive=1h
max_size=64m keys_zone=slashdotted:10m;
server {
# we need to flip the 200 status into the one >=300, so that
# we can then catch it through proxy_intercept_errors above
listen 2637;
error_page 429 @/.;
return 429;
location @/. {
proxy_cache slashdotted;
proxy_cache_valid 200 60s; # XXX: how often to get notifications?
proxy_pass http://localhost:2638;
}
}
server {
# IRL this would be an actual script, or
# a proxy_pass redirect to an HTTP to SMS or SMTP gateway
listen 2638;
return 200 authorities_alerted\n;
}
ध्यान दें कि यह उम्मीद के मुताबिक काम करता है:
% sh -c 'rm /tmp/slashdotted.nginx/*; mkdir /tmp/slashdotted.nginx; nginx -s reload; for i in 1 2 3; do curl -H "Referer: test" localhost:2636; sleep 2; done; tail /var/log/nginx/access.log'
/: going steady
/: we're too fast!
/: we're too fast!
127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.1" 200 16 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.0" 200 16 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 200 20 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"
%
आप देख सकते हैं कि पहला अनुरोध एक फ्रंट-एंड और एक बैकएंड हिट के परिणामस्वरूप होता है, जैसा कि अपेक्षित था (मुझे उस स्थान पर एक डमी बैकएंड जोड़ना होगा limit_req
, क्योंकि return 200
सीमा से अधिक पूर्वता होगी, एक वास्तविक बैकेंड आवश्यक नहीं है बाकी हैंडलिंग के लिए)।
दूसरा अनुरोध सीमा से ऊपर है, इसलिए, हम अलर्ट भेजते हैं (प्राप्त करते हैं 200
), और इसे कैश करते हैं, लौटाते हैं 429
(यह उपरोक्त सीमा के कारण आवश्यक है कि 300 से नीचे के अनुरोधों को पकड़ा नहीं जा सकता), जिसे बाद में फ्रंट-एंड द्वारा पकड़ा गया है , जो अब स्वतंत्र है जो चाहे वह करने के लिए स्वतंत्र है।
तीसरा अनुरोध अभी भी सीमा से अधिक है, लेकिन हमने पहले ही अलर्ट भेज दिया है, इसलिए, कोई नया अलर्ट नहीं भेजा जाता है।
किया हुआ! यह GitHub पर कांटा करने के लिए मत भूलना!