ใช่แน่นอนมันเป็นไปได้ใน NGINX!
สิ่งที่คุณสามารถทำได้คือใช้DFAต่อไปนี้:
ใช้การ จำกัด อัตราการใช้$http_referer
อาจขึ้นกับการใช้ regex ผ่าน a map
เพื่อทำให้ค่าปกติ เมื่อเกินขีด จำกัด หน้าข้อผิดพลาดภายในจะเพิ่มขึ้นซึ่งคุณสามารถติดต่อผ่านerror_page
ตัวจัดการตามคำถามที่เกี่ยวข้องไปยังตำแหน่งภายในใหม่เป็นการเปลี่ยนเส้นทางภายใน (มองไม่เห็นไคลเอนต์)
ในตำแหน่งด้านบนสำหรับข้อ จำกัด ที่เกินคุณดำเนินการตามคำขอการแจ้งเตือนโดยให้ตรรกะภายนอกทำการแจ้งเตือน คำขอนี้ถูกแคชในภายหลังเพื่อให้แน่ใจว่าคุณจะได้รับคำขอที่ไม่ซ้ำกัน 1 รายการต่อหน้าต่างเวลาที่ระบุ
รับรหัสสถานะ HTTP ของคำขอก่อนหน้านี้ (โดยส่งคืนรหัสสถานะ≥ 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"
%
คุณสามารถเห็นได้ว่าคำขอแรกส่งผลให้ front-end หนึ่งและแบ็กเอนด์ตีหนึ่งตามที่คาดไว้ (ฉันต้องเพิ่มแบ็กเอนด์ดัมมี่ไปยังตำแหน่งที่มีlimit_req
เนื่องจากreturn 200
จะมีความสำคัญเหนือกว่าขีด จำกัด แบ็กเอนด์จริงไม่จำเป็น สำหรับส่วนที่เหลือของการจัดการ)
คำขอที่สองอยู่เหนือขีด จำกัด ดังนั้นเราจึงส่งการแจ้งเตือน (รับ200
) และแคชส่งคืน429
(จำเป็นเนื่องจากข้อ จำกัด ที่กล่าวมาข้างต้นซึ่งไม่สามารถจับการร้องขอที่ต่ำกว่า 300) ซึ่งจะถูกดักจับโดย front-end ซึ่งฟรีตอนนี้สามารถทำอะไรก็ได้ที่ต้องการ
คำขอที่สามยังคงเกินขีด จำกัด แต่เราได้ส่งการแจ้งเตือนไปแล้วดังนั้นจึงไม่มีการส่งการแจ้งเตือนใหม่
ทำ! อย่าลืมแยกไว้บน GitHub!