介绍
本文是以下三部分系列中的第三篇。
(一)介绍背景和目的
② 使用 AWS Prototyping 程序进行开发
(3) 产品发布前解决的问题,引入的效果
本文涵盖以下主题:
- 关于 ASL(亚马逊软件许可证)
- GraphQL 缓存的工作原理
- 在产品发布前解决的问题
- 介绍效果
关于 ASL(亚马逊软件许可)
本文介绍的 GraphQL 缓存 Lambda 代码是使用 AWS Prototyping 程序开发的。本程序开发的可交付成果可根据 ASL(亚马逊软件许可证)使用。
它类似于 Apache License 2.0,但有一些区别,例如允许在 AWS 上使用。
在本文中,我们将发布实现 GraphQL 缓存的核心代码部分,但如果您想参考,请阅读上述许可。
GraphQL 缓存的工作原理
在产品发布之前存在许多挑战。后面为了介绍我是怎么清除问题的,先洛亚我将解释实际工作的 GraphQL 缓存机制。
建筑学
配置如下。
- 云前
- Lambda@Edge(缩写=L@E)
- Python 3.9 运行时
- CloudFront 功能(缩写 = CF2)
每个服务的角色
CloudFront
- 接受查看器向 API 端点发出的 POST 请求。
- 接受由 L@E 与 Origin Request 一起发出的 GET 请求。或者,绕过 L@E 并直接向源发送 POST 请求。 (用于后面描述的曝光措施)
- 如果缓存命中,则将缓存对象返回给 L@E。如果缓存对象不存在,则再次向源发送请求。
- 根据响应头策略向返回给查看器的响应添加静态响应头。
Lambda@Edge
- 从 POST 到 GET 的方法转换 & 拆分请求正文,将其存储在将成为缓存键的标头中,然后再次向 CloudFront 请求。对于无法缓存的请求(响应),请直接从 CloudFront 向源发出请求。
- 当 CloudFront 发生缓存命中时,使用返回的缓存对象创建响应并将响应返回给 CloudFront。
- 如果没有缓存命中,则从CloudFront返回一个GET请求到L@E,所以方法由GET转换为POST,从缓存key的请求头中提取原始请求体,合并,请求被发送到 origin.issue。
- 接收从源返回的响应,创建响应以返回查看器,并将响应返回给 CloudFront。
CloudFront 函数
- 使用查看器响应并将动态响应标头添加到返回给查看器的响应中。
如何使 CloudFront 缓存 GraphQL 响应
CloudFront 不会缓存 POST 请求的响应结果,因此您通常无法缓存 GraphQL 请求的响应。 CloudFront 只是将查看器发送的 POST 请求转发到您的源。
因此,与 Origin Request 一起使用的 L@E 将方法从 POST 转换为 GET。它还将 POST 请求的主体拆分为 1783 个字符,并将其存储在自定义标头
Payload0~4中以创建请求标头。这些标头用作 CloudFront 缓存策略中的缓存键。我们拆分为 1783 个字符的原因是 CloudFront 允许的自定义标头的最大长度为 1783 个字符。此外,还有 5 个缓存键
Payload0~4,因为 CloudFront 中组合的自定义标头值和名称的最大长度为 10240 个字符。
1783 * 5 = 8915,所以有 1325 个字符可以备用,但是这个备用字符用于自定义标头而不是Payload0~4,它是一个缓存键。创建一个 CloudFront 可以创建缓存对象的情况,方法是使用 L@E 将方法从 POST 转换为 GET,拆分 POST 请求的主体,将其作为缓存键存储在 header 中,然后向 CloudFront 发出 GET 请求。。
CloudFront 将 L@E 发出的 GET 请求转发到源,因为第一个请求没有缓存对象。此时,L@E 再次与第二个 OriginRequest 一起工作。
现在,将方法从 GET 转换为 POST,提取并组合
Payload0~4标头值,并创建请求正文。换句话说,它重新创建了查看器发送的原始 GraphQL 请求。 L@E 然后向源发出 POST 请求并从源接收响应。L@E 将从源端收到的响应返回给 CloudFront,作为对 CloudFront 转发的 GET 请求(自身发出的 GET 请求)的响应。 CloudFront 从 GET 请求响应创建一个缓存对象。
由于 L@E 发出了 GET 请求,因此从 CloudFront 返回响应。 L@E 使用从 CloudFront 返回的响应来创建对 Viewer 最初发送的 POST 请求的响应并将其返回给 CloudFront。
CloudFront 将从 L@E 创建的响应返回给查看器。
缓存启用/禁用控制
有些请求是可缓存的 GraphQL 查询,有些则不是。满足以下任一条件的请求不会导致 CloudFront 缓存响应。
- 请求缺少身份验证所需的标头
- 使用用户私人信息作为响应的请求
- 请求正文较大,无法存储在缓存键标头中
这些请求必须绕过 L@E 并直接在 CloudFront 和源之间进行。 CloudFront 不会创建缓存对象,因为绕过 L@E 时不会发生从 POST 到 GET 的方法转换。
下面是一个图像图。第一个 Origin Request 确定该请求是否可以缓存其响应。
第二个条件尤为重要,如果你缓存了包含用户个人信息的响应,就会发生用户个人信息泄露的缓存事故。1
缓存命中时的行为
如果有缓存命中,会进行如下处理。如果没有缓存命中,则会在第一个架构图中进行处理。
代码
我将介绍 L@E 的代码,它是 GraphQL 缓存的核心。太长了,折叠一下。
现在 RT-http-me tyod。 py
# reference to https://github.com/aws-samples/amazon-cloudfront-cache-graphql # note to https://aws.amazon.com/jp/asl/ # coding=utf-8 import urllib.request import urllib.parse import urllib.error import json import base64 import ssl # Lambda@EdgeからOriginへリクエストするときに "certificate verify failed: Hostname mismatch" のエラーが発生する事象の回避策 ssl._create_default_https_context = ssl._create_unverified_context from urllib.parse import parse_qs from urllib.parse import quote # キャッシュできる Payload のサイズは Header 値の最大に設定している # 参考: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html CACHE_PAYLOAD_SIZE_LIMIT = 1783 # Payload 分割数の最大値 # 「結合されるすべてのヘッダー値および名前の最大長 (10240)」を「ヘッダー値の最大長 (1783)」で割った数にしている # 参考: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html NUM_SPLIT_MAX = 5 # キャッシュ対象のエンドポイント # それ以外はバイパス処理 GRAPHQL_ENDPOINT = '/graphql/' cacheable_operations = { 'pc': [ ], 'sp': [ ], 'app-android': [ ], 'app-ios': [ ] } # CloudFront にキャッシュさせないためのステータスコードをRFCの空き番号から決定する RESPONSE_STATUS_CODE_NO_CACHE = # API から通常返却されるステータスコード RESPONSE_STATUS_CODE_DEFAULT = 200 def device_type(is_desktop, user_agent): if is_desktop == 'true': return 'pc' else: if 'androidのuser-agent' in user_agent.lower(): return 'app-android' elif 'iosのuser-agent' in user_agent.lower(): return 'app-ios' return 'sp' def exists_required_header(req_headers): if 'example-auth-header' in req_headers: return True return False def is_cacheable_operation(operation_name, is_desktop, user_agent): """operationName からキャッシュ可能な Query かを判定する関数 Args: operation_name: GraphQL Query の operationName is_desktop: ヘッダの cloudfront-is-desktop-viewer user_agent: ヘッダの user-agent Returns: True or False (True の場合はキャッシュ可能) """ if operation_name.lower() in cacheable_operations[device_type(is_desktop, user_agent)]: return True return False def http_request(endpoint, method='GET', headers={}, data=None): """HTTP リクエストを実行して CloudFront のレスポンスの形式に変換する関数 Args: endpoint: リクエスト先の Endpoint method : HTTP メソッド (デフォルト: GET) headers : ヘッダー (デフォルト: {}) data : リクエスト Body (デフォルト: None) Returns: CloudFront のレスポンスの形式に変換された Dict """ req = urllib.request.Request(endpoint, method=method, headers=headers, data=data) res = urllib.request.urlopen(req) res_code = res.getcode() res_body = res.read().decode('utf-8') res_headers = response_headers(res) return { 'status': res_code, 'headers': res_headers, 'body': res_body } def response_headers(res): """urllib のレスポンスの Header を CloudFront のレスポンスの形式に変換する関数 参考: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#lambda-event-structure-response-origin Args: res: urllib のレスポンス Returns: CloudFront の形式に変換された Headers """ headers_raw = dict(res.info()) headers = list(map(lambda x: { x: { 'key': x, 'value': headers_raw[x] } }, headers_raw.keys())) return headers def split_payload(data): """Payload を CACHE_PAYLOAD_SIZE_LIMIT で分割する Args: data: base64 の Payload Returns: 分割した Payload の配列 """ # Offset を示す cursor cursor = 0 # 分割した Payload payloads = [] while True: # cursor から Header に収まるサイズの data であれば終了 if len(data[cursor:]) <= CACHE_PAYLOAD_SIZE_LIMIT: payloads.append(data[cursor:]) break # CACHE_PAYLOAD_SIZE_LIMIT までデータを入れて、cursor を移動 else: payloads.append(data[cursor:cursor+CACHE_PAYLOAD_SIZE_LIMIT]) cursor += CACHE_PAYLOAD_SIZE_LIMIT # 分割数が NUM_SPLIT_MAX に満たない場合は、空文字列を挿入 if len(payloads) < NUM_SPLIT_MAX: for _ in range(NUM_SPLIT_MAX - len(payloads)): payloads.append('') return payloads def handler(event, context): """Lambda@Edge の Handler Args: event : CloudFront の Event context: Lambda の Context Returns: CloudFront のレスポンス or バイパス処理 """ # CloudFront のイベントを取得し、ドメイン名とリクエストを取得 # 参考: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#example-origin-request cloud_front_event = event['Records'][0]['cf'] cloud_front_domain_name = cloud_front_event['config']['distributionDomainName'] request = cloud_front_event['request'] origin_domain_name = request['origin']['custom']['domainName'] # GraphQL エンドポイントの場合は、GET <=> POST で変換する if request['uri'] == GRAPHQL_ENDPOINT: # POST を GET に変換する (Lambda@Edge => CloudFront) if request['method'] == 'POST': print('POST') # 認証に必要なヘッダーがないものはバイパスする if not exists_required_header(request['headers']): return request # POST の Payload (base64) を取得 data = request['body']['data'] # 以下のような形式の Payload を想定し、異なる場合はバイパスする # ``` # [ # { "operationName": "xxx", "query": "yyy", "variables": {...} }, # ... # ] # ``` try: queries = json.loads(base64.b64decode(data).decode()) is_desktop = request['headers']['cloudfront-is-desktop-viewer'][0]['value'] user_agent = request['headers']['user-agent'][0]['value'] if 'user-agent' in request['headers'] else '' if isinstance(queries, dict): queries = [queries] for query in queries: # キャッシュ不可な operationName が含まれている場合はバイパス処理 if not is_cacheable_operation(query['operationName'], is_desktop, user_agent): return request except: return request # Payload を分割する payloads = split_payload(data) # 分割数が NUM_SPLIT_MAX (許容量) を越えている場合はバイパス処理 if len(payloads) > NUM_SPLIT_MAX: return request headers = { # Payload は base64 のまま Header に格納 # Payload0~4は CloudFront の Cache Policy に指定する 'Payload0': payloads[0], 'Payload1': payloads[1], 'Payload2': payloads[2], 'Payload3': payloads[3], 'Payload4': payloads[4], # 以下のヘッダーは CloudFront の Origin Request Policy に指定する 'User-Agent': user_agent, 'example-auth-header': request['headers']['example-auth-header'][0]['value'], 'X-Forwarded-For': request['headers']['x-forwarded-for'][0]['value'], 'Host': request['headers']['host'][0]['value'], # Refererが欠けた場合のKeyError発生を回避 'Referer': request['headers']['referer'][0]['value'] if 'referer' in request['headers'] else '', 'true-client-ip': request['clientIp'] } # CloudFront (自分自身) に GET リクエストをし直す try: response = http_request(f'https://{cloud_front_domain_name}{GRAPHQL_ENDPOINT}', headers=headers) # ステータスコードが4xx, 5xxのレスポンスを受け取るとurllibがエラーを起こすので例外をキャッチする。 # CloudFront にキャッシュさせないステータスコードに400番台を使用している。 # GraphQLクライアント側は CloudFront にキャッシュさせないために書き換えたステータスコードを解釈できないため、200へ書き戻す。 except urllib.error.HTTPError as e: if e.code == RESPONSE_STATUS_CODE_NO_CACHE: response = { 'status': RESPONSE_STATUS_CODE_DEFAULT, 'headers': e.headers.as_string(), 'body': e.read() } return response # GET を POST に変換する (Lambda@Edge => Origin) elif request['method'] == 'GET': print('GET') # 認証に必要なヘッダーがないものはバイパスする if not exists_required_header(request['headers']): return request # Payload0 ~ Payload4 という Header がない GET リクエストの場合はオリジンにそのまま流す if 'payload0' not in request['headers'] or \ 'payload1' not in request['headers'] or \ 'payload2' not in request['headers'] or \ 'payload3' not in request['headers'] or \ 'payload4' not in request['headers']: return request # Header から Payload の値を取得 (中身は base64 の GraphQL Query) payload = \ request['headers']['payload0'][0]['value'] + \ request['headers']['payload1'][0]['value'] + \ request['headers']['payload2'][0]['value'] + \ request['headers']['payload3'][0]['value'] + \ request['headers']['payload4'][0]['value'] # base64 の payload をデコードする data = base64.b64decode(payload) # リクエスト用のヘッダーを作成 headers = { 'Content-Type': 'application/json', 'Content-Length': len(data), 'example-auth-header': request['headers']['example-auth-header'][0]['value'], # User-Agentが欠けた場合のKeyError発生を回避 'User-Agent': request['headers']['user-agent'][0]['value'] if 'user-agent' in request['headers'] else '', 'X-Forwarded-For': request['headers']['x-forwarded-for'][0]['value'], 'Host': request['headers']['host'][0]['value'], # Refererが欠けた場合のKeyError発生を回避 'Referer': request['headers']['referer'][0]['value'] if 'referer' in request['headers'] else '', 'true-client-ip': request['headers']['true-client-ip'][0]['value'] } # Origin に POST response = http_request(f'https://{origin_domain_name}{GRAPHQL_ENDPOINT}', method='POST', data=data, headers=headers) if response['status'] == RESPONSE_STATUS_CODE_DEFAULT: try: response_body = json.loads(response['body']) if 'errors' in response_body and len(response_body['errors']) > 0: raise Exception # エラーレスポンスであってもステータスコードが200になるため、ステータスコードを書き換えないとCloudFrontにエラーレスポンスがキャッシュされるので、CloudFront がキャッシュしないステータスコードへ書き換える except: response['status'] = RESPONSE_STATUS_CODE_NO_CACHE return response # リクエストをバイパスする return request生产发布前解决的问题
我将写下在产品发布之前解决的问题,解释考虑因素。
问题一:防止缓存事故(曝光对策)
第一个问题是曝光控制。
正如我在可缓存性/不可缓存性的控制中所写,CloudFront 不会缓存满足以下任何条件的请求。
- 请求缺少身份验证所需的标头
- 在响应中包含用户私人信息的请求
- 请求正文较大,无法存储在缓存键标头中
用流程图表示,可以表示如下。
大胆的条件是防止个人信息泄露的防线。图中的第二个条件
include a non-cacheable operationName in payloads对应于红色粗体条件。GraphQL 缓存检查请求正文。如果甚至包含一个不可缓存的 GraphQL operationName,CloudFront 将从查看器接收到的 POST 请求直接发送到源,以便 CloudFront 不会对其进行缓存。
缓存启用/禁用管理以白名单格式完成。仅缓存响应为全局信息(不包含用户个人信息)的 GraphQL 操作。如果采用黑名单格式管理,可能会因为黑名单更新失败而发生缓存事故。
问题 2:平衡与实时性能
第二个问题是与实时性能的平衡。正如我在第二篇文章中所写,GraphQL 有一个 URL 端点。由于只能为 CloudFront 行为设置一种路径模式,因此 TTL 必须匹配最短的可缓存响应。
在考虑 TTL 时,问题在于定时数据的处理。响应可能包含具有过期属性的数据。 (例:限时活动信息)
可以将此类响应设置为不缓存,但我发现如果将它们排除在缓存之外,几乎没有响应可以缓存。
所以在LOWYA中,需要在享受缓存带来好处的同时兼顾实时性能,所以我们将缓存对象的TTL设置为1分钟,看看效果如何。
问题3:有必要设计获取客户端IP的方法
第三个挑战是如何让后端获得正确的客户端 IP。
随着请求源和后端之间跃点数的增加,跃点的 IP 会添加到
x-forwarded-for标头中,因此我们需要付出额外的努力才能将客户端 IP 传递给后端。如下文所述,在 ELB 前面使用 CloudFront 的应用程序需要获取位于
x-forwarded-for标头顶部的 IP 以安全地获取客户端 IP。但是,随着 2021 年 10 月的更新,CloudFront 现在支持
CloudFront-Viewer-Address标头。2通过此更新,即使对于配置为 CloudFront → ELB → AppServer 的应用程序,现在也可以更轻松地获取客户端 IP。
CloudFront-Viewer-Address标头的格式为“ClientIPAddress:ClientPort”,因此您可以通过截去冒号后面的部分来获取客户端 IP。这一次,我尝试使用
CloudFront-Viewer-Address标头在后端应用程序端获取客户端 IP。
但是,在不可缓存的情况下,我能够将客户端 IP 传递给应用程序,但在可缓存的情况下却不行。在可缓存的情况下,Origin Request 的 L@E 发出一个新的 GET 方法的 HTTP 请求。由于是 L@E 向 CloudFront 发送 GET 请求,因此
CloudFront-Viewer-Address标头中存储的值是 L@E 的 IP。ip-ranges.json AWS 发布应该属于 L@E 的 IP3当我检查时,它在下面命令提取的IP地址范围内,所以我认为它可能是区域边缘缓存的IP。
jq -r '.prefixes[] | select(.region=="ap-northeast-1" and .service=="AMAZON" and .network_border_group=="ap-northeast-1") | .ip_prefix' < ip-ranges.json | sort -n为了即使在可缓存的情况下也能在后端获取客户端 IP,在 L@E 发送 GET 请求之前,获取记录在 Lambda@Edge 的请求事件中的客户端 IP 并使用自定义的
true-client-ip我存储了标头中的值。
然后,修改后端,在以下条件下获取客户端IP。流程图如下所示:在不可缓存的情况下,
true-client-ip头没有设置,所以CloudFront-Viewer-Address头被转发到源,后端可以通过引用这个头的值来检索客户端的 IP。问题4:origin给出的header消失了
第四个问题是origin给的header消失的现象。
显然,L@E 调用了 http_request 函数并直接返回了响应,因此似乎将 origin 给出的响应标头作为 CloudFront 的行为删除(初始化)。
结果,在可缓存情况下附加到响应的源的响应标头被初始化,从而阻止客户端正确处理响应。
例如,
content-type标头丢失,导致客户端无法正确处理响应,以及 CORS 相关标头丢失,导致错误。为了解决此错误,我们使用 CloudFront 的响应标头策略为具有静态值的标头提供固定响应标头。
此外,对于具有动态值的标头,已添加与 CloudFront 的查看器响应一起使用的 CF2。
问题 5:无意缓存错误响应
第五个问题是错误响应被无意缓存了。
负缓存4有一种思维方式。负缓存意味着缓存负响应,例如在 CDN 上缓存 404 错误。
缓存应该考虑不仅缓存像 200 这样的正面响应,还应该缓存负面响应。通过使用负缓存,您可以保护您的来源免受骚扰,因为骚扰会发送大量不存在 URL 的请求。
实际上,在第一次发布时,CloudFront 缓存了源端返回的错误响应,即使在发送导致错误的请求的用户之外的环境中也会发生错误。
原因是 GraphQL 的方式。即使将请求视为错误,GraphQL 也会返回状态码为 200 的响应。
当错误被缓存时,情况是:
- 一个用户的请求导致 401 Unauthorized 错误
- 后端应用程序将错误消息存储在正文中并返回状态码为 200 的响应
- CloudFront 缓存对导致 401 Unauthorized 的请求的响应
- CloudFront 向 1 中发送请求的用户返回错误响应
- CloudFront 将 3 中创建的缓存对象返回给另一个用户,该用户发送的请求与 1 中的请求具有相同的正文内容
在这个实现中,请求正文每 1783 个字符被分割并存储在
Payload0~4标头值中,这些标头用作缓存键。
会话令牌由请求标头发送,不包含在正文中。因此,即使是其他用户(会话),如果请求正文完全相同,也会返回CloudFront返回给其他用户的错误响应。为了避免这种现象,需要重写状态码。如果在可缓存情况下从源发送错误响应,请将状态代码从 200 重写为 CloudFront 不缓存的状态代码。然后,当向查看器返回响应时,状态码会再次写回 200。回写的原因是查看器(GraphQL 客户端)端不期望带有 CloudFront 不缓存的状态代码的响应。
# CloudFront (自分自身) に GET リクエストをし直す try: response = http_request(f'https://{cloud_front_domain_name}{GRAPHQL_ENDPOINT}', headers=headers) # ステータスコードが4xx, 5xxのレスポンスを受け取るとurllibがエラーを起こすので例外をキャッチする。 # CloudFront にキャッシュさせないステータスコードに400番台を使用している。 # GraphQLクライアント側は CloudFront にキャッシュさせないために書き換えたステータスコードを解釈できないため、200へ書き戻す。 except urllib.error.HTTPError as e: if e.code == RESPONSE_STATUS_CODE_NO_CACHE: response = { 'status': RESPONSE_STATUS_CODE_DEFAULT, 'headers': e.headers.as_string(), 'body': e.read() } return response # Origin に POST response = http_request(f'https://{origin_domain_name}{GRAPHQL_ENDPOINT}', method='POST', data=data, headers=headers) if response['status'] == RESPONSE_STATUS_CODE_DEFAULT: try: response_body = json.loads(response['body']) if 'errors' in response_body and len(response_body['errors']) > 0: raise Exception # エラーレスポンスであってもステータスコードが200になるため、ステータスコードを書き換えないとCloudFrontにエラーレスポンスがキャッシュされるので、CloudFront がキャッシュしないステータスコードへ書き換える except: response['status'] = RESPONSE_STATUS_CODE_NO_CACHE return response图像如下图所示。
这次我决定将CloudFront不缓存的状态码设置为471,而是RFC免费号5我刚刚从CloudFront 具有始终缓存的 HTTP 4xx、HTTP 5xx 状态代码6,这次的策略是不使用负缓存。
引进效果
引入时存在许多问题,但 GraphQL 缓存产生了预期的结果。
更快的 Ajax 请求响应时间
对比引入 GraphQL 缓存前后从浏览器向 API 发送的 Ajax 请求的响应时间,响应时间最多减少了 52%。
LOWYA 推出了名为 New Relic Browser 的 RUM(真实用户监控)工具,以及 timeToLoadEventStart7它是使用数据来衡量的。timeToLoadEventStart
AJAX 请求开始与其加载事件开始之间的时间量(以秒为单位)。此值表示单页应用程序 (SPA) 监控的 AJAX 请求的持续时间。原产地成本降低
在撰写本文时,CloudFront 的平均缓存命中率为 26%,这意味着 4 个请求中有 1 个是缓存命中。
缓存减轻了源站的负载,这使我们能够减少 ECS 任务的数量,从而为我们节省每月的 ECS 成本。综上所述
引入 GraphQL 缓存的目的是减少站点(GraphQL 服务器)的负载,降低 AWS 成本,并在不久的将来实现可扩展性。
自从我们引入 GraphQL 缓存以来,我们还没有经历过大规模的访问集中(spike),所以我们不能说它作为负载对策的效果如何。可以肯定的是,到达的请求数量正在减少,所以我愿意期待它。8
关于AWS成本降低,我们已经实现了。
目前尚不清楚可扩展性问题是否会在不久的将来实现,但我相信我们能够采取措施。
已经很久了,但感谢您阅读到最后。如果您能帮助遇到类似问题的人,我将不胜感激。
-
过去,Mercari 的 CDN 切换工作导致个人信息泄露。
https://engineering.mercari.com/blog/entry/2017-06-22-204500/
缓存事故也作为 IPA 网站上的曝光对策引入。
https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/405.html
我会写信阻止 Masakari,但我无意通过引用 Mercari 的案例来批评 Mercari 的反应。相反,我认为这是一个很好的回应,就像一个榜样。↩ -
https://aws.amazon.com/jp/about-aws/whats-new/2021/10/amazon-cloudfront-client-ip-address-connection-port-header/ ↩
-
https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-ip-ranges.html ↩
-
我认为负缓存是一个 DNS 术语。
https://jprs.jp/glossary/index.php?ID=0177#:~:text=DNS%E3%81%AB%E3%81%8A%E3%81%84%E3%81%A6%E3%80%81%E6%A4%9C%E7%B4%A2%E5%AF%BE%E8%B1%A1%E3%81%8C,%E3%83%8D%E3%82%AC%E3%83%86%E3%82%A3%E3%83%96%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%80%8D%E3%81%A8%E5%91%BC%E3%81%B3%E3%81%BE%E3%81%99%E3%80%82
它还用于 CDN 上下文中以缓存不需要的响应。
https://cloud.google.com/cdn/docs/using-negative-caching?hl=ja ↩ -
在此处查看可用号码
https://datatracker.ietf.org/doc/html/rfc7231#section-6.5 ↩ -
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/HTTPStatusCodes.html#HTTPStatusCodes-cached-errors ↩
-
https://docs.newrelic.com/jp/attribute-dictionary/?event=AjaxRequest&attribute=timeToLoadEventStart ↩
-
随着 API 缓存层数的增加,我们必须注意一个事实,即在负载度量方面需要考虑的点更多。特别是 Lambda@Edge 不支持预置并发,必须对突发并发配额敏感。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-scaling.html ↩
-
- 使用查看器响应并将动态响应标头添加到返回给查看器的响应中。
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308628094.html