ЁЯЪж рдзрдбрд╛ 07 тАФ Throttling рдЖрдгрд┐ usage plans: рджрд╛рд░рд╛рд╡рд░рдЪрд╛ token bucket
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 07 ┬╖ рдорд╛рдЧреЗ: lesson-06-auth ┬╖ рдкреБрдвреЗ: lesson-08-caching
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ06, рдЖрдгрд┐ office рдЧрд░реНрджреА рдХрд╢реА рдерд╛рдВрдмрд╡рддреЗ рддреЗ: token bucket (rate рдЖрдгрд┐
burst), рдкреНрд░рддреНрдпреЗрдХ stage рд╕рд╛рдареА рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ client рд╕рд╛рдареА рдорд░реНрдпрд╛рджрд╛ (API keys рдЖрдгрд┐ рджреИрдирд┐рдХ
quota рдЕрд╕рд▓реЗрд▓реЗ usage plans), рдЖрдгрд┐ 429 Too Many Requests caller рд▓рд╛ рдХрд╛рдп рдХрд░рд╛рдпрд▓рд╛ рд╕рд╛рдВрдЧрддреЛ.
apigw/demo.py рдордзрд▓реЗ throttle() рдПрдХрд╛рдЪ рдХреНрд╖рдгреА рдЖрда visitors рдкрд╛рдард╡рддреЗ.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рджрд╛рд░рд╛рд╡рд░ tokens рдЪреА рдмрд╛рджрд▓реА ЁЯкг рдареЗрд╡рд▓реЗрд▓реА рдЖрд╣реЗ. рдЖрдд рдпреЗрдгреНрдпрд╛рд╕рд╛рдареА рдкреНрд░рддреНрдпреЗрдХ visitor рдиреЗ рдПрдХ token рдШреНрдпрд╛рдпрд▓рд╛рдЪ рд╣рд╡рд╛. рдмрд╛рджрд▓реАрдд рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд 5 tokens рдорд╛рд╡рддрд╛рдд (рд╣рд╛ burst). рдПрдХ рдорджрддрдиреАрд╕ рджрд░ рд╕реЗрдХрдВрджрд╛рд▓рд╛ 5 рдирд╡реЗ tokens рдмрд╛рджрд▓реАрдд рдЯрд╛рдХрддреЛ (рд╣рд╛ rate) тАФ рдкрдг рдмрд╛рджрд▓реАрдд рдорд╛рд╡рддреАрд▓ рддреНрдпрд╛рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдХрдзреАрдЪ рдирд╛рд╣реА.
рдЖрда visitors рдПрдХрд╛рдЪ рдХреНрд╖рдгреА рдпреЗрддрд╛рдд. рдкрд╣рд┐рд▓реЗ рдкрд╛рдЪ рдЬрдг рдкрд╛рдЪрд╣реА tokens рдШреЗрддрд╛рдд рдЖрдгрд┐ рдЖрдд
рдЬрд╛рддрд╛рдд. рд╕рд╣рд╛рд╡рд╛, рд╕рд╛рддрд╡рд╛ рдЖрдгрд┐ рдЖрдард╡рд╛ рдпрд╛рдВрдирд╛ рдмрд╛рджрд▓реА рд░рд┐рдХрд╛рдореА рджрд┐рд╕рддреЗ: "рдХреГрдкрдпрд╛ рдерд╛рдВрдмрд╛ рдЖрдгрд┐
рдкрд░рдд рдпрд╛" (429). рдПрдХрд╛ рд╕реЗрдХрдВрджрд╛рдирдВрддрд░ рдмрд╛рджрд▓реАрдд рдкреБрдиреНрд╣рд╛ рдкрд╛рдЪ рддрд╛рдЬреЗ tokens рдЕрд╕рддрд╛рдд.
Partner рд╢рд╛рд│рд╛рдВрдирд╛ рддреНрдпрд╛рдВрдЪреА рд╕реНрд╡рддрдГрдЪреА рдЫреЛрдЯреА рдмрд╛рджрд▓реА рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪреНрдпрд╛ visitor pass рд╡рд░ рджреИрдирд┐рдХ рдорд░реНрдпрд╛рджрд╛ рдорд┐рд│рддреЗ тАФ рджрд┐рд╡рд╕рд╛рд▓рд╛ 1,000 рднреЗрдЯреА. 1,001 рд╡реА рднреЗрдЯ рдЙрджреНрдпрд╛рдкрд░реНрдпрдВрдд рдирд╛рдХрд╛рд░рд▓реА рдЬрд╛рддреЗ.
рдмрд╛рджрд▓реА рдХреЛрдгрд╛рд▓рд╛рд╣реА рд╢рд┐рдХреНрд╖рд╛ рдХрд░рдд рдирд╛рд╣реА. рддреА kitchen рд▓рд╛ рдЧрд░реНрджреАрдд рдмреБрдбрдгреНрдпрд╛рдкрд╛рд╕реВрди рд╡рд╛рдЪрд╡рддреЗ.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
crowd["ЁЯСе 8 requests<br/>same instant"] --> b["ЁЯкг stage dev bucket<br/>burst 5 ┬╖ refill 5/s"]
b -->|"tokens 5тЖТ0"| ok["тЬЕ 200 ├Ч 5"]
b -->|"empty"| no["тП│ 429 ├Ч 3<br/>Too Many Requests"]
key["ЁЯФС x-api-key: key-partner-1"] --> plan["ЁЯУЛ usage plan partner-basic<br/>2 rps ┬╖ burst 2 ┬╖ 1,000/day"]
plan -->|"over the rate"| r["429 Too Many Requests"]
plan -->|"over the quota"| q["429 Limit Exceeded"]
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрдХреГрддреА + рдПрдХ lab: https://school-edh.pages.dev/apigateway/lesson-diagrams.html#l07
тЭУ рдХрд╛рдп
- Token bucket тАФ API Gateway рд╡рд╛рдкрд░рддреЛ рддреЛ algorithm: burst = рдмрд╛рджрд▓реАрдЪрд╛ рдЖрдХрд╛рд░, rate = рджрд░ рд╕реЗрдХрдВрджрд╛рд▓рд╛ рднрд░рд▓реЗ рдЬрд╛рдгрд╛рд░реЗ tokens. Tokens рдЕрд╕реЗрдкрд░реНрдпрдВрдд requests рдЪрд╛ burst рдЪрд╛рд▓рддреЛ; рддреНрдпрд╛рдирдВрддрд░ requests rate рдЗрддрдХреНрдпрд╛рдЪ рд╡реЗрдЧрд╛рдиреЗ рдЬрд╛рддрд╛рдд.
- Account-level limit тАФ default рдиреБрд╕рд╛рд░ рджрд░ рд╕реЗрдХрдВрджрд╛рд▓рд╛ 10,000 requests рдЖрдгрд┐ 5,000 рдЪрд╛ burst, рдкреНрд░рддреНрдпреЗрдХ account, рдкреНрд░рддреНрдпреЗрдХ Region рд╕рд╛рдареА, рддреБрдордЪреНрдпрд╛ рд╕рдЧрд│реНрдпрд╛ APIs рдордзреНрдпреЗ рд╡рд╛рдЯрд▓реЗрд▓рд╛. рд╣рд╛ soft quota рдЖрд╣реЗ: Service Quotas рдордзреВрди рддреБрдореНрд╣реА рдЬрд╛рд╕реНрдд рдорд╛рдЧреВ рд╢рдХрддрд╛.
- Stage рдЖрдгрд┐ method limits тАФ рд╕рдВрдкреВрд░реНрдг stage рд╕рд╛рдареА рдХрд┐рдВрд╡рд╛ рдПрдХрд╛ method рд╕рд╛рдареА рдХрдореА рдорд░реНрдпрд╛рджрд╛ рдареЗрд╡рд╛, рдЙрджрд╛.
рд╕рд╛рд╡рдХрд╛рд╢ рдЪрд╛рд▓рдгрд╛рд▒реНрдпрд╛
POST /reportsрдЪреЗ рд░рдХреНрд╖рдг рдХрд░рд╛. - Usage plans (REST APIs) тАФ рдкреНрд░рддреНрдпреЗрдХ client рд╕рд╛рдареА throttle (rate, burst) рдЖрдгрд┐ quota
(рджрд┐рд╡рд╕, рдЖрдард╡рдбрд╛ рдХрд┐рдВрд╡рд╛ рдорд╣рд┐рдиреНрдпрд╛рд▓рд╛ рдХрд┐рддреА requests), API keys рд▓рд╛ рдЬреЛрдбрд▓реЗрд▓реЗ. Client рдЖрдкрд▓реА
key
x-api-keyheader рдордзреНрдпреЗ рдкрд╛рдард╡рддреЛ. - рдХреНрд░рдо тАФ рд╕рдЧрд│реНрдпрд╛рдд рдиреЗрдордХреА рдорд░реНрдпрд╛рджрд╛ рдЖрдзреА рд▓рд╛рдЧреВ рд╣реЛрддреЗ: client рдЪрд╛ usage plan, рдордЧ method, рдордЧ stage, рдордЧ account. рдорд░реНрдпрд╛рджрд╛ best-effort рдкрджреНрдзрддреАрдиреЗ рд▓рд╛рдЧреВ рд╣реЛрддрд╛рдд.
- 429 тАФ throttle рдЭрд╛рд▓реНрдпрд╛рд╡рд░
{"message": "Too Many Requests"}, quota рд╕рдВрдкрд▓реНрдпрд╛рд╡рд░{"message": "Limit Exceeded"}. Callers рдиреА exponential backoff рдЖрдгрд┐ jitter рд╕рд╣ retry рдХрд░рд╛рд╡реЗ (AWS SDKs рд╣реЗ рддреБрдордЪреНрдпрд╛рд╕рд╛рдареА рдХрд░рддрд╛рдд).
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг рдПрдХрдЪ рдмреЗрд▓рдЧрд╛рдо client тАФ mobile app рдордзрд▓рд╛ рдПрдЦрд╛рджрд╛ loop, partner рдЪрд╛ batch job тАФ рд╕рдВрдкреВрд░реНрдг рд╡рд╛рдЯрд▓реЗрд▓реА account limit рднрд░реВрди рдЯрд╛рдХреВ рд╢рдХрддреЛ рдЖрдгрд┐ Region рдордзрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ API рдмрдВрдж рдкрд╛рдбреВ рд╢рдХрддреЛ. рджрд╛рд░рд╛рд╡рд░рдЪреНрдпрд╛ рдорд░реНрдпрд╛рджрд╛ kitchen рд▓рд╛ рдЬрд┐рд╡рдВрдд рдареЗрд╡рддрд╛рдд рдЖрдгрд┐ рдХреНрд╖рдорддрд╛ рдиреНрдпрд╛рдпреНрдпрдкрдгреЗ рд╡рд╛рдЯрддрд╛рдд. Usage plans "рдПрдХ partner" рдЪреЗ рд░реВрдкрд╛рдВрддрд░ contract рдордзреНрдпреЗ рдорд╛рдиреНрдп рдХрд░рддрд╛ рдпреЗрддреАрд▓ рдЕрд╢рд╛ рдЖрдХрдбреНрдпрд╛рдВрдордзреНрдпреЗ рдХрд░рддрд╛рдд: рджрд░ рд╕реЗрдХрдВрджрд╛рд▓рд╛ рдЗрддрдХреЗ, рджрд┐рд╡рд╕рд╛рд▓рд╛ рдЗрддрдХреЗ.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
apigw/gateway.py рдордзрд▓рд╛ TokenBucket рдлрдХреНрдд рдирдК рдУрд│реАрдВрдЪрд╛ рдЖрд╣реЗ: take(now)
(now - t) * rate tokens рдЬреЛрдбрддреЛ (рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд burst) рдЖрдгрд┐ рд╢рдХреНрдп рдЕрд╕рд▓реНрдпрд╛рд╕ рдПрдХ рдШреЗрддреЛ. рдкреНрд░рддреНрдпреЗрдХ stage
рд▓рд╛ рд╕реНрд╡рддрдГрдЪреА bucket рдорд┐рд│рддреЗ (dev: 5 рдЖрдгрд┐ 5; prod: defaults RATE = 10_000,
BURST = 5_000). Gateway.usage_plan() рдкреНрд░рддреНрдпреЗрдХ plan рд╕рд╛рдареА bucket рдЖрдгрд┐ quota рдмрдирд╡рддреЛ рдЖрдгрд┐ API
keys рддреНрдпрд╛рд▓рд╛ рдЬреЛрдбрддреЛ. api_key=True рдЕрд╕рд▓реЗрд▓реНрдпрд╛ routes рд╕рд╛рдареА _handle() key рддрдкрд╛рд╕рддреЛ (рдЕрдиреЛрд│рдЦреА тЖТ 403),
quota (429 Limit Exceeded) рдЖрдгрд┐ plan рдЪреА bucket (429 Too Many Requests) тАФ
stage bucket рдЪреНрдпрд╛ рдЖрдзреА.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 apigw/demo.py throttle
python3 - <<'EOF'
import sys; sys.path.insert(0, "apigw"); from demo import office, Clock
from gateway import TokenBucket
b = TokenBucket(rate=5, burst=5, now=0)
print("t=0.0 ", [b.take(0) for _ in range(6)])
print("t=0.2 ", [b.take(0.2) for _ in range(2)])
print("t=10 ", b.take(10), "tokens left:", b.tokens)
c = Clock(); gw, _ = office(c)
gw.usage_plan("trial", rate=100, burst=100, quota_per_day=3, keys=["key-trial"])
print([gw.handle("GET", "/partner/students/9", {"x-api-key": "key-trial"})[0] for _ in range(5)], gw.handle("GET", "/partner/students/9", {"x-api-key": "key-trial"})[2])
print("no key:", gw.handle("GET", "/partner/students/9")[0::2])
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
throttle рдЖрдзреА stage dev: rate 5/s, burst 5 тАФ 8 requests in the same instant: рдЫрд╛рдкрддреЗ, рдордЧ
200 200 200 200 200 429 429 429, рдордЧ one second later: 200 200 200 200 200 429.
Usage plan рд╕рд╛рдареА: 200 200 429 429 ┬╖ unknown key тЖТ 403.
рддреБрдордЪрд╛ snippet t=0.0 [True, True, True, True, True, False] рдЫрд╛рдкрддреЛ (рдкрд╛рдЪ tokens, рдордЧ
рд░рд┐рдХрд╛рдореА), t=0.2 [True, False] (0.2 s ├Ч 5/s = рдПрдХ рдирд╡рд╛ token),
t=10 True tokens left: 4 (рджрд╣рд╛ рд╢рд╛рдВрдд рд╕реЗрдХрдВрджрд╛рдВрдирдВрддрд░рд╣реА рдмрд╛рджрд▓реАрдд 5 рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд tokens рдХрдзреАрдЪ
рдирд╕рддрд╛рдд), рдордЧ trial quota 3 рд╕рд╛рдареА [200, 200, 200, 429, 429] {'message': 'Limit Exceeded'},
рдЖрдгрд┐ no key: (403, {'message': 'Forbidden'}).
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рддреБрдореНрд╣реА рдкреНрд░рддреНрдпреЗрдХ 429 рдЖрдзреАрдЪ рд╕рд╛рдВрдЧреВ рд╢рдХрддрд╛: bucket рдордзрд▓реЗ tokens рдореЛрдЬрд╛, refill рдЬреЛрдбрд╛, рдЖрдгрд┐
quota рддрдкрд╛рд╕рд╛. рдкрд╣рд┐рд▓рд╛ рдХреНрд╖рдг burst рдард░рд╡рддреЛ; рддреНрдпрд╛рдирдВрддрд░рдЪрд╛ рдкреНрд░рддреНрдпреЗрдХ рд╕реЗрдХрдВрдж rate рдард░рд╡рддреЛ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- stage рдХрд┐рдВрд╡рд╛ method limits рдирд╕рдгреЗ, рддреНрдпрд╛рдореБрд│реЗ рдПрдХрдЪ client рд╕рдВрдкреВрд░реНрдг account рдЪреЗ 10,000 rps рд╡рд╛рдкрд░реВ рд╢рдХрддреЛ
429рд▓рд╛ рд▓рдЧреЗрдЪ рдШрдЯреНрдЯ loop рдордзреНрдпреЗ retry рдХрд░рдгреЗ тАФ рддреНрдпрд╛рдиреЗ рдЧрд░реНрджреА рд╡рд╛рдврддреЗ; рдерд╛рдВрдмреВрди, рд╡рд╛рдврддреНрдпрд╛ рдЕрдВрддрд░рд╛рдиреЗ retry рдХрд░рд╛- usage plan рдЪреА API key authentication рдЖрд╣реЗ рдЕрд╕реЗ рдорд╛рдирдгреЗ (рдзрдбрд╛ 06)
- alarm рдирд╕рд▓реЗрд▓рд╛ quota тАФ partner рд▓рд╛ рд╕рдХрд╛рд│реА 11 рд╡рд╛рдЬрддрд╛ рдХрд│рддреЗ рдХреА рддреЗ рдЙрджреНрдпрд╛рдкрд░реНрдпрдВрдд blocked рдЖрд╣реЗрдд
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ рдЖрдзреА stage limits, рдордЧ рдПрдХрд╛ partner рд╕рд╛рдареА key рд╕рд╣ usage plan:
aws apigateway update-stage --rest-api-id abc123 --stage-name prod --patch-operations \
op=replace,path=/*/*/throttling/rateLimit,value=500 \
op=replace,path=/*/*/throttling/burstLimit,value=1000
aws apigateway create-usage-plan --name partner-basic \
--throttle burstLimit=2,rateLimit=2 --quota limit=1000,period=DAY \
--api-stages apiId=abc123,stage=prod
aws apigateway create-api-key --name partner-1 --enabled
aws apigateway create-usage-plan-key --usage-plan-id pl4n01 --key-id k3y001 --key-type API_KEY
Terraform:
resource "aws_api_gateway_usage_plan" "partner_basic" {
name = "partner-basic"
api_stages {
api_id = aws_api_gateway_rest_api.school.id
stage = aws_api_gateway_stage.prod.stage_name
}
throttle_settings {
rate_limit = 2
burst_limit = 2
}
quota_settings {
limit = 1000
period = "DAY"
}
}
ЁЯПн Production рдордзреНрдпреЗ рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ: back end рдЦрд░реЛрдЦрд░ рдХрд┐рддреА рд╕рд╣рди рдХрд░реВ рд╢рдХрддреЛ рддреНрдпрд╛рдкреЗрдХреНрд╖рд╛ рдХрдореА stage limits рдареЗрд╡рд╛ (рддреЗ рдореЛрдЬрд╛), рдкреНрд░рддреНрдпреЗрдХ partner рд▓рд╛ usage plan рджреНрдпрд╛, рдЖрдгрд┐
429s рд╡рд░ alarm рд▓рд╛рд╡рд╛, рдореНрд╣рдгрдЬреЗ customer рддрдХреНрд░рд╛рд░ рдХрд░рдгреНрдпрд╛рдЖрдзреАрдЪ рддреБрдореНрд╣рд╛рд▓рд╛ рдорд░реНрдпрд╛рджрд╛ рджрд┐рд╕реЗрд▓.
тПня╕П рдкреБрдвреЗ
рдЧрд░реНрджреА рдирд┐рдпрдВрддреНрд░рдгрд╛рдд рдЖрд▓реА. рдЖрддрд╛ рддреЛрдЪ рдкреНрд░рд╢реНрди kitchen рд▓рд╛ рддреНрд░рд╛рд╕ рди рджреЗрддрд╛ рд╕реЛрдбрд╡рд╛: рд╕реВрдЪрдирд╛ рдлрд▓рдХ тАФ caching.
git checkout lesson-08-caching