{"id":59721,"date":"2026-01-14T11:33:29","date_gmt":"2026-01-14T03:33:29","guid":{"rendered":"https:\/\/www.wsisp.com\/helps\/59721.html"},"modified":"2026-01-14T11:33:29","modified_gmt":"2026-01-14T03:33:29","slug":"http-%e7%8a%b6%e6%80%81%e7%a0%81%ef%bc%9a%e5%ae%a2%e6%88%b7%e7%ab%af%e4%b8%8e%e6%9c%8d%e5%8a%a1%e5%99%a8%e7%9a%84%e9%80%9a%e4%bf%a1%e8%af%ad%e8%a8%80-%e7%ac%ac%e5%9b%9b%e9%83%a8-2","status":"publish","type":"post","link":"https:\/\/www.wsisp.com\/helps\/59721.html","title":{"rendered":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09"},"content":{"rendered":"<h2>\u7b2c20\u7ae0&#xff1a;405 Method Not Allowed &#8211; \u65b9\u6cd5\u4e0d\u5141\u8bb8\u6df1\u5ea6\u5206\u6790<\/h2>\n<h3>20.1 \u5b9a\u4e49\u4e0e\u8bed\u4e49<\/h3>\n<p>405 Method Not Allowed\u00a0\u72b6\u6001\u7801\u8868\u793a\u8bf7\u6c42\u884c\u4e2d\u6307\u5b9a\u7684\u65b9\u6cd5\u5bf9\u8bf7\u6c42URI\u6807\u8bc6\u7684\u8d44\u6e90\u662f\u5df2\u77e5\u7684&#xff0c;\u4f46\u4e0d\u88ab\u76ee\u6807\u8d44\u6e90\u652f\u6301\u3002\u670d\u52a1\u5668\u5fc5\u987b\u751f\u6210\u4e00\u4e2a\u00a0Allow\u00a0\u54cd\u5e94\u5934\u5b57\u6bb5&#xff0c;\u5305\u542b\u76ee\u6807\u8d44\u6e90\u5f53\u524d\u652f\u6301\u7684\u65b9\u6cd5\u5217\u8868\u3002<\/p>\n<p>\u6838\u5fc3\u8bed\u4e49&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u670d\u52a1\u5668\u7406\u89e3\u8bf7\u6c42&#xff0c;\u4f46\u660e\u786e\u62d2\u7edd\u6267\u884c\u8be5\u65b9\u6cd5<\/p>\n<\/li>\n<li>\n<p>\u4e0e501&#xff08;\u672a\u5b9e\u73b0&#xff09;\u4e0d\u540c&#xff1a;405\u8868\u793a\u65b9\u6cd5\u6709\u6548&#xff0c;\u4f46\u6b64\u8d44\u6e90\u4e0d\u652f\u6301<\/p>\n<\/li>\n<li>\n<p>\u4e0e403&#xff08;\u7981\u6b62&#xff09;\u4e0d\u540c&#xff1a;405\u662f\u5173\u4e8eHTTP\u65b9\u6cd5&#xff0c;\u4e0d\u662f\u8bbf\u95ee\u6743\u9650<\/p>\n<\/li>\n<\/ul>\n<p>\u5173\u952e\u8981\u6c42&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u5fc5\u987b\u5305\u542b\u00a0Allow\u00a0\u5934\u90e8<\/p>\n<\/li>\n<li>\n<p>\u54cd\u5e94\u5e94\u8be5\u662f\u5ba2\u6237\u7aef\u53ef\u4ee5\u7406\u89e3\u7684<\/p>\n<\/li>\n<li>\n<p>\u53ef\u4ee5\u5305\u542b\u54cd\u5e94\u4f53\u89e3\u91ca\u9519\u8bef\u539f\u56e0<\/p>\n<\/li>\n<\/ul>\n<h3>20.2 \u89e6\u53d1\u573a\u666f\u4e0e\u5206\u7c7b<\/h3>\n<h4>20.2.1 \u5e38\u89c1\u7684405\u89e6\u53d1\u573a\u666f<\/h4>\n<p>python<\/p>\n<p># RESTful API\u4e2d\u7684\u5178\u578b405\u573a\u666f<br \/>\nclass RESTfulAPI:<br \/>\n    # 1. \u5bf9\u53ea\u8bfb\u8d44\u6e90\u4f7f\u7528\u5199\u65b9\u6cd5<br \/>\n    # GET \/api\/articles\/123 \u2705<br \/>\n    # PUT \/api\/articles\/123 \u274c 405<\/p>\n<p>    # 2. \u5bf9\u53ea\u5199\u8d44\u6e90\u4f7f\u7528\u8bfb\u65b9\u6cd5<br \/>\n    # POST \/api\/webhooks \u2705<br \/>\n    # GET \/api\/webhooks \u274c 405<\/p>\n<p>    # 3. \u7aef\u70b9\u4ec5\u652f\u6301\u7279\u5b9a\u65b9\u6cd5<br \/>\n    # POST \/api\/auth\/login \u2705<br \/>\n    # GET \/api\/auth\/login \u274c 405<\/p>\n<p>    # 4. \u7248\u672c\u5316API\u4e2d\u7684\u65b9\u6cd5\u5f03\u7528<br \/>\n    # PATCH \/api\/v2\/users\/123 \u2705<br \/>\n    # PUT \/api\/v2\/users\/123 \u274c 405&#xff08;PUT\u5728v2\u4e2d\u5df2\u5f03\u7528&#xff09;<\/p>\n<h4>20.2.2 \u65b9\u6cd5\u8bed\u4e49\u4e0d\u5339\u914d<\/h4>\n<p>http<\/p>\n<p># \u793a\u4f8b&#xff1a;\u5bf9\u96c6\u5408\u4f7f\u7528\u4e0d\u9002\u5f53\u7684\u65b9\u6cd5<br \/>\nPUT \/api\/users HTTP\/1.1<br \/>\nContent-Type: application\/json<br \/>\nContent-Length: 45<\/p>\n<p>{&#034;name&#034;: &#034;John&#034;, &#034;email&#034;: &#034;john&#064;example.com&#034;}<\/p>\n<p># \u54cd\u5e94 &#8211; \u5e94\u8be5\u4f7f\u7528POST\u521b\u5efa\u8d44\u6e90<br \/>\nHTTP\/1.1 405 Method Not Allowed<br \/>\nAllow: GET, POST, HEAD, OPTIONS<br \/>\nContent-Type: application\/json<\/p>\n<p>{<br \/>\n  &#034;error&#034;: {<br \/>\n    &#034;code&#034;: &#034;METHOD_NOT_ALLOWED&#034;,<br \/>\n    &#034;message&#034;: &#034;PUT method is not allowed on this collection. Use POST to create new resources.&#034;,<br \/>\n    &#034;allowed_methods&#034;: [&#034;GET&#034;, &#034;POST&#034;, &#034;HEAD&#034;, &#034;OPTIONS&#034;],<br \/>\n    &#034;suggested_method&#034;: &#034;POST&#034;<br \/>\n  }<br \/>\n}<\/p>\n<h3>20.3 \u8be6\u7ec6\u5b9e\u73b0\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h3>\n<h4>20.3.1 Allow\u5934\u90e8\u7684\u6b63\u786e\u5b9e\u73b0<\/h4>\n<p>python<\/p>\n<p># Flask\u5b9e\u73b0&#xff1a;\u6b63\u786e\u7684Allow\u5934\u90e8\u751f\u6210<br \/>\nfrom flask import Flask, request, jsonify<br \/>\nfrom functools import wraps<br \/>\nimport inspect<\/p>\n<p>class MethodAllowanceManager:<br \/>\n    def __init__(self, app):<br \/>\n        self.app &#061; app<br \/>\n        self.route_methods &#061; {}<br \/>\n        self.init_method_tracking()<\/p>\n<p>    def init_method_tracking(self):<br \/>\n        &#034;&#034;&#034;\u521d\u59cb\u5316\u65b9\u6cd5\u8ddf\u8e2a&#034;&#034;&#034;<br \/>\n        &#064;self.app.before_request<br \/>\n        def track_route():<br \/>\n            # \u5b58\u50a8\u5f53\u524d\u8def\u7531\u652f\u6301\u7684\u65b9\u6cd5<br \/>\n            if request.endpoint:<br \/>\n                rule &#061; self.app.url_map._rules_by_endpoint[request.endpoint][0]<br \/>\n                if request.endpoint not in self.route_methods:<br \/>\n                    self.route_methods[request.endpoint] &#061; set(rule.methods)<\/p>\n<p>    def get_allowed_methods(self, endpoint):<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u7aef\u70b9\u5141\u8bb8\u7684\u65b9\u6cd5&#034;&#034;&#034;<br \/>\n        if endpoint in self.route_methods:<br \/>\n            return self.route_methods[endpoint]<\/p>\n<p>        # \u5982\u679c\u6ca1\u6709\u627e\u5230&#xff0c;\u68c0\u67e5\u6240\u6709\u53ef\u80fd\u7684\u8def\u7531<br \/>\n        for rule in self.app.url_map.iter_rules():<br \/>\n            if rule.endpoint &#061;&#061; endpoint:<br \/>\n                return set(rule.methods)<\/p>\n<p>        return set()<\/p>\n<p>app &#061; Flask(__name__)<br \/>\nmethod_manager &#061; MethodAllowanceManager(app)<\/p>\n<p># \u81ea\u5b9a\u4e49405\u9519\u8bef\u5904\u7406\u5668<br \/>\n&#064;app.errorhandler(405)<br \/>\ndef handle_method_not_allowed(e):<br \/>\n    &#034;&#034;&#034;\u751f\u6210\u6b63\u786e\u7684405\u54cd\u5e94&#034;&#034;&#034;<br \/>\n    # \u786e\u5b9a\u5f53\u524d\u7aef\u70b9<br \/>\n    endpoint &#061; request.endpoint<\/p>\n<p>    # \u83b7\u53d6\u5141\u8bb8\u7684\u65b9\u6cd5<br \/>\n    allowed_methods &#061; method_manager.get_allowed_methods(endpoint)<\/p>\n<p>    # \u6392\u5e8f\u65b9\u6cd5&#xff1a;\u5e38\u7528\u65b9\u6cd5\u5728\u524d<br \/>\n    method_order &#061; [&#039;GET&#039;, &#039;POST&#039;, &#039;PUT&#039;, &#039;PATCH&#039;, &#039;DELETE&#039;, &#039;HEAD&#039;, &#039;OPTIONS&#039;]<br \/>\n    sorted_methods &#061; sorted(<br \/>\n        allowed_methods,<br \/>\n        key&#061;lambda x: method_order.index(x) if x in method_order else len(method_order)<br \/>\n    )<\/p>\n<p>    # \u751f\u6210\u54cd\u5e94<br \/>\n    response &#061; jsonify({<br \/>\n        &#034;error&#034;: {<br \/>\n            &#034;code&#034;: &#034;METHOD_NOT_ALLOWED&#034;,<br \/>\n            &#034;message&#034;: f&#034;The {request.method} method is not allowed for the requested resource.&#034;,<br \/>\n            &#034;requested_method&#034;: request.method,<br \/>\n            &#034;allowed_methods&#034;: list(sorted_methods),<br \/>\n            &#034;path&#034;: request.path<br \/>\n        }<br \/>\n    })<\/p>\n<p>    response.status_code &#061; 405<br \/>\n    response.headers[&#039;Allow&#039;] &#061; &#039;, &#039;.join(sorted_methods)<\/p>\n<p>    # \u6dfb\u52a0\u7f13\u5b58\u63a7\u5236\u5934<br \/>\n    response.headers[&#039;Cache-Control&#039;] &#061; &#039;no-store&#039;<\/p>\n<p>    return response<\/p>\n<p># \u8def\u7531\u793a\u4f8b<br \/>\n&#064;app.route(&#039;\/api\/articles&#039;, methods&#061;[&#039;GET&#039;, &#039;POST&#039;])<br \/>\ndef articles_collection():<br \/>\n    &#034;&#034;&#034;\u6587\u7ae0\u96c6\u5408 &#8211; \u652f\u6301GET\u548cPOST&#034;&#034;&#034;<br \/>\n    if request.method &#061;&#061; &#039;GET&#039;:<br \/>\n        return jsonify({&#034;articles&#034;: []})<br \/>\n    else:  # POST<br \/>\n        return jsonify({&#034;id&#034;: 1}), 201<\/p>\n<p>&#064;app.route(&#039;\/api\/articles\/&lt;int:article_id&gt;&#039;, methods&#061;[&#039;GET&#039;, &#039;PUT&#039;, &#039;DELETE&#039;])<br \/>\ndef article_detail(article_id):<br \/>\n    &#034;&#034;&#034;\u6587\u7ae0\u8be6\u60c5 &#8211; \u652f\u6301GET\u3001PUT\u3001DELETE&#034;&#034;&#034;<br \/>\n    if request.method &#061;&#061; &#039;GET&#039;:<br \/>\n        return jsonify({&#034;id&#034;: article_id, &#034;title&#034;: &#034;Sample&#034;})<br \/>\n    elif request.method &#061;&#061; &#039;PUT&#039;:<br \/>\n        return jsonify({&#034;updated&#034;: True}), 200<br \/>\n    else:  # DELETE<br \/>\n        return &#039;&#039;, 204<\/p>\n<h4>20.3.2 \u9ad8\u7ea7\u65b9\u6cd5\u9a8c\u8bc1\u4e2d\u95f4\u4ef6<\/h4>\n<p>python<\/p>\n<p># \u65b9\u6cd5\u9a8c\u8bc1\u548c\u667a\u80fd\u5efa\u8bae\u4e2d\u95f4\u4ef6<br \/>\nfrom enum import Enum<br \/>\nfrom typing import Dict, List, Optional, Set<br \/>\nfrom dataclasses import dataclass<br \/>\nimport re<\/p>\n<p>class HTTPMethod(Enum):<br \/>\n    GET &#061; &#034;GET&#034;<br \/>\n    POST &#061; &#034;POST&#034;<br \/>\n    PUT &#061; &#034;PUT&#034;<br \/>\n    PATCH &#061; &#034;PATCH&#034;<br \/>\n    DELETE &#061; &#034;DELETE&#034;<br \/>\n    HEAD &#061; &#034;HEAD&#034;<br \/>\n    OPTIONS &#061; &#034;OPTIONS&#034;<br \/>\n    TRACE &#061; &#034;TRACE&#034;<br \/>\n    CONNECT &#061; &#034;CONNECT&#034;<\/p>\n<p>&#064;dataclass<br \/>\nclass MethodRule:<br \/>\n    &#034;&#034;&#034;\u65b9\u6cd5\u89c4\u5219\u5b9a\u4e49&#034;&#034;&#034;<br \/>\n    pattern: str<br \/>\n    allowed_methods: Set[HTTPMethod]<br \/>\n    suggestions: Dict[HTTPMethod, str] &#061; None<br \/>\n    reason: str &#061; None<\/p>\n<p>class IntelligentMethodValidator:<br \/>\n    &#034;&#034;&#034;\u667a\u80fd\u65b9\u6cd5\u9a8c\u8bc1\u5668&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        # \u5b9a\u4e49RESTful\u7ea6\u5b9a<br \/>\n        self.restful_rules &#061; [<br \/>\n            # \u96c6\u5408\u7aef\u70b9<br \/>\n            MethodRule(<br \/>\n                pattern&#061;r&#039;^\/api\/([^\/]&#043;)$&#039;,<br \/>\n                allowed_methods&#061;{HTTPMethod.GET, HTTPMethod.POST, HTTPMethod.OPTIONS},<br \/>\n                suggestions&#061;{<br \/>\n                    HTTPMethod.PUT: &#034;Use POST to create resources in a collection&#034;,<br \/>\n                    HTTPMethod.DELETE: &#034;To delete a collection, use a specific resource endpoint&#034;<br \/>\n                }<br \/>\n            ),<br \/>\n            # \u8d44\u6e90\u7aef\u70b9<br \/>\n            MethodRule(<br \/>\n                pattern&#061;r&#039;^\/api\/([^\/]&#043;)\/\\\\d&#043;$&#039;,<br \/>\n                allowed_methods&#061;{HTTPMethod.GET, HTTPMethod.PUT, HTTPMethod.PATCH, HTTPMethod.DELETE, HTTPMethod.OPTIONS},<br \/>\n                suggestions&#061;{<br \/>\n                    HTTPMethod.POST: &#034;Use PUT or PATCH to update an existing resource&#034;<br \/>\n                }<br \/>\n            ),<br \/>\n            # \u5b50\u8d44\u6e90\u96c6\u5408<br \/>\n            MethodRule(<br \/>\n                pattern&#061;r&#039;^\/api\/([^\/]&#043;)\/\\\\d&#043;\/([^\/]&#043;)$&#039;,<br \/>\n                allowed_methods&#061;{HTTPMethod.GET, HTTPMethod.POST, HTTPMethod.OPTIONS},<br \/>\n                suggestions&#061;{<br \/>\n                    HTTPMethod.PUT: &#034;Use POST to create sub-resources&#034;,<br \/>\n                    HTTPMethod.DELETE: &#034;To delete a sub-resource, use its specific endpoint&#034;<br \/>\n                }<br \/>\n            ),<br \/>\n            # \u53ea\u8bfb\u7aef\u70b9<br \/>\n            MethodRule(<br \/>\n                pattern&#061;r&#039;^\/api\/reports\/.&#043;$&#039;,<br \/>\n                allowed_methods&#061;{HTTPMethod.GET, HTTPMethod.HEAD, HTTPMethod.OPTIONS},<br \/>\n                reason&#061;&#034;This is a read-only reporting endpoint&#034;<br \/>\n            )<br \/>\n        ]<\/p>\n<p>    def validate_method(self, path: str, method: HTTPMethod, allowed_methods: Set[HTTPMethod]) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u9a8c\u8bc1\u65b9\u6cd5\u5e76\u63d0\u4f9b\u667a\u80fd\u53cd\u9988&#034;&#034;&#034;<\/p>\n<p>        # \u57fa\u672c\u9a8c\u8bc1<br \/>\n        if method in allowed_methods:<br \/>\n            return {&#034;valid&#034;: True}<\/p>\n<p>        # \u83b7\u53d6\u667a\u80fd\u5efa\u8bae<br \/>\n        suggestions &#061; self.get_suggestions(path, method)<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u662f\u5e38\u89c1\u9519\u8bef<br \/>\n        common_errors &#061; self.check_common_errors(path, method)<\/p>\n<p>        return {<br \/>\n            &#034;valid&#034;: False,<br \/>\n            &#034;allowed_methods&#034;: allowed_methods,<br \/>\n            &#034;suggestions&#034;: suggestions,<br \/>\n            &#034;common_errors&#034;: common_errors,<br \/>\n            &#034;method_meaning&#034;: self.get_method_meaning(method)<br \/>\n        }<\/p>\n<p>    def get_suggestions(self, path: str, attempted_method: HTTPMethod) -&gt; List[str]:<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u9488\u5bf9\u7279\u5b9a\u8def\u5f84\u548c\u65b9\u6cd5\u7684\u5efa\u8bae&#034;&#034;&#034;<br \/>\n        suggestions &#061; []<\/p>\n<p>        for rule in self.restful_rules:<br \/>\n            if re.match(rule.pattern, path):<br \/>\n                if rule.suggestions and attempted_method in rule.suggestions:<br \/>\n                    suggestions.append(rule.suggestions[attempted_method])<br \/>\n                if rule.reason:<br \/>\n                    suggestions.append(f&#034;Reason: {rule.reason}&#034;)<\/p>\n<p>        # \u901a\u7528\u5efa\u8bae<br \/>\n        generic_suggestions &#061; {<br \/>\n            HTTPMethod.GET: [&#034;Use GET to retrieve resources&#034;],<br \/>\n            HTTPMethod.POST: [&#034;Use POST to create new resources&#034;],<br \/>\n            HTTPMethod.PUT: [&#034;Use PUT to fully replace existing resources&#034;],<br \/>\n            HTTPMethod.PATCH: [&#034;Use PATCH to partially update resources&#034;],<br \/>\n            HTTPMethod.DELETE: [&#034;Use DELETE to remove resources&#034;]<br \/>\n        }<\/p>\n<p>        if attempted_method in generic_suggestions:<br \/>\n            suggestions.extend(generic_suggestions[attempted_method])<\/p>\n<p>        return suggestions<\/p>\n<p>    def check_common_errors(self, path: str, method: HTTPMethod) -&gt; List[Dict]:<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u5e38\u89c1\u9519\u8bef\u6a21\u5f0f&#034;&#034;&#034;<br \/>\n        errors &#061; []<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u662f\u96c6\u5408\u4e0e\u8d44\u6e90\u6df7\u6dc6<br \/>\n        if method &#061;&#061; HTTPMethod.PUT and re.match(r&#039;^\/api\/([^\/]&#043;)$&#039;, path):<br \/>\n            errors.append({<br \/>\n                &#034;type&#034;: &#034;collection_resource_confusion&#034;,<br \/>\n                &#034;message&#034;: &#034;PUT is typically used for individual resources, not collections&#034;,<br \/>\n                &#034;suggestion&#034;: &#034;Use POST to create a new resource in the collection&#034;<br \/>\n            })<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u8bef\u7528PATCH<br \/>\n        if method &#061;&#061; HTTPMethod.PATCH and re.match(r&#039;^\/api\/([^\/]&#043;)$&#039;, path):<br \/>\n            errors.append({<br \/>\n                &#034;type&#034;: &#034;patch_on_collection&#034;,<br \/>\n                &#034;message&#034;: &#034;PATCH cannot be applied to collections&#034;,<br \/>\n                &#034;suggestion&#034;: &#034;Apply PATCH to specific resource URLs&#034;<br \/>\n            })<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u4f7f\u7528DELETE\u5220\u9664\u96c6\u5408<br \/>\n        if method &#061;&#061; HTTPMethod.DELETE and re.match(r&#039;^\/api\/([^\/]&#043;)$&#039;, path):<br \/>\n            errors.append({<br \/>\n                &#034;type&#034;: &#034;delete_collection&#034;,<br \/>\n                &#034;message&#034;: &#034;Collections typically don&#039;t support DELETE&#034;,<br \/>\n                &#034;suggestion&#034;: &#034;Delete individual resources instead&#034;<br \/>\n            })<\/p>\n<p>        return errors<\/p>\n<p>    def get_method_meaning(self, method: HTTPMethod) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6HTTP\u65b9\u6cd5\u7684\u8bed\u4e49\u542b\u4e49&#034;&#034;&#034;<br \/>\n        meanings &#061; {<br \/>\n            HTTPMethod.GET: {<br \/>\n                &#034;semantics&#034;: &#034;Safe, idempotent retrieval&#034;,<br \/>\n                &#034;use_case&#034;: &#034;Read data without side effects&#034;,<br \/>\n                &#034;cacheable&#034;: True<br \/>\n            },<br \/>\n            HTTPMethod.POST: {<br \/>\n                &#034;semantics&#034;: &#034;Create new resource or non-idempotent operation&#034;,<br \/>\n                &#034;use_case&#034;: &#034;Submit data, create resources&#034;,<br \/>\n                &#034;cacheable&#034;: False<br \/>\n            },<br \/>\n            HTTPMethod.PUT: {<br \/>\n                &#034;semantics&#034;: &#034;Idempotent replacement&#034;,<br \/>\n                &#034;use_case&#034;: &#034;Update entire resource&#034;,<br \/>\n                &#034;cacheable&#034;: False<br \/>\n            },<br \/>\n            HTTPMethod.PATCH: {<br \/>\n                &#034;semantics&#034;: &#034;Partial update&#034;,<br \/>\n                &#034;use_case&#034;: &#034;Update specific fields&#034;,<br \/>\n                &#034;cacheable&#034;: False<br \/>\n            },<br \/>\n            HTTPMethod.DELETE: {<br \/>\n                &#034;semantics&#034;: &#034;Remove resource&#034;,<br \/>\n                &#034;use_case&#034;: &#034;Delete existing resource&#034;,<br \/>\n                &#034;cacheable&#034;: False<br \/>\n            }<br \/>\n        }<\/p>\n<p>        return meanings.get(method, {&#034;semantics&#034;: &#034;Unknown&#034;})<\/p>\n<p># Flask\u4e2d\u95f4\u4ef6\u96c6\u6210<br \/>\nvalidator &#061; IntelligentMethodValidator()<\/p>\n<p>&#064;app.before_request<br \/>\ndef validate_http_method():<br \/>\n    &#034;&#034;&#034;\u9a8c\u8bc1HTTP\u65b9\u6cd5&#034;&#034;&#034;<br \/>\n    if request.method &#061;&#061; &#039;OPTIONS&#039;:<br \/>\n        return  # OPTIONS\u603b\u662f\u5141\u8bb8\u7684<\/p>\n<p>    # \u83b7\u53d6\u5f53\u524d\u7aef\u70b9\u5141\u8bb8\u7684\u65b9\u6cd5<br \/>\n    endpoint &#061; request.endpoint<br \/>\n    if endpoint:<br \/>\n        allowed_methods &#061; method_manager.get_allowed_methods(endpoint)<\/p>\n<p>        # \u8f6c\u6362\u4e3aHTTPMethod\u679a\u4e3e<br \/>\n        allowed_methods_enum &#061; {HTTPMethod(m) for m in allowed_methods}<br \/>\n        current_method &#061; HTTPMethod(request.method)<\/p>\n<p>        # \u9a8c\u8bc1\u65b9\u6cd5<br \/>\n        validation_result &#061; validator.validate_method(<br \/>\n            request.path,<br \/>\n            current_method,<br \/>\n            allowed_methods_enum<br \/>\n        )<\/p>\n<p>        if not validation_result[&#034;valid&#034;]:<br \/>\n            # \u521b\u5efa\u8be6\u7ec6\u7684405\u54cd\u5e94<br \/>\n            response &#061; jsonify({<br \/>\n                &#034;error&#034;: {<br \/>\n                    &#034;code&#034;: &#034;METHOD_NOT_ALLOWED&#034;,<br \/>\n                    &#034;message&#034;: f&#034;The {request.method} method is not supported for this endpoint.&#034;,<br \/>\n                    &#034;details&#034;: {<br \/>\n                        &#034;attempted_method&#034;: {<br \/>\n                            &#034;name&#034;: request.method,<br \/>\n                            &#034;semantics&#034;: validation_result[&#034;method_meaning&#034;]<br \/>\n                        },<br \/>\n                        &#034;validation_result&#034;: validation_result<br \/>\n                    }<br \/>\n                }<br \/>\n            })<\/p>\n<p>            response.status_code &#061; 405<br \/>\n            response.headers[&#039;Allow&#039;] &#061; &#039;, &#039;.join([m.value for m in allowed_methods_enum])<\/p>\n<p>            # \u6dfb\u52a0\u63cf\u8ff0\u6027\u5934\u90e8<br \/>\n            response.headers[&#039;X-Method-Validation&#039;] &#061; &#039;failed&#039;<br \/>\n            if validation_result[&#034;suggestions&#034;]:<br \/>\n                response.headers[&#039;X-Method-Suggestions&#039;] &#061; &#039;; &#039;.join(validation_result[&#034;suggestions&#034;])<\/p>\n<p>            return response<\/p>\n<h4>20.3.3 OPTIONS\u65b9\u6cd5\u7684\u5b9e\u73b0<\/h4>\n<p>python<\/p>\n<p># \u5b8c\u6574\u7684OPTIONS\u65b9\u6cd5\u5b9e\u73b0<br \/>\nclass OptionsHandler:<br \/>\n    &#034;&#034;&#034;\u5904\u7406OPTIONS\u8bf7\u6c42&#xff0c;\u63d0\u4f9bAPI\u53d1\u73b0\u529f\u80fd&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, app):<br \/>\n        self.app &#061; app<br \/>\n        self.api_documentation &#061; {}<\/p>\n<p>    def generate_options_response(self, endpoint, path):<br \/>\n        &#034;&#034;&#034;\u751f\u6210OPTIONS\u54cd\u5e94&#034;&#034;&#034;<\/p>\n<p>        # \u83b7\u53d6\u5141\u8bb8\u7684\u65b9\u6cd5<br \/>\n        allowed_methods &#061; method_manager.get_allowed_methods(endpoint)<\/p>\n<p>        # \u83b7\u53d6\u7aef\u70b9\u6587\u6863<br \/>\n        endpoint_docs &#061; self.get_endpoint_documentation(endpoint, path)<\/p>\n<p>        # \u6784\u5efa\u54cd\u5e94<br \/>\n        response_body &#061; {<br \/>\n            &#034;endpoint&#034;: endpoint,<br \/>\n            &#034;path&#034;: path,<br \/>\n            &#034;allowed_methods&#034;: list(allowed_methods),<br \/>\n            &#034;documentation&#034;: endpoint_docs,<br \/>\n            &#034;links&#034;: self.generate_links(path, allowed_methods)<br \/>\n        }<\/p>\n<p>        # \u6dfb\u52a0\u76f8\u5173\u7aef\u70b9<br \/>\n        related_endpoints &#061; self.find_related_endpoints(path)<br \/>\n        if related_endpoints:<br \/>\n            response_body[&#034;related_endpoints&#034;] &#061; related_endpoints<\/p>\n<p>        return response_body<\/p>\n<p>    def get_endpoint_documentation(self, endpoint, path):<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u7aef\u70b9\u6587\u6863&#034;&#034;&#034;<br \/>\n        if endpoint in self.api_documentation:<br \/>\n            return self.api_documentation[endpoint]<\/p>\n<p>        # \u52a8\u6001\u751f\u6210\u6587\u6863<br \/>\n        return {<br \/>\n            &#034;description&#034;: self.infer_endpoint_description(path),<br \/>\n            &#034;parameters&#034;: self.infer_parameters(path),<br \/>\n            &#034;request_format&#034;: self.infer_request_format(path),<br \/>\n            &#034;response_format&#034;: self.infer_response_format(path)<br \/>\n        }<\/p>\n<p>    def infer_endpoint_description(self, path):<br \/>\n        &#034;&#034;&#034;\u63a8\u65ad\u7aef\u70b9\u63cf\u8ff0&#034;&#034;&#034;<br \/>\n        if re.match(r&#039;^\/api\/([^\/]&#043;)$&#039;, path):<br \/>\n            resource &#061; re.match(r&#039;^\/api\/([^\/]&#043;)$&#039;, path).group(1)<br \/>\n            return f&#034;Collection of {resource} resources&#034;<br \/>\n        elif re.match(r&#039;^\/api\/([^\/]&#043;)\/\\\\d&#043;$&#039;, path):<br \/>\n            resource &#061; re.match(r&#039;^\/api\/([^\/]&#043;)\/\\\\d&#043;$&#039;, path).group(1)<br \/>\n            return f&#034;Individual {resource} resource&#034;<br \/>\n        return &#034;API endpoint&#034;<\/p>\n<p>    def infer_parameters(self, path):<br \/>\n        &#034;&#034;&#034;\u63a8\u65ad\u53c2\u6570&#034;&#034;&#034;<br \/>\n        parameters &#061; {}<\/p>\n<p>        # \u8def\u5f84\u53c2\u6570<br \/>\n        path_params &#061; re.findall(r&#039;&lt;([^&gt;]&#043;)&gt;&#039;, path)<br \/>\n        for param in path_params:<br \/>\n            param_name &#061; param.split(&#039;:&#039;)[-1] if &#039;:&#039; in param else param<br \/>\n            parameters[param_name] &#061; {<br \/>\n                &#034;in&#034;: &#034;path&#034;,<br \/>\n                &#034;required&#034;: True,<br \/>\n                &#034;type&#034;: self.infer_param_type(param)<br \/>\n            }<\/p>\n<p>        return parameters<\/p>\n<p>    def infer_param_type(self, param):<br \/>\n        &#034;&#034;&#034;\u63a8\u65ad\u53c2\u6570\u7c7b\u578b&#034;&#034;&#034;<br \/>\n        if param.startswith(&#039;int:&#039;):<br \/>\n            return &#034;integer&#034;<br \/>\n        elif param.startswith(&#039;float:&#039;):<br \/>\n            return &#034;number&#034;<br \/>\n        elif param.startswith(&#039;uuid:&#039;):<br \/>\n            return &#034;string (UUID)&#034;<br \/>\n        else:<br \/>\n            return &#034;string&#034;<\/p>\n<p>    def infer_request_format(self, path):<br \/>\n        &#034;&#034;&#034;\u63a8\u65ad\u8bf7\u6c42\u683c\u5f0f&#034;&#034;&#034;<br \/>\n        # \u6839\u636e\u8def\u5f84\u6a21\u5f0f\u63a8\u65ad<br \/>\n        if re.match(r&#039;^\/api\/([^\/]&#043;)$&#039;, path):<br \/>\n            return {<br \/>\n                &#034;POST&#034;: {<br \/>\n                    &#034;content-type&#034;: &#034;application\/json&#034;,<br \/>\n                    &#034;schema&#034;: {<br \/>\n                        &#034;type&#034;: &#034;object&#034;,<br \/>\n                        &#034;properties&#034;: {<br \/>\n                            &#034;id&#034;: {&#034;type&#034;: &#034;integer&#034;, &#034;readOnly&#034;: True},<br \/>\n                            &#034;name&#034;: {&#034;type&#034;: &#034;string&#034;},<br \/>\n                            &#034;created_at&#034;: {&#034;type&#034;: &#034;string&#034;, &#034;format&#034;: &#034;date-time&#034;, &#034;readOnly&#034;: True}<br \/>\n                        }<br \/>\n                    }<br \/>\n                }<br \/>\n            }<br \/>\n        return {}<\/p>\n<p>    def infer_response_format(self, path):<br \/>\n        &#034;&#034;&#034;\u63a8\u65ad\u54cd\u5e94\u683c\u5f0f&#034;&#034;&#034;<br \/>\n        return {<br \/>\n            &#034;application\/json&#034;: {<br \/>\n                &#034;schema&#034;: {&#034;type&#034;: &#034;object&#034;}<br \/>\n            }<br \/>\n        }<\/p>\n<p>    def generate_links(self, path, allowed_methods):<br \/>\n        &#034;&#034;&#034;\u751f\u6210HATEOAS\u94fe\u63a5&#034;&#034;&#034;<br \/>\n        links &#061; []<\/p>\n<p>        # \u81ea\u6211\u94fe\u63a5<br \/>\n        links.append({<br \/>\n            &#034;rel&#034;: &#034;self&#034;,<br \/>\n            &#034;href&#034;: path,<br \/>\n            &#034;method&#034;: &#034;GET&#034; if HTTPMethod.GET in allowed_methods else &#034;OPTIONS&#034;<br \/>\n        })<\/p>\n<p>        # \u7236\u94fe\u63a5<br \/>\n        if path !&#061; &#039;\/&#039;:<br \/>\n            parent_path &#061; &#039;\/&#039;.join(path.split(&#039;\/&#039;)[:-1]) or &#039;\/&#039;<br \/>\n            links.append({<br \/>\n                &#034;rel&#034;: &#034;parent&#034;,<br \/>\n                &#034;href&#034;: parent_path,<br \/>\n                &#034;method&#034;: &#034;GET&#034;<br \/>\n            })<\/p>\n<p>        # \u76f8\u5173\u64cd\u4f5c\u94fe\u63a5<br \/>\n        if HTTPMethod.POST in allowed_methods:<br \/>\n            links.append({<br \/>\n                &#034;rel&#034;: &#034;create&#034;,<br \/>\n                &#034;href&#034;: path,<br \/>\n                &#034;method&#034;: &#034;POST&#034;<br \/>\n            })<\/p>\n<p>        return links<\/p>\n<p>    def find_related_endpoints(self, path):<br \/>\n        &#034;&#034;&#034;\u67e5\u627e\u76f8\u5173\u7aef\u70b9&#034;&#034;&#034;<br \/>\n        related &#061; []<\/p>\n<p>        # \u67e5\u627e\u76f8\u540c\u8d44\u6e90\u7684\u4e0d\u540c\u64cd\u4f5c<br \/>\n        if re.match(r&#039;^\/api\/([^\/]&#043;)\/\\\\d&#043;$&#039;, path):<br \/>\n            # \u8fd9\u662f\u8d44\u6e90\u8be6\u60c5&#xff0c;\u76f8\u5173\u7684\u662f\u96c6\u5408<br \/>\n            collection_path &#061; re.match(r&#039;^(\/api\/[^\/]&#043;)\/\\\\d&#043;$&#039;, path).group(1)<br \/>\n            related.append({<br \/>\n                &#034;relation&#034;: &#034;collection&#034;,<br \/>\n                &#034;path&#034;: collection_path,<br \/>\n                &#034;methods&#034;: [&#034;GET&#034;, &#034;POST&#034;]<br \/>\n            })<\/p>\n<p>        return related<\/p>\n<p># \u96c6\u6210OPTIONS\u5904\u7406\u5668<br \/>\noptions_handler &#061; OptionsHandler(app)<\/p>\n<p>&#064;app.route(&#039;\/api\/&lt;path:path&gt;&#039;, methods&#061;[&#039;OPTIONS&#039;])<br \/>\ndef handle_options(path):<br \/>\n    &#034;&#034;&#034;\u5904\u7406\u6240\u6709API\u7aef\u70b9\u7684OPTIONS\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n    full_path &#061; f&#039;\/api\/{path}&#039;<\/p>\n<p>    # \u67e5\u627e\u5339\u914d\u7684\u7aef\u70b9<br \/>\n    adapter &#061; app.url_map.bind(&#039;localhost&#039;)<br \/>\n    try:<br \/>\n        endpoint, values &#061; adapter.match(full_path, method&#061;&#039;OPTIONS&#039;)<br \/>\n    except:<br \/>\n        # \u6ca1\u6709\u627e\u5230\u5339\u914d\u7684\u7aef\u70b9<br \/>\n        return jsonify({<br \/>\n            &#034;error&#034;: &#034;Endpoint not found&#034;,<br \/>\n            &#034;path&#034;: full_path<br \/>\n        }), 404<\/p>\n<p>    # \u751f\u6210OPTIONS\u54cd\u5e94<br \/>\n    response_data &#061; options_handler.generate_options_response(endpoint, full_path)<\/p>\n<p>    # \u6dfb\u52a0\u5fc5\u8981\u7684\u5934\u90e8<br \/>\n    response &#061; jsonify(response_data)<br \/>\n    response.headers[&#039;Allow&#039;] &#061; &#039;, &#039;.join(response_data[&#039;allowed_methods&#039;])<br \/>\n    response.headers[&#039;Access-Control-Allow-Methods&#039;] &#061; &#039;, &#039;.join(response_data[&#039;allowed_methods&#039;])<br \/>\n    response.headers[&#039;Access-Control-Allow-Headers&#039;] &#061; &#039;Content-Type, Authorization&#039;<br \/>\n    response.headers[&#039;Access-Control-Max-Age&#039;] &#061; &#039;86400&#039;  # 24\u5c0f\u65f6<\/p>\n<p>    return response<\/p>\n<h3>20.4 \u5ba2\u6237\u7aef\u5904\u7406\u7b56\u7565<\/h3>\n<h4>20.4.1 \u667a\u80fd\u65b9\u6cd5\u53d1\u73b0\u4e0e\u9002\u914d<\/h4>\n<p>javascript<\/p>\n<p>\/\/ \u5ba2\u6237\u7aef\u65b9\u6cd5\u53d1\u73b0\u548c\u9002\u914d\u7cfb\u7edf<br \/>\nclass MethodDiscoveryClient {<br \/>\n  constructor(baseURL, options &#061; {}) {<br \/>\n    this.baseURL &#061; baseURL;<br \/>\n    this.options &#061; {<br \/>\n      cacheDuration: 5 * 60 * 1000, \/\/ 5\u5206\u949f<br \/>\n      autoDiscover: true,<br \/>\n      maxRetries: 2,<br \/>\n      &#8230;options<br \/>\n    };<\/p>\n<p>    this.methodCache &#061; new Map(); \/\/ \u7f13\u5b58\u7aef\u70b9\u652f\u6301\u7684\u65b9\u6cd5<br \/>\n    this.schemaCache &#061; new Map(); \/\/ \u7f13\u5b58\u7aef\u70b9\u6a21\u5f0f<br \/>\n    this.fallbackStrategies &#061; new Map(); \/\/ \u5907\u7528\u7b56\u7565<br \/>\n  }<\/p>\n<p>  async discoverMethods(endpoint) {<br \/>\n    &#034;&#034;&#034;\u53d1\u73b0\u7aef\u70b9\u652f\u6301\u7684\u65b9\u6cd5&#034;&#034;&#034;<\/p>\n<p>    \/\/ \u68c0\u67e5\u7f13\u5b58<br \/>\n    const cached &#061; this.getCachedMethods(endpoint);<br \/>\n    if (cached) {<br \/>\n      return cached;<br \/>\n    }<\/p>\n<p>    try {<br \/>\n      \/\/ \u53d1\u9001OPTIONS\u8bf7\u6c42<br \/>\n      const response &#061; await this.sendOptionsRequest(endpoint);<\/p>\n<p>      if (response.status &#061;&#061;&#061; 200) {<br \/>\n        const data &#061; await response.json();<\/p>\n<p>        \/\/ \u89e3\u6790\u5141\u8bb8\u7684\u65b9\u6cd5<br \/>\n        const allowedMethods &#061; this.parseAllowedMethods(response, data);<\/p>\n<p>        \/\/ \u7f13\u5b58\u7ed3\u679c<br \/>\n        this.cacheMethods(endpoint, allowedMethods, data);<\/p>\n<p>        return {<br \/>\n          success: true,<br \/>\n          methods: allowedMethods,<br \/>\n          documentation: data<br \/>\n        };<br \/>\n      }<\/p>\n<p>      return {<br \/>\n        success: false,<br \/>\n        error: &#096;OPTIONS request failed with status ${response.status}&#096;,<br \/>\n        methods: this.getDefaultMethods(endpoint)<br \/>\n      };<\/p>\n<p>    } catch (error) {<br \/>\n      console.warn(&#096;Method discovery failed for ${endpoint}:&#096;, error);<\/p>\n<p>      return {<br \/>\n        success: false,<br \/>\n        error: error.message,<br \/>\n        methods: this.getDefaultMethods(endpoint)<br \/>\n      };<br \/>\n    }<br \/>\n  }<\/p>\n<p>  parseAllowedMethods(response, data) {<br \/>\n    &#034;&#034;&#034;\u89e3\u6790\u5141\u8bb8\u7684\u65b9\u6cd5&#034;&#034;&#034;<br \/>\n    const methods &#061; new Set();<\/p>\n<p>    \/\/ \u4eceAllow\u5934\u89e3\u6790<br \/>\n    const allowHeader &#061; response.headers.get(&#039;Allow&#039;);<br \/>\n    if (allowHeader) {<br \/>\n      allowHeader.split(&#039;,&#039;).forEach(method &#061;&gt; {<br \/>\n        methods.add(method.trim());<br \/>\n      });<br \/>\n    }<\/p>\n<p>    \/\/ \u4ece\u54cd\u5e94\u4f53\u89e3\u6790<br \/>\n    if (data &amp;&amp; data.allowed_methods) {<br \/>\n      data.allowed_methods.forEach(method &#061;&gt; methods.add(method));<br \/>\n    }<\/p>\n<p>    return Array.from(methods);<br \/>\n  }<\/p>\n<p>  async requestWithDiscovery(endpoint, options &#061; {}) {<br \/>\n    &#034;&#034;&#034;\u5e26\u6709\u65b9\u6cd5\u53d1\u73b0\u7684\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n    const { method, &#8230;restOptions } &#061; options;<\/p>\n<p>    \/\/ 1. \u53d1\u73b0\u7aef\u70b9\u652f\u6301\u7684\u65b9\u6cd5<br \/>\n    const discovery &#061; await this.discoverMethods(endpoint);<\/p>\n<p>    \/\/ 2. \u68c0\u67e5\u8bf7\u6c42\u65b9\u6cd5\u662f\u5426\u5141\u8bb8<br \/>\n    if (discovery.methods &amp;&amp; !discovery.methods.includes(method)) {<br \/>\n      \/\/ \u65b9\u6cd5\u4e0d\u5141\u8bb8&#xff0c;\u5c1d\u8bd5\u627e\u5230\u66ff\u4ee3\u65b9\u6cd5<br \/>\n      const alternative &#061; this.findAlternativeMethod(method, discovery.methods, endpoint);<\/p>\n<p>      if (alternative) {<br \/>\n        console.log(&#096;Method ${method} not allowed. Using alternative: ${alternative.method}&#096;);<\/p>\n<p>        \/\/ \u8c03\u6574\u8bf7\u6c42<br \/>\n        const adjustedOptions &#061; this.adjustRequestForAlternative(<br \/>\n          method,<br \/>\n          alternative.method,<br \/>\n          restOptions<br \/>\n        );<\/p>\n<p>        return this.fetchWithRetry(endpoint, {<br \/>\n          method: alternative.method,<br \/>\n          &#8230;adjustedOptions<br \/>\n        });<br \/>\n      }<\/p>\n<p>      \/\/ \u6ca1\u6709\u627e\u5230\u66ff\u4ee3\u65b9\u6cd5&#xff0c;\u629b\u51fa\u9519\u8bef<br \/>\n      throw new MethodNotAllowedError(endpoint, method, discovery.methods);<br \/>\n    }<\/p>\n<p>    \/\/ 3. \u6267\u884c\u8bf7\u6c42<br \/>\n    return this.fetchWithRetry(endpoint, options);<br \/>\n  }<\/p>\n<p>  findAlternativeMethod(requestedMethod, allowedMethods, endpoint) {<br \/>\n    &#034;&#034;&#034;\u67e5\u627e\u66ff\u4ee3\u65b9\u6cd5&#034;&#034;&#034;<\/p>\n<p>    \/\/ \u9884\u5b9a\u4e49\u7684\u66ff\u4ee3\u7b56\u7565<br \/>\n    const alternativeStrategies &#061; {<br \/>\n      &#039;PUT&#039;: {<br \/>\n        alternatives: [&#039;PATCH&#039;, &#039;POST&#039;],<br \/>\n        conditions: {<br \/>\n          &#039;PATCH&#039;: (endpoint) &#061;&gt; endpoint.includes(&#039;\/api\/&#039;), \/\/ \u53ea\u5bf9API\u7aef\u70b9<br \/>\n          &#039;POST&#039;: (endpoint) &#061;&gt; !endpoint.match(\/\\\\\/\\\\d&#043;$\/) \/\/ \u4e0d\u5bf9\u8d44\u6e90ID\u7aef\u70b9<br \/>\n        }<br \/>\n      },<br \/>\n      &#039;PATCH&#039;: {<br \/>\n        alternatives: [&#039;PUT&#039;],<br \/>\n        conditions: {<br \/>\n          &#039;PUT&#039;: (endpoint) &#061;&gt; true<br \/>\n        }<br \/>\n      },<br \/>\n      &#039;DELETE&#039;: {<br \/>\n        alternatives: [&#039;POST&#039;],<br \/>\n        conditions: {<br \/>\n          &#039;POST&#039;: (endpoint) &#061;&gt; endpoint.includes(&#039;\/delete&#039;) \/\/ \u7279\u6b8a\u5220\u9664\u7aef\u70b9<br \/>\n        }<br \/>\n      }<br \/>\n    };<\/p>\n<p>    const strategy &#061; alternativeStrategies[requestedMethod];<br \/>\n    if (!strategy) return null;<\/p>\n<p>    for (const alternative of strategy.alternatives) {<br \/>\n      if (allowedMethods.includes(alternative)) {<br \/>\n        const condition &#061; strategy.conditions[alternative];<br \/>\n        if (!condition || condition(endpoint)) {<br \/>\n          return {<br \/>\n            method: alternative,<br \/>\n            reason: &#096;Suggested alternative for ${requestedMethod}&#096;<br \/>\n          };<br \/>\n        }<br \/>\n      }<br \/>\n    }<\/p>\n<p>    return null;<br \/>\n  }<\/p>\n<p>  adjustRequestForAlternative(originalMethod, alternativeMethod, options) {<br \/>\n    &#034;&#034;&#034;\u4e3a\u66ff\u4ee3\u65b9\u6cd5\u8c03\u6574\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n    const adjusted &#061; { &#8230;options };<\/p>\n<p>    \/\/ \u7279\u6b8a\u8c03\u6574<br \/>\n    if (originalMethod &#061;&#061;&#061; &#039;PUT&#039; &amp;&amp; alternativeMethod &#061;&#061;&#061; &#039;PATCH&#039;) {<br \/>\n      \/\/ PUT -&gt; PATCH: \u53ef\u80fd\u9700\u8981\u8c03\u6574\u8bf7\u6c42\u4f53<br \/>\n      if (adjusted.headers) {<br \/>\n        adjusted.headers[&#039;X-Original-Method&#039;] &#061; &#039;PUT&#039;;<br \/>\n      }<br \/>\n    }<\/p>\n<p>    if (originalMethod &#061;&#061;&#061; &#039;DELETE&#039; &amp;&amp; alternativeMethod &#061;&#061;&#061; &#039;POST&#039;) {<br \/>\n      \/\/ DELETE -&gt; POST: \u6dfb\u52a0\u5220\u9664\u6307\u793a\u5668<br \/>\n      if (adjusted.body) {<br \/>\n        const body &#061; JSON.parse(adjusted.body);<br \/>\n        adjusted.body &#061; JSON.stringify({<br \/>\n          &#8230;body,<br \/>\n          _action: &#039;delete&#039;<br \/>\n        });<br \/>\n      }<br \/>\n    }<\/p>\n<p>    return adjusted;<br \/>\n  }<\/p>\n<p>  async fetchWithRetry(endpoint, options, retryCount &#061; 0) {<br \/>\n    &#034;&#034;&#034;\u5e26\u91cd\u8bd5\u7684fetch&#034;&#034;&#034;<br \/>\n    try {<br \/>\n      const response &#061; await fetch(&#096;${this.baseURL}${endpoint}&#096;, options);<\/p>\n<p>      \/\/ \u5904\u7406405\u54cd\u5e94&#xff08;\u5373\u4f7f\u5df2\u7ecf\u68c0\u67e5\u8fc7&#xff09;<br \/>\n      if (response.status &#061;&#061;&#061; 405) {<br \/>\n        if (retryCount &lt; this.options.maxRetries) {<br \/>\n          \/\/ \u6e05\u9664\u7f13\u5b58\u5e76\u91cd\u8bd5<br \/>\n          this.clearCache(endpoint);<br \/>\n          return this.requestWithDiscovery(endpoint, options);<br \/>\n        }<br \/>\n      }<\/p>\n<p>      return response;<\/p>\n<p>    } catch (error) {<br \/>\n      if (retryCount &lt; this.options.maxRetries) {<br \/>\n        \/\/ \u6307\u6570\u9000\u907f<br \/>\n        const delay &#061; Math.pow(2, retryCount) * 1000;<br \/>\n        await this.sleep(delay);<br \/>\n        return this.fetchWithRetry(endpoint, options, retryCount &#043; 1);<br \/>\n      }<br \/>\n      throw error;<br \/>\n    }<br \/>\n  }<\/p>\n<p>  getCachedMethods(endpoint) {<br \/>\n    &#034;&#034;&#034;\u83b7\u53d6\u7f13\u5b58\u7684\u65b9\u6cd5&#034;&#034;&#034;<br \/>\n    const cacheEntry &#061; this.methodCache.get(endpoint);<br \/>\n    if (cacheEntry &amp;&amp; Date.now() &#8211; cacheEntry.timestamp &lt; this.options.cacheDuration) {<br \/>\n      return cacheEntry.data;<br \/>\n    }<br \/>\n    return null;<br \/>\n  }<\/p>\n<p>  cacheMethods(endpoint, methods, documentation) {<br \/>\n    &#034;&#034;&#034;\u7f13\u5b58\u65b9\u6cd5&#034;&#034;&#034;<br \/>\n    this.methodCache.set(endpoint, {<br \/>\n      timestamp: Date.now(),<br \/>\n      data: { methods, documentation }<br \/>\n    });<br \/>\n  }<\/p>\n<p>  clearCache(endpoint &#061; null) {<br \/>\n    &#034;&#034;&#034;\u6e05\u9664\u7f13\u5b58&#034;&#034;&#034;<br \/>\n    if (endpoint) {<br \/>\n      this.methodCache.delete(endpoint);<br \/>\n    } else {<br \/>\n      this.methodCache.clear();<br \/>\n    }<br \/>\n  }<\/p>\n<p>  getDefaultMethods(endpoint) {<br \/>\n    &#034;&#034;&#034;\u83b7\u53d6\u9ed8\u8ba4\u65b9\u6cd5&#034;&#034;&#034;<br \/>\n    \/\/ \u57fa\u4e8e\u7aef\u70b9\u6a21\u5f0f\u63a8\u65ad<br \/>\n    if (endpoint.match(\/\\\\\/\\\\d&#043;$\/)) {<br \/>\n      return [&#039;GET&#039;, &#039;PUT&#039;, &#039;PATCH&#039;, &#039;DELETE&#039;]; \/\/ \u8d44\u6e90\u7aef\u70b9<br \/>\n    } else {<br \/>\n      return [&#039;GET&#039;, &#039;POST&#039;]; \/\/ \u96c6\u5408\u7aef\u70b9<br \/>\n    }<br \/>\n  }<\/p>\n<p>  sleep(ms) {<br \/>\n    return new Promise(resolve &#061;&gt; setTimeout(resolve, ms));<br \/>\n  }<br \/>\n}<\/p>\n<p>class MethodNotAllowedError extends Error {<br \/>\n  constructor(endpoint, method, allowedMethods) {<br \/>\n    super(&#096;Method ${method} not allowed for ${endpoint}&#096;);<br \/>\n    this.name &#061; &#039;MethodNotAllowedError&#039;;<br \/>\n    this.endpoint &#061; endpoint;<br \/>\n    this.method &#061; method;<br \/>\n    this.allowedMethods &#061; allowedMethods;<br \/>\n  }<br \/>\n}<\/p>\n<p>\/\/ \u4f7f\u7528\u793a\u4f8b<br \/>\nconst client &#061; new MethodDiscoveryClient(&#039;https:\/\/api.example.com&#039;);<\/p>\n<p>\/\/ \u667a\u80fd\u8bf7\u6c42<br \/>\nasync function updateUser(userId, data) {<br \/>\n  try {<br \/>\n    const response &#061; await client.requestWithDiscovery(&#096;\/api\/users\/${userId}&#096;, {<br \/>\n      method: &#039;PUT&#039;, \/\/ \u53ef\u80fd\u81ea\u52a8\u8f6c\u4e3aPATCH<br \/>\n      headers: {<br \/>\n        &#039;Content-Type&#039;: &#039;application\/json&#039;,<br \/>\n        &#039;Authorization&#039;: &#096;Bearer ${token}&#096;<br \/>\n      },<br \/>\n      body: JSON.stringify(data)<br \/>\n    });<\/p>\n<p>    if (!response.ok) {<br \/>\n      throw new Error(&#096;Request failed: ${response.status}&#096;);<br \/>\n    }<\/p>\n<p>    return await response.json();<\/p>\n<p>  } catch (error) {<br \/>\n    if (error.name &#061;&#061;&#061; &#039;MethodNotAllowedError&#039;) {<br \/>\n      \/\/ \u663e\u793a\u7528\u6237\u53cb\u597d\u7684\u9519\u8bef<br \/>\n      showMethodErrorUI(error);<br \/>\n      return null;<br \/>\n    }<br \/>\n    throw error;<br \/>\n  }<br \/>\n}<\/p>\n<p>\/\/ UI\u7ec4\u4ef6&#xff1a;\u663e\u793a\u65b9\u6cd5\u9519\u8bef<br \/>\nfunction showMethodErrorUI(error) {<br \/>\n  const container &#061; document.getElementById(&#039;error-container&#039;);<\/p>\n<p>  const html &#061; &#096;<br \/>\n    &lt;div class&#061;&#034;method-error&#034;&gt;<br \/>\n      &lt;h3&gt;\u274c Operation Not Supported&lt;\/h3&gt;<br \/>\n      &lt;p&gt;The requested operation (&lt;code&gt;${error.method}&lt;\/code&gt;) is not supported for this resource.&lt;\/p&gt;<\/p>\n<p>      &lt;div class&#061;&#034;allowed-methods&#034;&gt;<br \/>\n        &lt;h4&gt;Supported Operations:&lt;\/h4&gt;<br \/>\n        &lt;ul&gt;<br \/>\n          ${error.allowedMethods.map(method &#061;&gt; &#096;<br \/>\n            &lt;li&gt;<br \/>\n              &lt;code&gt;${method}&lt;\/code&gt;<br \/>\n              &lt;span class&#061;&#034;method-description&#034;&gt;${getMethodDescription(method)}&lt;\/span&gt;<br \/>\n            &lt;\/li&gt;<br \/>\n          &#096;).join(&#039;&#039;)}<br \/>\n        &lt;\/ul&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      &lt;div class&#061;&#034;actions&#034;&gt;<br \/>\n        &lt;button \u03bfnclick&#061;&#034;retryWithGet()&#034;&gt;View Resource&lt;\/button&gt;<br \/>\n        &lt;button \u03bfnclick&#061;&#034;showAlternativeOptions()&#034;&gt;Other Options&lt;\/button&gt;<br \/>\n        &lt;button \u03bfnclick&#061;&#034;dismissError()&#034;&gt;Dismiss&lt;\/button&gt;<br \/>\n      &lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n  &#096;;<\/p>\n<p>  container.innerHTML &#061; html;<br \/>\n}<\/p>\n<p>function getMethodDescription(method) {<br \/>\n  const descriptions &#061; {<br \/>\n    &#039;GET&#039;: &#039;Retrieve data&#039;,<br \/>\n    &#039;POST&#039;: &#039;Create new resource&#039;,<br \/>\n    &#039;PUT&#039;: &#039;Replace entire resource&#039;,<br \/>\n    &#039;PATCH&#039;: &#039;Update partial resource&#039;,<br \/>\n    &#039;DELETE&#039;: &#039;Remove resource&#039;,<br \/>\n    &#039;OPTIONS&#039;: &#039;Get supported methods&#039;<br \/>\n  };<br \/>\n  return descriptions[method] || &#039;&#039;;<br \/>\n}<\/p>\n<h3>20.5 \u76d1\u63a7\u4e0e\u5206\u6790<\/h3>\n<h4>20.5.1 405\u9519\u8bef\u5206\u6790\u7cfb\u7edf<\/h4>\n<p>python<\/p>\n<p># 405\u9519\u8bef\u76d1\u63a7\u4e0e\u5206\u6790<br \/>\nfrom datetime import datetime, timedelta<br \/>\nfrom collections import defaultdict, Counter<br \/>\nimport json<br \/>\nfrom typing import Dict, List, Any<br \/>\nfrom dataclasses import dataclass, asdict<br \/>\nimport re<\/p>\n<p>&#064;dataclass<br \/>\nclass MethodNotAllowedEvent:<br \/>\n    timestamp: datetime<br \/>\n    endpoint: str<br \/>\n    attempted_method: str<br \/>\n    allowed_methods: List[str]<br \/>\n    client_ip: str<br \/>\n    user_agent: str<br \/>\n    referer: str<br \/>\n    api_version: str &#061; None<br \/>\n    user_id: str &#061; None<br \/>\n    request_body_preview: str &#061; None<\/p>\n<p>    def to_dict(self):<br \/>\n        data &#061; asdict(self)<br \/>\n        data[&#039;timestamp&#039;] &#061; self.timestamp.isoformat()<br \/>\n        return data<\/p>\n<p>class MethodNotAllowedAnalyzer:<br \/>\n    def __init__(self):<br \/>\n        self.events &#061; []<br \/>\n        self.patterns &#061; defaultdict(lambda: {<br \/>\n            &#039;total&#039;: 0,<br \/>\n            &#039;by_method&#039;: Counter(),<br \/>\n            &#039;by_client&#039;: Counter(),<br \/>\n            &#039;by_api_version&#039;: Counter(),<br \/>\n            &#039;first_seen&#039;: None,<br \/>\n            &#039;last_seen&#039;: None<br \/>\n        })<\/p>\n<p>        # \u5e38\u89c1\u9519\u8bef\u6a21\u5f0f\u68c0\u6d4b\u5668<br \/>\n        self.error_patterns &#061; {<br \/>\n            &#039;collection_put&#039;: re.compile(r&#039;^\/api\/([^\/]&#043;)$&#039;),<br \/>\n            &#039;resource_post&#039;: re.compile(r&#039;^\/api\/([^\/]&#043;)\/\\\\d&#043;$&#039;),<br \/>\n            &#039;incorrect_http_method&#039;: re.compile(r&#039;PUT|POST|PATCH|DELETE&#039;, re.I),<br \/>\n            &#039;legacy_api&#039;: re.compile(r&#039;\/v1\/|\/legacy\/&#039;),<br \/>\n        }<\/p>\n<p>    def record_event(self, event: MethodNotAllowedEvent):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55405\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        self.events.append(event)<\/p>\n<p>        # \u66f4\u65b0\u6a21\u5f0f\u7edf\u8ba1<br \/>\n        pattern &#061; self.categorize_endpoint(event.endpoint)<br \/>\n        pattern_data &#061; self.patterns[pattern]<\/p>\n<p>        pattern_data[&#039;total&#039;] &#043;&#061; 1<br \/>\n        pattern_data[&#039;by_method&#039;][event.attempted_method] &#043;&#061; 1<br \/>\n        pattern_data[&#039;by_client&#039;][event.client_ip] &#043;&#061; 1<br \/>\n        if event.api_version:<br \/>\n            pattern_data[&#039;by_api_version&#039;][event.api_version] &#043;&#061; 1<\/p>\n<p>        pattern_data[&#039;last_seen&#039;] &#061; event.timestamp<br \/>\n        if not pattern_data[&#039;first_seen&#039;]:<br \/>\n            pattern_data[&#039;first_seen&#039;] &#061; event.timestamp<\/p>\n<p>        # \u5b9e\u65f6\u5206\u6790<br \/>\n        self.realtime_analysis(event)<\/p>\n<p>        # \u6301\u4e45\u5316<br \/>\n        self.persist_event(event)<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u9700\u8981\u8b66\u62a5<br \/>\n        self.check_for_alerts(event, pattern_data)<\/p>\n<p>    def categorize_endpoint(self, endpoint: str) -&gt; str:<br \/>\n        &#034;&#034;&#034;\u5bf9\u7aef\u70b9\u8fdb\u884c\u5206\u7c7b&#034;&#034;&#034;<br \/>\n        # \u63d0\u53d6\u6a21\u5f0f&#xff1a;\u5c06ID\u7b49\u66ff\u6362\u4e3a\u5360\u4f4d\u7b26<br \/>\n        pattern &#061; re.sub(r&#039;\/\\\\d&#043;&#039;, &#039;\/{id}&#039;, endpoint)<br \/>\n        pattern &#061; re.sub(r&#039;\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}&#039;, &#039;\/{uuid}&#039;, pattern, flags&#061;re.I)<br \/>\n        pattern &#061; re.sub(r&#039;\/[a-f0-9]{32}&#039;, &#039;\/{hash}&#039;, pattern)<\/p>\n<p>        return pattern<\/p>\n<p>    def realtime_analysis(self, event: MethodNotAllowedEvent):<br \/>\n        &#034;&#034;&#034;\u5b9e\u65f6\u5206\u6790405\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        insights &#061; []<\/p>\n<p>        # 1. \u68c0\u6d4b\u5e38\u89c1RESTful\u9519\u8bef<br \/>\n        if self.error_patterns[&#039;collection_put&#039;].match(event.endpoint) and event.attempted_method &#061;&#061; &#039;PUT&#039;:<br \/>\n            insights.append({<br \/>\n                &#039;type&#039;: &#039;collection_put_error&#039;,<br \/>\n                &#039;message&#039;: &#039;PUT method used on collection endpoint&#039;,<br \/>\n                &#039;suggestion&#039;: &#039;Use POST for creating resources in collections&#039;<br \/>\n            })<\/p>\n<p>        if self.error_patterns[&#039;resource_post&#039;].match(event.endpoint) and event.attempted_method &#061;&#061; &#039;POST&#039;:<br \/>\n            insights.append({<br \/>\n                &#039;type&#039;: &#039;resource_post_error&#039;,<br \/>\n                &#039;message&#039;: &#039;POST method used on resource endpoint&#039;,<br \/>\n                &#039;suggestion&#039;: &#039;Use PUT or PATCH for updating existing resources&#039;<br \/>\n            })<\/p>\n<p>        # 2. \u68c0\u6d4bAPI\u7248\u672c\u95ee\u9898<br \/>\n        if self.error_patterns[&#039;legacy_api&#039;].search(event.endpoint):<br \/>\n            insights.append({<br \/>\n                &#039;type&#039;: &#039;legacy_api_access&#039;,<br \/>\n                &#039;message&#039;: &#039;Access to legacy API version&#039;,<br \/>\n                &#039;suggestion&#039;: &#039;Migrate to newer API version&#039;<br \/>\n            })<\/p>\n<p>        # 3. \u68c0\u6d4b\u53ef\u80fd\u7684\u6076\u610f\u626b\u63cf<br \/>\n        if self.is_scanning_attempt(event):<br \/>\n            insights.append({<br \/>\n                &#039;type&#039;: &#039;possible_scanning&#039;,<br \/>\n                &#039;message&#039;: &#039;Possible scanning attempt detected&#039;,<br \/>\n                &#039;severity&#039;: &#039;high&#039;<br \/>\n            })<\/p>\n<p>        # \u8bb0\u5f55\u6d1e\u5bdf<br \/>\n        if insights:<br \/>\n            self.record_insights(event, insights)<\/p>\n<p>    def is_scanning_attempt(self, event: MethodNotAllowedEvent) -&gt; bool:<br \/>\n        &#034;&#034;&#034;\u68c0\u6d4b\u662f\u5426\u662f\u626b\u63cf\u5c1d\u8bd5&#034;&#034;&#034;<br \/>\n        # \u68c0\u67e5\u77ed\u65f6\u95f4\u5185\u591a\u79cd\u65b9\u6cd5\u5c1d\u8bd5<br \/>\n        recent_events &#061; self.get_recent_events(event.client_ip, minutes&#061;5)<\/p>\n<p>        if len(recent_events) &gt; 10:<br \/>\n            # \u77ed\u65f6\u95f4\u5185\u5927\u91cf405\u9519\u8bef<br \/>\n            return True<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u5c1d\u8bd5\u4e86\u5371\u9669\u65b9\u6cd5<br \/>\n        dangerous_methods &#061; [&#039;CONNECT&#039;, &#039;TRACE&#039;]<br \/>\n        if event.attempted_method in dangerous_methods:<br \/>\n            return True<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u5c1d\u8bd5\u4e86\u591a\u79cd\u65b9\u6cd5<br \/>\n        unique_methods &#061; set(e.attempted_method for e in recent_events)<br \/>\n        if len(unique_methods) &gt; 5:<br \/>\n            return True<\/p>\n<p>        return False<\/p>\n<p>    def get_recent_events(self, client_ip: str, minutes: int &#061; 5) -&gt; List[MethodNotAllowedEvent]:<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u5ba2\u6237\u7aef\u6700\u8fd1\u7684405\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        cutoff &#061; datetime.utcnow() &#8211; timedelta(minutes&#061;minutes)<br \/>\n        return [<br \/>\n            e for e in self.events<br \/>\n            if e.client_ip &#061;&#061; client_ip and e.timestamp &gt; cutoff<br \/>\n        ]<\/p>\n<p>    def check_for_alerts(self, event: MethodNotAllowedEvent, pattern_data: Dict):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u662f\u5426\u9700\u8981\u53d1\u9001\u8b66\u62a5&#034;&#034;&#034;<\/p>\n<p>        # 1. \u9ad8\u9891405\u6a21\u5f0f\u8b66\u62a5<br \/>\n        if pattern_data[&#039;total&#039;] &gt; 100:<br \/>\n            self.send_alert({<br \/>\n                &#039;type&#039;: &#039;high_frequency_405&#039;,<br \/>\n                &#039;pattern&#039;: event.endpoint,<br \/>\n                &#039;count&#039;: pattern_data[&#039;total&#039;],<br \/>\n                &#039;timestamp&#039;: datetime.utcnow().isoformat(),<br \/>\n                &#039;severity&#039;: &#039;medium&#039;<br \/>\n            })<\/p>\n<p>        # 2. \u5ba2\u6237\u7aef\u5f02\u5e38\u884c\u4e3a\u8b66\u62a5<br \/>\n        if pattern_data[&#039;by_client&#039;][event.client_ip] &gt; 20:<br \/>\n            self.send_alert({<br \/>\n                &#039;type&#039;: &#039;client_405_abuse&#039;,<br \/>\n                &#039;client_ip&#039;: event.client_ip,<br \/>\n                &#039;count&#039;: pattern_data[&#039;by_client&#039;][event.client_ip],<br \/>\n                &#039;timestamp&#039;: datetime.utcnow().isoformat(),<br \/>\n                &#039;severity&#039;: &#039;high&#039;<br \/>\n            })<\/p>\n<p>        # 3. API\u7248\u672c\u5f03\u7528\u8b66\u62a5<br \/>\n        if event.api_version and &#039;v1&#039; in event.api_version:<br \/>\n            self.send_alert({<br \/>\n                &#039;type&#039;: &#039;legacy_api_usage&#039;,<br \/>\n                &#039;endpoint&#039;: event.endpoint,<br \/>\n                &#039;api_version&#039;: event.api_version,<br \/>\n                &#039;method&#039;: event.attempted_method,<br \/>\n                &#039;timestamp&#039;: datetime.utcnow().isoformat(),<br \/>\n                &#039;severity&#039;: &#039;low&#039;<br \/>\n            })<\/p>\n<p>    def generate_report(self, period: str &#061; &#039;24h&#039;) -&gt; Dict[str, Any]:<br \/>\n        &#034;&#034;&#034;\u751f\u6210405\u5206\u6790\u62a5\u544a&#034;&#034;&#034;<br \/>\n        if period &#061;&#061; &#039;24h&#039;:<br \/>\n            cutoff &#061; datetime.utcnow() &#8211; timedelta(hours&#061;24)<br \/>\n        elif period &#061;&#061; &#039;7d&#039;:<br \/>\n            cutoff &#061; datetime.utcnow() &#8211; timedelta(days&#061;7)<br \/>\n        elif period &#061;&#061; &#039;30d&#039;:<br \/>\n            cutoff &#061; datetime.utcnow() &#8211; timedelta(days&#061;30)<br \/>\n        else:<br \/>\n            cutoff &#061; datetime.utcnow() &#8211; timedelta(hours&#061;24)<\/p>\n<p>        period_events &#061; [e for e in self.events if e.timestamp &gt; cutoff]<\/p>\n<p>        report &#061; {<br \/>\n            &#039;period&#039;: period,<br \/>\n            &#039;time_range&#039;: {<br \/>\n                &#039;start&#039;: cutoff.isoformat(),<br \/>\n                &#039;end&#039;: datetime.utcnow().isoformat()<br \/>\n            },<br \/>\n            &#039;summary&#039;: {<br \/>\n                &#039;total_405s&#039;: len(period_events),<br \/>\n                &#039;unique_endpoints&#039;: len(set(e.endpoint for e in period_events)),<br \/>\n                &#039;unique_clients&#039;: len(set(e.client_ip for e in period_events)),<br \/>\n                &#039;methods_attempted&#039;: list(set(e.attempted_method for e in period_events))<br \/>\n            },<br \/>\n            &#039;top_patterns&#039;: sorted(<br \/>\n                [(pattern, data[&#039;total&#039;]) for pattern, data in self.patterns.items()],<br \/>\n                key&#061;lambda x: x[1],<br \/>\n                reverse&#061;True<br \/>\n            )[:10],<br \/>\n            &#039;top_methods&#039;: Counter(e.attempted_method for e in period_events).most_common(),<br \/>\n            &#039;top_clients&#039;: Counter(e.client_ip for e in period_events).most_common(10),<br \/>\n            &#039;hourly_distribution&#039;: self.get_hourly_distribution(period_events),<br \/>\n            &#039;insights&#039;: self.generate_insights(period_events)<br \/>\n        }<\/p>\n<p>        return report<\/p>\n<p>    def get_hourly_distribution(self, events: List[MethodNotAllowedEvent]) -&gt; Dict[str, int]:<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u6309\u5c0f\u65f6\u5206\u5e03&#034;&#034;&#034;<br \/>\n        distribution &#061; defaultdict(int)<br \/>\n        for event in events:<br \/>\n            hour &#061; event.timestamp.strftime(&#039;%H:00&#039;)<br \/>\n            distribution[hour] &#043;&#061; 1<br \/>\n        return dict(sorted(distribution.items()))<\/p>\n<p>    def generate_insights(self, events: List[MethodNotAllowedEvent]) -&gt; List[Dict]:<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u6d1e\u5bdf\u548c\u5efa\u8bae&#034;&#034;&#034;<br \/>\n        insights &#061; []<\/p>\n<p>        if not events:<br \/>\n            return insights<\/p>\n<p>        # \u6309\u7aef\u70b9\u5206\u7ec4<br \/>\n        endpoint_groups &#061; defaultdict(list)<br \/>\n        for event in events:<br \/>\n            endpoint_groups[event.endpoint].append(event)<\/p>\n<p>        # \u5206\u6790\u6bcf\u4e2a\u9891\u7e41\u51fa\u73b0405\u7684\u7aef\u70b9<br \/>\n        for endpoint, endpoint_events in endpoint_groups.items():<br \/>\n            if len(endpoint_events) &gt; 5:  # \u9608\u503c<br \/>\n                insight &#061; self.analyze_endpoint_pattern(endpoint, endpoint_events)<br \/>\n                if insight:<br \/>\n                    insights.append(insight)<\/p>\n<p>        # \u603b\u4f53\u6d1e\u5bdf<br \/>\n        overall_insights &#061; self.analyze_overall_patterns(events)<br \/>\n        insights.extend(overall_insights)<\/p>\n<p>        return insights[:20]  # \u9650\u5236\u6570\u91cf<\/p>\n<p>    def analyze_endpoint_pattern(self, endpoint: str, events: List[MethodNotAllowedEvent]) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u5206\u6790\u7279\u5b9a\u7aef\u70b9\u7684405\u6a21\u5f0f&#034;&#034;&#034;<br \/>\n        methods_attempted &#061; Counter(e.attempted_method for e in events)<br \/>\n        clients &#061; Counter(e.client_ip for e in events)<\/p>\n<p>        insight &#061; {<br \/>\n            &#039;endpoint&#039;: endpoint,<br \/>\n            &#039;total_attempts&#039;: len(events),<br \/>\n            &#039;top_methods&#039;: methods_attempted.most_common(3),<br \/>\n            &#039;unique_clients&#039;: len(clients),<br \/>\n            &#039;suggestions&#039;: []<br \/>\n        }<\/p>\n<p>        # \u6839\u636e\u6a21\u5f0f\u751f\u6210\u5efa\u8bae<br \/>\n        if re.match(r&#039;^\/api\/([^\/]&#043;)$&#039;, endpoint):<br \/>\n            # \u96c6\u5408\u7aef\u70b9<br \/>\n            if &#039;PUT&#039; in methods_attempted:<br \/>\n                insight[&#039;suggestions&#039;].append(<br \/>\n                    &#039;Consider adding PUT support or clarifying documentation&#039;<br \/>\n                )<\/p>\n<p>        elif re.match(r&#039;^\/api\/([^\/]&#043;)\/\\\\d&#043;$&#039;, endpoint):<br \/>\n            # \u8d44\u6e90\u7aef\u70b9<br \/>\n            if &#039;POST&#039; in methods_attempted:<br \/>\n                insight[&#039;suggestions&#039;].append(<br \/>\n                    &#039;Clients are trying to POST to resource endpoints. &#039;<br \/>\n                    &#039;This might indicate confusion about RESTful conventions.&#039;<br \/>\n                )<\/p>\n<p>        # \u5982\u679c\u6709\u5927\u91cf\u4e0d\u540c\u7684\u5ba2\u6237\u7aef\u51fa\u73b0\u76f8\u540c\u9519\u8bef<br \/>\n        if len(clients) &gt; 10:<br \/>\n            insight[&#039;suggestions&#039;].append(<br \/>\n                &#039;Multiple clients are making the same error. &#039;<br \/>\n                &#039;Consider improving API documentation or client SDKs.&#039;<br \/>\n            )<\/p>\n<p>        return insight if insight[&#039;suggestions&#039;] else None<\/p>\n<p>    def persist_event(self, event: MethodNotAllowedEvent):<br \/>\n        &#034;&#034;&#034;\u6301\u4e45\u5316\u4e8b\u4ef6\u5230\u6587\u4ef6&#034;&#034;&#034;<br \/>\n        with open(&#039;405_method_errors.jsonl&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(event.to_dict()) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    def record_insights(self, event: MethodNotAllowedEvent, insights: List[Dict]):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u6d1e\u5bdf&#034;&#034;&#034;<br \/>\n        insight_record &#061; {<br \/>\n            &#039;timestamp&#039;: datetime.utcnow().isoformat(),<br \/>\n            &#039;event&#039;: event.to_dict(),<br \/>\n            &#039;insights&#039;: insights<br \/>\n        }<\/p>\n<p>        with open(&#039;405_insights.jsonl&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(insight_record) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    def send_alert(self, alert: Dict):<br \/>\n        &#034;&#034;&#034;\u53d1\u9001\u8b66\u62a5&#034;&#034;&#034;<br \/>\n        # \u8fd9\u91cc\u53ef\u4ee5\u96c6\u6210\u5404\u79cd\u901a\u77e5\u7cfb\u7edf<br \/>\n        print(f&#034;[405 Alert] {json.dumps(alert)}&#034;)<\/p>\n<p>        # \u8bb0\u5f55\u5230\u6587\u4ef6<br \/>\n        with open(&#039;405_alerts.jsonl&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(alert) &#043; &#039;\\\\n&#039;)<\/p>\n<h4>20.5.2 405\u9519\u8bef\u76d1\u63a7\u4eea\u8868\u677f<\/h4>\n<p>javascript<\/p>\n<p>\/\/ React\u7ec4\u4ef6&#xff1a;405\u76d1\u63a7\u4eea\u8868\u677f<br \/>\nimport React, { useState, useEffect } from &#039;react&#039;;<br \/>\nimport {<br \/>\n  BarChart, Bar, LineChart, Line, PieChart, Pie, Cell,<br \/>\n  XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer<br \/>\n} from &#039;recharts&#039;;<\/p>\n<p>function MethodNotAllowedDashboard() {<br \/>\n  const [timeRange, setTimeRange] &#061; useState(&#039;24h&#039;);<br \/>\n  const [stats, setStats] &#061; useState(null);<br \/>\n  const [realtimeEvents, setRealtimeEvents] &#061; useState([]);<br \/>\n  const [topPatterns, setTopPatterns] &#061; useState([]);<\/p>\n<p>  \/\/ \u83b7\u53d6\u7edf\u8ba1\u6570\u636e<br \/>\n  useEffect(() &#061;&gt; {<br \/>\n    const fetchStats &#061; async () &#061;&gt; {<br \/>\n      const response &#061; await fetch(&#096;\/api\/monitor\/405-stats?range&#061;${timeRange}&#096;);<br \/>\n      const data &#061; await response.json();<br \/>\n      setStats(data);<\/p>\n<p>      \/\/ \u5904\u7406\u6a21\u5f0f\u6570\u636e\u7528\u4e8e\u56fe\u8868<br \/>\n      if (data.top_patterns) {<br \/>\n        setTopPatterns(data.top_patterns.map(([pattern, count]) &#061;&gt; ({<br \/>\n          pattern,<br \/>\n          count,<br \/>\n          shortPattern: pattern.length &gt; 30 ? pattern.substring(0, 30) &#043; &#039;&#8230;&#039; : pattern<br \/>\n        })));<br \/>\n      }<br \/>\n    };<\/p>\n<p>    fetchStats();<br \/>\n    const interval &#061; setInterval(fetchStats, 60000); \/\/ \u6bcf\u5206\u949f\u66f4\u65b0<\/p>\n<p>    return () &#061;&gt; clearInterval(interval);<br \/>\n  }, [timeRange]);<\/p>\n<p>  \/\/ WebSocket\u8fde\u63a5\u63a5\u6536\u5b9e\u65f6\u4e8b\u4ef6<br \/>\n  useEffect(() &#061;&gt; {<br \/>\n    const ws &#061; new WebSocket(&#039;wss:\/\/api.example.com\/monitor\/405-events&#039;);<\/p>\n<p>    ws.onmessage &#061; (event) &#061;&gt; {<br \/>\n      const eventData &#061; JSON.parse(event.data);<\/p>\n<p>      setRealtimeEvents(prev &#061;&gt; [<br \/>\n        { &#8230;eventData, id: Date.now() },<br \/>\n        &#8230;prev.slice(0, 49) \/\/ \u4fdd\u7559\u6700\u8fd150\u4e2a<br \/>\n      ]);<br \/>\n    };<\/p>\n<p>    return () &#061;&gt; ws.close();<br \/>\n  }, []);<\/p>\n<p>  if (!stats) {<br \/>\n    return &lt;div className&#061;&#034;loading&#034;&gt;\u52a0\u8f7d\u4e2d&#8230;&lt;\/div&gt;;<br \/>\n  }<\/p>\n<p>  return (<br \/>\n    &lt;div className&#061;&#034;dashboard method-not-allowed-dashboard&#034;&gt;<br \/>\n      &lt;div className&#061;&#034;dashboard-header&#034;&gt;<br \/>\n        &lt;h1&gt;405 Method Not Allowed \u76d1\u63a7&lt;\/h1&gt;<br \/>\n        &lt;div className&#061;&#034;time-range-selector&#034;&gt;<br \/>\n          {[&#039;1h&#039;, &#039;24h&#039;, &#039;7d&#039;, &#039;30d&#039;].map(range &#061;&gt; (<br \/>\n            &lt;button<br \/>\n              key&#061;{range}<br \/>\n              className&#061;{timeRange &#061;&#061;&#061; range ? &#039;active&#039; : &#039;&#039;}<br \/>\n              onClick&#061;{() &#061;&gt; setTimeRange(range)}<br \/>\n            &gt;<br \/>\n              \u8fc7\u53bb{range}<br \/>\n            &lt;\/button&gt;<br \/>\n          ))}<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u6982\u89c8\u5361\u7247 *\/}<br \/>\n      &lt;div className&#061;&#034;overview-cards&#034;&gt;<br \/>\n        &lt;div className&#061;&#034;card total&#034;&gt;<br \/>\n          &lt;h3&gt;\u603b405\u9519\u8bef\u6570&lt;\/h3&gt;<br \/>\n          &lt;p className&#061;&#034;value&#034;&gt;{stats.summary.total_405s.toLocaleString()}&lt;\/p&gt;<br \/>\n          &lt;p className&#061;&#034;trend&#034;&gt;\u8fc7\u53bb24\u5c0f\u65f6&lt;\/p&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;card endpoints&#034;&gt;<br \/>\n          &lt;h3&gt;\u53d7\u5f71\u54cd\u7aef\u70b9&lt;\/h3&gt;<br \/>\n          &lt;p className&#061;&#034;value&#034;&gt;{stats.summary.unique_endpoints}&lt;\/p&gt;<br \/>\n          &lt;p className&#061;&#034;trend&#034;&gt;{topPatterns.length} \u4e2a\u4e3b\u8981\u6a21\u5f0f&lt;\/p&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;card clients&#034;&gt;<br \/>\n          &lt;h3&gt;\u5f71\u54cd\u7684\u5ba2\u6237\u7aef&lt;\/h3&gt;<br \/>\n          &lt;p className&#061;&#034;value&#034;&gt;{stats.summary.unique_clients}&lt;\/p&gt;<br \/>\n          &lt;p className&#061;&#034;trend&#034;&gt;{stats.top_clients?.[0]?.[1] || 0} \u6b21\u6765\u81ea\u6700\u6d3b\u8dc3\u5ba2\u6237\u7aef&lt;\/p&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;card methods&#034;&gt;<br \/>\n          &lt;h3&gt;\u9519\u8bef\u65b9\u6cd5&lt;\/h3&gt;<br \/>\n          &lt;p className&#061;&#034;value&#034;&gt;{stats.summary.methods_attempted.length}&lt;\/p&gt;<br \/>\n          &lt;p className&#061;&#034;trend&#034;&gt;<br \/>\n            \u6700\u5e38\u89c1: {stats.top_methods?.[0]?.[0] || &#039;N\/A&#039;}<br \/>\n          &lt;\/p&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u56fe\u8868\u533a\u57df *\/}<br \/>\n      &lt;div className&#061;&#034;charts-grid&#034;&gt;<br \/>\n        &lt;div className&#061;&#034;chart-container large&#034;&gt;<br \/>\n          &lt;h3&gt;405\u9519\u8bef\u8d8b\u52bf&lt;\/h3&gt;<br \/>\n          &lt;ResponsiveContainer width&#061;&#034;100%&#034; height&#061;{300}&gt;<br \/>\n            &lt;LineChart data&#061;{stats.hourly_distribution ?<br \/>\n              Object.entries(stats.hourly_distribution).map(([hour, count]) &#061;&gt; ({ hour, count })) : []}&gt;<br \/>\n              &lt;CartesianGrid strokeDasharray&#061;&#034;3 3&#034; \/&gt;<br \/>\n              &lt;XAxis dataKey&#061;&#034;hour&#034; \/&gt;<br \/>\n              &lt;YAxis \/&gt;<br \/>\n              &lt;Tooltip \/&gt;<br \/>\n              &lt;Legend \/&gt;<br \/>\n              &lt;Line type&#061;&#034;monotone&#034; dataKey&#061;&#034;count&#034; stroke&#061;&#034;#8884d8&#034; name&#061;&#034;405\u9519\u8bef\u6570&#034; \/&gt;<br \/>\n            &lt;\/LineChart&gt;<br \/>\n          &lt;\/ResponsiveContainer&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;chart-container&#034;&gt;<br \/>\n          &lt;h3&gt;\u9519\u8bef\u65b9\u6cd5\u5206\u5e03&lt;\/h3&gt;<br \/>\n          &lt;ResponsiveContainer width&#061;&#034;100%&#034; height&#061;{300}&gt;<br \/>\n            &lt;PieChart&gt;<br \/>\n              &lt;Pie<br \/>\n                data&#061;{stats.top_methods?.map(([method, count]) &#061;&gt; ({ method, count })) || []}<br \/>\n                cx&#061;&#034;50%&#034;<br \/>\n                cy&#061;&#034;50%&#034;<br \/>\n                labelLine&#061;{false}<br \/>\n                label&#061;{({ method, percent }) &#061;&gt; &#096;${method}: ${(percent * 100).toFixed(1)}%&#096;}<br \/>\n                outerRadius&#061;{80}<br \/>\n                fill&#061;&#034;#8884d8&#034;<br \/>\n                dataKey&#061;&#034;count&#034;<br \/>\n              &gt;<br \/>\n                {stats.top_methods?.map((entry, index) &#061;&gt; (<br \/>\n                  &lt;Cell key&#061;{&#096;cell-${index}&#096;} fill&#061;{[<br \/>\n                    &#039;#8884d8&#039;, &#039;#82ca9d&#039;, &#039;#ffc658&#039;,<br \/>\n                    &#039;#ff7300&#039;, &#039;#0088fe&#039;<br \/>\n                  ][index % 5]} \/&gt;<br \/>\n                ))}<br \/>\n              &lt;\/Pie&gt;<br \/>\n              &lt;Tooltip \/&gt;<br \/>\n            &lt;\/PieChart&gt;<br \/>\n          &lt;\/ResponsiveContainer&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;chart-container large&#034;&gt;<br \/>\n          &lt;h3&gt;\u6700\u5e38\u89c1\u7aef\u70b9\u6a21\u5f0f&lt;\/h3&gt;<br \/>\n          &lt;ResponsiveContainer width&#061;&#034;100%&#034; height&#061;{400}&gt;<br \/>\n            &lt;BarChart data&#061;{topPatterns.slice(0, 10)}&gt;<br \/>\n              &lt;CartesianGrid strokeDasharray&#061;&#034;3 3&#034; \/&gt;<br \/>\n              &lt;XAxis dataKey&#061;&#034;shortPattern&#034; angle&#061;{-45} textAnchor&#061;&#034;end&#034; height&#061;{80} \/&gt;<br \/>\n              &lt;YAxis \/&gt;<br \/>\n              &lt;Tooltip<br \/>\n                formatter&#061;{(value, name, props) &#061;&gt; [<br \/>\n                  value,<br \/>\n                  props.payload.pattern.length &gt; 30 ?<br \/>\n                    &#096;${props.payload.pattern.substring(0, 30)}&#8230;&#096; :<br \/>\n                    props.payload.pattern<br \/>\n                ]}<br \/>\n              \/&gt;<br \/>\n              &lt;Bar dataKey&#061;&#034;count&#034; fill&#061;&#034;#82ca9d&#034; name&#061;&#034;\u9519\u8bef\u6b21\u6570&#034; \/&gt;<br \/>\n            &lt;\/BarChart&gt;<br \/>\n          &lt;\/ResponsiveContainer&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;chart-container&#034;&gt;<br \/>\n          &lt;h3&gt;\u5ba2\u6237\u7aef\u5206\u5e03&lt;\/h3&gt;<br \/>\n          &lt;ResponsiveContainer width&#061;&#034;100%&#034; height&#061;{300}&gt;<br \/>\n            &lt;BarChart data&#061;{stats.top_clients?.slice(0, 5).map(([ip, count]) &#061;&gt; ({ ip, count })) || []}&gt;<br \/>\n              &lt;CartesianGrid strokeDasharray&#061;&#034;3 3&#034; \/&gt;<br \/>\n              &lt;XAxis dataKey&#061;&#034;ip&#034; \/&gt;<br \/>\n              &lt;YAxis \/&gt;<br \/>\n              &lt;Tooltip \/&gt;<br \/>\n              &lt;Bar dataKey&#061;&#034;count&#034; fill&#061;&#034;#ffc658&#034; name&#061;&#034;\u9519\u8bef\u6b21\u6570&#034; \/&gt;<br \/>\n            &lt;\/BarChart&gt;<br \/>\n          &lt;\/ResponsiveContainer&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u5b9e\u65f6\u4e8b\u4ef6\u6d41 *\/}<br \/>\n      &lt;div className&#061;&#034;realtime-events&#034;&gt;<br \/>\n        &lt;h3&gt;\u5b9e\u65f6405\u4e8b\u4ef6&lt;\/h3&gt;<br \/>\n        &lt;div className&#061;&#034;events-table&#034;&gt;<br \/>\n          &lt;table&gt;<br \/>\n            &lt;thead&gt;<br \/>\n              &lt;tr&gt;<br \/>\n                &lt;th&gt;\u65f6\u95f4&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u7aef\u70b9&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u5c1d\u8bd5\u7684\u65b9\u6cd5&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u5ba2\u6237\u7aefIP&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u5141\u8bb8\u7684\u65b9\u6cd5&lt;\/th&gt;<br \/>\n              &lt;\/tr&gt;<br \/>\n            &lt;\/thead&gt;<br \/>\n            &lt;tbody&gt;<br \/>\n              {realtimeEvents.map(event &#061;&gt; (<br \/>\n                &lt;tr key&#061;{event.id} className&#061;&#034;event-row&#034;&gt;<br \/>\n                  &lt;td&gt;{new Date(event.timestamp).toLocaleTimeString()}&lt;\/td&gt;<br \/>\n                  &lt;td className&#061;&#034;endpoint-cell&#034;&gt;<br \/>\n                    &lt;code title&#061;{event.endpoint}&gt;<br \/>\n                      {event.endpoint.length &gt; 40 ?<br \/>\n                        event.endpoint.substring(0, 40) &#043; &#039;&#8230;&#039; :<br \/>\n                        event.endpoint}<br \/>\n                    &lt;\/code&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    &lt;span className&#061;{&#096;method-badge method-${event.attempted_method.toLowerCase()}&#096;}&gt;<br \/>\n                      {event.attempted_method}<br \/>\n                    &lt;\/span&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    &lt;code&gt;{event.client_ip}&lt;\/code&gt;<br \/>\n                    {event.user_agent &amp;&amp; (<br \/>\n                      &lt;div className&#061;&#034;user-agent-tooltip&#034;&gt;<br \/>\n                        {event.user_agent}<br \/>\n                      &lt;\/div&gt;<br \/>\n                    )}<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    &lt;div className&#061;&#034;allowed-methods&#034;&gt;<br \/>\n                      {event.allowed_methods?.map(method &#061;&gt; (<br \/>\n                        &lt;span key&#061;{method} className&#061;&#034;method-tag&#034;&gt;<br \/>\n                          {method}<br \/>\n                        &lt;\/span&gt;<br \/>\n                      ))}<br \/>\n                    &lt;\/div&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                &lt;\/tr&gt;<br \/>\n              ))}<br \/>\n            &lt;\/tbody&gt;<br \/>\n          &lt;\/table&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u6d1e\u5bdf\u548c\u5efa\u8bae *\/}<br \/>\n      {stats.insights &amp;&amp; stats.insights.length &gt; 0 &amp;&amp; (<br \/>\n        &lt;div className&#061;&#034;insights-section&#034;&gt;<br \/>\n          &lt;h3&gt;\u6d1e\u5bdf\u548c\u5efa\u8bae&lt;\/h3&gt;<br \/>\n          &lt;div className&#061;&#034;insights-list&#034;&gt;<br \/>\n            {stats.insights.map((insight, index) &#061;&gt; (<br \/>\n              &lt;div key&#061;{index} className&#061;&#034;insight-card&#034;&gt;<br \/>\n                &lt;h4&gt;\u7aef\u70b9\u6a21\u5f0f: {insight.endpoint}&lt;\/h4&gt;<br \/>\n                &lt;div className&#061;&#034;insight-details&#034;&gt;<br \/>\n                  &lt;p&gt;&lt;strong&gt;\u603b\u9519\u8bef\u6570:&lt;\/strong&gt; {insight.total_attempts}&lt;\/p&gt;<br \/>\n                  &lt;p&gt;&lt;strong&gt;\u5f71\u54cd\u5ba2\u6237\u7aef:&lt;\/strong&gt; {insight.unique_clients}&lt;\/p&gt;<br \/>\n                  &lt;div className&#061;&#034;top-methods&#034;&gt;<br \/>\n                    &lt;strong&gt;\u5e38\u89c1\u9519\u8bef\u65b9\u6cd5:&lt;\/strong&gt;<br \/>\n                    &lt;div className&#061;&#034;method-list&#034;&gt;<br \/>\n                      {insight.top_methods?.map(([method, count]) &#061;&gt; (<br \/>\n                        &lt;span key&#061;{method} className&#061;&#034;method-with-count&#034;&gt;<br \/>\n                          {method} ({count})<br \/>\n                        &lt;\/span&gt;<br \/>\n                      ))}<br \/>\n                    &lt;\/div&gt;<br \/>\n                  &lt;\/div&gt;<br \/>\n                  {insight.suggestions &amp;&amp; insight.suggestions.length &gt; 0 &amp;&amp; (<br \/>\n                    &lt;div className&#061;&#034;suggestions&#034;&gt;<br \/>\n                      &lt;strong&gt;\u5efa\u8bae:&lt;\/strong&gt;<br \/>\n                      &lt;ul&gt;<br \/>\n                        {insight.suggestions.map((suggestion, i) &#061;&gt; (<br \/>\n                          &lt;li key&#061;{i}&gt;{suggestion}&lt;\/li&gt;<br \/>\n                        ))}<br \/>\n                      &lt;\/ul&gt;<br \/>\n                    &lt;\/div&gt;<br \/>\n                  )}<br \/>\n                &lt;\/div&gt;<br \/>\n              &lt;\/div&gt;<br \/>\n            ))}<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      )}<\/p>\n<p>      {\/* \u5bfc\u51fa\u548c\u64cd\u4f5c *\/}<br \/>\n      &lt;div className&#061;&#034;actions-section&#034;&gt;<br \/>\n        &lt;button<br \/>\n          className&#061;&#034;btn btn-primary&#034;<br \/>\n          onClick&#061;{() &#061;&gt; window.open(&#039;\/api\/monitor\/405-report\/export?format&#061;csv&#039;)}<br \/>\n        &gt;<br \/>\n          \u5bfc\u51faCSV\u62a5\u544a<br \/>\n        &lt;\/button&gt;<br \/>\n        &lt;button<br \/>\n          className&#061;&#034;btn btn-secondary&#034;<br \/>\n          onClick&#061;{() &#061;&gt; window.open(&#039;\/api\/monitor\/405-report\/details&#039;)}<br \/>\n        &gt;<br \/>\n          \u67e5\u770b\u8be6\u7ec6\u62a5\u544a<br \/>\n        &lt;\/button&gt;<br \/>\n        &lt;button<br \/>\n          className&#061;&#034;btn btn-tertiary&#034;<br \/>\n          onClick&#061;{() &#061;&gt; refreshDashboard()}<br \/>\n        &gt;<br \/>\n          \u5237\u65b0\u6570\u636e<br \/>\n        &lt;\/button&gt;<br \/>\n      &lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n  );<br \/>\n}<\/p>\n<h3>20.6 \u5b89\u5168\u8003\u8651<\/h3>\n<h4>20.6.1 \u9632\u6b62\u65b9\u6cd5\u679a\u4e3e\u653b\u51fb<\/h4>\n<p>python<\/p>\n<p># \u9632\u6b62HTTP\u65b9\u6cd5\u679a\u4e3e\u653b\u51fb<br \/>\nclass MethodEnumerationProtection:<br \/>\n    def __init__(self, config&#061;None):<br \/>\n        self.config &#061; config or {<br \/>\n            &#039;max_attempts_per_ip&#039;: 50,  # \u6bcf\u4e2aIP\u6700\u5927\u5c1d\u8bd5\u6b21\u6570<br \/>\n            &#039;time_window_minutes&#039;: 5,    # \u65f6\u95f4\u7a97\u53e3<br \/>\n            &#039;ban_duration_minutes&#039;: 30,  # \u5c01\u7981\u65f6\u957f<br \/>\n            &#039;sensitive_endpoints&#039;: [     # \u654f\u611f\u7aef\u70b9<br \/>\n                &#039;\/admin&#039;,<br \/>\n                &#039;\/api\/internal&#039;,<br \/>\n                &#039;\/debug&#039;,<br \/>\n                &#039;\/config&#039;<br \/>\n            ]<br \/>\n        }<\/p>\n<p>        self.attempt_tracker &#061; defaultdict(list)<br \/>\n        self.banned_ips &#061; {}<br \/>\n        self.suspicious_patterns &#061; [<br \/>\n            r&#039;\\\\.(php|asp|jsp|aspx)$&#039;,  # \u811a\u672c\u6587\u4ef6<br \/>\n            r&#039;\/(bin|etc|usr|var|opt)\/&#039;,  # \u7cfb\u7edf\u76ee\u5f55<br \/>\n            r&#039;\\\\.(bak|old|backup|tar|gz)$&#039;,  # \u5907\u4efd\u6587\u4ef6<br \/>\n        ]<\/p>\n<p>    def check_request(self, request):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u8bf7\u6c42&#xff0c;\u9632\u6b62\u65b9\u6cd5\u679a\u4e3e\u653b\u51fb&#034;&#034;&#034;<br \/>\n        client_ip &#061; request.remote_addr<br \/>\n        path &#061; request.path<br \/>\n        method &#061; request.method<\/p>\n<p>        # 1. \u68c0\u67e5\u662f\u5426\u88ab\u5c01\u7981<br \/>\n        if self.is_banned(client_ip):<br \/>\n            return {<br \/>\n                &#039;allowed&#039;: False,<br \/>\n                &#039;reason&#039;: &#039;IP temporarily banned&#039;,<br \/>\n                &#039;status&#039;: 429<br \/>\n            }<\/p>\n<p>        # 2. \u68c0\u67e5\u662f\u5426\u662f\u654f\u611f\u7aef\u70b9<br \/>\n        if self.is_sensitive_endpoint(path):<br \/>\n            # \u5bf9\u654f\u611f\u7aef\u70b9\u8fdb\u884c\u66f4\u4e25\u683c\u7684\u68c0\u67e5<br \/>\n            if not self.validate_sensitive_access(client_ip, path, method):<br \/>\n                return {<br \/>\n                    &#039;allowed&#039;: False,<br \/>\n                    &#039;reason&#039;: &#039;Sensitive endpoint access denied&#039;,<br \/>\n                    &#039;status&#039;: 403<br \/>\n                }<\/p>\n<p>        # 3. \u68c0\u67e5\u65b9\u6cd5\u679a\u4e3e\u5c1d\u8bd5<br \/>\n        if self.is_method_enumeration(client_ip, path, method):<br \/>\n            self.record_suspicious_activity(client_ip, &#039;method_enumeration&#039;, {<br \/>\n                &#039;path&#039;: path,<br \/>\n                &#039;method&#039;: method,<br \/>\n                &#039;timestamp&#039;: datetime.utcnow().isoformat()<br \/>\n            })<\/p>\n<p>            # \u89e6\u53d1\u4fdd\u62a4<br \/>\n            self.ban_ip(client_ip)<\/p>\n<p>            return {<br \/>\n                &#039;allowed&#039;: False,<br \/>\n                &#039;reason&#039;: &#039;Method enumeration detected&#039;,<br \/>\n                &#039;status&#039;: 429<br \/>\n            }<\/p>\n<p>        # 4. \u68c0\u67e5\u53ef\u7591\u8def\u5f84\u6a21\u5f0f<br \/>\n        if self.is_suspicious_path(path):<br \/>\n            self.record_suspicious_activity(client_ip, &#039;suspicious_path&#039;, {<br \/>\n                &#039;path&#039;: path,<br \/>\n                &#039;method&#039;: method,<br \/>\n                &#039;timestamp&#039;: datetime.utcnow().isoformat()<br \/>\n            })<\/p>\n<p>            # \u8fd4\u56de\u7b80\u5316\u7684405\u54cd\u5e94&#xff0c;\u4e0d\u6cc4\u9732\u4fe1\u606f<br \/>\n            return {<br \/>\n                &#039;allowed&#039;: False,<br \/>\n                &#039;reason&#039;: &#039;Suspicious request&#039;,<br \/>\n                &#039;status&#039;: 404,  # \u6545\u610f\u8fd4\u56de404\u800c\u4e0d\u662f405<br \/>\n                &#039;simplified_response&#039;: True<br \/>\n            }<\/p>\n<p>        return {&#039;allowed&#039;: True}<\/p>\n<p>    def is_banned(self, client_ip):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5IP\u662f\u5426\u88ab\u5c01\u7981&#034;&#034;&#034;<br \/>\n        if client_ip in self.banned_ips:<br \/>\n            ban_time &#061; self.banned_ips[client_ip]<br \/>\n            ban_duration &#061; timedelta(minutes&#061;self.config[&#039;ban_duration_minutes&#039;])<\/p>\n<p>            if datetime.utcnow() &#8211; ban_time &lt; ban_duration:<br \/>\n                return True<br \/>\n            else:<br \/>\n                # \u5c01\u7981\u8fc7\u671f<br \/>\n                del self.banned_ips[client_ip]<\/p>\n<p>        return False<\/p>\n<p>    def ban_ip(self, client_ip):<br \/>\n        &#034;&#034;&#034;\u5c01\u7981IP&#034;&#034;&#034;<br \/>\n        self.banned_ips[client_ip] &#061; datetime.utcnow()<\/p>\n<p>        # \u8bb0\u5f55\u5b89\u5168\u4e8b\u4ef6<br \/>\n        self.log_security_event(&#039;ip_banned&#039;, {<br \/>\n            &#039;client_ip&#039;: client_ip,<br \/>\n            &#039;reason&#039;: &#039;Method enumeration&#039;,<br \/>\n            &#039;timestamp&#039;: datetime.utcnow().isoformat(),<br \/>\n            &#039;duration_minutes&#039;: self.config[&#039;ban_duration_minutes&#039;]<br \/>\n        })<\/p>\n<p>    def is_sensitive_endpoint(self, path):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u662f\u5426\u662f\u654f\u611f\u7aef\u70b9&#034;&#034;&#034;<br \/>\n        path_lower &#061; path.lower()<br \/>\n        return any(ep in path_lower for ep in self.config[&#039;sensitive_endpoints&#039;])<\/p>\n<p>    def validate_sensitive_access(self, client_ip, path, method):<br \/>\n        &#034;&#034;&#034;\u9a8c\u8bc1\u5bf9\u654f\u611f\u7aef\u70b9\u7684\u8bbf\u95ee&#034;&#034;&#034;<br \/>\n        # \u8fd9\u91cc\u53ef\u4ee5\u5b9e\u73b0\u66f4\u590d\u6742\u7684\u903b\u8f91&#xff0c;\u5982&#xff1a;<br \/>\n        # &#8211; \u68c0\u67e5\u767d\u540d\u5355<br \/>\n        # &#8211; \u9a8c\u8bc1API\u5bc6\u94a5<br \/>\n        # &#8211; \u68c0\u67e5\u5730\u7406\u4f4d\u7f6e<\/p>\n<p>        # \u7b80\u5355\u793a\u4f8b&#xff1a;\u53ea\u5141\u8bb8GET\u548cOPTIONS\u65b9\u6cd5<br \/>\n        allowed_methods &#061; {&#039;GET&#039;, &#039;OPTIONS&#039;}<br \/>\n        return method in allowed_methods<\/p>\n<p>    def is_method_enumeration(self, client_ip, path, method):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u662f\u5426\u662f\u65b9\u6cd5\u679a\u4e3e\u5c1d\u8bd5&#034;&#034;&#034;<br \/>\n        # \u8bb0\u5f55\u5c1d\u8bd5<br \/>\n        key &#061; f&#034;{client_ip}:{path}&#034;<br \/>\n        self.attempt_tracker[key].append({<br \/>\n            &#039;method&#039;: method,<br \/>\n            &#039;timestamp&#039;: datetime.utcnow()<br \/>\n        })<\/p>\n<p>        # \u6e05\u7406\u65e7\u8bb0\u5f55<br \/>\n        cutoff &#061; datetime.utcnow() &#8211; timedelta(minutes&#061;self.config[&#039;time_window_minutes&#039;])<br \/>\n        self.attempt_tracker[key] &#061; [<br \/>\n            attempt for attempt in self.attempt_tracker[key]<br \/>\n            if attempt[&#039;timestamp&#039;] &gt; cutoff<br \/>\n        ]<\/p>\n<p>        # \u68c0\u67e5\u5c1d\u8bd5\u6b21\u6570<br \/>\n        if len(self.attempt_tracker[key]) &gt; self.config[&#039;max_attempts_per_ip&#039;]:<br \/>\n            return True<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u5c1d\u8bd5\u4e86\u591a\u79cd\u65b9\u6cd5<br \/>\n        unique_methods &#061; set(attempt[&#039;method&#039;] for attempt in self.attempt_tracker[key])<br \/>\n        if len(unique_methods) &gt; 5:  # \u5c1d\u8bd5\u4e865\u79cd\u4ee5\u4e0a\u4e0d\u540c\u65b9\u6cd5<br \/>\n            return True<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u5c1d\u8bd5\u4e86\u5371\u9669\u65b9\u6cd5<br \/>\n        dangerous_methods &#061; {&#039;CONNECT&#039;, &#039;TRACE&#039;}<br \/>\n        if any(attempt[&#039;method&#039;] in dangerous_methods for attempt in self.attempt_tracker[key]):<br \/>\n            return True<\/p>\n<p>        return False<\/p>\n<p>    def is_suspicious_path(self, path):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u662f\u5426\u662f\u53ef\u7591\u8def\u5f84&#034;&#034;&#034;<br \/>\n        path_lower &#061; path.lower()<\/p>\n<p>        # \u68c0\u67e5\u53ef\u7591\u6a21\u5f0f<br \/>\n        for pattern in self.suspicious_patterns:<br \/>\n            if re.search(pattern, path_lower):<br \/>\n                return True<\/p>\n<p>        # \u68c0\u67e5\u8def\u5f84\u904d\u5386<br \/>\n        if &#039;..&#039; in path or &#039;%2e%2e&#039; in path_lower:<br \/>\n            return True<\/p>\n<p>        # \u68c0\u67e5\u8fc7\u957f\u7684\u8def\u5f84<br \/>\n        if len(path) &gt; 500:<br \/>\n            return True<\/p>\n<p>        return False<\/p>\n<p>    def secure_405_response(self, path, method, allowed_methods, request, simplified&#061;False):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u5b89\u5168\u7684405\u54cd\u5e94&#034;&#034;&#034;<br \/>\n        client_ip &#061; request.remote_addr<\/p>\n<p>        if simplified:<br \/>\n            # \u7b80\u5316\u54cd\u5e94&#xff0c;\u4e0d\u6cc4\u9732\u4fe1\u606f<br \/>\n            response &#061; jsonify({<br \/>\n                &#039;error&#039;: {<br \/>\n                    &#039;code&#039;: &#039;NOT_FOUND&#039;,<br \/>\n                    &#039;message&#039;: &#039;Resource not found&#039;<br \/>\n                }<br \/>\n            })<br \/>\n            response.status_code &#061; 404<br \/>\n            response.headers[&#039;Allow&#039;] &#061; &#039;GET, POST&#039;  # \u901a\u7528\u5217\u8868<br \/>\n        else:<br \/>\n            # \u6b63\u5e38405\u54cd\u5e94<br \/>\n            response &#061; jsonify({<br \/>\n                &#039;error&#039;: {<br \/>\n                    &#039;code&#039;: &#039;METHOD_NOT_ALLOWED&#039;,<br \/>\n                    &#039;message&#039;: f&#039;Method {method} not allowed&#039;<br \/>\n                }<br \/>\n            })<br \/>\n            response.status_code &#061; 405<br \/>\n            response.headers[&#039;Allow&#039;] &#061; &#039;, &#039;.join(allowed_methods)<\/p>\n<p>        # \u6dfb\u52a0\u5b89\u5168\u5934\u90e8<br \/>\n        response.headers[&#039;X-Content-Type-Options&#039;] &#061; &#039;nosniff&#039;<br \/>\n        response.headers[&#039;X-Frame-Options&#039;] &#061; &#039;DENY&#039;<\/p>\n<p>        # \u8bb0\u5f55\u8bbf\u95ee&#xff08;\u5b89\u5168\u7248\u672c&#xff09;<br \/>\n        self.log_405_access(client_ip, path, method, simplified)<\/p>\n<p>        return response<\/p>\n<p>    def log_405_access(self, client_ip, path, method, simplified):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55405\u8bbf\u95ee&#xff08;\u5b89\u5168\u7248\u672c&#xff09;&#034;&#034;&#034;<br \/>\n        # \u8131\u654f\u5904\u7406<br \/>\n        safe_path &#061; self.sanitize_path(path)<\/p>\n<p>        log_entry &#061; {<br \/>\n            &#039;timestamp&#039;: datetime.utcnow().isoformat(),<br \/>\n            &#039;client_ip&#039;: client_ip[:8] &#043; &#039;&#8230;&#039;,  # \u90e8\u5206\u9690\u85cfIP<br \/>\n            &#039;path&#039;: safe_path,<br \/>\n            &#039;method&#039;: method,<br \/>\n            &#039;simplified_response&#039;: simplified<br \/>\n        }<\/p>\n<p>        with open(&#039;secure_405_logs.jsonl&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(log_entry) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    def sanitize_path(self, path):<br \/>\n        &#034;&#034;&#034;\u8131\u654f\u8def\u5f84&#xff0c;\u79fb\u9664\u654f\u611f\u4fe1\u606f&#034;&#034;&#034;<br \/>\n        # \u79fb\u9664\u67e5\u8be2\u53c2\u6570<br \/>\n        clean_path &#061; path.split(&#039;?&#039;)[0]<\/p>\n<p>        # \u66ff\u6362ID\u7b49\u654f\u611f\u4fe1\u606f<br \/>\n        clean_path &#061; re.sub(r&#039;\/\\\\d&#043;&#039;, &#039;\/{id}&#039;, clean_path)<br \/>\n        clean_path &#061; re.sub(r&#039;\/[a-f0-9]{32}&#039;, &#039;\/{hash}&#039;, clean_path, flags&#061;re.I)<\/p>\n<p>        return clean_path<\/p>\n<p>    def record_suspicious_activity(self, client_ip, activity_type, data):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u53ef\u7591\u6d3b\u52a8&#034;&#034;&#034;<br \/>\n        event &#061; {<br \/>\n            &#039;type&#039;: activity_type,<br \/>\n            &#039;client_ip&#039;: client_ip,<br \/>\n            &#039;data&#039;: data,<br \/>\n            &#039;timestamp&#039;: datetime.utcnow().isoformat()<br \/>\n        }<\/p>\n<p>        with open(&#039;security_events.jsonl&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(event) &#043; &#039;\\\\n&#039;)<\/p>\n<p>        # \u53d1\u9001\u8b66\u62a5<br \/>\n        self.send_security_alert(event)<\/p>\n<p>    def log_security_event(self, event_type, data):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u5b89\u5168\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        event &#061; {<br \/>\n            &#039;type&#039;: event_type,<br \/>\n            &#039;data&#039;: data,<br \/>\n            &#039;timestamp&#039;: datetime.utcnow().isoformat()<br \/>\n        }<\/p>\n<p>        with open(&#039;security_logs.jsonl&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(event) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    def send_security_alert(self, event):<br \/>\n        &#034;&#034;&#034;\u53d1\u9001\u5b89\u5168\u8b66\u62a5&#034;&#034;&#034;<br \/>\n        # \u8fd9\u91cc\u53ef\u4ee5\u96c6\u6210\u5404\u79cd\u901a\u77e5\u7cfb\u7edf<br \/>\n        print(f&#034;[Security Alert] {json.dumps(event)}&#034;)<\/p>\n<p>        # \u793a\u4f8b&#xff1a;\u53d1\u9001\u5230Slack<br \/>\n        if event[&#039;type&#039;] in [&#039;method_enumeration&#039;, &#039;ip_banned&#039;]:<br \/>\n            self.send_slack_alert(event)<\/p>\n<p># \u96c6\u6210\u4fdd\u62a4\u673a\u5236<br \/>\nprotection &#061; MethodEnumerationProtection()<\/p>\n<p>&#064;app.before_request<br \/>\ndef apply_method_protection():<br \/>\n    &#034;&#034;&#034;\u5e94\u7528\u65b9\u6cd5\u4fdd\u62a4&#034;&#034;&#034;<br \/>\n    if request.method not in [&#039;GET&#039;, &#039;POST&#039;, &#039;PUT&#039;, &#039;PATCH&#039;, &#039;DELETE&#039;, &#039;OPTIONS&#039;]:<br \/>\n        # \u4e0d\u5e38\u89c1\u7684\u65b9\u6cd5&#xff0c;\u7acb\u5373\u68c0\u67e5<br \/>\n        result &#061; protection.check_request(request)<br \/>\n        if not result[&#039;allowed&#039;]:<br \/>\n            if result.get(&#039;simplified_response&#039;):<br \/>\n                return protection.secure_405_response(<br \/>\n                    request.path, request.method, [], request, simplified&#061;True<br \/>\n                )<br \/>\n            else:<br \/>\n                return jsonify({&#039;error&#039;: result[&#039;reason&#039;]}), result.get(&#039;status&#039;, 403)<\/p>\n<p>&#064;app.errorhandler(405)<br \/>\ndef handle_405_with_protection(e):<br \/>\n    &#034;&#034;&#034;\u5e26\u4fdd\u62a4\u7684405\u5904\u7406&#034;&#034;&#034;<br \/>\n    # \u5148\u8fdb\u884c\u5b89\u5168\u68c0\u67e5<br \/>\n    result &#061; protection.check_request(request)<\/p>\n<p>    if not result[&#039;allowed&#039;] and result.get(&#039;simplified_response&#039;):<br \/>\n        return protection.secure_405_response(<br \/>\n            request.path, request.method, [], request, simplified&#061;True<br \/>\n        )<\/p>\n<p>    # \u6b63\u5e38405\u5904\u7406<br \/>\n    allowed_methods &#061; method_manager.get_allowed_methods(request.endpoint)<br \/>\n    return protection.secure_405_response(<br \/>\n        request.path, request.method, allowed_methods, request, simplified&#061;False<br \/>\n    )<\/p>\n<h3>20.7 \u6027\u80fd\u4f18\u5316<\/h3>\n<h4>20.7.1 \u9ad8\u6548\u7684\u65b9\u6cd5\u9a8c\u8bc1<\/h4>\n<p>python<\/p>\n<p># \u9ad8\u6548\u7684\u65b9\u6cd5\u9a8c\u8bc1\u548c\u7f13\u5b58\u7cfb\u7edf<br \/>\nfrom functools import lru_cache<br \/>\nfrom typing import Set, Tuple<br \/>\nimport time<br \/>\nimport threading<\/p>\n<p>class OptimizedMethodValidator:<br \/>\n    &#034;&#034;&#034;\u4f18\u5316\u7684\u65b9\u6cd5\u9a8c\u8bc1\u5668&#xff0c;\u5e26\u7f13\u5b58\u548c\u5e76\u53d1\u652f\u6301&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, app, cache_size&#061;1000, ttl&#061;300):<br \/>\n        self.app &#061; app<br \/>\n        self.cache_size &#061; cache_size<br \/>\n        self.ttl &#061; ttl  # \u7f13\u5b58\u751f\u5b58\u65f6\u95f4&#xff08;\u79d2&#xff09;<\/p>\n<p>        # \u7ebf\u7a0b\u5b89\u5168\u7684\u7f13\u5b58<br \/>\n        self.cache &#061; {}<br \/>\n        self.cache_timestamps &#061; {}<br \/>\n        self.cache_lock &#061; threading.Lock()<\/p>\n<p>        # \u8def\u7531\u524d\u7f00\u6811&#xff0c;\u7528\u4e8e\u5feb\u901f\u5339\u914d<br \/>\n        self.route_trie &#061; self.build_route_trie()<\/p>\n<p>        # \u70ed\u70b9\u8def\u7531\u7edf\u8ba1<br \/>\n        self.route_stats &#061; defaultdict(int)<br \/>\n        self.hot_routes &#061; set()<\/p>\n<p>    def build_route_trie(self):<br \/>\n        &#034;&#034;&#034;\u6784\u5efa\u8def\u7531\u524d\u7f00\u6811&#034;&#034;&#034;<br \/>\n        trie &#061; {}<\/p>\n<p>        for rule in self.app.url_map.iter_rules():<br \/>\n            path &#061; rule.rule<br \/>\n            methods &#061; frozenset(rule.methods)<\/p>\n<p>            # \u6784\u5efatrie<br \/>\n            current &#061; trie<br \/>\n            for segment in path.split(&#039;\/&#039;):<br \/>\n                if segment.startswith(&#039;&lt;&#039;):<br \/>\n                    # \u53c2\u6570\u6bb5&#xff0c;\u4f7f\u7528\u901a\u914d\u7b26<br \/>\n                    if &#039;*&#039; not in current:<br \/>\n                        current[&#039;*&#039;] &#061; {}<br \/>\n                    current &#061; current[&#039;*&#039;]<br \/>\n                else:<br \/>\n                    if segment not in current:<br \/>\n                        current[segment] &#061; {}<br \/>\n                    current &#061; current[segment]<\/p>\n<p>            # \u5b58\u50a8\u7aef\u70b9\u7684\u65b9\u6cd5<br \/>\n            current[&#039;__methods__&#039;] &#061; methods<br \/>\n            current[&#039;__endpoint__&#039;] &#061; rule.endpoint<\/p>\n<p>        return trie<\/p>\n<p>    def find_route_methods(self, path: str) -&gt; Tuple[str, Set[str]]:<br \/>\n        &#034;&#034;&#034;\u4f7f\u7528trie\u67e5\u627e\u8def\u7531\u548c\u65b9\u6cd5&#034;&#034;&#034;<br \/>\n        segments &#061; path.strip(&#039;\/&#039;).split(&#039;\/&#039;)<\/p>\n<p>        current &#061; self.route_trie<br \/>\n        params &#061; {}<\/p>\n<p>        for segment in segments:<br \/>\n            if segment in current:<br \/>\n                current &#061; current[segment]<br \/>\n            elif &#039;*&#039; in current:<br \/>\n                current &#061; current[&#039;*&#039;]<br \/>\n                params[segment] &#061; segment<br \/>\n            else:<br \/>\n                return None, set()<\/p>\n<p>        if &#039;__methods__&#039; in current:<br \/>\n            return current.get(&#039;__endpoint__&#039;), current[&#039;__methods__&#039;]<\/p>\n<p>        return None, set()<\/p>\n<p>    &#064;lru_cache(maxsize&#061;1000)<br \/>\n    def get_allowed_methods_cached(self, endpoint: str) -&gt; Set[str]:<br \/>\n        &#034;&#034;&#034;\u5e26\u7f13\u5b58\u7684\u65b9\u6cd5\u83b7\u53d6&#034;&#034;&#034;<br \/>\n        # \u5b9e\u9645\u67e5\u8be2\u903b\u8f91<br \/>\n        for rule in self.app.url_map.iter_rules():<br \/>\n            if rule.endpoint &#061;&#061; endpoint:<br \/>\n                return frozenset(rule.methods)<br \/>\n        return frozenset()<\/p>\n<p>    def validate_method_optimized(self, path: str, method: str) -&gt; dict:<br \/>\n        &#034;&#034;&#034;\u4f18\u5316\u7684\u65b9\u6cd5\u9a8c\u8bc1&#034;&#034;&#034;<br \/>\n        start_time &#061; time.time()<\/p>\n<p>        # \u68c0\u67e5\u7f13\u5b58<br \/>\n        cache_key &#061; (path, method)<\/p>\n<p>        with self.cache_lock:<br \/>\n            if cache_key in self.cache:<br \/>\n                cache_entry &#061; self.cache[cache_key]<br \/>\n                if time.time() &#8211; self.cache_timestamps[cache_key] &lt; self.ttl:<br \/>\n                    # \u7f13\u5b58\u547d\u4e2d<br \/>\n                    self.cache_timestamps[cache_key] &#061; time.time()<br \/>\n                    return cache_entry<\/p>\n<p>        # \u7f13\u5b58\u672a\u547d\u4e2d&#xff0c;\u4f7f\u7528trie\u67e5\u627e<br \/>\n        endpoint, allowed_methods &#061; self.find_route_methods(path)<\/p>\n<p>        if not endpoint:<br \/>\n            # \u6ca1\u6709\u627e\u5230\u8def\u7531<br \/>\n            result &#061; {<br \/>\n                &#039;valid&#039;: False,<br \/>\n                &#039;allowed_methods&#039;: {&#039;OPTIONS&#039;},<br \/>\n                &#039;reason&#039;: &#039;ROUTE_NOT_FOUND&#039;,<br \/>\n                &#039;endpoint&#039;: None<br \/>\n            }<br \/>\n        else:<br \/>\n            # \u627e\u5230\u8def\u7531&#xff0c;\u68c0\u67e5\u65b9\u6cd5<br \/>\n            is_valid &#061; method in allowed_methods<\/p>\n<p>            # \u66f4\u65b0\u8def\u7531\u7edf\u8ba1<br \/>\n            self.route_stats[endpoint] &#043;&#061; 1<br \/>\n            if self.route_stats[endpoint] &gt; 100:<br \/>\n                self.hot_routes.add(endpoint)<\/p>\n<p>            result &#061; {<br \/>\n                &#039;valid&#039;: is_valid,<br \/>\n                &#039;allowed_methods&#039;: allowed_methods,<br \/>\n                &#039;reason&#039;: &#039;METHOD_NOT_ALLOWED&#039; if not is_valid else &#039;OK&#039;,<br \/>\n                &#039;endpoint&#039;: endpoint<br \/>\n            }<\/p>\n<p>        # \u7f13\u5b58\u7ed3\u679c&#xff08;\u5982\u679c\u503c\u5f97\u7f13\u5b58&#xff09;<br \/>\n        if self.should_cache_result(result, path):<br \/>\n            with self.cache_lock:<br \/>\n                # \u6e05\u7406\u8fc7\u671f\u7f13\u5b58<br \/>\n                self.cleanup_cache()<\/p>\n<p>                # \u5b58\u50a8\u65b0\u7f13\u5b58<br \/>\n                self.cache[cache_key] &#061; result<br \/>\n                self.cache_timestamps[cache_key] &#061; time.time()<\/p>\n<p>        # \u8bb0\u5f55\u6027\u80fd\u6570\u636e<br \/>\n        elapsed &#061; time.time() &#8211; start_time<br \/>\n        if elapsed &gt; 0.01:  # \u8d85\u8fc710ms<br \/>\n            self.log_slow_validation(path, method, elapsed)<\/p>\n<p>        return result<\/p>\n<p>    def should_cache_result(self, result: dict, path: str) -&gt; bool:<br \/>\n        &#034;&#034;&#034;\u5224\u65ad\u662f\u5426\u5e94\u8be5\u7f13\u5b58\u7ed3\u679c&#034;&#034;&#034;<br \/>\n        # \u4e0d\u7f13\u5b58\u7684\u60c5\u51b5&#xff1a;<br \/>\n        # 1. \u8def\u7531\u672a\u627e\u5230<br \/>\n        if not result[&#039;endpoint&#039;]:<br \/>\n            return False<\/p>\n<p>        # 2. \u8def\u5f84\u5305\u542b\u53c2\u6570&#xff08;\u52a8\u6001\u8def\u5f84&#xff09;<br \/>\n        if re.search(r&#039;\/\\\\d&#043;|\/[a-f0-9]{32}|\/[^\/]&#043;\/[^\/]&#043;\/[^\/]&#043;&#039;, path):<br \/>\n            return False<\/p>\n<p>        # 3. \u70ed\u70b9\u8def\u7531\u603b\u662f\u7f13\u5b58<br \/>\n        if result[&#039;endpoint&#039;] in self.hot_routes:<br \/>\n            return True<\/p>\n<p>        # 4. \u9759\u6001\u8def\u5f84\u6216\u7b80\u5355API\u8def\u5f84<br \/>\n        if re.match(r&#039;^\/api\/([^\/]&#043;)$&#039;, path) or re.match(r&#039;^\/static\/&#039;, path):<br \/>\n            return True<\/p>\n<p>        return False<\/p>\n<p>    def cleanup_cache(self):<br \/>\n        &#034;&#034;&#034;\u6e05\u7406\u8fc7\u671f\u7f13\u5b58&#034;&#034;&#034;<br \/>\n        if len(self.cache) &lt; self.cache_size * 1.5:<br \/>\n            return  # \u7f13\u5b58\u672a\u6ee1&#xff0c;\u4e0d\u9700\u8981\u6e05\u7406<\/p>\n<p>        current_time &#061; time.time()<br \/>\n        expired_keys &#061; []<\/p>\n<p>        for key, timestamp in self.cache_timestamps.items():<br \/>\n            if current_time &#8211; timestamp &gt; self.ttl:<br \/>\n                expired_keys.append(key)<\/p>\n<p>        # \u79fb\u9664\u8fc7\u671f\u7f13\u5b58<br \/>\n        for key in expired_keys:<br \/>\n            self.cache.pop(key, None)<br \/>\n            self.cache_timestamps.pop(key, None)<\/p>\n<p>        # \u5982\u679c\u4ecd\u7136\u592a\u5927&#xff0c;\u79fb\u9664\u6700\u65e7\u7684<br \/>\n        if len(self.cache) &gt; self.cache_size:<br \/>\n            sorted_keys &#061; sorted(<br \/>\n                self.cache_timestamps.items(),<br \/>\n                key&#061;lambda x: x[1]<br \/>\n            )[:len(self.cache) &#8211; self.cache_size]<\/p>\n<p>            for key, _ in sorted_keys:<br \/>\n                self.cache.pop(key, None)<br \/>\n                self.cache_timestamps.pop(key, None)<\/p>\n<p>    def log_slow_validation(self, path: str, method: str, elapsed: float):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u6162\u901f\u9a8c\u8bc1&#034;&#034;&#034;<br \/>\n        log_entry &#061; {<br \/>\n            &#039;timestamp&#039;: datetime.utcnow().isoformat(),<br \/>\n            &#039;path&#039;: path,<br \/>\n            &#039;method&#039;: method,<br \/>\n            &#039;elapsed_ms&#039;: round(elapsed * 1000, 2),<br \/>\n            &#039;type&#039;: &#039;slow_method_validation&#039;<br \/>\n        }<\/p>\n<p>        with open(&#039;performance_logs.jsonl&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(log_entry) &#043; &#039;\\\\n&#039;)<\/p>\n<p>        # \u5982\u679c\u7279\u522b\u6162&#xff0c;\u53d1\u9001\u8b66\u62a5<br \/>\n        if elapsed &gt; 0.1:  # \u8d85\u8fc7100ms<br \/>\n            self.send_performance_alert(log_entry)<\/p>\n<p>    def get_performance_report(self) -&gt; dict:<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u6027\u80fd\u62a5\u544a&#034;&#034;&#034;<br \/>\n        with self.cache_lock:<br \/>\n            cache_stats &#061; {<br \/>\n                &#039;total_cached&#039;: len(self.cache),<br \/>\n                &#039;cache_hits&#039;: getattr(self, &#039;cache_hits&#039;, 0),<br \/>\n                &#039;cache_misses&#039;: getattr(self, &#039;cache_misses&#039;, 0),<br \/>\n                &#039;hot_routes&#039;: len(self.hot_routes)<br \/>\n            }<\/p>\n<p>        # \u8ba1\u7b97\u547d\u4e2d\u7387<br \/>\n        total &#061; cache_stats[&#039;cache_hits&#039;] &#043; cache_stats[&#039;cache_misses&#039;]<br \/>\n        cache_stats[&#039;hit_rate&#039;] &#061; cache_stats[&#039;cache_hits&#039;] \/ total if total &gt; 0 else 0<\/p>\n<p>        return {<br \/>\n            &#039;cache&#039;: cache_stats,<br \/>\n            &#039;route_stats&#039;: dict(sorted(<br \/>\n                self.route_stats.items(),<br \/>\n                key&#061;lambda x: x[1],<br \/>\n                reverse&#061;True<br \/>\n            )[:10])<br \/>\n        }<\/p>\n<p>    def send_performance_alert(self, log_entry: dict):<br \/>\n        &#034;&#034;&#034;\u53d1\u9001\u6027\u80fd\u8b66\u62a5&#034;&#034;&#034;<br \/>\n        alert &#061; {<br \/>\n            &#039;type&#039;: &#039;slow_method_validation&#039;,<br \/>\n            &#039;data&#039;: log_entry,<br \/>\n            &#039;timestamp&#039;: datetime.utcnow().isoformat(),<br \/>\n            &#039;severity&#039;: &#039;warning&#039;<br \/>\n        }<\/p>\n<p>        print(f&#034;[Performance Alert] {json.dumps(alert)}&#034;)<\/p>\n<p># \u96c6\u6210\u4f18\u5316\u7684\u9a8c\u8bc1\u5668<br \/>\noptimized_validator &#061; OptimizedMethodValidator(app)<\/p>\n<p>&#064;app.before_request<br \/>\ndef fast_method_validation():<br \/>\n    &#034;&#034;&#034;\u5feb\u901f\u65b9\u6cd5\u9a8c\u8bc1\u4e2d\u95f4\u4ef6&#034;&#034;&#034;<br \/>\n    # \u8df3\u8fc7OPTIONS\u548cHEAD\u65b9\u6cd5<br \/>\n    if request.method in [&#039;OPTIONS&#039;, &#039;HEAD&#039;]:<br \/>\n        return<\/p>\n<p>    # \u4f7f\u7528\u4f18\u5316\u9a8c\u8bc1\u5668<br \/>\n    validation_result &#061; optimized_validator.validate_method_optimized(<br \/>\n        request.path,<br \/>\n        request.method<br \/>\n    )<\/p>\n<p>    if not validation_result[&#039;valid&#039;]:<br \/>\n        # \u7acb\u5373\u8fd4\u56de405&#xff0c;\u907f\u514d\u8fdb\u5165\u89c6\u56fe\u51fd\u6570<br \/>\n        response &#061; jsonify({<br \/>\n            &#039;error&#039;: {<br \/>\n                &#039;code&#039;: &#039;METHOD_NOT_ALLOWED&#039;,<br \/>\n                &#039;message&#039;: f&#039;Method {request.method} is not allowed for this endpoint&#039;,<br \/>\n                &#039;allowed_methods&#039;: list(validation_result[&#039;allowed_methods&#039;])<br \/>\n            }<br \/>\n        })<\/p>\n<p>        response.status_code &#061; 405<br \/>\n        response.headers[&#039;Allow&#039;] &#061; &#039;, &#039;.join(validation_result[&#039;allowed_methods&#039;])<\/p>\n<p>        return response<\/p>\n<p># \u6027\u80fd\u76d1\u63a7\u7aef\u70b9<br \/>\n&#064;app.route(&#039;\/api\/performance\/method-validation&#039;)<br \/>\ndef get_method_validation_stats():<br \/>\n    &#034;&#034;&#034;\u83b7\u53d6\u65b9\u6cd5\u9a8c\u8bc1\u6027\u80fd\u7edf\u8ba1&#034;&#034;&#034;<br \/>\n    report &#061; optimized_validator.get_performance_report()<br \/>\n    return jsonify(report)<\/p>\n<h3>20.8 \u7279\u6b8a\u573a\u666f\u5904\u7406<\/h3>\n<h4>20.8.1 API\u7248\u672c\u8fc1\u79fb\u7684\u65b9\u6cd5\u5904\u7406<\/h4>\n<p>python<\/p>\n<p># API\u7248\u672c\u8fc1\u79fb\u4e2d\u7684\u65b9\u6cd5\u5904\u7406<br \/>\nclass APIVersionMigrationHandler:<br \/>\n    &#034;&#034;&#034;\u5904\u7406API\u7248\u672c\u8fc1\u79fb\u4e2d\u7684\u65b9\u6cd5\u53d8\u5316&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.version_mappings &#061; {<br \/>\n            &#039;v1&#039;: {<br \/>\n                &#039;deprecated_methods&#039;: {<br \/>\n                    &#039;PUT&#039;: {<br \/>\n                        &#039;since&#039;: &#039;2024-01-01&#039;,<br \/>\n                        &#039;alternative&#039;: &#039;PATCH&#039;,<br \/>\n                        &#039;sunset&#039;: &#039;2025-01-01&#039;<br \/>\n                    },<br \/>\n                    &#039;POST&#039;: {<br \/>\n                        &#039;since&#039;: &#039;2024-03-01&#039;,<br \/>\n                        &#039;alternative&#039;: &#039;PUT&#039;,<br \/>\n                        &#039;contexts&#039;: [&#039;\/api\/v1\/users\/&lt;id&gt;\/permissions&#039;]<br \/>\n                    }<br \/>\n                },<br \/>\n                &#039;new_methods&#039;: {<br \/>\n                    &#039;PATCH&#039;: {<br \/>\n                        &#039;introduced&#039;: &#039;2024-01-01&#039;,<br \/>\n                        &#039;description&#039;: &#039;Partial updates&#039;<br \/>\n                    }<br \/>\n                }<br \/>\n            },<br \/>\n            &#039;v2&#039;: {<br \/>\n                &#039;deprecated_methods&#039;: {},<br \/>\n                &#039;new_methods&#039;: {<br \/>\n                    &#039;PATCH&#039;: {},<br \/>\n                    &#039;HEAD&#039;: {<br \/>\n                        &#039;introduced&#039;: &#039;2024-01-01&#039;,<br \/>\n                        &#039;description&#039;: &#039;Resource metadata&#039;<br \/>\n                    }<br \/>\n                }<br \/>\n            }<br \/>\n        }<\/p>\n<p>        self.version_redirects &#061; {<br \/>\n            &#039;\/api\/v1\/users&#039;: &#039;\/api\/v2\/users&#039;,<br \/>\n            &#039;\/api\/v1\/products&#039;: &#039;\/api\/v2\/products&#039;<br \/>\n        }<\/p>\n<p>    def handle_method_for_version(self, path: str, method: str, request) -&gt; dict:<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u7279\u5b9a\u7248\u672c\u7684\u65b9\u6cd5&#034;&#034;&#034;<br \/>\n        # \u63d0\u53d6API\u7248\u672c<br \/>\n        version &#061; self.extract_api_version(path)<\/p>\n<p>        if not version:<br \/>\n            return {&#039;proceed&#039;: True}<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u6709\u91cd\u5b9a\u5411<br \/>\n        redirected_path &#061; self.check_version_redirect(path)<br \/>\n        if redirected_path:<br \/>\n            return {<br \/>\n                &#039;proceed&#039;: False,<br \/>\n                &#039;action&#039;: &#039;redirect&#039;,<br \/>\n                &#039;new_path&#039;: redirected_path,<br \/>\n                &#039;status&#039;: 301<br \/>\n            }<\/p>\n<p>        # \u68c0\u67e5\u65b9\u6cd5\u5f03\u7528<br \/>\n        deprecation_info &#061; self.check_method_deprecation(version, path, method)<br \/>\n        if deprecation_info:<br \/>\n            return {<br \/>\n                &#039;proceed&#039;: True,<br \/>\n                &#039;deprecation&#039;: deprecation_info<br \/>\n            }<\/p>\n<p>        # \u68c0\u67e5\u65b9\u6cd5\u652f\u6301<br \/>\n        if not self.is_method_supported(version, path, method):<br \/>\n            # \u63d0\u4f9b\u7248\u672c\u7279\u5b9a\u7684405\u54cd\u5e94<br \/>\n            return {<br \/>\n                &#039;proceed&#039;: False,<br \/>\n                &#039;action&#039;: &#039;version_specific_405&#039;,<br \/>\n                &#039;version&#039;: version,<br \/>\n                &#039;method&#039;: method<br \/>\n            }<\/p>\n<p>        return {&#039;proceed&#039;: True}<\/p>\n<p>    def extract_api_version(self, path: str) -&gt; str:<br \/>\n        &#034;&#034;&#034;\u63d0\u53d6API\u7248\u672c&#034;&#034;&#034;<br \/>\n        match &#061; re.match(r&#039;^\/api\/(v[0-9]&#043;)\/&#039;, path)<br \/>\n        return match.group(1) if match else None<\/p>\n<p>    def check_version_redirect(self, path: str) -&gt; str:<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u7248\u672c\u91cd\u5b9a\u5411&#034;&#034;&#034;<br \/>\n        for old_path, new_path in self.version_redirects.items():<br \/>\n            if path.startswith(old_path):<br \/>\n                # \u66ff\u6362\u8def\u5f84\u524d\u7f00<br \/>\n                return path.replace(old_path, new_path, 1)<br \/>\n        return None<\/p>\n<p>    def check_method_deprecation(self, version: str, path: str, method: str) -&gt; dict:<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u65b9\u6cd5\u662f\u5426\u5df2\u5f03\u7528&#034;&#034;&#034;<br \/>\n        if version not in self.version_mappings:<br \/>\n            return None<\/p>\n<p>        deprecated_methods &#061; self.version_mappings[version].get(&#039;deprecated_methods&#039;, {})<\/p>\n<p>        if method in deprecated_methods:<br \/>\n            info &#061; deprecated_methods[method]<\/p>\n<p>            # \u68c0\u67e5\u662f\u5426\u9002\u7528\u4e8e\u5f53\u524d\u4e0a\u4e0b\u6587<br \/>\n            if &#039;contexts&#039; in info:<br \/>\n                if not any(path.startswith(ctx.replace(&#039;&lt;id&gt;&#039;, &#039;&#039;)) for ctx in info[&#039;contexts&#039;]):<br \/>\n                    return None<\/p>\n<p>            return {<br \/>\n                &#039;method&#039;: method,<br \/>\n                &#039;deprecated_since&#039;: info[&#039;since&#039;],<br \/>\n                &#039;alternative&#039;: info.get(&#039;alternative&#039;),<br \/>\n                &#039;sunset&#039;: info.get(&#039;sunset&#039;),<br \/>\n                &#039;message&#039;: f&#039;Method {method} is deprecated in {version}&#039;<br \/>\n            }<\/p>\n<p>        return None<\/p>\n<p>    def is_method_supported(self, version: str, path: str, method: str) -&gt; bool:<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u65b9\u6cd5\u5728\u7279\u5b9a\u7248\u672c\u4e2d\u662f\u5426\u652f\u6301&#034;&#034;&#034;<br \/>\n        # \u83b7\u53d6\u57fa\u7840\u5141\u8bb8\u7684\u65b9\u6cd5<br \/>\n        endpoint &#061; request.endpoint<br \/>\n        if endpoint:<br \/>\n            allowed_methods &#061; method_manager.get_allowed_methods(endpoint)<br \/>\n            return method in allowed_methods<\/p>\n<p>        # \u7248\u672c\u7279\u5b9a\u7684\u68c0\u67e5<br \/>\n        if version &#061;&#061; &#039;v1&#039;:<br \/>\n            # v1\u4e0d\u652f\u6301PATCH<br \/>\n            if method &#061;&#061; &#039;PATCH&#039;:<br \/>\n                return False<\/p>\n<p>        return True<\/p>\n<p>    def create_version_specific_405(self, version: str, method: str, path: str) -&gt; tuple:<br \/>\n        &#034;&#034;&#034;\u521b\u5efa\u7248\u672c\u7279\u5b9a\u7684405\u54cd\u5e94&#034;&#034;&#034;<br \/>\n        # \u83b7\u53d6\u5141\u8bb8\u7684\u65b9\u6cd5<br \/>\n        endpoint &#061; request.endpoint<br \/>\n        allowed_methods &#061; method_manager.get_allowed_methods(endpoint) if endpoint else set()<\/p>\n<p>        # \u7248\u672c\u7279\u5b9a\u7684\u8c03\u6574<br \/>\n        if version &#061;&#061; &#039;v1&#039;:<br \/>\n            # v1\u4e2d\u79fb\u9664PATCH<br \/>\n            allowed_methods &#061; {m for m in allowed_methods if m !&#061; &#039;PATCH&#039;}<\/p>\n<p>        response_data &#061; {<br \/>\n            &#034;error&#034;: {<br \/>\n                &#034;code&#034;: &#034;METHOD_NOT_ALLOWED&#034;,<br \/>\n                &#034;message&#034;: f&#034;Method {method} is not supported in API {version}&#034;,<br \/>\n                &#034;api_version&#034;: version,<br \/>\n                &#034;allowed_methods&#034;: list(allowed_methods),<br \/>\n                &#034;version_specific&#034;: True<br \/>\n            }<br \/>\n        }<\/p>\n<p>        # \u6dfb\u52a0\u7248\u672c\u8fc1\u79fb\u4fe1\u606f<br \/>\n        if version &#061;&#061; &#039;v1&#039;:<br \/>\n            response_data[&#034;error&#034;][&#034;migration_note&#034;] &#061; (<br \/>\n                &#034;Consider migrating to API v2 for additional features and methods.&#034;<br \/>\n            )<br \/>\n            response_data[&#034;error&#034;][&#034;v2_endpoint&#034;] &#061; f&#034;\/api\/v2{path[7:]}&#034;  # \u79fb\u9664\/v1\u524d\u7f00<\/p>\n<p>        response &#061; jsonify(response_data)<br \/>\n        response.status_code &#061; 405<br \/>\n        response.headers[&#039;Allow&#039;] &#061; &#039;, &#039;.join(allowed_methods)<br \/>\n        response.headers[&#039;X-API-Version&#039;] &#061; version<br \/>\n        response.headers[&#039;Deprecation&#039;] &#061; &#039;true&#039;<\/p>\n<p>        return response<\/p>\n<p>    def create_deprecation_response(self, deprecation_info: dict) -&gt; tuple:<br \/>\n        &#034;&#034;&#034;\u521b\u5efa\u5f03\u7528\u8b66\u544a\u54cd\u5e94&#034;&#034;&#034;<br \/>\n        response &#061; jsonify({<br \/>\n            &#034;warning&#034;: {<br \/>\n                &#034;code&#034;: &#034;METHOD_DEPRECATED&#034;,<br \/>\n                &#034;message&#034;: deprecation_info[&#039;message&#039;],<br \/>\n                &#034;deprecated_method&#034;: deprecation_info[&#039;method&#039;],<br \/>\n                &#034;deprecated_since&#034;: deprecation_info[&#039;deprecated_since&#039;],<br \/>\n                &#034;alternative_method&#034;: deprecation_info.get(&#039;alternative&#039;),<br \/>\n                &#034;sunset_date&#034;: deprecation_info.get(&#039;sunset&#039;),<br \/>\n                &#034;documentation&#034;: f&#034;https:\/\/api.example.com\/docs\/migration-guide&#034;<br \/>\n            }<br \/>\n        })<\/p>\n<p>        response.status_code &#061; 200  # \u4ecd\u7136\u5904\u7406\u8bf7\u6c42&#xff0c;\u4f46\u6dfb\u52a0\u8b66\u544a<br \/>\n        response.headers[&#039;Deprecation&#039;] &#061; f&#039;date&#061;&#034;{deprecation_info[&#034;deprecated_since&#034;]}&#034;&#039;<\/p>\n<p>        if deprecation_info.get(&#039;sunset&#039;):<br \/>\n            response.headers[&#039;Sunset&#039;] &#061; deprecation_info[&#039;sunset&#039;]<\/p>\n<p>        if deprecation_info.get(&#039;alternative&#039;):<br \/>\n            response.headers[&#039;Link&#039;] &#061; (<br \/>\n                f&#039;&lt;\/api\/v2{request.path[7:]}&gt;; rel&#061;&#034;successor-version&#034;; type&#061;&#034;application\/json&#034;&#039;<br \/>\n            )<\/p>\n<p>        return response<\/p>\n<p># \u96c6\u6210\u7248\u672c\u8fc1\u79fb\u5904\u7406\u5668<br \/>\nversion_handler &#061; APIVersionMigrationHandler()<\/p>\n<p>&#064;app.before_request<br \/>\ndef handle_api_version_migration():<br \/>\n    &#034;&#034;&#034;\u5904\u7406API\u7248\u672c\u8fc1\u79fb&#034;&#034;&#034;<br \/>\n    # \u68c0\u67e5API\u7248\u672c\u548c\u65b9\u6cd5<br \/>\n    result &#061; version_handler.handle_method_for_version(<br \/>\n        request.path, request.method, request<br \/>\n    )<\/p>\n<p>    if not result[&#039;proceed&#039;]:<br \/>\n        if result[&#039;action&#039;] &#061;&#061; &#039;redirect&#039;:<br \/>\n            # \u91cd\u5b9a\u5411\u5230\u65b0\u7248\u672c<br \/>\n            response &#061; jsonify({<br \/>\n                &#034;error&#034;: {<br \/>\n                    &#034;code&#034;: &#034;VERSION_REDIRECT&#034;,<br \/>\n                    &#034;message&#034;: f&#034;API endpoint has moved to {result[&#039;new_path&#039;]}&#034;,<br \/>\n                    &#034;new_location&#034;: result[&#039;new_path&#039;]<br \/>\n                }<br \/>\n            })<br \/>\n            response.status_code &#061; result[&#039;status&#039;]<br \/>\n            response.headers[&#039;Location&#039;] &#061; result[&#039;new_path&#039;]<br \/>\n            return response<\/p>\n<p>        elif result[&#039;action&#039;] &#061;&#061; &#039;version_specific_405&#039;:<br \/>\n            # \u7248\u672c\u7279\u5b9a\u7684405<br \/>\n            return version_handler.create_version_specific_405(<br \/>\n                result[&#039;version&#039;], result[&#039;method&#039;], request.path<br \/>\n            )<\/p>\n<p>    elif &#039;deprecation&#039; in result:<br \/>\n        # \u65b9\u6cd5\u5df2\u5f03\u7528&#xff0c;\u6dfb\u52a0\u8b66\u544a\u5934\u90e8<br \/>\n        request.deprecation_info &#061; result[&#039;deprecation&#039;]<\/p>\n<p>&#064;app.after_request<br \/>\ndef add_deprecation_headers(response):<br \/>\n    &#034;&#034;&#034;\u6dfb\u52a0\u5f03\u7528\u5934\u90e8&#034;&#034;&#034;<br \/>\n    if hasattr(request, &#039;deprecation_info&#039;):<br \/>\n        deprecation_info &#061; request.deprecation_info<\/p>\n<p>        response.headers[&#039;Deprecation&#039;] &#061; f&#039;date&#061;&#034;{deprecation_info[&#034;deprecated_since&#034;]}&#034;&#039;<\/p>\n<p>        if deprecation_info.get(&#039;sunset&#039;):<br \/>\n            response.headers[&#039;Sunset&#039;] &#061; deprecation_info[&#039;sunset&#039;]<\/p>\n<p>        # \u5982\u679c\u54cd\u5e94\u662fJSON&#xff0c;\u53ef\u4ee5\u5728\u54cd\u5e94\u4f53\u4e2d\u6dfb\u52a0\u8b66\u544a<br \/>\n        if response.headers.get(&#039;Content-Type&#039;, &#039;&#039;).startswith(&#039;application\/json&#039;):<br \/>\n            try:<br \/>\n                data &#061; json.loads(response.get_data(as_text&#061;True))<br \/>\n                if isinstance(data, dict):<br \/>\n                    data[&#039;_warning&#039;] &#061; {<br \/>\n                        &#039;deprecated_method&#039;: deprecation_info[&#039;method&#039;],<br \/>\n                        &#039;message&#039;: deprecation_info[&#039;message&#039;],<br \/>\n                        &#039;alternative&#039;: deprecation_info.get(&#039;alternative&#039;)<br \/>\n                    }<br \/>\n                    response.set_data(json.dumps(data))<br \/>\n            except:<br \/>\n                pass  # \u5ffd\u7565JSON\u89e3\u6790\u9519\u8bef<\/p>\n<p>    return response<\/p>\n<h2>\u7b2c21\u7ae0&#xff1a;408 Request Timeout &#8211; \u8bf7\u6c42\u8d85\u65f6\u7ba1\u7406\u6df1\u5ea6\u5206\u6790<\/h2>\n<h3>21.1 \u5b9a\u4e49\u4e0e\u8bed\u4e49<\/h3>\n<p>408 Request Timeout\u00a0\u72b6\u6001\u7801\u8868\u793a\u670d\u52a1\u5668\u5728\u7b49\u5f85\u8bf7\u6c42\u7684\u5b8c\u6574\u4f20\u8f93\u65f6\u8d85\u8fc7\u4e86\u9884\u8bbe\u7684\u65f6\u95f4\u9650\u5236\u3002\u8be5\u72b6\u6001\u7801\u7684\u6838\u5fc3\u542b\u4e49\u662f&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u670d\u52a1\u5668\u6ca1\u6709\u5728\u671f\u671b\u7684\u65f6\u95f4\u5185\u6536\u5230\u5b8c\u6574\u7684\u8bf7\u6c42<\/p>\n<\/li>\n<li>\n<p>\u8fde\u63a5\u53ef\u80fd\u4ecd\u7136\u4fdd\u6301\u6253\u5f00\u72b6\u6001<\/p>\n<\/li>\n<li>\n<p>\u5ba2\u6237\u7aef\u53ef\u4ee5\u5728\u7a0d\u540e\u65f6\u95f4\u7528\u76f8\u540c\u7684\u8bf7\u6c42\u91cd\u8bd5<\/p>\n<\/li>\n<\/ul>\n<p>\u5173\u952e\u7279\u6027&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u4e0d\u662f\u5ba2\u6237\u7aef\u9519\u8bef&#xff1a;\u800c\u662f\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u4e4b\u95f4\u7684\u901a\u4fe1\u95ee\u9898<\/p>\n<\/li>\n<li>\n<p>\u670d\u52a1\u5668\u4e3b\u52a8\u5173\u95ed\u8fde\u63a5&#xff1a;\u7531\u4e8e\u8d85\u65f6\u800c\u7ec8\u6b62\u7b49\u5f85<\/p>\n<\/li>\n<li>\n<p>\u4e0e504 Gateway Timeout\u7684\u533a\u522b&#xff1a;504\u662f\u4e0a\u6e38\u670d\u52a1\u5668\u8d85\u65f6&#xff0c;408\u662f\u5ba2\u6237\u7aef\u8bf7\u6c42\u8d85\u65f6<\/p>\n<\/li>\n<\/ul>\n<p>\u534f\u8bae\u8981\u6c42&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u670d\u52a1\u5668\u5e94\u8be5\u53d1\u9001\u4e00\u4e2a\u5173\u95ed\u8fde\u63a5\u7684\u54cd\u5e94<\/p>\n<\/li>\n<li>\n<p>\u53ef\u4ee5\u5305\u542b\u63cf\u8ff0\u8d85\u65f6\u7684\u54cd\u5e94\u4f53<\/p>\n<\/li>\n<li>\n<p>\u5ba2\u6237\u7aef\u53ef\u4ee5\u91cd\u8bd5\u76f8\u540c\u7684\u8bf7\u6c42<\/p>\n<\/li>\n<\/ul>\n<h3>21.2 \u89e6\u53d1\u573a\u666f\u5206\u7c7b<\/h3>\n<h4>21.2.1 \u7f51\u7edc\u5c42\u8d85\u65f6\u573a\u666f<\/h4>\n<p>python<\/p>\n<p># \u7f51\u7edc\u5c42\u8d85\u65f6\u5206\u6790<br \/>\nclass NetworkTimeoutAnalyzer:<br \/>\n    def __init__(self):<br \/>\n        self.timeout_scenarios &#061; {<br \/>\n            # 1. \u8fde\u63a5\u5efa\u7acb\u8d85\u65f6<br \/>\n            &#039;tcp_handshake&#039;: {<br \/>\n                &#039;description&#039;: &#039;TCP\u4e09\u6b21\u63e1\u624b\u672a\u5728\u65f6\u95f4\u5185\u5b8c\u6210&#039;,<br \/>\n                &#039;typical_timeout&#039;: 60,  # \u79d2<br \/>\n                &#039;common_causes&#039;: [<br \/>\n                    &#039;\u7f51\u7edc\u62e5\u585e&#039;,<br \/>\n                    &#039;\u9632\u706b\u5899\u963b\u6b62&#039;,<br \/>\n                    &#039;DNS\u89e3\u6790\u5931\u8d25&#039;<br \/>\n                ]<br \/>\n            },<\/p>\n<p>            # 2. TLS\u63e1\u624b\u8d85\u65f6<br \/>\n            &#039;tls_handshake&#039;: {<br \/>\n                &#039;description&#039;: &#039;TLS\u63e1\u624b\u8fc7\u7a0b\u8d85\u65f6&#039;,<br \/>\n                &#039;typical_timeout&#039;: 30,  # \u79d2<br \/>\n                &#039;common_causes&#039;: [<br \/>\n                    &#039;\u8bc1\u4e66\u94fe\u9a8c\u8bc1\u590d\u6742&#039;,<br \/>\n                    &#039;\u5ba2\u6237\u7aef\/\u670d\u52a1\u5668CPU\u8d1f\u8f7d\u9ad8&#039;,<br \/>\n                    &#039;\u5f31\u52a0\u5bc6\u5957\u4ef6\u534f\u5546&#039;<br \/>\n                ]<br \/>\n            },<\/p>\n<p>            # 3. \u8bf7\u6c42\u5934\u4f20\u8f93\u8d85\u65f6<br \/>\n            &#039;headers_transmission&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u8bf7\u6c42\u5934\u4f20\u8f93\u65f6\u95f4\u8fc7\u957f&#039;,<br \/>\n                &#039;typical_timeout&#039;: 30,  # \u79d2<br \/>\n                &#039;common_causes&#039;: [<br \/>\n                    &#039;\u5934\u90e8\u8fc7\u5927&#xff08;\u5982Cookie&#xff09;&#039;,<br \/>\n                    &#039;\u7f51\u7edc\u5e26\u5bbd\u9650\u5236&#039;,<br \/>\n                    &#039;\u4ee3\u7406\u670d\u52a1\u5668\u5904\u7406\u5ef6\u8fdf&#039;<br \/>\n                ]<br \/>\n            },<\/p>\n<p>            # 4. \u8bf7\u6c42\u4f53\u4f20\u8f93\u8d85\u65f6<br \/>\n            &#039;body_transmission&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u8bf7\u6c42\u4f53\u4f20\u8f93\u65f6\u95f4\u8fc7\u957f&#039;,<br \/>\n                &#039;typical_timeout&#039;: 60,  # \u79d2<br \/>\n                &#039;common_causes&#039;: [<br \/>\n                    &#039;\u5927\u6587\u4ef6\u4e0a\u4f20&#039;,<br \/>\n                    &#039;\u6162\u901f\u7f51\u7edc\u8fde\u63a5&#039;,<br \/>\n                    &#039;\u5ba2\u6237\u7aef\u5904\u7406\u5ef6\u8fdf&#039;<br \/>\n                ]<br \/>\n            },<\/p>\n<p>            # 5. \u6162\u901floris\u653b\u51fb<br \/>\n            &#039;slow_loris&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u5ba2\u6237\u7aef\u7f13\u6162\u53d1\u9001\u6570\u636e&#xff0c;\u5360\u7528\u8fde\u63a5&#039;,<br \/>\n                &#039;typical_timeout&#039;: 300,  # \u79d2<br \/>\n                &#039;common_causes&#039;: [<br \/>\n                    &#039;\u6076\u610f\u653b\u51fb&#039;,<br \/>\n                    &#039;\u5ba2\u6237\u7aef\u5b9e\u73b0\u7f3a\u9677&#039;,<br \/>\n                    &#039;\u7f51\u7edc\u95ee\u9898&#039;<br \/>\n                ]<br \/>\n            }<br \/>\n        }<\/p>\n<p>    def analyze_timeout(self, request_data, network_stats):<br \/>\n        &#034;&#034;&#034;\u5206\u6790\u8d85\u65f6\u539f\u56e0&#034;&#034;&#034;<br \/>\n        timeout_type &#061; self.determine_timeout_type(request_data, network_stats)<\/p>\n<p>        if timeout_type:<br \/>\n            scenario &#061; self.timeout_scenarios[timeout_type]<br \/>\n            return {<br \/>\n                &#039;type&#039;: timeout_type,<br \/>\n                &#039;description&#039;: scenario[&#039;description&#039;],<br \/>\n                &#039;causes&#039;: self.rank_causes(request_data, scenario[&#039;common_causes&#039;]),<br \/>\n                &#039;recommendations&#039;: self.generate_recommendations(timeout_type)<br \/>\n            }<\/p>\n<p>        return None<\/p>\n<p>    def determine_timeout_type(self, request_data, network_stats):<br \/>\n        &#034;&#034;&#034;\u786e\u5b9a\u8d85\u65f6\u7c7b\u578b&#034;&#034;&#034;<\/p>\n<p>        # \u5206\u6790\u8fde\u63a5\u65f6\u95f4<br \/>\n        if network_stats.get(&#039;connection_time&#039;, 0) &gt; 60:<br \/>\n            return &#039;tcp_handshake&#039;<\/p>\n<p>        # \u5206\u6790TLS\u63e1\u624b\u65f6\u95f4<br \/>\n        if network_stats.get(&#039;tls_handshake_time&#039;, 0) &gt; 30:<br \/>\n            return &#039;tls_handshake&#039;<\/p>\n<p>        # \u5206\u6790\u8bf7\u6c42\u5934\u5927\u5c0f<br \/>\n        headers_size &#061; sum(len(k) &#043; len(v) for k, v in request_data.get(&#039;headers&#039;, {}).items())<br \/>\n        if headers_size &gt; 8192:  # 8KB<br \/>\n            return &#039;headers_transmission&#039;<\/p>\n<p>        # \u5206\u6790\u8bf7\u6c42\u4f53\u5927\u5c0f\u548c\u4f20\u8f93\u901f\u7387<br \/>\n        body_size &#061; request_data.get(&#039;content_length&#039;, 0)<br \/>\n        if body_size &gt; 10 * 1024 * 1024:  # 10MB<br \/>\n            if network_stats.get(&#039;transfer_rate&#039;, 0) &lt; 1024:  # &lt; 1KB\/s<br \/>\n                return &#039;body_transmission&#039;<\/p>\n<p>        # \u68c0\u67e5\u6162\u901floris\u6a21\u5f0f<br \/>\n        if self.is_slow_loris_pattern(request_data, network_stats):<br \/>\n            return &#039;slow_loris&#039;<\/p>\n<p>        return None<\/p>\n<p>    def is_slow_loris_pattern(self, request_data, network_stats):<br \/>\n        &#034;&#034;&#034;\u68c0\u6d4b\u6162\u901floris\u653b\u51fb\u6a21\u5f0f&#034;&#034;&#034;<br \/>\n        # \u68c0\u67e5\u8bf7\u6c42\u662f\u5426\u5206\u6bb5\u4f20\u8f93<br \/>\n        if network_stats.get(&#039;chunk_count&#039;, 0) &gt; 10:<br \/>\n            # \u68c0\u67e5\u4f20\u8f93\u95f4\u9694<br \/>\n            intervals &#061; network_stats.get(&#039;chunk_intervals&#039;, [])<br \/>\n            if intervals:<br \/>\n                avg_interval &#061; sum(intervals) \/ len(intervals)<br \/>\n                if 1 &lt; avg_interval &lt; 10:  # \u6bcf\u79d2\u53d1\u9001\u5c11\u91cf\u6570\u636e<br \/>\n                    return True<\/p>\n<p>        # \u68c0\u67e5Keep-Alive\u6ee5\u7528<br \/>\n        if network_stats.get(&#039;keep_alive_requests&#039;, 0) &gt; 100:<br \/>\n            return True<\/p>\n<p>        return False<\/p>\n<p>    def rank_causes(self, request_data, possible_causes):<br \/>\n        &#034;&#034;&#034;\u5bf9\u53ef\u80fd\u539f\u56e0\u8fdb\u884c\u6392\u5e8f&#034;&#034;&#034;<br \/>\n        ranked &#061; []<\/p>\n<p>        # \u57fa\u4e8e\u8bf7\u6c42\u7279\u5f81\u6392\u5e8f<br \/>\n        for cause in possible_causes:<br \/>\n            score &#061; 0<\/p>\n<p>            if cause &#061;&#061; &#039;\u7f51\u7edc\u62e5\u585e&#039; and request_data.get(&#039;high_latency&#039;, False):<br \/>\n                score &#043;&#061; 3<\/p>\n<p>            if cause &#061;&#061; &#039;\u5927\u6587\u4ef6\u4e0a\u4f20&#039; and request_data.get(&#039;content_length&#039;, 0) &gt; 5 * 1024 * 1024:<br \/>\n                score &#043;&#061; 3<\/p>\n<p>            if cause &#061;&#061; &#039;\u6076\u610f\u653b\u51fb&#039; and self.is_slow_loris_pattern(request_data, {}):<br \/>\n                score &#043;&#061; 5<\/p>\n<p>            ranked.append((cause, score))<\/p>\n<p>        # \u6309\u5206\u6570\u6392\u5e8f<br \/>\n        ranked.sort(key&#061;lambda x: x[1], reverse&#061;True)<br \/>\n        return [cause for cause, score in ranked if score &gt; 0]<\/p>\n<p>    def generate_recommendations(self, timeout_type):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u63a8\u8350\u89e3\u51b3\u65b9\u6848&#034;&#034;&#034;<br \/>\n        recommendations &#061; {<br \/>\n            &#039;tcp_handshake&#039;: [<br \/>\n                &#039;\u68c0\u67e5\u7f51\u7edc\u8fde\u63a5\u7a33\u5b9a\u6027&#039;,<br \/>\n                &#039;\u9a8c\u8bc1\u9632\u706b\u5899\u914d\u7f6e&#039;,<br \/>\n                &#039;\u4f7f\u7528\u8fde\u63a5\u6c60\u51cf\u5c11\u63e1\u624b\u6b21\u6570&#039;<br \/>\n            ],<br \/>\n            &#039;tls_handshake&#039;: [<br \/>\n                &#039;\u4f18\u5316\u8bc1\u4e66\u94fe&#039;,<br \/>\n                &#039;\u4f7f\u7528\u66f4\u5feb\u7684\u52a0\u5bc6\u7b97\u6cd5&#039;,<br \/>\n                &#039;\u542f\u7528TLS\u4f1a\u8bdd\u6062\u590d&#039;<br \/>\n            ],<br \/>\n            &#039;headers_transmission&#039;: [<br \/>\n                &#039;\u51cf\u5c11Cookie\u5927\u5c0f&#039;,<br \/>\n                &#039;\u538b\u7f29\u8bf7\u6c42\u5934&#039;,<br \/>\n                &#039;\u4f7f\u7528HTTP\/2\u5934\u90e8\u538b\u7f29&#039;<br \/>\n            ],<br \/>\n            &#039;body_transmission&#039;: [<br \/>\n                &#039;\u5206\u5757\u4e0a\u4f20\u5927\u6587\u4ef6&#039;,<br \/>\n                &#039;\u542f\u7528\u538b\u7f29&#039;,<br \/>\n                &#039;\u589e\u52a0\u8d85\u65f6\u65f6\u95f4&#039;<br \/>\n            ],<br \/>\n            &#039;slow_loris&#039;: [<br \/>\n                &#039;\u914d\u7f6e\u8bf7\u6c42\u901f\u7387\u9650\u5236&#039;,<br \/>\n                &#039;\u9650\u5236\u6700\u5c0f\u4f20\u8f93\u901f\u7387&#039;,<br \/>\n                &#039;\u4f7f\u7528Web\u5e94\u7528\u9632\u706b\u5899&#039;<br \/>\n            ]<br \/>\n        }<\/p>\n<p>        return recommendations.get(timeout_type, [])<\/p>\n<h4>21.2.2 \u5e94\u7528\u5c42\u8d85\u65f6\u573a\u666f<\/h4>\n<p>python<\/p>\n<p># \u5e94\u7528\u5c42\u8d85\u65f6\u573a\u666f\u5206\u6790<br \/>\nclass ApplicationTimeoutAnalyzer:<br \/>\n    def __init__(self):<br \/>\n        self.request_stages &#061; {<br \/>\n            &#039;parsing&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u8bf7\u6c42\u89e3\u6790\u9636\u6bb5&#039;,<br \/>\n                &#039;timeout&#039;: 5,  # \u79d2<br \/>\n                &#039;metrics&#039;: [&#039;request_size&#039;, &#039;complexity&#039;]<br \/>\n            },<br \/>\n            &#039;authentication&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u8eab\u4efd\u9a8c\u8bc1\u9636\u6bb5&#039;,<br \/>\n                &#039;timeout&#039;: 10,<br \/>\n                &#039;metrics&#039;: [&#039;auth_method&#039;, &#039;token_validation&#039;]<br \/>\n            },<br \/>\n            &#039;authorization&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u6388\u6743\u68c0\u67e5\u9636\u6bb5&#039;,<br \/>\n                &#039;timeout&#039;: 5,<br \/>\n                &#039;metrics&#039;: [&#039;permission_checks&#039;, &#039;role_validation&#039;]<br \/>\n            },<br \/>\n            &#039;validation&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u8bf7\u6c42\u9a8c\u8bc1\u9636\u6bb5&#039;,<br \/>\n                &#039;timeout&#039;: 10,<br \/>\n                &#039;metrics&#039;: [&#039;data_size&#039;, &#039;validation_rules&#039;]<br \/>\n            },<br \/>\n            &#039;processing&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u4e1a\u52a1\u5904\u7406\u9636\u6bb5&#039;,<br \/>\n                &#039;timeout&#039;: 30,<br \/>\n                &#039;metrics&#039;: [&#039;complexity&#039;, &#039;external_calls&#039;]<br \/>\n            },<br \/>\n            &#039;response&#039;: {<br \/>\n                &#039;description&#039;: &#039;\u54cd\u5e94\u751f\u6210\u9636\u6bb5&#039;,<br \/>\n                &#039;timeout&#039;: 10,<br \/>\n                &#039;metrics&#039;: [&#039;response_size&#039;, &#039;serialization&#039;]<br \/>\n            }<br \/>\n        }<\/p>\n<p>    def track_request_stage(self, request_id, stage, start_time):<br \/>\n        &#034;&#034;&#034;\u8ddf\u8e2a\u8bf7\u6c42\u9636\u6bb5&#034;&#034;&#034;<br \/>\n        return {<br \/>\n            &#039;request_id&#039;: request_id,<br \/>\n            &#039;stage&#039;: stage,<br \/>\n            &#039;start_time&#039;: start_time,<br \/>\n            &#039;timeout&#039;: self.request_stages[stage][&#039;timeout&#039;]<br \/>\n        }<\/p>\n<p>    def check_stage_timeout(self, stage_info):<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u9636\u6bb5\u662f\u5426\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        current_time &#061; time.time()<br \/>\n        elapsed &#061; current_time &#8211; stage_info[&#039;start_time&#039;]<\/p>\n<p>        if elapsed &gt; stage_info[&#039;timeout&#039;]:<br \/>\n            return {<br \/>\n                &#039;timed_out&#039;: True,<br \/>\n                &#039;stage&#039;: stage_info[&#039;stage&#039;],<br \/>\n                &#039;elapsed&#039;: elapsed,<br \/>\n                &#039;timeout&#039;: stage_info[&#039;timeout&#039;],<br \/>\n                &#039;description&#039;: self.request_stages[stage_info[&#039;stage&#039;]][&#039;description&#039;]<br \/>\n            }<\/p>\n<p>        return {&#039;timed_out&#039;: False}<\/p>\n<p>    def analyze_staleness(self, request_data):<br \/>\n        &#034;&#034;&#034;\u5206\u6790\u8bf7\u6c42\u9648\u65e7\u6027&#034;&#034;&#034;<br \/>\n        # \u68c0\u67e5\u8bf7\u6c42\u662f\u5426\u5728\u961f\u5217\u4e2d\u7b49\u5f85\u592a\u4e45<br \/>\n        received_time &#061; request_data.get(&#039;received_time&#039;)<br \/>\n        start_time &#061; request_data.get(&#039;start_time&#039;)<\/p>\n<p>        if received_time and start_time:<br \/>\n            queue_time &#061; start_time &#8211; received_time<\/p>\n<p>            if queue_time &gt; 30:  # \u5728\u961f\u5217\u4e2d\u7b49\u5f85\u8d85\u8fc730\u79d2<br \/>\n                return {<br \/>\n                    &#039;stale&#039;: True,<br \/>\n                    &#039;queue_time&#039;: queue_time,<br \/>\n                    &#039;recommendation&#039;: &#039;\u589e\u52a0\u5904\u7406\u80fd\u529b\u6216\u51cf\u5c11\u961f\u5217\u957f\u5ea6&#039;<br \/>\n                }<\/p>\n<p>        return {&#039;stale&#039;: False}<\/p>\n<p>    def generate_timeout_response(self, timeout_info):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u8d85\u65f6\u54cd\u5e94&#034;&#034;&#034;<br \/>\n        response &#061; {<br \/>\n            &#039;error&#039;: {<br \/>\n                &#039;code&#039;: &#039;REQUEST_TIMEOUT&#039;,<br \/>\n                &#039;message&#039;: &#039;Request processing timed out&#039;,<br \/>\n                &#039;details&#039;: {<br \/>\n                    &#039;stage&#039;: timeout_info[&#039;stage&#039;],<br \/>\n                    &#039;elapsed_seconds&#039;: round(timeout_info[&#039;elapsed&#039;], 2),<br \/>\n                    &#039;timeout_seconds&#039;: timeout_info[&#039;timeout&#039;],<br \/>\n                    &#039;description&#039;: timeout_info[&#039;description&#039;]<br \/>\n                }<br \/>\n            }<br \/>\n        }<\/p>\n<p>        # \u6dfb\u52a0\u6062\u590d\u5efa\u8bae<br \/>\n        if timeout_info[&#039;stage&#039;] &#061;&#061; &#039;processing&#039;:<br \/>\n            response[&#039;error&#039;][&#039;suggestions&#039;] &#061; [<br \/>\n                &#039;\u7b80\u5316\u8bf7\u6c42\u590d\u6742\u6027&#039;,<br \/>\n                &#039;\u4f7f\u7528\u5f02\u6b65\u5904\u7406&#039;,<br \/>\n                &#039;\u589e\u52a0\u8d85\u65f6\u65f6\u95f4\u914d\u7f6e&#039;<br \/>\n            ]<\/p>\n<p>        return response<\/p>\n<h3>21.3 \u8be6\u7ec6\u5b9e\u73b0\u4e0e\u6700\u4f73\u5b9e\u8df5<\/h3>\n<h4>21.3.1 \u5b8c\u6574\u7684\u8d85\u65f6\u7ba1\u7406\u4e2d\u95f4\u4ef6<\/h4>\n<p>python<\/p>\n<p># \u5b8c\u6574\u7684\u8d85\u65f6\u7ba1\u7406\u4e2d\u95f4\u4ef6\u5b9e\u73b0<br \/>\nimport time<br \/>\nimport asyncio<br \/>\nfrom concurrent.futures import ThreadPoolExecutor, TimeoutError<br \/>\nfrom contextlib import contextmanager<br \/>\nfrom typing import Dict, Optional, Callable, Any<br \/>\nimport signal<br \/>\nimport threading<\/p>\n<p>class TimeoutManager:<br \/>\n    &#034;&#034;&#034;\u5168\u9762\u7684\u8d85\u65f6\u7ba1\u7406\u5668&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, config: Dict &#061; None):<br \/>\n        self.config &#061; {<br \/>\n            &#039;connection_timeout&#039;: 60,      # \u8fde\u63a5\u5efa\u7acb\u8d85\u65f6<br \/>\n            &#039;read_timeout&#039;: 60,            # \u8bfb\u53d6\u8bf7\u6c42\u8d85\u65f6<br \/>\n            &#039;write_timeout&#039;: 60,           # \u5199\u5165\u54cd\u5e94\u8d85\u65f6<br \/>\n            &#039;request_timeout&#039;: 120,        # \u603b\u8bf7\u6c42\u5904\u7406\u8d85\u65f6<br \/>\n            &#039;keep_alive_timeout&#039;: 15,      # Keep-Alive\u8d85\u65f6<br \/>\n            &#039;max_header_size&#039;: 8192,       # \u6700\u5927\u5934\u90e8\u5927\u5c0f<br \/>\n            &#039;max_body_size&#039;: 10 * 1024 * 1024,  # \u6700\u5927\u8bf7\u6c42\u4f53\u5927\u5c0f<br \/>\n            &#039;slow_request_threshold&#039;: 10,  # \u6162\u8bf7\u6c42\u9608\u503c&#xff08;\u79d2&#xff09;<br \/>\n            ** (config or {})<br \/>\n        }<\/p>\n<p>        self.active_connections &#061; {}<br \/>\n        self.slow_requests &#061; []<br \/>\n        self.timeout_stats &#061; {<br \/>\n            &#039;total_timeouts&#039;: 0,<br \/>\n            &#039;by_stage&#039;: {},<br \/>\n            &#039;by_client&#039;: {}<br \/>\n        }<\/p>\n<p>        # \u521d\u59cb\u5316\u8d85\u65f6\u5904\u7406\u5668<br \/>\n        self.init_timeout_handlers()<\/p>\n<p>    def init_timeout_handlers(self):<br \/>\n        &#034;&#034;&#034;\u521d\u59cb\u5316\u8d85\u65f6\u5904\u7406\u5668&#034;&#034;&#034;<br \/>\n        self.handlers &#061; {<br \/>\n            &#039;connection&#039;: self.handle_connection_timeout,<br \/>\n            &#039;read&#039;: self.handle_read_timeout,<br \/>\n            &#039;write&#039;: self.handle_write_timeout,<br \/>\n            &#039;request&#039;: self.handle_request_timeout,<br \/>\n            &#039;keep_alive&#039;: self.handle_keep_alive_timeout<br \/>\n        }<\/p>\n<p>    &#064;contextmanager<br \/>\n    def request_context(self, client_ip: str, request_id: str):<br \/>\n        &#034;&#034;&#034;\u8bf7\u6c42\u4e0a\u4e0b\u6587\u7ba1\u7406\u5668&#034;&#034;&#034;<br \/>\n        start_time &#061; time.time()<br \/>\n        connection_info &#061; {<br \/>\n            &#039;client_ip&#039;: client_ip,<br \/>\n            &#039;request_id&#039;: request_id,<br \/>\n            &#039;start_time&#039;: start_time,<br \/>\n            &#039;stage&#039;: &#039;initial&#039;,<br \/>\n            &#039;last_activity&#039;: start_time,<br \/>\n            &#039;bytes_received&#039;: 0,<br \/>\n            &#039;bytes_sent&#039;: 0<br \/>\n        }<\/p>\n<p>        self.active_connections[request_id] &#061; connection_info<\/p>\n<p>        try:<br \/>\n            yield connection_info<br \/>\n        finally:<br \/>\n            # \u6e05\u7406\u8fde\u63a5\u4fe1\u606f<br \/>\n            if request_id in self.active_connections:<br \/>\n                elapsed &#061; time.time() &#8211; start_time<\/p>\n<p>                if elapsed &gt; self.config[&#039;slow_request_threshold&#039;]:<br \/>\n                    self.record_slow_request(connection_info, elapsed)<\/p>\n<p>                del self.active_connections[request_id]<\/p>\n<p>    def monitor_connection(self, request_id: str):<br \/>\n        &#034;&#034;&#034;\u76d1\u63a7\u8fde\u63a5\u72b6\u6001&#034;&#034;&#034;<br \/>\n        def monitor():<br \/>\n            while request_id in self.active_connections:<br \/>\n                conn &#061; self.active_connections[request_id]<br \/>\n                idle_time &#061; time.time() &#8211; conn[&#039;last_activity&#039;]<\/p>\n<p>                # \u68c0\u67e5\u8bfb\u8d85\u65f6<br \/>\n                if idle_time &gt; self.config[&#039;read_timeout&#039;] and conn[&#039;stage&#039;] &#061;&#061; &#039;reading&#039;:<br \/>\n                    self.handle_timeout(&#039;read&#039;, conn)<br \/>\n                    break<\/p>\n<p>                # \u68c0\u67e5\u603b\u8bf7\u6c42\u8d85\u65f6<br \/>\n                total_time &#061; time.time() &#8211; conn[&#039;start_time&#039;]<br \/>\n                if total_time &gt; self.config[&#039;request_timeout&#039;]:<br \/>\n                    self.handle_timeout(&#039;request&#039;, conn)<br \/>\n                    break<\/p>\n<p>                time.sleep(1)  # \u6bcf\u79d2\u68c0\u67e5\u4e00\u6b21<\/p>\n<p>        # \u542f\u52a8\u76d1\u63a7\u7ebf\u7a0b<br \/>\n        thread &#061; threading.Thread(target&#061;monitor, daemon&#061;True)<br \/>\n        thread.start()<br \/>\n        return thread<\/p>\n<p>    def handle_timeout(self, timeout_type: str, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        handler &#061; self.handlers.get(timeout_type)<br \/>\n        if handler:<br \/>\n            handler(connection_info)<\/p>\n<p>        # \u8bb0\u5f55\u7edf\u8ba1<br \/>\n        self.record_timeout(timeout_type, connection_info)<\/p>\n<p>        # \u53d1\u9001\u8b66\u62a5<br \/>\n        self.send_timeout_alert(timeout_type, connection_info)<\/p>\n<p>    def handle_connection_timeout(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u8fde\u63a5\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        # \u8bb0\u5f55\u8fde\u63a5\u8d85\u65f6\u8be6\u60c5<br \/>\n        log_data &#061; {<br \/>\n            &#039;type&#039;: &#039;connection_timeout&#039;,<br \/>\n            &#039;client_ip&#039;: connection_info[&#039;client_ip&#039;],<br \/>\n            &#039;request_id&#039;: connection_info[&#039;request_id&#039;],<br \/>\n            &#039;stage&#039;: connection_info[&#039;stage&#039;],<br \/>\n            &#039;duration&#039;: time.time() &#8211; connection_info[&#039;start_time&#039;],<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        self.log_timeout_event(log_data)<\/p>\n<p>        # \u5173\u95ed\u8fde\u63a5<br \/>\n        self.close_connection(connection_info)<\/p>\n<p>    def handle_read_timeout(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u8bfb\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        log_data &#061; {<br \/>\n            &#039;type&#039;: &#039;read_timeout&#039;,<br \/>\n            &#039;client_ip&#039;: connection_info[&#039;client_ip&#039;],<br \/>\n            &#039;request_id&#039;: connection_info[&#039;request_id&#039;],<br \/>\n            &#039;stage&#039;: connection_info[&#039;stage&#039;],<br \/>\n            &#039;bytes_received&#039;: connection_info[&#039;bytes_received&#039;],<br \/>\n            &#039;idle_time&#039;: time.time() &#8211; connection_info[&#039;last_activity&#039;],<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        self.log_timeout_event(log_data)<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u662f\u6162\u901floris\u653b\u51fb<br \/>\n        if self.is_slow_loris_attack(connection_info):<br \/>\n            self.handle_slow_loris(connection_info)<\/p>\n<p>    def handle_write_timeout(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u5199\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        log_data &#061; {<br \/>\n            &#039;type&#039;: &#039;write_timeout&#039;,<br \/>\n            &#039;client_ip&#039;: connection_info[&#039;client_ip&#039;],<br \/>\n            &#039;request_id&#039;: connection_info[&#039;request_id&#039;],<br \/>\n            &#039;stage&#039;: connection_info[&#039;stage&#039;],<br \/>\n            &#039;bytes_sent&#039;: connection_info[&#039;bytes_sent&#039;],<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        self.log_timeout_event(log_data)<\/p>\n<p>    def handle_request_timeout(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u8bf7\u6c42\u5904\u7406\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        log_data &#061; {<br \/>\n            &#039;type&#039;: &#039;request_timeout&#039;,<br \/>\n            &#039;client_ip&#039;: connection_info[&#039;client_ip&#039;],<br \/>\n            &#039;request_id&#039;: connection_info[&#039;request_id&#039;],<br \/>\n            &#039;stage&#039;: connection_info[&#039;stage&#039;],<br \/>\n            &#039;total_duration&#039;: time.time() &#8211; connection_info[&#039;start_time&#039;],<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        self.log_timeout_event(log_data)<\/p>\n<p>        # \u751f\u6210408\u54cd\u5e94<br \/>\n        self.send_408_response(connection_info)<\/p>\n<p>    def handle_keep_alive_timeout(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u5904\u7406Keep-Alive\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        log_data &#061; {<br \/>\n            &#039;type&#039;: &#039;keep_alive_timeout&#039;,<br \/>\n            &#039;client_ip&#039;: connection_info[&#039;client_ip&#039;],<br \/>\n            &#039;request_id&#039;: connection_info[&#039;request_id&#039;],<br \/>\n            &#039;idle_time&#039;: time.time() &#8211; connection_info[&#039;last_activity&#039;],<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        self.log_timeout_event(log_data)<\/p>\n<p>    def is_slow_loris_attack(self, connection_info: Dict) -&gt; bool:<br \/>\n        &#034;&#034;&#034;\u68c0\u6d4b\u6162\u901floris\u653b\u51fb&#034;&#034;&#034;<br \/>\n        # \u68c0\u67e5\u63a5\u6536\u901f\u7387<br \/>\n        if connection_info[&#039;bytes_received&#039;] &gt; 0:<br \/>\n            duration &#061; time.time() &#8211; connection_info[&#039;start_time&#039;]<br \/>\n            rate &#061; connection_info[&#039;bytes_received&#039;] \/ duration<\/p>\n<p>            if rate &lt; 100:  # \u5c0f\u4e8e100\u5b57\u8282\/\u79d2<br \/>\n                return True<\/p>\n<p>        # \u68c0\u67e5\u957f\u65f6\u95f4\u4fdd\u6301\u8fde\u63a5\u4f46\u6ca1\u6709\u6570\u636e<br \/>\n        idle_time &#061; time.time() &#8211; connection_info[&#039;last_activity&#039;]<br \/>\n        if idle_time &gt; 30 and connection_info[&#039;bytes_received&#039;] &lt; 100:<br \/>\n            return True<\/p>\n<p>        return False<\/p>\n<p>    def handle_slow_loris(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u6162\u901floris\u653b\u51fb&#034;&#034;&#034;<br \/>\n        # \u6dfb\u52a0\u5230\u9ed1\u540d\u5355<br \/>\n        self.add_to_blacklist(connection_info[&#039;client_ip&#039;])<\/p>\n<p>        # \u7acb\u5373\u5173\u95ed\u8fde\u63a5<br \/>\n        self.close_connection_immediately(connection_info)<\/p>\n<p>        # \u8bb0\u5f55\u5b89\u5168\u4e8b\u4ef6<br \/>\n        self.log_security_event(&#039;slow_loris_attack&#039;, connection_info)<\/p>\n<p>    def send_408_response(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u53d1\u9001408\u54cd\u5e94&#034;&#034;&#034;<br \/>\n        response &#061; self.create_408_response(connection_info)<\/p>\n<p>        # \u5728\u5b9e\u9645\u5e94\u7528\u4e2d&#xff0c;\u8fd9\u91cc\u4f1a\u901a\u8fc7\u8fde\u63a5\u53d1\u9001\u54cd\u5e94<br \/>\n        # \u4e3a\u4e86\u793a\u4f8b&#xff0c;\u6211\u4eec\u53ea\u8bb0\u5f55<br \/>\n        self.log_response(response)<\/p>\n<p>    def create_408_response(self, connection_info: Dict) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u521b\u5efa408\u54cd\u5e94&#034;&#034;&#034;<br \/>\n        response &#061; {<br \/>\n            &#039;status_code&#039;: 408,<br \/>\n            &#039;headers&#039;: {<br \/>\n                &#039;Content-Type&#039;: &#039;application\/json&#039;,<br \/>\n                &#039;Connection&#039;: &#039;close&#039;,<br \/>\n                &#039;X-Request-Timeout&#039;: &#039;true&#039;,<br \/>\n                &#039;X-Timeout-Stage&#039;: connection_info[&#039;stage&#039;],<br \/>\n                &#039;X-Request-ID&#039;: connection_info[&#039;request_id&#039;]<br \/>\n            },<br \/>\n            &#039;body&#039;: {<br \/>\n                &#039;error&#039;: {<br \/>\n                    &#039;code&#039;: &#039;REQUEST_TIMEOUT&#039;,<br \/>\n                    &#039;message&#039;: &#039;Server timeout waiting for the request&#039;,<br \/>\n                    &#039;request_id&#039;: connection_info[&#039;request_id&#039;],<br \/>\n                    &#039;client_ip&#039;: connection_info[&#039;client_ip&#039;],<br \/>\n                    &#039;timeout_type&#039;: &#039;request_processing&#039;,<br \/>\n                    &#039;duration_seconds&#039;: round(time.time() &#8211; connection_info[&#039;start_time&#039;], 2),<br \/>\n                    &#039;recommendations&#039;: [<br \/>\n                        &#039;Simplify the request&#039;,<br \/>\n                        &#039;Retry with exponential backoff&#039;,<br \/>\n                        &#039;Check network connectivity&#039;<br \/>\n                    ]<br \/>\n                }<br \/>\n            }<br \/>\n        }<\/p>\n<p>        return response<\/p>\n<p>    def record_timeout(self, timeout_type: str, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u8d85\u65f6\u7edf\u8ba1&#034;&#034;&#034;<br \/>\n        self.timeout_stats[&#039;total_timeouts&#039;] &#043;&#061; 1<\/p>\n<p>        # \u6309\u9636\u6bb5\u7edf\u8ba1<br \/>\n        stage &#061; connection_info[&#039;stage&#039;]<br \/>\n        if stage not in self.timeout_stats[&#039;by_stage&#039;]:<br \/>\n            self.timeout_stats[&#039;by_stage&#039;][stage] &#061; 0<br \/>\n        self.timeout_stats[&#039;by_stage&#039;][stage] &#043;&#061; 1<\/p>\n<p>        # \u6309\u5ba2\u6237\u7aef\u7edf\u8ba1<br \/>\n        client_ip &#061; connection_info[&#039;client_ip&#039;]<br \/>\n        if client_ip not in self.timeout_stats[&#039;by_client&#039;]:<br \/>\n            self.timeout_stats[&#039;by_client&#039;][client_ip] &#061; 0<br \/>\n        self.timeout_stats[&#039;by_client&#039;][client_ip] &#043;&#061; 1<\/p>\n<p>    def record_slow_request(self, connection_info: Dict, duration: float):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u6162\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n        slow_request &#061; {<br \/>\n            &#039;request_id&#039;: connection_info[&#039;request_id&#039;],<br \/>\n            &#039;client_ip&#039;: connection_info[&#039;client_ip&#039;],<br \/>\n            &#039;duration&#039;: duration,<br \/>\n            &#039;stage&#039;: connection_info[&#039;stage&#039;],<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        self.slow_requests.append(slow_request)<\/p>\n<p>        # \u4fdd\u6301\u6700\u8fd11000\u4e2a\u6162\u8bf7\u6c42<br \/>\n        if len(self.slow_requests) &gt; 1000:<br \/>\n            self.slow_requests &#061; self.slow_requests[-1000:]<\/p>\n<p>    def log_timeout_event(self, log_data: Dict):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u8d85\u65f6\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        # \u5728\u5b9e\u9645\u5e94\u7528\u4e2d&#xff0c;\u8fd9\u91cc\u4f1a\u8bb0\u5f55\u5230\u65e5\u5fd7\u7cfb\u7edf<br \/>\n        print(f&#034;[Timeout Event] {log_data}&#034;)<\/p>\n<p>        # \u5199\u5165\u6587\u4ef6\u65e5\u5fd7<br \/>\n        with open(&#039;timeout_events.log&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(log_data) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    def log_response(self, response: Dict):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u54cd\u5e94&#034;&#034;&#034;<br \/>\n        with open(&#039;408_responses.log&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(response) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    def add_to_blacklist(self, client_ip: str):<br \/>\n        &#034;&#034;&#034;\u6dfb\u52a0\u5230\u9ed1\u540d\u5355&#034;&#034;&#034;<br \/>\n        # \u5728\u5b9e\u9645\u5e94\u7528\u4e2d&#xff0c;\u8fd9\u91cc\u4f1a\u66f4\u65b0\u9ed1\u540d\u5355\u5b58\u50a8<br \/>\n        with open(&#039;blacklist.log&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(f&#034;{client_ip},{time.time()}\\\\n&#034;)<\/p>\n<p>    def log_security_event(self, event_type: str, data: Dict):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u5b89\u5168\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        event &#061; {<br \/>\n            &#039;type&#039;: event_type,<br \/>\n            &#039;data&#039;: data,<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        with open(&#039;security_events.log&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(event) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    def close_connection(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u5173\u95ed\u8fde\u63a5&#034;&#034;&#034;<br \/>\n        # \u5728\u5b9e\u9645\u5e94\u7528\u4e2d&#xff0c;\u8fd9\u91cc\u4f1a\u5173\u95ed\u7f51\u7edc\u8fde\u63a5<br \/>\n        print(f&#034;Closing connection for request {connection_info[&#039;request_id&#039;]}&#034;)<\/p>\n<p>    def close_connection_immediately(self, connection_info: Dict):<br \/>\n        &#034;&#034;&#034;\u7acb\u5373\u5173\u95ed\u8fde\u63a5&#034;&#034;&#034;<br \/>\n        # \u5f3a\u5236\u5173\u95ed\u8fde\u63a5<br \/>\n        print(f&#034;Immediately closing connection for suspected attack: {connection_info[&#039;request_id&#039;]}&#034;)<\/p>\n<p>    def get_statistics(self) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u7edf\u8ba1\u4fe1\u606f&#034;&#034;&#034;<br \/>\n        return {<br \/>\n            &#039;active_connections&#039;: len(self.active_connections),<br \/>\n            &#039;total_timeouts&#039;: self.timeout_stats[&#039;total_timeouts&#039;],<br \/>\n            &#039;timeouts_by_stage&#039;: self.timeout_stats[&#039;by_stage&#039;],<br \/>\n            &#039;slow_requests_count&#039;: len(self.slow_requests),<br \/>\n            &#039;recent_slow_requests&#039;: self.slow_requests[-10:] if self.slow_requests else []<br \/>\n        }<\/p>\n<h4>21.3.2 \u5f02\u6b65\u8d85\u65f6\u5904\u7406\u6846\u67b6<\/h4>\n<p>python<\/p>\n<p># \u5f02\u6b65\u8d85\u65f6\u5904\u7406\u6846\u67b6<br \/>\nimport asyncio<br \/>\nfrom typing import Optional, Callable, Any, Dict<br \/>\nimport aiohttp<br \/>\nfrom aiohttp import web<br \/>\nimport async_timeout<\/p>\n<p>class AsyncTimeoutFramework:<br \/>\n    &#034;&#034;&#034;\u5f02\u6b65\u8d85\u65f6\u5904\u7406\u6846\u67b6&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, app: web.Application):<br \/>\n        self.app &#061; app<br \/>\n        self.timeout_config &#061; {<br \/>\n            &#039;global_timeout&#039;: 120,<br \/>\n            &#039;stage_timeouts&#039;: {<br \/>\n                &#039;request_parsing&#039;: 5,<br \/>\n                &#039;authentication&#039;: 10,<br \/>\n                &#039;validation&#039;: 5,<br \/>\n                &#039;processing&#039;: 30,<br \/>\n                &#039;response_generation&#039;: 10<br \/>\n            },<br \/>\n            &#039;retry_policy&#039;: {<br \/>\n                &#039;max_retries&#039;: 3,<br \/>\n                &#039;backoff_factor&#039;: 2,<br \/>\n                &#039;max_backoff&#039;: 60<br \/>\n            }<br \/>\n        }<\/p>\n<p>        # \u4e2d\u95f4\u4ef6\u8bbe\u7f6e<br \/>\n        self.setup_middleware()<\/p>\n<p>    def setup_middleware(self):<br \/>\n        &#034;&#034;&#034;\u8bbe\u7f6e\u8d85\u65f6\u4e2d\u95f4\u4ef6&#034;&#034;&#034;<\/p>\n<p>        &#064;web.middleware<br \/>\n        async def timeout_middleware(request: web.Request, handler: Callable):<br \/>\n            &#034;&#034;&#034;\u8d85\u65f6\u4e2d\u95f4\u4ef6&#034;&#034;&#034;<br \/>\n            request_start &#061; asyncio.get_event_loop().time()<br \/>\n            request_id &#061; request.headers.get(&#039;X-Request-ID&#039;, str(uuid.uuid4()))<\/p>\n<p>            # \u521b\u5efa\u8d85\u65f6\u4e0a\u4e0b\u6587<br \/>\n            timeout_context &#061; {<br \/>\n                &#039;request_id&#039;: request_id,<br \/>\n                &#039;start_time&#039;: request_start,<br \/>\n                &#039;current_stage&#039;: &#039;initial&#039;,<br \/>\n                &#039;timeout_tasks&#039;: []<br \/>\n            }<\/p>\n<p>            request[&#039;timeout_context&#039;] &#061; timeout_context<\/p>\n<p>            try:<br \/>\n                # \u8bbe\u7f6e\u5168\u5c40\u8d85\u65f6<br \/>\n                async with async_timeout.timeout(self.timeout_config[&#039;global_timeout&#039;]):<br \/>\n                    # \u6267\u884c\u5904\u7406<br \/>\n                    response &#061; await self.execute_with_stage_timeouts(request, handler)<br \/>\n                    return response<\/p>\n<p>            except asyncio.TimeoutError:<br \/>\n                # \u5168\u5c40\u8d85\u65f6<br \/>\n                elapsed &#061; asyncio.get_event_loop().time() &#8211; request_start<br \/>\n                return await self.handle_global_timeout(request, elapsed)<\/p>\n<p>            except Exception as e:<br \/>\n                # \u5176\u4ed6\u5f02\u5e38<br \/>\n                return await self.handle_timeout_exception(request, e)<\/p>\n<p>            finally:<br \/>\n                # \u6e05\u7406\u8d85\u65f6\u4efb\u52a1<br \/>\n                await self.cleanup_timeout_tasks(timeout_context)<\/p>\n<p>        self.app.middlewares.append(timeout_middleware)<\/p>\n<p>    async def execute_with_stage_timeouts(self, request: web.Request, handler: Callable):<br \/>\n        &#034;&#034;&#034;\u5206\u9636\u6bb5\u6267\u884c\u8bf7\u6c42\u5904\u7406&#034;&#034;&#034;<br \/>\n        timeout_context &#061; request[&#039;timeout_context&#039;]<\/p>\n<p>        # \u9636\u6bb51: \u8bf7\u6c42\u89e3\u6790<br \/>\n        timeout_context[&#039;current_stage&#039;] &#061; &#039;request_parsing&#039;<br \/>\n        await self.start_stage_timeout(&#039;request_parsing&#039;, timeout_context)<\/p>\n<p>        # \u89e3\u6790\u8bf7\u6c42<br \/>\n        try:<br \/>\n            data &#061; await request.json()<br \/>\n            request[&#039;parsed_data&#039;] &#061; data<br \/>\n        except:<br \/>\n            pass  # \u975eJSON\u8bf7\u6c42<\/p>\n<p>        # \u9636\u6bb52: \u8ba4\u8bc1<br \/>\n        timeout_context[&#039;current_stage&#039;] &#061; &#039;authentication&#039;<br \/>\n        await self.start_stage_timeout(&#039;authentication&#039;, timeout_context)<\/p>\n<p>        # \u6267\u884c\u8ba4\u8bc1<br \/>\n        await self.authenticate_request(request)<\/p>\n<p>        # \u9636\u6bb53: \u9a8c\u8bc1<br \/>\n        timeout_context[&#039;current_stage&#039;] &#061; &#039;validation&#039;<br \/>\n        await self.start_stage_timeout(&#039;validation&#039;, timeout_context)<\/p>\n<p>        # \u9a8c\u8bc1\u8bf7\u6c42<br \/>\n        await self.validate_request(request)<\/p>\n<p>        # \u9636\u6bb54: \u5904\u7406<br \/>\n        timeout_context[&#039;current_stage&#039;] &#061; &#039;processing&#039;<br \/>\n        await self.start_stage_timeout(&#039;processing&#039;, timeout_context)<\/p>\n<p>        # \u6267\u884c\u4e1a\u52a1\u903b\u8f91<br \/>\n        response &#061; await handler(request)<\/p>\n<p>        # \u9636\u6bb55: \u54cd\u5e94\u751f\u6210<br \/>\n        timeout_context[&#039;current_stage&#039;] &#061; &#039;response_generation&#039;<br \/>\n        await self.start_stage_timeout(&#039;response_generation&#039;, timeout_context)<\/p>\n<p>        # \u6dfb\u52a0\u8d85\u65f6\u76f8\u5173\u5934\u90e8<br \/>\n        response.headers.update({<br \/>\n            &#039;X-Request-Timeout-Context&#039;: json.dumps({<br \/>\n                &#039;request_id&#039;: timeout_context[&#039;request_id&#039;],<br \/>\n                &#039;total_time&#039;: round(asyncio.get_event_loop().time() &#8211; timeout_context[&#039;start_time&#039;], 3)<br \/>\n            })<br \/>\n        })<\/p>\n<p>        return response<\/p>\n<p>    async def start_stage_timeout(self, stage: str, timeout_context: Dict):<br \/>\n        &#034;&#034;&#034;\u542f\u52a8\u9636\u6bb5\u8d85\u65f6\u76d1\u63a7&#034;&#034;&#034;<br \/>\n        timeout_seconds &#061; self.timeout_config[&#039;stage_timeouts&#039;].get(stage, 10)<\/p>\n<p>        task &#061; asyncio.create_task(<br \/>\n            self.monitor_stage_timeout(stage, timeout_seconds, timeout_context)<br \/>\n        )<\/p>\n<p>        timeout_context[&#039;timeout_tasks&#039;].append(task)<\/p>\n<p>    async def monitor_stage_timeout(self, stage: str, timeout_seconds: float, timeout_context: Dict):<br \/>\n        &#034;&#034;&#034;\u76d1\u63a7\u9636\u6bb5\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        try:<br \/>\n            await asyncio.sleep(timeout_seconds)<\/p>\n<p>            # \u5982\u679c\u8d85\u65f6\u540e\u4ecd\u7136\u5728\u8fd9\u4e2a\u9636\u6bb5&#xff0c;\u89e6\u53d1\u8d85\u65f6\u5904\u7406<br \/>\n            if timeout_context[&#039;current_stage&#039;] &#061;&#061; stage:<br \/>\n                await self.handle_stage_timeout(stage, timeout_context)<\/p>\n<p>        except asyncio.CancelledError:<br \/>\n            # \u4efb\u52a1\u88ab\u53d6\u6d88&#xff0c;\u6b63\u5e38\u60c5\u51b5<br \/>\n            pass<\/p>\n<p>    async def handle_stage_timeout(self, stage: str, timeout_context: Dict):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u9636\u6bb5\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        # \u8bb0\u5f55\u8d85\u65f6\u4e8b\u4ef6<br \/>\n        await self.log_stage_timeout(stage, timeout_context)<\/p>\n<p>        # \u6839\u636e\u9636\u6bb5\u51b3\u5b9a\u5904\u7406\u65b9\u5f0f<br \/>\n        if stage &#061;&#061; &#039;processing&#039;:<br \/>\n            # \u5904\u7406\u9636\u6bb5\u8d85\u65f6&#xff0c;\u53ef\u80fd\u9700\u8981\u4e2d\u65ad\u5904\u7406<br \/>\n            await self.interrupt_processing(timeout_context)<\/p>\n<p>    async def handle_global_timeout(self, request: web.Request, elapsed: float):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u5168\u5c40\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        timeout_context &#061; request.get(&#039;timeout_context&#039;, {})<br \/>\n        current_stage &#061; timeout_context.get(&#039;current_stage&#039;, &#039;unknown&#039;)<\/p>\n<p>        # \u521b\u5efa408\u54cd\u5e94<br \/>\n        response_data &#061; {<br \/>\n            &#039;error&#039;: {<br \/>\n                &#039;code&#039;: &#039;REQUEST_TIMEOUT&#039;,<br \/>\n                &#039;message&#039;: &#039;Request processing exceeded maximum time limit&#039;,<br \/>\n                &#039;details&#039;: {<br \/>\n                    &#039;elapsed_seconds&#039;: round(elapsed, 2),<br \/>\n                    &#039;max_timeout_seconds&#039;: self.timeout_config[&#039;global_timeout&#039;],<br \/>\n                    &#039;stage_at_timeout&#039;: current_stage,<br \/>\n                    &#039;request_id&#039;: timeout_context.get(&#039;request_id&#039;)<br \/>\n                },<br \/>\n                &#039;suggestions&#039;: [<br \/>\n                    &#039;Simplify the request payload&#039;,<br \/>\n                    &#039;Use pagination for large datasets&#039;,<br \/>\n                    &#039;Implement asynchronous processing&#039;<br \/>\n                ]<br \/>\n            }<br \/>\n        }<\/p>\n<p>        response &#061; web.json_response(<br \/>\n            response_data,<br \/>\n            status&#061;408,<br \/>\n            headers&#061;{<br \/>\n                &#039;Connection&#039;: &#039;close&#039;,<br \/>\n                &#039;X-Request-Timeout&#039;: &#039;true&#039;,<br \/>\n                &#039;X-Timeout-Stage&#039;: current_stage<br \/>\n            }<br \/>\n        )<\/p>\n<p>        # \u8bb0\u5f55\u8d85\u65f6<br \/>\n        await self.log_global_timeout(request, elapsed, current_stage)<\/p>\n<p>        return response<\/p>\n<p>    async def handle_timeout_exception(self, request: web.Request, exception: Exception):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u8d85\u65f6\u5f02\u5e38&#034;&#034;&#034;<br \/>\n        # \u8bb0\u5f55\u5f02\u5e38<br \/>\n        await self.log_timeout_exception(request, exception)<\/p>\n<p>        # \u8fd4\u56de500\u9519\u8bef<br \/>\n        return web.json_response(<br \/>\n            {&#039;error&#039;: &#039;Internal server error&#039;},<br \/>\n            status&#061;500<br \/>\n        )<\/p>\n<p>    async def cleanup_timeout_tasks(self, timeout_context: Dict):<br \/>\n        &#034;&#034;&#034;\u6e05\u7406\u8d85\u65f6\u4efb\u52a1&#034;&#034;&#034;<br \/>\n        for task in timeout_context.get(&#039;timeout_tasks&#039;, []):<br \/>\n            if not task.done():<br \/>\n                task.cancel()<br \/>\n                try:<br \/>\n                    await task<br \/>\n                except asyncio.CancelledError:<br \/>\n                    pass<\/p>\n<p>    async def authenticate_request(self, request: web.Request):<br \/>\n        &#034;&#034;&#034;\u8ba4\u8bc1\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n        # \u5b9e\u9645\u8ba4\u8bc1\u903b\u8f91<br \/>\n        pass<\/p>\n<p>    async def validate_request(self, request: web.Request):<br \/>\n        &#034;&#034;&#034;\u9a8c\u8bc1\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n        # \u5b9e\u9645\u9a8c\u8bc1\u903b\u8f91<br \/>\n        pass<\/p>\n<p>    async def interrupt_processing(self, timeout_context: Dict):<br \/>\n        &#034;&#034;&#034;\u4e2d\u65ad\u5904\u7406&#034;&#034;&#034;<br \/>\n        # \u53d1\u9001\u4e2d\u65ad\u4fe1\u53f7\u6216\u8bbe\u7f6e\u6807\u5fd7<br \/>\n        timeout_context[&#039;interrupted&#039;] &#061; True<\/p>\n<p>    async def log_stage_timeout(self, stage: str, timeout_context: Dict):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u9636\u6bb5\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        log_entry &#061; {<br \/>\n            &#039;type&#039;: &#039;stage_timeout&#039;,<br \/>\n            &#039;stage&#039;: stage,<br \/>\n            &#039;request_id&#039;: timeout_context.get(&#039;request_id&#039;),<br \/>\n            &#039;timestamp&#039;: time.time(),<br \/>\n            &#039;timeout_seconds&#039;: self.timeout_config[&#039;stage_timeouts&#039;].get(stage, 10)<br \/>\n        }<\/p>\n<p>        # \u5728\u5b9e\u9645\u5e94\u7528\u4e2d&#xff0c;\u8fd9\u91cc\u4f1a\u8bb0\u5f55\u5230\u65e5\u5fd7\u7cfb\u7edf<br \/>\n        print(f&#034;[Stage Timeout] {log_entry}&#034;)<\/p>\n<p>    async def log_global_timeout(self, request: web.Request, elapsed: float, stage: str):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u5168\u5c40\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        log_entry &#061; {<br \/>\n            &#039;type&#039;: &#039;global_timeout&#039;,<br \/>\n            &#039;request_id&#039;: request.headers.get(&#039;X-Request-ID&#039;),<br \/>\n            &#039;client_ip&#039;: request.remote,<br \/>\n            &#039;path&#039;: request.path,<br \/>\n            &#039;method&#039;: request.method,<br \/>\n            &#039;elapsed_seconds&#039;: round(elapsed, 2),<br \/>\n            &#039;stage_at_timeout&#039;: stage,<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        # \u8bb0\u5f55\u5230\u6587\u4ef6<br \/>\n        with open(&#039;global_timeouts.log&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(log_entry) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    async def log_timeout_exception(self, request: web.Request, exception: Exception):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u8d85\u65f6\u5f02\u5e38&#034;&#034;&#034;<br \/>\n        log_entry &#061; {<br \/>\n            &#039;type&#039;: &#039;timeout_exception&#039;,<br \/>\n            &#039;request_id&#039;: request.headers.get(&#039;X-Request-ID&#039;),<br \/>\n            &#039;exception_type&#039;: type(exception).__name__,<br \/>\n            &#039;exception_message&#039;: str(exception),<br \/>\n            &#039;timestamp&#039;: time.time()<br \/>\n        }<\/p>\n<p>        with open(&#039;timeout_exceptions.log&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(log_entry) &#043; &#039;\\\\n&#039;)<\/p>\n<h4>21.3.3 \u667a\u80fd\u91cd\u8bd5\u4e0e\u56de\u9000\u7b56\u7565<\/h4>\n<p>python<\/p>\n<p># \u667a\u80fd\u91cd\u8bd5\u4e0e\u56de\u9000\u7b56\u7565<br \/>\nclass SmartRetryStrategy:<br \/>\n    &#034;&#034;&#034;\u667a\u80fd\u91cd\u8bd5\u7b56\u7565\u7ba1\u7406\u5668&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, config: Dict &#061; None):<br \/>\n        self.config &#061; {<br \/>\n            &#039;max_retries&#039;: 3,<br \/>\n            &#039;base_delay&#039;: 1.0,  # \u57fa\u7840\u5ef6\u8fdf&#xff08;\u79d2&#xff09;<br \/>\n            &#039;max_delay&#039;: 60.0,  # \u6700\u5927\u5ef6\u8fdf<br \/>\n            &#039;jitter&#039;: 0.1,      # \u6296\u52a8\u56e0\u5b50<br \/>\n            &#039;backoff_factor&#039;: 2.0,  # \u9000\u907f\u56e0\u5b50<br \/>\n            &#039;timeout_multiplier&#039;: 1.5,  # \u8d85\u65f6\u4e58\u6570<br \/>\n            &#039;retryable_errors&#039;: [408, 429, 500, 502, 503, 504],<br \/>\n            &#039;circuit_breaker&#039;: {<br \/>\n                &#039;failure_threshold&#039;: 5,<br \/>\n                &#039;reset_timeout&#039;: 60,<br \/>\n                &#039;half_open_max_requests&#039;: 3<br \/>\n            },<br \/>\n            ** (config or {})<br \/>\n        }<\/p>\n<p>        self.circuit_breakers &#061; {}<br \/>\n        self.retry_stats &#061; defaultdict(lambda: {<br \/>\n            &#039;total_attempts&#039;: 0,<br \/>\n            &#039;successful_retries&#039;: 0,<br \/>\n            &#039;failed_retries&#039;: 0,<br \/>\n            &#039;total_delay&#039;: 0.0<br \/>\n        })<\/p>\n<p>    async def execute_with_retry(self, request_func: Callable, request_data: Dict,<br \/>\n                                 context: Dict &#061; None) -&gt; Any:<br \/>\n        &#034;&#034;&#034;\u6267\u884c\u5e26\u91cd\u8bd5\u7684\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n        context &#061; context or {}<br \/>\n        retry_count &#061; 0<br \/>\n        last_exception &#061; None<\/p>\n<p>        while retry_count &lt;&#061; self.config[&#039;max_retries&#039;]:<br \/>\n            attempt_start &#061; time.time()<\/p>\n<p>            # \u68c0\u67e5\u7194\u65ad\u5668<br \/>\n            circuit_key &#061; self.get_circuit_key(request_data)<br \/>\n            if not self.allow_request(circuit_key):<br \/>\n                raise CircuitBreakerOpenError(f&#034;Circuit breaker open for {circuit_key}&#034;)<\/p>\n<p>            try:<br \/>\n                # \u8ba1\u7b97\u672c\u6b21\u5c1d\u8bd5\u7684\u8d85\u65f6\u65f6\u95f4<br \/>\n                timeout &#061; self.calculate_timeout(retry_count)<\/p>\n<p>                # \u6267\u884c\u8bf7\u6c42<br \/>\n                async with async_timeout.timeout(timeout):<br \/>\n                    result &#061; await request_func(request_data)<\/p>\n<p>                # \u8bf7\u6c42\u6210\u529f<br \/>\n                if retry_count &gt; 0:<br \/>\n                    self.record_successful_retry(circuit_key, retry_count)<br \/>\n                    self.reset_circuit_breaker(circuit_key)<\/p>\n<p>                return result<\/p>\n<p>            except asyncio.TimeoutError:<br \/>\n                # \u8d85\u65f6\u5f02\u5e38<br \/>\n                last_exception &#061; TimeoutError(f&#034;Request timeout after {timeout} seconds&#034;)<br \/>\n                await self.handle_timeout(circuit_key, retry_count)<\/p>\n<p>            except Exception as e:<br \/>\n                # \u5176\u4ed6\u5f02\u5e38<br \/>\n                last_exception &#061; e<br \/>\n                if not self.should_retry(e):<br \/>\n                    raise e<br \/>\n                await self.handle_error(circuit_key, e, retry_count)<\/p>\n<p>            # \u51c6\u5907\u91cd\u8bd5<br \/>\n            retry_count &#043;&#061; 1<\/p>\n<p>            if retry_count &lt;&#061; self.config[&#039;max_retries&#039;]:<br \/>\n                # \u8ba1\u7b97\u5ef6\u8fdf<br \/>\n                delay &#061; self.calculate_delay(retry_count)<br \/>\n                self.retry_stats[circuit_key][&#039;total_delay&#039;] &#043;&#061; delay<\/p>\n<p>                # \u8bb0\u5f55\u91cd\u8bd5<br \/>\n                await self.log_retry_attempt(request_data, retry_count, delay, last_exception)<\/p>\n<p>                # \u7b49\u5f85\u5ef6\u8fdf<br \/>\n                await asyncio.sleep(delay)<\/p>\n<p>        # \u6240\u6709\u91cd\u8bd5\u90fd\u5931\u8d25<br \/>\n        raise MaxRetriesExceededError(<br \/>\n            f&#034;Max retries ({self.config[&#039;max_retries&#039;]}) exceeded&#034;,<br \/>\n            last_exception&#061;last_exception<br \/>\n        )<\/p>\n<p>    def get_circuit_key(self, request_data: Dict) -&gt; str:<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u7194\u65ad\u5668\u952e&#034;&#034;&#034;<br \/>\n        # \u57fa\u4e8e\u7aef\u70b9\u548c\u65b9\u6cd5\u521b\u5efa\u952e<br \/>\n        endpoint &#061; request_data.get(&#039;endpoint&#039;, &#039;unknown&#039;)<br \/>\n        method &#061; request_data.get(&#039;method&#039;, &#039;GET&#039;)<br \/>\n        return f&#034;{method}:{endpoint}&#034;<\/p>\n<p>    def allow_request(self, circuit_key: str) -&gt; bool:<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u662f\u5426\u5141\u8bb8\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n        if circuit_key not in self.circuit_breakers:<br \/>\n            return True<\/p>\n<p>        cb &#061; self.circuit_breakers[circuit_key]<\/p>\n<p>        if cb[&#039;state&#039;] &#061;&#061; &#039;open&#039;:<br \/>\n            # \u68c0\u67e5\u662f\u5426\u5e94\u8be5\u8fdb\u5165\u534a\u5f00\u72b6\u6001<br \/>\n            if time.time() &#8211; cb[&#039;opened_at&#039;] &gt;&#061; self.config[&#039;circuit_breaker&#039;][&#039;reset_timeout&#039;]:<br \/>\n                cb[&#039;state&#039;] &#061; &#039;half_open&#039;<br \/>\n                cb[&#039;half_open_attempts&#039;] &#061; 0<br \/>\n                return True<br \/>\n            return False<\/p>\n<p>        elif cb[&#039;state&#039;] &#061;&#061; &#039;half_open&#039;:<br \/>\n            # \u9650\u5236\u534a\u5f00\u72b6\u6001\u7684\u5e76\u53d1\u8bf7\u6c42<br \/>\n            if cb[&#039;half_open_attempts&#039;] &gt;&#061; self.config[&#039;circuit_breaker&#039;][&#039;half_open_max_requests&#039;]:<br \/>\n                return False<br \/>\n            cb[&#039;half_open_attempts&#039;] &#043;&#061; 1<br \/>\n            return True<\/p>\n<p>        return True  # closed\u72b6\u6001<\/p>\n<p>    def calculate_timeout(self, retry_count: int) -&gt; float:<br \/>\n        &#034;&#034;&#034;\u8ba1\u7b97\u8d85\u65f6\u65f6\u95f4&#034;&#034;&#034;<br \/>\n        # \u6307\u6570\u9000\u907f&#xff1a;\u6bcf\u6b21\u91cd\u8bd5\u589e\u52a0\u8d85\u65f6\u65f6\u95f4<br \/>\n        base_timeout &#061; 30.0  # \u57fa\u7840\u8d85\u65f630\u79d2<br \/>\n        timeout &#061; base_timeout * (self.config[&#039;timeout_multiplier&#039;] ** retry_count)<br \/>\n        return min(timeout, 300.0)  # \u6700\u59275\u5206\u949f<\/p>\n<p>    def calculate_delay(self, retry_count: int) -&gt; float:<br \/>\n        &#034;&#034;&#034;\u8ba1\u7b97\u91cd\u8bd5\u5ef6\u8fdf&#034;&#034;&#034;<br \/>\n        # \u6307\u6570\u9000\u907f\u7b97\u6cd5<br \/>\n        delay &#061; self.config[&#039;base_delay&#039;] * (self.config[&#039;backoff_factor&#039;] ** (retry_count &#8211; 1))<\/p>\n<p>        # \u6dfb\u52a0\u968f\u673a\u6296\u52a8<br \/>\n        jitter &#061; random.uniform(1 &#8211; self.config[&#039;jitter&#039;], 1 &#043; self.config[&#039;jitter&#039;])<br \/>\n        delay *&#061; jitter<\/p>\n<p>        # \u9650\u5236\u6700\u5927\u5ef6\u8fdf<br \/>\n        return min(delay, self.config[&#039;max_delay&#039;])<\/p>\n<p>    def should_retry(self, exception: Exception) -&gt; bool:<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u662f\u5426\u5e94\u8be5\u91cd\u8bd5&#034;&#034;&#034;<br \/>\n        # \u68c0\u67e5HTTP\u72b6\u6001\u7801<br \/>\n        if hasattr(exception, &#039;status_code&#039;):<br \/>\n            return exception.status_code in self.config[&#039;retryable_errors&#039;]<\/p>\n<p>        # \u68c0\u67e5\u5f02\u5e38\u7c7b\u578b<br \/>\n        retryable_exceptions &#061; [<br \/>\n            TimeoutError,<br \/>\n            ConnectionError,<br \/>\n            asyncio.TimeoutError<br \/>\n        ]<\/p>\n<p>        for exc_type in retryable_exceptions:<br \/>\n            if isinstance(exception, exc_type):<br \/>\n                return True<\/p>\n<p>        return False<\/p>\n<p>    async def handle_timeout(self, circuit_key: str, retry_count: int):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u8d85\u65f6&#034;&#034;&#034;<br \/>\n        # \u8bb0\u5f55\u8d85\u65f6<br \/>\n        self.record_failure(circuit_key)<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u5e94\u8be5\u6253\u5f00\u7194\u65ad\u5668<br \/>\n        if self.should_open_circuit(circuit_key):<br \/>\n            self.open_circuit_breaker(circuit_key)<\/p>\n<p>    async def handle_error(self, circuit_key: str, error: Exception, retry_count: int):<br \/>\n        &#034;&#034;&#034;\u5904\u7406\u9519\u8bef&#034;&#034;&#034;<br \/>\n        # \u8bb0\u5f55\u9519\u8bef<br \/>\n        self.record_failure(circuit_key)<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u5e94\u8be5\u6253\u5f00\u7194\u65ad\u5668<br \/>\n        if self.should_open_circuit(circuit_key):<br \/>\n            self.open_circuit_breaker(circuit_key)<\/p>\n<p>    def record_failure(self, circuit_key: str):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u5931\u8d25&#034;&#034;&#034;<br \/>\n        if circuit_key not in self.circuit_breakers:<br \/>\n            self.circuit_breakers[circuit_key] &#061; {<br \/>\n                &#039;state&#039;: &#039;closed&#039;,<br \/>\n                &#039;failure_count&#039;: 0,<br \/>\n                &#039;success_count&#039;: 0,<br \/>\n                &#039;last_failure&#039;: None,<br \/>\n                &#039;opened_at&#039;: None<br \/>\n            }<\/p>\n<p>        cb &#061; self.circuit_breakers[circuit_key]<br \/>\n        cb[&#039;failure_count&#039;] &#043;&#061; 1<br \/>\n        cb[&#039;last_failure&#039;] &#061; time.time()<\/p>\n<p>        # \u68c0\u67e5\u5931\u8d25\u9608\u503c<br \/>\n        if (cb[&#039;state&#039;] &#061;&#061; &#039;closed&#039; and<br \/>\n            cb[&#039;failure_count&#039;] &gt;&#061; self.config[&#039;circuit_breaker&#039;][&#039;failure_threshold&#039;]):<br \/>\n            self.open_circuit_breaker(circuit_key)<\/p>\n<p>    def record_successful_retry(self, circuit_key: str, retry_count: int):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u6210\u529f\u91cd\u8bd5&#034;&#034;&#034;<br \/>\n        self.retry_stats[circuit_key][&#039;successful_retries&#039;] &#043;&#061; 1<br \/>\n        self.retry_stats[circuit_key][&#039;total_attempts&#039;] &#043;&#061; retry_count &#043; 1<\/p>\n<p>        # \u66f4\u65b0\u7194\u65ad\u5668<br \/>\n        if circuit_key in self.circuit_breakers:<br \/>\n            cb &#061; self.circuit_breakers[circuit_key]<br \/>\n            cb[&#039;success_count&#039;] &#043;&#061; 1<\/p>\n<p>            # \u5982\u679c\u5728\u534a\u5f00\u72b6\u6001\u6210\u529f&#xff0c;\u5173\u95ed\u7194\u65ad\u5668<br \/>\n            if cb[&#039;state&#039;] &#061;&#061; &#039;half_open&#039; and cb[&#039;success_count&#039;] &gt;&#061; 3:<br \/>\n                self.close_circuit_breaker(circuit_key)<\/p>\n<p>    def should_open_circuit(self, circuit_key: str) -&gt; bool:<br \/>\n        &#034;&#034;&#034;\u68c0\u67e5\u662f\u5426\u5e94\u8be5\u6253\u5f00\u7194\u65ad\u5668&#034;&#034;&#034;<br \/>\n        if circuit_key not in self.circuit_breakers:<br \/>\n            return False<\/p>\n<p>        cb &#061; self.circuit_breakers[circuit_key]<\/p>\n<p>        # \u68c0\u67e5\u5931\u8d25\u7387<br \/>\n        total_requests &#061; cb[&#039;failure_count&#039;] &#043; cb[&#039;success_count&#039;]<br \/>\n        if total_requests &lt; 10:  # \u9700\u8981\u6700\u5c0f\u6837\u672c\u91cf<br \/>\n            return False<\/p>\n<p>        failure_rate &#061; cb[&#039;failure_count&#039;] \/ total_requests<br \/>\n        return failure_rate &gt; 0.5  # \u5931\u8d25\u7387\u8d85\u8fc750%<\/p>\n<p>    def open_circuit_breaker(self, circuit_key: str):<br \/>\n        &#034;&#034;&#034;\u6253\u5f00\u7194\u65ad\u5668&#034;&#034;&#034;<br \/>\n        cb &#061; self.circuit_breakers[circuit_key]<br \/>\n        cb[&#039;state&#039;] &#061; &#039;open&#039;<br \/>\n        cb[&#039;opened_at&#039;] &#061; time.time()<\/p>\n<p>        # \u8bb0\u5f55\u7194\u65ad\u4e8b\u4ef6<br \/>\n        self.log_circuit_event(circuit_key, &#039;opened&#039;)<\/p>\n<p>    def close_circuit_breaker(self, circuit_key: str):<br \/>\n        &#034;&#034;&#034;\u5173\u95ed\u7194\u65ad\u5668&#034;&#034;&#034;<br \/>\n        cb &#061; self.circuit_breakers[circuit_key]<br \/>\n        cb[&#039;state&#039;] &#061; &#039;closed&#039;<br \/>\n        cb[&#039;failure_count&#039;] &#061; 0<br \/>\n        cb[&#039;success_count&#039;] &#061; 0<\/p>\n<p>        # \u8bb0\u5f55\u7194\u65ad\u4e8b\u4ef6<br \/>\n        self.log_circuit_event(circuit_key, &#039;closed&#039;)<\/p>\n<p>    def reset_circuit_breaker(self, circuit_key: str):<br \/>\n        &#034;&#034;&#034;\u91cd\u7f6e\u7194\u65ad\u5668&#034;&#034;&#034;<br \/>\n        if circuit_key in self.circuit_breakers:<br \/>\n            cb &#061; self.circuit_breakers[circuit_key]<br \/>\n            if cb[&#039;state&#039;] &#061;&#061; &#039;closed&#039;:<br \/>\n                # \u9010\u6e10\u51cf\u5c11\u5931\u8d25\u8ba1\u6570&#xff08;\u8870\u51cf&#xff09;<br \/>\n                cb[&#039;failure_count&#039;] &#061; max(0, cb[&#039;failure_count&#039;] &#8211; 1)<\/p>\n<p>    async def log_retry_attempt(self, request_data: Dict, retry_count: int,<br \/>\n                                delay: float, exception: Exception):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u91cd\u8bd5\u5c1d\u8bd5&#034;&#034;&#034;<br \/>\n        log_entry &#061; {<br \/>\n            &#039;timestamp&#039;: time.time(),<br \/>\n            &#039;request_id&#039;: request_data.get(&#039;request_id&#039;),<br \/>\n            &#039;endpoint&#039;: request_data.get(&#039;endpoint&#039;),<br \/>\n            &#039;method&#039;: request_data.get(&#039;method&#039;),<br \/>\n            &#039;retry_count&#039;: retry_count,<br \/>\n            &#039;delay_seconds&#039;: round(delay, 2),<br \/>\n            &#039;exception_type&#039;: type(exception).__name__,<br \/>\n            &#039;exception_message&#039;: str(exception)[:200]<br \/>\n        }<\/p>\n<p>        # \u5728\u5b9e\u9645\u5e94\u7528\u4e2d&#xff0c;\u8fd9\u91cc\u4f1a\u8bb0\u5f55\u5230\u65e5\u5fd7\u7cfb\u7edf<br \/>\n        print(f&#034;[Retry Attempt] {log_entry}&#034;)<\/p>\n<p>    def log_circuit_event(self, circuit_key: str, event: str):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u7194\u65ad\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        log_entry &#061; {<br \/>\n            &#039;timestamp&#039;: time.time(),<br \/>\n            &#039;circuit_key&#039;: circuit_key,<br \/>\n            &#039;event&#039;: event,<br \/>\n            &#039;state&#039;: self.circuit_breakers[circuit_key][&#039;state&#039;]<br \/>\n        }<\/p>\n<p>        with open(&#039;circuit_breaker_events.log&#039;, &#039;a&#039;) as f:<br \/>\n            f.write(json.dumps(log_entry) &#043; &#039;\\\\n&#039;)<\/p>\n<p>    def get_retry_statistics(self) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u83b7\u53d6\u91cd\u8bd5\u7edf\u8ba1&#034;&#034;&#034;<br \/>\n        stats &#061; {<br \/>\n            &#039;circuit_breakers&#039;: {},<br \/>\n            &#039;retry_stats&#039;: dict(self.retry_stats),<br \/>\n            &#039;summary&#039;: {<br \/>\n                &#039;total_circuits&#039;: len(self.circuit_breakers),<br \/>\n                &#039;open_circuits&#039;: sum(1 for cb in self.circuit_breakers.values()<br \/>\n                                   if cb[&#039;state&#039;] &#061;&#061; &#039;open&#039;),<br \/>\n                &#039;half_open_circuits&#039;: sum(1 for cb in self.circuit_breakers.values()<br \/>\n                                        if cb[&#039;state&#039;] &#061;&#061; &#039;half_open&#039;)<br \/>\n            }<br \/>\n        }<\/p>\n<p>        for key, cb in self.circuit_breakers.items():<br \/>\n            stats[&#039;circuit_breakers&#039;][key] &#061; {<br \/>\n                &#039;state&#039;: cb[&#039;state&#039;],<br \/>\n                &#039;failure_count&#039;: cb[&#039;failure_count&#039;],<br \/>\n                &#039;success_count&#039;: cb[&#039;success_count&#039;]<br \/>\n            }<\/p>\n<p>        return stats<\/p>\n<p>class CircuitBreakerOpenError(Exception):<br \/>\n    &#034;&#034;&#034;\u7194\u65ad\u5668\u6253\u5f00\u5f02\u5e38&#034;&#034;&#034;<br \/>\n    pass<\/p>\n<p>class MaxRetriesExceededError(Exception):<br \/>\n    &#034;&#034;&#034;\u6700\u5927\u91cd\u8bd5\u6b21\u6570\u8d85\u51fa\u5f02\u5e38&#034;&#034;&#034;<br \/>\n    def __init__(self, message, last_exception&#061;None):<br \/>\n        super().__init__(message)<br \/>\n        self.last_exception &#061; last_exception<\/p>\n<h3>21.4 \u5ba2\u6237\u7aef\u5904\u7406\u7b56\u7565<\/h3>\n<h4>21.4.1 \u81ea\u9002\u5e94\u91cd\u8bd5\u4e0e\u964d\u7ea7\u673a\u5236<\/h4>\n<p>javascript<\/p>\n<p>\/\/ \u5ba2\u6237\u7aef\u81ea\u9002\u5e94\u91cd\u8bd5\u7b56\u7565<br \/>\nclass AdaptiveRetryClient {<br \/>\n  constructor(config &#061; {}) {<br \/>\n    this.config &#061; {<br \/>\n      maxRetries: 3,<br \/>\n      baseDelay: 1000,<br \/>\n      maxDelay: 30000,<br \/>\n      timeout: 30000,<br \/>\n      timeoutMultiplier: 1.5,<br \/>\n      backoffFactor: 2,<br \/>\n      jitter: 0.2,<br \/>\n      circuitBreaker: {<br \/>\n        failureThreshold: 5,<br \/>\n        resetTimeout: 60000,<br \/>\n        halfOpenMaxRequests: 2<br \/>\n      },<br \/>\n      fallbackStrategies: {<br \/>\n        timeout: &#039;return_cached&#039;,<br \/>\n        network_error: &#039;retry_with_exponential_backoff&#039;,<br \/>\n        server_error: &#039;use_alternative_endpoint&#039;<br \/>\n      },<br \/>\n      &#8230;config<br \/>\n    };<\/p>\n<p>    this.circuitBreakers &#061; new Map();<br \/>\n    this.requestHistory &#061; new Map();<br \/>\n    this.fallbackCache &#061; new Map();<br \/>\n    this.metrics &#061; {<br \/>\n      totalRequests: 0,<br \/>\n      successfulRequests: 0,<br \/>\n      failedRequests: 0,<br \/>\n      timeouts: 0,<br \/>\n      retries: 0<br \/>\n    };<\/p>\n<p>    \/\/ \u7f51\u7edc\u8d28\u91cf\u68c0\u6d4b<br \/>\n    this.networkQuality &#061; {<br \/>\n      latency: 0,<br \/>\n      throughput: 0,<br \/>\n      reliability: 1.0<br \/>\n    };<\/p>\n<p>    \/\/ \u521d\u59cb\u5316\u7f51\u7edc\u76d1\u63a7<br \/>\n    this.initNetworkMonitoring();<br \/>\n  }<\/p>\n<p>  async fetchWithRetry(url, options &#061; {}) {<br \/>\n    const requestId &#061; this.generateRequestId();<br \/>\n    const startTime &#061; Date.now();<\/p>\n<p>    this.metrics.totalRequests&#043;&#043;;<\/p>\n<p>    \/\/ \u521b\u5efa\u8bf7\u6c42\u4e0a\u4e0b\u6587<br \/>\n    const context &#061; {<br \/>\n      requestId,<br \/>\n      url,<br \/>\n      method: options.method || &#039;GET&#039;,<br \/>\n      startTime,<br \/>\n      attempt: 0,<br \/>\n      delays: [],<br \/>\n      errors: []<br \/>\n    };<\/p>\n<p>    \/\/ \u68c0\u67e5\u7194\u65ad\u5668<br \/>\n    const circuitKey &#061; this.getCircuitKey(url, options.method);<br \/>\n    if (!this.allowRequest(circuitKey)) {<br \/>\n      return await this.executeFallback(&#039;circuit_breaker_open&#039;, context, options);<br \/>\n    }<\/p>\n<p>    \/\/ \u6267\u884c\u5e26\u91cd\u8bd5\u7684\u8bf7\u6c42<br \/>\n    try {<br \/>\n      const result &#061; await this.executeWithAdaptiveRetry(context, options);<\/p>\n<p>      \/\/ \u8bf7\u6c42\u6210\u529f<br \/>\n      this.metrics.successfulRequests&#043;&#043;;<br \/>\n      this.recordSuccess(circuitKey);<\/p>\n<p>      \/\/ \u66f4\u65b0\u7f51\u7edc\u8d28\u91cf\u6307\u6807<br \/>\n      this.updateNetworkQuality(context, true);<\/p>\n<p>      return result;<\/p>\n<p>    } catch (error) {<br \/>\n      \/\/ \u8bf7\u6c42\u5931\u8d25<br \/>\n      this.metrics.failedRequests&#043;&#043;;<\/p>\n<p>      if (error.name &#061;&#061;&#061; &#039;TimeoutError&#039;) {<br \/>\n        this.metrics.timeouts&#043;&#043;;<br \/>\n      }<\/p>\n<p>      \/\/ \u8bb0\u5f55\u5931\u8d25<br \/>\n      this.recordFailure(circuitKey, error);<\/p>\n<p>      \/\/ \u66f4\u65b0\u7f51\u7edc\u8d28\u91cf\u6307\u6807<br \/>\n      this.updateNetworkQuality(context, false);<\/p>\n<p>      \/\/ \u6267\u884c\u964d\u7ea7\u7b56\u7565<br \/>\n      return await this.executeFallback(error.name, context, options, error);<br \/>\n    }<br \/>\n  }<\/p>\n<p>  async executeWithAdaptiveRetry(context, options) {<br \/>\n    let lastError;<\/p>\n<p>    for (let attempt &#061; 0; attempt &lt;&#061; this.config.maxRetries; attempt&#043;&#043;) {<br \/>\n      context.attempt &#061; attempt;<\/p>\n<p>      try {<br \/>\n        \/\/ \u8ba1\u7b97\u672c\u6b21\u5c1d\u8bd5\u7684\u8d85\u65f6\u65f6\u95f4<br \/>\n        const timeout &#061; this.calculateTimeout(attempt);<\/p>\n<p>        \/\/ \u521b\u5efa\u5e26\u8d85\u65f6\u7684fetch\u8bf7\u6c42<br \/>\n        const controller &#061; new AbortController();<br \/>\n        const timeoutId &#061; setTimeout(() &#061;&gt; controller.abort(), timeout);<\/p>\n<p>        const fetchOptions &#061; {<br \/>\n          &#8230;options,<br \/>\n          signal: controller.signal<br \/>\n        };<\/p>\n<p>        \/\/ \u6267\u884c\u8bf7\u6c42<br \/>\n        const startAttempt &#061; Date.now();<br \/>\n        const response &#061; await fetch(context.url, fetchOptions);<br \/>\n        const attemptDuration &#061; Date.now() &#8211; startAttempt;<\/p>\n<p>        clearTimeout(timeoutId);<\/p>\n<p>        \/\/ \u68c0\u67e5HTTP\u72b6\u6001\u7801<br \/>\n        if (!response.ok) {<br \/>\n          const error &#061; new Error(&#096;HTTP ${response.status}&#096;);<br \/>\n          error.statusCode &#061; response.status;<\/p>\n<p>          \/\/ \u51b3\u5b9a\u662f\u5426\u91cd\u8bd5<br \/>\n          if (this.shouldRetry(error, attempt)) {<br \/>\n            lastError &#061; error;<br \/>\n            context.errors.push(error);<br \/>\n            await this.handleRetry(context, attempt, error);<br \/>\n            continue;<br \/>\n          }<\/p>\n<p>          throw error;<br \/>\n        }<\/p>\n<p>        \/\/ \u8bf7\u6c42\u6210\u529f<br \/>\n        if (attempt &gt; 0) {<br \/>\n          this.metrics.retries&#043;&#043;;<br \/>\n        }<\/p>\n<p>        return response;<\/p>\n<p>      } catch (error) {<br \/>\n        lastError &#061; error;<br \/>\n        context.errors.push(error);<\/p>\n<p>        \/\/ \u68c0\u67e5\u662f\u5426\u5e94\u8be5\u91cd\u8bd5<br \/>\n        if (attempt &lt; this.config.maxRetries &amp;&amp; this.shouldRetry(error, attempt)) {<br \/>\n          await this.handleRetry(context, attempt, error);<br \/>\n          continue;<br \/>\n        }<\/p>\n<p>        \/\/ \u4e0d\u518d\u91cd\u8bd5&#xff0c;\u629b\u51fa\u9519\u8bef<br \/>\n        break;<br \/>\n      }<br \/>\n    }<\/p>\n<p>    throw lastError || new Error(&#039;Request failed&#039;);<br \/>\n  }<\/p>\n<p>  calculateTimeout(attempt) {<br \/>\n    \/\/ \u81ea\u9002\u5e94\u8d85\u65f6&#xff1a;\u57fa\u4e8e\u5386\u53f2\u54cd\u5e94\u65f6\u95f4<br \/>\n    const baseTimeout &#061; this.config.timeout;<\/p>\n<p>    \/\/ \u8003\u8651\u7f51\u7edc\u8d28\u91cf<br \/>\n    const networkFactor &#061; Math.max(0.5, Math.min(2.0,<br \/>\n      this.networkQuality.latency \/ 100)); \/\/ \u5047\u8bbe\u57fa\u51c6\u5ef6\u8fdf100ms<\/p>\n<p>    \/\/ \u6307\u6570\u9000\u907f<br \/>\n    const backoffTimeout &#061; baseTimeout *<br \/>\n      Math.pow(this.config.timeoutMultiplier, attempt) *<br \/>\n      networkFactor;<\/p>\n<p>    return Math.min(backoffTimeout, this.config.maxDelay * 2);<br \/>\n  }<\/p>\n<p>  calculateDelay(attempt, error) {<br \/>\n    \/\/ \u57fa\u7840\u9000\u907f<br \/>\n    let delay &#061; this.config.baseDelay * Math.pow(this.config.backoffFactor, attempt);<\/p>\n<p>    \/\/ \u6dfb\u52a0\u6296\u52a8<br \/>\n    const jitter &#061; 1 &#043; (Math.random() * 2 &#8211; 1) * this.config.jitter;<br \/>\n    delay *&#061; jitter;<\/p>\n<p>    \/\/ \u57fa\u4e8e\u9519\u8bef\u7c7b\u578b\u8c03\u6574<br \/>\n    if (error &amp;&amp; error.name &#061;&#061;&#061; &#039;TimeoutError&#039;) {<br \/>\n      delay *&#061; 1.5; \/\/ \u8d85\u65f6\u9519\u8bef\u589e\u52a0\u5ef6\u8fdf<br \/>\n    }<\/p>\n<p>    \/\/ \u57fa\u4e8e\u7f51\u7edc\u8d28\u91cf\u8c03\u6574<br \/>\n    if (this.networkQuality.reliability &lt; 0.5) {<br \/>\n      delay *&#061; 2; \/\/ \u7f51\u7edc\u4e0d\u53ef\u9760\u65f6\u589e\u52a0\u5ef6\u8fdf<br \/>\n    }<\/p>\n<p>    return Math.min(delay, this.config.maxDelay);<br \/>\n  }<\/p>\n<p>  shouldRetry(error, attempt) {<br \/>\n    \/\/ \u57fa\u4e8e\u9519\u8bef\u7c7b\u578b\u51b3\u5b9a\u662f\u5426\u91cd\u8bd5<br \/>\n    const retryableErrors &#061; [<br \/>\n      &#039;TimeoutError&#039;,<br \/>\n      &#039;TypeError&#039;, \/\/ \u7f51\u7edc\u9519\u8bef<br \/>\n      &#039;AbortError&#039;<br \/>\n    ];<\/p>\n<p>    if (retryableErrors.includes(error.name)) {<br \/>\n      return true;<br \/>\n    }<\/p>\n<p>    \/\/ \u57fa\u4e8eHTTP\u72b6\u6001\u7801\u51b3\u5b9a\u662f\u5426\u91cd\u8bd5<br \/>\n    if (error.statusCode) {<br \/>\n      const retryableStatusCodes &#061; [408, 429, 500, 502, 503, 504];<br \/>\n      return retryableStatusCodes.includes(error.statusCode);<br \/>\n    }<\/p>\n<p>    \/\/ \u57fa\u4e8e\u5c1d\u8bd5\u6b21\u6570\u51b3\u5b9a\u662f\u5426\u91cd\u8bd5<br \/>\n    if (attempt &gt;&#061; this.config.maxRetries) {<br \/>\n      return false;<br \/>\n    }<\/p>\n<p>    \/\/ \u9ed8\u8ba4\u4e0d\u91cd\u8bd5<br \/>\n    return false;<br \/>\n  }<\/p>\n<p>  async handleRetry(context, attempt, error) {<br \/>\n    \/\/ \u8ba1\u7b97\u5ef6\u8fdf<br \/>\n    const delay &#061; this.calculateDelay(attempt, error);<br \/>\n    context.delays.push(delay);<\/p>\n<p>    \/\/ \u8bb0\u5f55\u91cd\u8bd5<br \/>\n    this.logRetryAttempt(context, attempt, delay, error);<\/p>\n<p>    \/\/ \u7b49\u5f85\u5ef6\u8fdf<br \/>\n    await this.sleep(delay);<br \/>\n  }<\/p>\n<p>  async executeFallback(failureType, context, options, error) {<br \/>\n    const fallbackStrategy &#061; this.config.fallbackStrategies[failureType] ||<br \/>\n                            this.config.fallbackStrategies.default;<\/p>\n<p>    switch (fallbackStrategy) {<br \/>\n      case &#039;return_cached&#039;:<br \/>\n        return this.returnCachedResponse(context.url, options);<\/p>\n<p>      case &#039;retry_with_exponential_backoff&#039;:<br \/>\n        \/\/ \u5df2\u5728\u4e0a\u5c42\u5904\u7406<br \/>\n        throw error;<\/p>\n<p>      case &#039;use_alternative_endpoint&#039;:<br \/>\n        return this.useAlternativeEndpoint(context.url, options);<\/p>\n<p>      case &#039;return_degraded_response&#039;:<br \/>\n        return this.returnDegradedResponse(context, options);<\/p>\n<p>      case &#039;notify_user&#039;:<br \/>\n        return this.notifyUserAndFail(context, error);<\/p>\n<p>      default:<br \/>\n        throw error;<br \/>\n    }<br \/>\n  }<\/p>\n<p>  async returnCachedResponse(url, options) {<br \/>\n    \/\/ \u68c0\u67e5\u7f13\u5b58<br \/>\n    const cacheKey &#061; this.getCacheKey(url, options);<br \/>\n    const cached &#061; this.fallbackCache.get(cacheKey);<\/p>\n<p>    if (cached &amp;&amp; Date.now() &#8211; cached.timestamp &lt; 300000) { \/\/ 5\u5206\u949f\u7f13\u5b58<br \/>\n      console.log(&#096;Using cached response for ${url}&#096;);<\/p>\n<p>      \/\/ \u521b\u5efa\u6a21\u62df\u54cd\u5e94<br \/>\n      return new Response(JSON.stringify(cached.data), {<br \/>\n        status: 200,<br \/>\n        headers: {<br \/>\n          &#039;Content-Type&#039;: &#039;application\/json&#039;,<br \/>\n          &#039;X-Cached-Response&#039;: &#039;true&#039;,<br \/>\n          &#039;X-Cache-Age&#039;: Math.round((Date.now() &#8211; cached.timestamp) \/ 1000) &#043; &#039;s&#039;<br \/>\n        }<br \/>\n      });<br \/>\n    }<\/p>\n<p>    throw new Error(&#039;No cached response available&#039;);<br \/>\n  }<\/p>\n<p>  async useAlternativeEndpoint(originalUrl, options) {<br \/>\n    \/\/ \u751f\u6210\u66ff\u4ee3\u7aef\u70b9<br \/>\n    const alternativeUrl &#061; this.generateAlternativeUrl(originalUrl);<\/p>\n<p>    if (alternativeUrl &amp;&amp; alternativeUrl !&#061;&#061; originalUrl) {<br \/>\n      console.log(&#096;Trying alternative endpoint: ${alternativeUrl}&#096;);<\/p>\n<p>      \/\/ \u4f7f\u7528\u66ff\u4ee3\u7aef\u70b9\u91cd\u8bd5&#xff08;\u6700\u591a\u4e00\u6b21&#xff09;<br \/>\n      return this.fetchWithRetry(alternativeUrl, {<br \/>\n        &#8230;options,<br \/>\n        headers: {<br \/>\n          &#8230;options.headers,<br \/>\n          &#039;X-Original-URL&#039;: originalUrl<br \/>\n        }<br \/>\n      });<br \/>\n    }<\/p>\n<p>    throw new Error(&#039;No alternative endpoint available&#039;);<br \/>\n  }<\/p>\n<p>  generateAlternativeUrl(url) {<br \/>\n    const urlObj &#061; new URL(url);<\/p>\n<p>    \/\/ \u5c1d\u8bd5\u4e0d\u540c\u7684\u7b56\u7565<br \/>\n    const strategies &#061; [<br \/>\n      \/\/ 1. \u4f7f\u7528\u5907\u7528\u57df\u540d<br \/>\n      () &#061;&gt; {<br \/>\n        if (urlObj.hostname &#061;&#061;&#061; &#039;api.example.com&#039;) {<br \/>\n          urlObj.hostname &#061; &#039;api-backup.example.com&#039;;<br \/>\n          return urlObj.toString();<br \/>\n        }<br \/>\n        return null;<br \/>\n      },<\/p>\n<p>      \/\/ 2. \u4f7f\u7528\u4e0d\u540c\u7aef\u53e3<br \/>\n      () &#061;&gt; {<br \/>\n        if (!urlObj.port) {<br \/>\n          urlObj.port &#061; &#039;8080&#039;;<br \/>\n          return urlObj.toString();<br \/>\n        }<br \/>\n        return null;<br \/>\n      },<\/p>\n<p>      \/\/ 3. \u4f7f\u7528\u4e0d\u540cAPI\u7248\u672c<br \/>\n      () &#061;&gt; {<br \/>\n        if (urlObj.pathname.includes(&#039;\/v1\/&#039;)) {<br \/>\n          return urlObj.toString().replace(&#039;\/v1\/&#039;, &#039;\/v2\/&#039;);<br \/>\n        }<br \/>\n        return null;<br \/>\n      },<\/p>\n<p>      \/\/ 4. \u4f7f\u7528HTTP\u4ee3\u66ffHTTPS&#xff08;\u4ec5\u9650\u5f00\u53d1\u73af\u5883&#xff09;<br \/>\n      () &#061;&gt; {<br \/>\n        if (urlObj.protocol &#061;&#061;&#061; &#039;https:&#039; &amp;&amp; window.location.hostname &#061;&#061;&#061; &#039;localhost&#039;) {<br \/>\n          urlObj.protocol &#061; &#039;http:&#039;;<br \/>\n          return urlObj.toString();<br \/>\n        }<br \/>\n        return null;<br \/>\n      }<br \/>\n    ];<\/p>\n<p>    for (const strategy of strategies) {<br \/>\n      const result &#061; strategy();<br \/>\n      if (result) {<br \/>\n        return result;<br \/>\n      }<br \/>\n    }<\/p>\n<p>    return null;<br \/>\n  }<\/p>\n<p>  returnDegradedResponse(context, options) {<br \/>\n    \/\/ \u8fd4\u56de\u964d\u7ea7\u54cd\u5e94<br \/>\n    const degradedData &#061; {<br \/>\n      data: null,<br \/>\n      warning: {<br \/>\n        code: &#039;DEGRADED_SERVICE&#039;,<br \/>\n        message: &#039;Service is temporarily degraded. Some features may be unavailable.&#039;,<br \/>\n        request_id: context.requestId,<br \/>\n        original_url: context.url<br \/>\n      },<br \/>\n      metadata: {<br \/>\n        degraded: true,<br \/>\n        timestamp: new Date().toISOString(),<br \/>\n        attempts: context.attempt<br \/>\n      }<br \/>\n    };<\/p>\n<p>    return new Response(JSON.stringify(degradedData), {<br \/>\n      status: 200,<br \/>\n      headers: {<br \/>\n        &#039;Content-Type&#039;: &#039;application\/json&#039;,<br \/>\n        &#039;X-Degraded-Service&#039;: &#039;true&#039;<br \/>\n      }<br \/>\n    });<br \/>\n  }<\/p>\n<p>  notifyUserAndFail(context, error) {<br \/>\n    \/\/ \u663e\u793a\u7528\u6237\u901a\u77e5<br \/>\n    this.showUserNotification({<br \/>\n      type: &#039;error&#039;,<br \/>\n      title: &#039;Connection Error&#039;,<br \/>\n      message: &#039;Unable to connect to the server. Please check your connection and try again.&#039;,<br \/>\n      details: {<br \/>\n        requestId: context.requestId,<br \/>\n        url: context.url,<br \/>\n        error: error.message<br \/>\n      }<br \/>\n    });<\/p>\n<p>    throw error;<br \/>\n  }<\/p>\n<p>  \/\/ \u7194\u65ad\u5668\u76f8\u5173\u65b9\u6cd5<br \/>\n  getCircuitKey(url, method) {<br \/>\n    const urlObj &#061; new URL(url);<br \/>\n    return &#096;${method}:${urlObj.hostname}:${urlObj.pathname.split(&#039;\/&#039;)[1] || &#039;&#039;}&#096;;<br \/>\n  }<\/p>\n<p>  allowRequest(circuitKey) {<br \/>\n    if (!this.circuitBreakers.has(circuitKey)) {<br \/>\n      this.circuitBreakers.set(circuitKey, {<br \/>\n        state: &#039;closed&#039;,<br \/>\n        failureCount: 0,<br \/>\n        successCount: 0,<br \/>\n        lastFailure: null,<br \/>\n        openedAt: null,<br \/>\n        halfOpenAttempts: 0<br \/>\n      });<br \/>\n      return true;<br \/>\n    }<\/p>\n<p>    const cb &#061; this.circuitBreakers.get(circuitKey);<\/p>\n<p>    if (cb.state &#061;&#061;&#061; &#039;open&#039;) {<br \/>\n      \/\/ \u68c0\u67e5\u662f\u5426\u5e94\u8be5\u8fdb\u5165\u534a\u5f00\u72b6\u6001<br \/>\n      if (Date.now() &#8211; cb.openedAt &gt;&#061; this.config.circuitBreaker.resetTimeout) {<br \/>\n        cb.state &#061; &#039;half_open&#039;;<br \/>\n        cb.halfOpenAttempts &#061; 0;<br \/>\n        return true;<br \/>\n      }<br \/>\n      return false;<br \/>\n    }<\/p>\n<p>    if (cb.state &#061;&#061;&#061; &#039;half_open&#039;) {<br \/>\n      if (cb.halfOpenAttempts &gt;&#061; this.config.circuitBreaker.halfOpenMaxRequests) {<br \/>\n        return false;<br \/>\n      }<br \/>\n      cb.halfOpenAttempts&#043;&#043;;<br \/>\n      return true;<br \/>\n    }<\/p>\n<p>    return true; \/\/ closed\u72b6\u6001<br \/>\n  }<\/p>\n<p>  recordSuccess(circuitKey) {<br \/>\n    const cb &#061; this.circuitBreakers.get(circuitKey);<br \/>\n    if (cb) {<br \/>\n      cb.successCount&#043;&#043;;<br \/>\n      cb.failureCount &#061; Math.max(0, cb.failureCount &#8211; 1); \/\/ \u8870\u51cf\u5931\u8d25\u8ba1\u6570<\/p>\n<p>      if (cb.state &#061;&#061;&#061; &#039;half_open&#039; &amp;&amp; cb.successCount &gt;&#061; 3) {<br \/>\n        cb.state &#061; &#039;closed&#039;;<br \/>\n        cb.failureCount &#061; 0;<br \/>\n        cb.successCount &#061; 0;<br \/>\n      }<br \/>\n    }<br \/>\n  }<\/p>\n<p>  recordFailure(circuitKey, error) {<br \/>\n    const cb &#061; this.circuitBreakers.get(circuitKey);<br \/>\n    if (cb) {<br \/>\n      cb.failureCount&#043;&#043;;<br \/>\n      cb.lastFailure &#061; Date.now();<\/p>\n<p>      \/\/ \u68c0\u67e5\u662f\u5426\u5e94\u8be5\u6253\u5f00\u7194\u65ad\u5668<br \/>\n      if (cb.state &#061;&#061;&#061; &#039;closed&#039; &amp;&amp;<br \/>\n          cb.failureCount &gt;&#061; this.config.circuitBreaker.failureThreshold) {<br \/>\n        cb.state &#061; &#039;open&#039;;<br \/>\n        cb.openedAt &#061; Date.now();<br \/>\n        this.logCircuitEvent(circuitKey, &#039;opened&#039;, error);<br \/>\n      }<br \/>\n    }<br \/>\n  }<\/p>\n<p>  \/\/ \u7f51\u7edc\u76d1\u63a7<br \/>\n  initNetworkMonitoring() {<br \/>\n    \/\/ \u5b9a\u671f\u68c0\u67e5\u7f51\u7edc\u8d28\u91cf<br \/>\n    setInterval(() &#061;&gt; this.checkNetworkQuality(), 30000);<\/p>\n<p>    \/\/ \u76d1\u542c\u5728\u7ebf\/\u79bb\u7ebf\u4e8b\u4ef6<br \/>\n    window.addEventListener(&#039;online&#039;, () &#061;&gt; this.handleNetworkOnline());<br \/>\n    window.addEventListener(&#039;offline&#039;, () &#061;&gt; this.handleNetworkOffline());<br \/>\n  }<\/p>\n<p>  async checkNetworkQuality() {<br \/>\n    try {<br \/>\n      \/\/ \u6d4b\u91cf\u5230\u5df2\u77e5\u670d\u52a1\u5668\u7684\u5ef6\u8fdf<br \/>\n      const latency &#061; await this.measureLatency();<\/p>\n<p>      \/\/ \u6d4b\u91cf\u541e\u5410\u91cf<br \/>\n      const throughput &#061; await this.measureThroughput();<\/p>\n<p>      \/\/ \u8ba1\u7b97\u53ef\u9760\u6027<br \/>\n      const recentRequests &#061; Array.from(this.requestHistory.values())<br \/>\n        .slice(-20); \/\/ \u6700\u8fd120\u4e2a\u8bf7\u6c42<\/p>\n<p>      const successfulRequests &#061; recentRequests.filter(r &#061;&gt; r.success);<br \/>\n      const reliability &#061; recentRequests.length &gt; 0 ?<br \/>\n        successfulRequests.length \/ recentRequests.length : 1.0;<\/p>\n<p>      this.networkQuality &#061; {<br \/>\n        latency,<br \/>\n        throughput,<br \/>\n        reliability<br \/>\n      };<\/p>\n<p>      console.log(&#096;Network quality updated:&#096;, this.networkQuality);<\/p>\n<p>    } catch (error) {<br \/>\n      console.warn(&#039;Network quality check failed:&#039;, error);<br \/>\n    }<br \/>\n  }<\/p>\n<p>  async measureLatency() {<br \/>\n    const startTime &#061; Date.now();<br \/>\n    try {<br \/>\n      await fetch(&#039;\/ping&#039;, {<br \/>\n        method: &#039;HEAD&#039;,<br \/>\n        cache: &#039;no-store&#039;<br \/>\n      });<br \/>\n      return Date.now() &#8211; startTime;<br \/>\n    } catch {<br \/>\n      return 1000; \/\/ \u9ed8\u8ba4\u9ad8\u5ef6\u8fdf<br \/>\n    }<br \/>\n  }<\/p>\n<p>  async measureThroughput() {<br \/>\n    \/\/ \u7b80\u5355\u6d4b\u8bd5&#xff1a;\u4e0b\u8f7d\u5c0f\u6587\u4ef6\u6d4b\u91cf\u901f\u5ea6<br \/>\n    const testSize &#061; 10000; \/\/ 10KB<br \/>\n    const startTime &#061; Date.now();<\/p>\n<p>    try {<br \/>\n      const response &#061; await fetch(&#096;\/test-data?size&#061;${testSize}&#096;);<br \/>\n      const blob &#061; await response.blob();<br \/>\n      const duration &#061; Date.now() &#8211; startTime;<\/p>\n<p>      return duration &gt; 0 ? (testSize * 8) \/ (duration \/ 1000) : 0; \/\/ bps<br \/>\n    } catch {<br \/>\n      return 0;<br \/>\n    }<br \/>\n  }<\/p>\n<p>  updateNetworkQuality(context, success) {<br \/>\n    \/\/ \u8bb0\u5f55\u8bf7\u6c42\u5386\u53f2<br \/>\n    this.requestHistory.set(context.requestId, {<br \/>\n      url: context.url,<br \/>\n      success,<br \/>\n      duration: Date.now() &#8211; context.startTime,<br \/>\n      timestamp: Date.now(),<br \/>\n      attempts: context.attempt<br \/>\n    });<\/p>\n<p>    \/\/ \u4fdd\u6301\u5386\u53f2\u8bb0\u5f55\u5927\u5c0f<br \/>\n    if (this.requestHistory.size &gt; 100) {<br \/>\n      const oldestKey &#061; this.requestHistory.keys().next().value;<br \/>\n      this.requestHistory.delete(oldestKey);<br \/>\n    }<br \/>\n  }<\/p>\n<p>  handleNetworkOnline() {<br \/>\n    console.log(&#039;Network came online, resetting circuit breakers&#039;);<br \/>\n    \/\/ \u91cd\u7f6e\u6240\u6709\u7194\u65ad\u5668<br \/>\n    for (const cb of this.circuitBreakers.values()) {<br \/>\n      if (cb.state &#061;&#061;&#061; &#039;open&#039;) {<br \/>\n        cb.state &#061; &#039;half_open&#039;;<br \/>\n        cb.halfOpenAttempts &#061; 0;<br \/>\n      }<br \/>\n    }<br \/>\n  }<\/p>\n<p>  handleNetworkOffline() {<br \/>\n    console.log(&#039;Network went offline&#039;);<br \/>\n    this.networkQuality.reliability &#061; 0;<br \/>\n  }<\/p>\n<p>  \/\/ \u5de5\u5177\u65b9\u6cd5<br \/>\n  generateRequestId() {<br \/>\n    return &#096;req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}&#096;;<br \/>\n  }<\/p>\n<p>  getCacheKey(url, options) {<br \/>\n    const urlObj &#061; new URL(url);<br \/>\n    return &#096;${urlObj.pathname}?${urlObj.searchParams.toString()}:${options.method || &#039;GET&#039;}&#096;;<br \/>\n  }<\/p>\n<p>  sleep(ms) {<br \/>\n    return new Promise(resolve &#061;&gt; setTimeout(resolve, ms));<br \/>\n  }<\/p>\n<p>  logRetryAttempt(context, attempt, delay, error) {<br \/>\n    console.log(&#096;Retry attempt ${attempt &#043; 1} for ${context.url}, delay: ${delay}ms&#096;, error);<br \/>\n  }<\/p>\n<p>  logCircuitEvent(circuitKey, event, error) {<br \/>\n    console.log(&#096;Circuit breaker ${circuitKey} ${event}&#096;, error);<br \/>\n  }<\/p>\n<p>  showUserNotification(notification) {<br \/>\n    \/\/ \u5728\u5b9e\u9645\u5e94\u7528\u4e2d&#xff0c;\u8fd9\u91cc\u4f1a\u663e\u793aUI\u901a\u77e5<br \/>\n    console.log(&#039;User notification:&#039;, notification);<br \/>\n  }<\/p>\n<p>  getMetrics() {<br \/>\n    return {<br \/>\n      &#8230;this.metrics,<br \/>\n      circuitBreakers: {<br \/>\n        total: this.circuitBreakers.size,<br \/>\n        open: Array.from(this.circuitBreakers.values()).filter(cb &#061;&gt; cb.state &#061;&#061;&#061; &#039;open&#039;).length,<br \/>\n        halfOpen: Array.from(this.circuitBreakers.values()).filter(cb &#061;&gt; cb.state &#061;&#061;&#061; &#039;half_open&#039;).length<br \/>\n      },<br \/>\n      networkQuality: this.networkQuality<br \/>\n    };<br \/>\n  }<br \/>\n}<\/p>\n<p>\/\/ \u4f7f\u7528\u793a\u4f8b<br \/>\nconst client &#061; new AdaptiveRetryClient({<br \/>\n  maxRetries: 3,<br \/>\n  timeout: 10000,<br \/>\n  fallbackStrategies: {<br \/>\n    timeout: &#039;return_cached&#039;,<br \/>\n    network_error: &#039;use_alternative_endpoint&#039;,<br \/>\n    server_error: &#039;return_degraded_response&#039;<br \/>\n  }<br \/>\n});<\/p>\n<p>\/\/ \u53d1\u8d77\u8bf7\u6c42<br \/>\nasync function fetchUserData(userId) {<br \/>\n  try {<br \/>\n    const response &#061; await client.fetchWithRetry(&#096;\/api\/users\/${userId}&#096;, {<br \/>\n      headers: {<br \/>\n        &#039;Authorization&#039;: &#096;Bearer ${token}&#096;<br \/>\n      }<br \/>\n    });<\/p>\n<p>    if (response.headers.get(&#039;X-Degraded-Service&#039;)) {<br \/>\n      \/\/ \u5904\u7406\u964d\u7ea7\u54cd\u5e94<br \/>\n      return handleDegradedResponse(await response.json());<br \/>\n    }<\/p>\n<p>    return await response.json();<\/p>\n<p>  } catch (error) {<br \/>\n    console.error(&#039;Failed to fetch user data:&#039;, error);<br \/>\n    return null;<br \/>\n  }<br \/>\n}<\/p>\n<h3>21.5 \u76d1\u63a7\u4e0e\u5206\u6790<\/h3>\n<h4>21.5.1 \u8d85\u65f6\u76d1\u63a7\u4e0e\u6839\u56e0\u5206\u6790\u7cfb\u7edf<\/h4>\n<p>python<\/p>\n<p># \u8d85\u65f6\u76d1\u63a7\u4e0e\u6839\u56e0\u5206\u6790\u7cfb\u7edf<br \/>\nfrom datetime import datetime, timedelta<br \/>\nfrom collections import defaultdict, deque<br \/>\nimport statistics<br \/>\nfrom typing import Dict, List, Optional, Tuple<br \/>\nimport numpy as np<br \/>\nfrom scipy import stats<\/p>\n<p>class TimeoutRootCauseAnalyzer:<br \/>\n    &#034;&#034;&#034;\u8d85\u65f6\u6839\u56e0\u5206\u6790\u7cfb\u7edf&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, config: Dict &#061; None):<br \/>\n        self.config &#061; {<br \/>\n            &#039;window_size&#039;: 1000,  # \u5206\u6790\u7a97\u53e3\u5927\u5c0f<br \/>\n            &#039;percentile_threshold&#039;: 95,  # \u767e\u5206\u4f4d\u9608\u503c<br \/>\n            &#039;anomaly_threshold&#039;: 3.0,  # \u5f02\u5e38\u9608\u503c&#xff08;\u6807\u51c6\u5dee&#xff09;<br \/>\n            &#039;correlation_threshold&#039;: 0.7,  # \u76f8\u5173\u6027\u9608\u503c<br \/>\n            &#039;min_samples&#039;: 50,  # \u6700\u5c0f\u6837\u672c\u6570<br \/>\n            ** (config or {})<br \/>\n        }<\/p>\n<p>        # \u6570\u636e\u5b58\u50a8<br \/>\n        self.timeout_data &#061; deque(maxlen&#061;self.config[&#039;window_size&#039;])<br \/>\n        self.normal_data &#061; deque(maxlen&#061;self.config[&#039;window_size&#039;])<br \/>\n        self.metrics_history &#061; defaultdict(lambda: deque(maxlen&#061;100))<\/p>\n<p>        # \u7edf\u8ba1\u4fe1\u606f<br \/>\n        self.statistics &#061; {<br \/>\n            &#039;total_timeouts&#039;: 0,<br \/>\n            &#039;timeout_rate&#039;: 0.0,<br \/>\n            &#039;avg_timeout_duration&#039;: 0.0,<br \/>\n            &#039;timeout_patterns&#039;: defaultdict(int)<br \/>\n        }<\/p>\n<p>        # \u6839\u56e0\u5206\u6790\u6a21\u578b<br \/>\n        self.cause_models &#061; self.initialize_cause_models()<\/p>\n<p>    def initialize_cause_models(self) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u521d\u59cb\u5316\u6839\u56e0\u5206\u6790\u6a21\u578b&#034;&#034;&#034;<br \/>\n        return {<br \/>\n            &#039;network&#039;: {<br \/>\n                &#039;indicators&#039;: [&#039;latency&#039;, &#039;packet_loss&#039;, &#039;throughput&#039;],<br \/>\n                &#039;thresholds&#039;: {<br \/>\n                    &#039;latency&#039;: 500,  # ms<br \/>\n                    &#039;packet_loss&#039;: 0.1,  # 10%<br \/>\n                    &#039;throughput&#039;: 1024  # 1KB\/s<br \/>\n                },<br \/>\n                &#039;weight&#039;: 0.3<br \/>\n            },<br \/>\n            &#039;server&#039;: {<br \/>\n                &#039;indicators&#039;: [&#039;cpu_usage&#039;, &#039;memory_usage&#039;, &#039;disk_io&#039;],<br \/>\n                &#039;thresholds&#039;: {<br \/>\n                    &#039;cpu_usage&#039;: 0.8,  # 80%<br \/>\n                    &#039;memory_usage&#039;: 0.9,  # 90%<br \/>\n                    &#039;disk_io&#039;: 1000  # 1000 IOPS<br \/>\n                },<br \/>\n                &#039;weight&#039;: 0.4<br \/>\n            },<br \/>\n            &#039;application&#039;: {<br \/>\n                &#039;indicators&#039;: [&#039;response_time&#039;, &#039;error_rate&#039;, &#039;queue_length&#039;],<br \/>\n                &#039;thresholds&#039;: {<br \/>\n                    &#039;response_time&#039;: 5.0,  # 5\u79d2<br \/>\n                    &#039;error_rate&#039;: 0.05,  # 5%<br \/>\n                    &#039;queue_length&#039;: 100<br \/>\n                },<br \/>\n                &#039;weight&#039;: 0.2<br \/>\n            },<br \/>\n            &#039;client&#039;: {<br \/>\n                &#039;indicators&#039;: [&#039;request_size&#039;, &#039;concurrent_requests&#039;, &#039;geolocation&#039;],<br \/>\n                &#039;thresholds&#039;: {<br \/>\n                    &#039;request_size&#039;: 10 * 1024 * 1024,  # 10MB<br \/>\n                    &#039;concurrent_requests&#039;: 10,<br \/>\n                    &#039;geolocation_distance&#039;: 1000  # 1000km<br \/>\n                },<br \/>\n                &#039;weight&#039;: 0.1<br \/>\n            }<br \/>\n        }<\/p>\n<p>    def record_timeout(self, timeout_event: Dict):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u8d85\u65f6\u4e8b\u4ef6&#034;&#034;&#034;<br \/>\n        self.timeout_data.append({<br \/>\n            &#039;timestamp&#039;: datetime.utcnow(),<br \/>\n            &#039;data&#039;: timeout_event<br \/>\n        })<\/p>\n<p>        self.statistics[&#039;total_timeouts&#039;] &#043;&#061; 1<\/p>\n<p>        # \u66f4\u65b0\u7edf\u8ba1<br \/>\n        self.update_statistics()<\/p>\n<p>        # \u5206\u6790\u6839\u56e0<br \/>\n        root_cause &#061; self.analyze_root_cause(timeout_event)<\/p>\n<p>        # \u8bb0\u5f55\u6a21\u5f0f<br \/>\n        if root_cause:<br \/>\n            self.statistics[&#039;timeout_patterns&#039;][root_cause[&#039;primary_cause&#039;]] &#043;&#061; 1<\/p>\n<p>        return root_cause<\/p>\n<p>    def record_normal_request(self, request_data: Dict):<br \/>\n        &#034;&#034;&#034;\u8bb0\u5f55\u6b63\u5e38\u8bf7\u6c42&#034;&#034;&#034;<br \/>\n        self.normal_data.append({<br \/>\n            &#039;timestamp&#039;: datetime.utcnow(),<br \/>\n            &#039;data&#039;: request_data<br \/>\n        })<\/p>\n<p>        # \u66f4\u65b0\u6307\u6807\u5386\u53f2<br \/>\n        for metric, value in request_data.get(&#039;metrics&#039;, {}).items():<br \/>\n            self.metrics_history[metric].append(value)<\/p>\n<p>    def analyze_root_cause(self, timeout_event: Dict) -&gt; Optional[Dict]:<br \/>\n        &#034;&#034;&#034;\u5206\u6790\u8d85\u65f6\u6839\u56e0&#034;&#034;&#034;<\/p>\n<p>        # \u6536\u96c6\u6307\u6807<br \/>\n        indicators &#061; self.collect_indicators(timeout_event)<\/p>\n<p>        # \u8ba1\u7b97\u5404\u539f\u56e0\u7684\u53ef\u80fd\u6027<br \/>\n        cause_scores &#061; {}<br \/>\n        for cause_name, cause_model in self.cause_models.items():<br \/>\n            score &#061; self.calculate_cause_score(cause_model, indicators)<br \/>\n            cause_scores[cause_name] &#061; score<\/p>\n<p>        # \u627e\u51fa\u4e3b\u8981\u539f\u56e0<br \/>\n        primary_cause &#061; max(cause_scores.items(), key&#061;lambda x: x[1])<\/p>\n<p>        if primary_cause[1] &gt; 0.5:  # \u9608\u503c<br \/>\n            return {<br \/>\n                &#039;primary_cause&#039;: primary_cause[0],<br \/>\n                &#039;confidence&#039;: primary_cause[1],<br \/>\n                &#039;cause_scores&#039;: cause_scores,<br \/>\n                &#039;indicators&#039;: indicators,<br \/>\n                &#039;recommendations&#039;: self.generate_recommendations(primary_cause[0], indicators)<br \/>\n            }<\/p>\n<p>        return None<\/p>\n<p>    def collect_indicators(self, timeout_event: Dict) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u6536\u96c6\u6307\u6807&#034;&#034;&#034;<br \/>\n        indicators &#061; {}<\/p>\n<p>        # \u7f51\u7edc\u6307\u6807<br \/>\n        indicators[&#039;latency&#039;] &#061; timeout_event.get(&#039;latency&#039;, 0)<br \/>\n        indicators[&#039;packet_loss&#039;] &#061; timeout_event.get(&#039;packet_loss&#039;, 0)<br \/>\n        indicators[&#039;throughput&#039;] &#061; timeout_event.get(&#039;throughput&#039;, 0)<\/p>\n<p>        # \u670d\u52a1\u5668\u6307\u6807<br \/>\n        indicators[&#039;cpu_usage&#039;] &#061; timeout_event.get(&#039;cpu_usage&#039;, 0)<br \/>\n        indicators[&#039;memory_usage&#039;] &#061; timeout_event.get(&#039;memory_usage&#039;, 0)<br \/>\n        indicators[&#039;disk_io&#039;] &#061; timeout_event.get(&#039;disk_io&#039;, 0)<\/p>\n<p>        # \u5e94\u7528\u6307\u6807<br \/>\n        indicators[&#039;response_time&#039;] &#061; timeout_event.get(&#039;response_time&#039;, 0)<br \/>\n        indicators[&#039;error_rate&#039;] &#061; timeout_event.get(&#039;error_rate&#039;, 0)<br \/>\n        indicators[&#039;queue_length&#039;] &#061; timeout_event.get(&#039;queue_length&#039;, 0)<\/p>\n<p>        # \u5ba2\u6237\u7aef\u6307\u6807<br \/>\n        indicators[&#039;request_size&#039;] &#061; timeout_event.get(&#039;request_size&#039;, 0)<br \/>\n        indicators[&#039;concurrent_requests&#039;] &#061; timeout_event.get(&#039;concurrent_requests&#039;, 0)<br \/>\n        indicators[&#039;geolocation_distance&#039;] &#061; timeout_event.get(&#039;geolocation_distance&#039;, 0)<\/p>\n<p>        return indicators<\/p>\n<p>    def calculate_cause_score(self, cause_model: Dict, indicators: Dict) -&gt; float:<br \/>\n        &#034;&#034;&#034;\u8ba1\u7b97\u539f\u56e0\u5206\u6570&#034;&#034;&#034;<br \/>\n        score &#061; 0.0<br \/>\n        total_weight &#061; 0.0<\/p>\n<p>        for indicator in cause_model[&#039;indicators&#039;]:<br \/>\n            if indicator in indicators:<br \/>\n                value &#061; indicators[indicator]<br \/>\n                threshold &#061; cause_model[&#039;thresholds&#039;].get(indicator, 0)<\/p>\n<p>                # \u8ba1\u7b97\u6307\u6807\u5206\u6570<br \/>\n                indicator_score &#061; self.calculate_indicator_score(value, threshold)<\/p>\n<p>                # \u52a0\u6743<br \/>\n                score &#043;&#061; indicator_score<br \/>\n                total_weight &#043;&#061; 1.0<\/p>\n<p>        if total_weight &gt; 0:<br \/>\n            score &#061; score \/ total_weight * cause_model[&#039;weight&#039;]<\/p>\n<p>        return score<\/p>\n<p>    def calculate_indicator_score(self, value: float, threshold: float) -&gt; float:<br \/>\n        &#034;&#034;&#034;\u8ba1\u7b97\u6307\u6807\u5206\u6570&#034;&#034;&#034;<br \/>\n        if threshold &#061;&#061; 0:<br \/>\n            return 0.0<\/p>\n<p>        # \u5f52\u4e00\u5316\u5e76\u8ba1\u7b97\u8d85\u51fa\u7a0b\u5ea6<br \/>\n        normalized &#061; min(value \/ threshold, 2.0)  # \u9650\u5236\u57282\u500d\u4ee5\u5185<br \/>\n        score &#061; max(0, normalized &#8211; 1)  # \u8d85\u8fc7\u9608\u503c\u90e8\u5206<\/p>\n<p>        return score<\/p>\n<p>    def generate_recommendations(self, cause: str, indicators: Dict) -&gt; List[str]:<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u63a8\u8350\u89e3\u51b3\u65b9\u6848&#034;&#034;&#034;<br \/>\n        recommendations &#061; []<\/p>\n<p>        if cause &#061;&#061; &#039;network&#039;:<br \/>\n            if indicators.get(&#039;latency&#039;, 0) &gt; 500:<br \/>\n                recommendations.append(&#039;\u4f18\u5316\u7f51\u7edc\u8def\u7531\u6216\u4f7f\u7528CDN&#039;)<br \/>\n            if indicators.get(&#039;packet_loss&#039;, 0) &gt; 0.1:<br \/>\n                recommendations.append(&#039;\u68c0\u67e5\u7f51\u7edc\u7a33\u5b9a\u6027\u6216\u589e\u52a0\u91cd\u8bd5\u673a\u5236&#039;)<br \/>\n            if indicators.get(&#039;throughput&#039;, 0) &lt; 1024:<br \/>\n                recommendations.append(&#039;\u5347\u7ea7\u7f51\u7edc\u5e26\u5bbd\u6216\u538b\u7f29\u4f20\u8f93\u6570\u636e&#039;)<\/p>\n<p>        elif cause &#061;&#061; &#039;server&#039;:<br \/>\n            if indicators.get(&#039;cpu_usage&#039;, 0) &gt; 0.8:<br \/>\n                recommendations.append(&#039;\u589e\u52a0CPU\u8d44\u6e90\u6216\u4f18\u5316\u4ee3\u7801&#039;)<br \/>\n            if indicators.get(&#039;memory_usage&#039;, 0) &gt; 0.9:<br \/>\n                recommendations.append(&#039;\u589e\u52a0\u5185\u5b58\u6216\u4f18\u5316\u5185\u5b58\u4f7f\u7528&#039;)<br \/>\n            if indicators.get(&#039;disk_io&#039;, 0) &gt; 1000:<br \/>\n                recommendations.append(&#039;\u4f18\u5316\u78c1\u76d8I\/O\u6216\u4f7f\u7528SSD&#039;)<\/p>\n<p>        elif cause &#061;&#061; &#039;application&#039;:<br \/>\n            if indicators.get(&#039;response_time&#039;, 0) &gt; 5.0:<br \/>\n                recommendations.append(&#039;\u4f18\u5316\u6570\u636e\u5e93\u67e5\u8be2\u6216\u7f13\u5b58\u7b56\u7565&#039;)<br \/>\n            if indicators.get(&#039;error_rate&#039;, 0) &gt; 0.05:<br \/>\n                recommendations.append(&#039;\u4fee\u590d\u5e94\u7528\u9519\u8bef\u6216\u589e\u52a0\u9519\u8bef\u5904\u7406&#039;)<br \/>\n            if indicators.get(&#039;queue_length&#039;, 0) &gt; 100:<br \/>\n                recommendations.append(&#039;\u589e\u52a0\u5904\u7406\u80fd\u529b\u6216\u5b9e\u73b0\u8d1f\u8f7d\u5747\u8861&#039;)<\/p>\n<p>        elif cause &#061;&#061; &#039;client&#039;:<br \/>\n            if indicators.get(&#039;request_size&#039;, 0) &gt; 10 * 1024 * 1024:<br \/>\n                recommendations.append(&#039;\u538b\u7f29\u8bf7\u6c42\u6570\u636e\u6216\u5206\u5757\u4e0a\u4f20&#039;)<br \/>\n            if indicators.get(&#039;concurrent_requests&#039;, 0) &gt; 10:<br \/>\n                recommendations.append(&#039;\u9650\u5236\u5ba2\u6237\u7aef\u5e76\u53d1\u8bf7\u6c42\u6570&#039;)<\/p>\n<p>        # \u901a\u7528\u63a8\u8350<br \/>\n        recommendations.extend([<br \/>\n            &#039;\u589e\u52a0\u8d85\u65f6\u65f6\u95f4\u914d\u7f6e&#039;,<br \/>\n            &#039;\u5b9e\u73b0\u66f4\u597d\u7684\u91cd\u8bd5\u673a\u5236&#039;,<br \/>\n            &#039;\u76d1\u63a7\u7cfb\u7edf\u6027\u80fd\u6307\u6807&#039;<br \/>\n        ])<\/p>\n<p>        return recommendations<\/p>\n<p>    def detect_anomalies(self) -&gt; List[Dict]:<br \/>\n        &#034;&#034;&#034;\u68c0\u6d4b\u5f02\u5e38\u6a21\u5f0f&#034;&#034;&#034;<br \/>\n        anomalies &#061; []<\/p>\n<p>        if len(self.timeout_data) &lt; self.config[&#039;min_samples&#039;]:<br \/>\n            return anomalies<\/p>\n<p>        # \u5206\u6790\u8d85\u65f6\u7387<br \/>\n        timeout_rate &#061; self.calculate_timeout_rate()<br \/>\n        if timeout_rate &gt; self.calculate_threshold(&#039;timeout_rate&#039;):<br \/>\n            anomalies.append({<br \/>\n                &#039;type&#039;: &#039;high_timeout_rate&#039;,<br \/>\n                &#039;value&#039;: timeout_rate,<br \/>\n                &#039;threshold&#039;: self.calculate_threshold(&#039;timeout_rate&#039;),<br \/>\n                &#039;severity&#039;: &#039;high&#039;<br \/>\n            })<\/p>\n<p>        # \u5206\u6790\u8d85\u65f6\u6301\u7eed\u65f6\u95f4<br \/>\n        durations &#061; [event[&#039;data&#039;].get(&#039;duration&#039;, 0) for event in self.timeout_data]<br \/>\n        if durations:<br \/>\n            avg_duration &#061; statistics.mean(durations)<br \/>\n            p95_duration &#061; np.percentile(durations, 95)<\/p>\n<p>            if p95_duration &gt; self.calculate_threshold(&#039;timeout_duration&#039;):<br \/>\n                anomalies.append({<br \/>\n                    &#039;type&#039;: &#039;long_timeout_duration&#039;,<br \/>\n                    &#039;p95_duration&#039;: p95_duration,<br \/>\n                    &#039;threshold&#039;: self.calculate_threshold(&#039;timeout_duration&#039;),<br \/>\n                    &#039;severity&#039;: &#039;medium&#039;<br \/>\n                })<\/p>\n<p>        # \u5206\u6790\u65f6\u95f4\u76f8\u5173\u6027<br \/>\n        time_pattern &#061; self.analyze_time_pattern()<br \/>\n        if time_pattern[&#039;has_pattern&#039;]:<br \/>\n            anomalies.append({<br \/>\n                &#039;type&#039;: &#039;time_pattern_detected&#039;,<br \/>\n                &#039;pattern&#039;: time_pattern[&#039;pattern&#039;],<br \/>\n                &#039;confidence&#039;: time_pattern[&#039;confidence&#039;],<br \/>\n                &#039;severity&#039;: &#039;low&#039;<br \/>\n            })<\/p>\n<p>        return anomalies<\/p>\n<p>    def calculate_timeout_rate(self) -&gt; float:<br \/>\n        &#034;&#034;&#034;\u8ba1\u7b97\u8d85\u65f6\u7387&#034;&#034;&#034;<br \/>\n        window_seconds &#061; 300  # 5\u5206\u949f\u7a97\u53e3<br \/>\n        cutoff &#061; datetime.utcnow() &#8211; timedelta(seconds&#061;window_seconds)<\/p>\n<p>        timeout_count &#061; sum(1 for event in self.timeout_data<br \/>\n                          if event[&#039;timestamp&#039;] &gt; cutoff)<\/p>\n<p>        total_count &#061; timeout_count &#043; len([event for event in self.normal_data<br \/>\n                                         if event[&#039;timestamp&#039;] &gt; cutoff])<\/p>\n<p>        return timeout_count \/ total_count if total_count &gt; 0 else 0.0<\/p>\n<p>    def calculate_threshold(self, metric: str) -&gt; float:<br \/>\n        &#034;&#034;&#034;\u8ba1\u7b97\u5f02\u5e38\u9608\u503c&#034;&#034;&#034;<br \/>\n        # \u57fa\u4e8e\u5386\u53f2\u6570\u636e\u8ba1\u7b97\u52a8\u6001\u9608\u503c<br \/>\n        if metric &#061;&#061; &#039;timeout_rate&#039;:<br \/>\n            historical_values &#061; [self.statistics.get(&#039;timeout_rate&#039;, 0)]<br \/>\n            return np.mean(historical_values) &#043; 2 * np.std(historical_values)<\/p>\n<p>        elif metric &#061;&#061; &#039;timeout_duration&#039;:<br \/>\n            durations &#061; [event[&#039;data&#039;].get(&#039;duration&#039;, 0) for event in self.timeout_data]<br \/>\n            if durations:<br \/>\n                return np.percentile(durations, 95) * 1.5<br \/>\n            return 30.0  # \u9ed8\u8ba430\u79d2<\/p>\n<p>        return 0.0<\/p>\n<p>    def analyze_time_pattern(self) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u5206\u6790\u65f6\u95f4\u6a21\u5f0f&#034;&#034;&#034;<br \/>\n        if len(self.timeout_data) &lt; 10:<br \/>\n            return {&#039;has_pattern&#039;: False}<\/p>\n<p>        # \u63d0\u53d6\u8d85\u65f6\u65f6\u95f4\u70b9<br \/>\n        time_points &#061; [event[&#039;timestamp&#039;].hour for event in self.timeout_data]<\/p>\n<p>        # \u5206\u6790\u65f6\u95f4\u5206\u5e03<br \/>\n        hour_counts &#061; defaultdict(int)<br \/>\n        for hour in time_points:<br \/>\n            hour_counts[hour] &#043;&#061; 1<\/p>\n<p>        # \u68c0\u67e5\u662f\u5426\u6709\u660e\u663e\u7684\u65f6\u95f4\u6a21\u5f0f<br \/>\n        max_hour &#061; max(hour_counts.items(), key&#061;lambda x: x[1])<br \/>\n        total &#061; len(time_points)<\/p>\n<p>        if max_hour[1] \/ total &gt; 0.3:  # 30%\u7684\u8d85\u65f6\u53d1\u751f\u5728\u540c\u4e00\u5c0f\u65f6<br \/>\n            return {<br \/>\n                &#039;has_pattern&#039;: True,<br \/>\n                &#039;pattern&#039;: f&#039;Peak at hour {max_hour[0]}&#039;,<br \/>\n                &#039;confidence&#039;: max_hour[1] \/ total<br \/>\n            }<\/p>\n<p>        return {&#039;has_pattern&#039;: False}<\/p>\n<p>    def update_statistics(self):<br \/>\n        &#034;&#034;&#034;\u66f4\u65b0\u7edf\u8ba1\u4fe1\u606f&#034;&#034;&#034;<br \/>\n        window_seconds &#061; 3600  # 1\u5c0f\u65f6\u7a97\u53e3<br \/>\n        cutoff &#061; datetime.utcnow() &#8211; timedelta(seconds&#061;window_seconds)<\/p>\n<p>        # \u8ba1\u7b97\u8d85\u65f6\u7387<br \/>\n        timeout_count &#061; sum(1 for event in self.timeout_data<br \/>\n                          if event[&#039;timestamp&#039;] &gt; cutoff)<br \/>\n        total_count &#061; timeout_count &#043; len([event for event in self.normal_data<br \/>\n                                         if event[&#039;timestamp&#039;] &gt; cutoff])<\/p>\n<p>        self.statistics[&#039;timeout_rate&#039;] &#061; timeout_count \/ total_count if total_count &gt; 0 else 0.0<\/p>\n<p>        # \u8ba1\u7b97\u5e73\u5747\u8d85\u65f6\u6301\u7eed\u65f6\u95f4<br \/>\n        recent_timeouts &#061; [event for event in self.timeout_data<br \/>\n                         if event[&#039;timestamp&#039;] &gt; cutoff]<\/p>\n<p>        if recent_timeouts:<br \/>\n            durations &#061; [event[&#039;data&#039;].get(&#039;duration&#039;, 0) for event in recent_timeouts]<br \/>\n            self.statistics[&#039;avg_timeout_duration&#039;] &#061; statistics.mean(durations)<\/p>\n<p>    def generate_report(self, period: str &#061; &#039;24h&#039;) -&gt; Dict:<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u5206\u6790\u62a5\u544a&#034;&#034;&#034;<br \/>\n        if period &#061;&#061; &#039;24h&#039;:<br \/>\n            cutoff &#061; datetime.utcnow() &#8211; timedelta(hours&#061;24)<br \/>\n        elif period &#061;&#061; &#039;7d&#039;:<br \/>\n            cutoff &#061; datetime.utcnow() &#8211; timedelta(days&#061;7)<br \/>\n        else:<br \/>\n            cutoff &#061; datetime.utcnow() &#8211; timedelta(hours&#061;24)<\/p>\n<p>        # \u7b5b\u9009\u671f\u95f4\u6570\u636e<br \/>\n        period_timeouts &#061; [event for event in self.timeout_data<br \/>\n                          if event[&#039;timestamp&#039;] &gt; cutoff]<br \/>\n        period_normals &#061; [event for event in self.normal_data<br \/>\n                         if event[&#039;timestamp&#039;] &gt; cutoff]<\/p>\n<p>        # \u57fa\u7840\u7edf\u8ba1<br \/>\n        total_requests &#061; len(period_timeouts) &#043; len(period_normals)<br \/>\n        timeout_rate &#061; len(period_timeouts) \/ total_requests if total_requests &gt; 0 else 0<\/p>\n<p>        # \u6839\u56e0\u5206\u5e03<br \/>\n        cause_distribution &#061; defaultdict(int)<br \/>\n        for event in period_timeouts:<br \/>\n            root_cause &#061; self.analyze_root_cause(event[&#039;data&#039;])<br \/>\n            if root_cause:<br \/>\n                cause_distribution[root_cause[&#039;primary_cause&#039;]] &#043;&#061; 1<\/p>\n<p>        # \u65f6\u95f4\u5206\u5e03<br \/>\n        hourly_distribution &#061; defaultdict(int)<br \/>\n        for event in period_timeouts:<br \/>\n            hour &#061; event[&#039;timestamp&#039;].hour<br \/>\n            hourly_distribution[f&#039;{hour:02d}:00&#039;] &#043;&#061; 1<\/p>\n<p>        report &#061; {<br \/>\n            &#039;period&#039;: period,<br \/>\n            &#039;time_range&#039;: {<br \/>\n                &#039;start&#039;: cutoff.isoformat(),<br \/>\n                &#039;end&#039;: datetime.utcnow().isoformat()<br \/>\n            },<br \/>\n            &#039;summary&#039;: {<br \/>\n                &#039;total_requests&#039;: total_requests,<br \/>\n                &#039;timeout_count&#039;: len(period_timeouts),<br \/>\n                &#039;timeout_rate&#039;: timeout_rate,<br \/>\n                &#039;avg_timeout_duration&#039;: self.statistics[&#039;avg_timeout_duration&#039;]<br \/>\n            },<br \/>\n            &#039;cause_analysis&#039;: {<br \/>\n                &#039;distribution&#039;: dict(cause_distribution),<br \/>\n                &#039;top_causes&#039;: sorted(cause_distribution.items(),<br \/>\n                                   key&#061;lambda x: x[1], reverse&#061;True)[:5]<br \/>\n            },<br \/>\n            &#039;time_distribution&#039;: dict(sorted(hourly_distribution.items())),<br \/>\n            &#039;anomalies&#039;: self.detect_anomalies(),<br \/>\n            &#039;recommendations&#039;: self.generate_overall_recommendations()<br \/>\n        }<\/p>\n<p>        return report<\/p>\n<p>    def generate_overall_recommendations(self) -&gt; List[str]:<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u6574\u4f53\u63a8\u8350&#034;&#034;&#034;<br \/>\n        recommendations &#061; []<\/p>\n<p>        # \u57fa\u4e8e\u6839\u56e0\u5206\u6790<br \/>\n        for cause, count in self.statistics[&#039;timeout_patterns&#039;].items():<br \/>\n            if count &gt; 10:  # \u9891\u7e41\u51fa\u73b0\u7684\u539f\u56e0<br \/>\n                if cause &#061;&#061; &#039;network&#039;:<br \/>\n                    recommendations.append(&#039;\u8003\u8651\u4f18\u5316\u7f51\u7edc\u57fa\u7840\u8bbe\u65bd\u6216\u4f7f\u7528CDN&#039;)<br \/>\n                elif cause &#061;&#061; &#039;server&#039;:<br \/>\n                    recommendations.append(&#039;\u5efa\u8bae\u589e\u52a0\u670d\u52a1\u5668\u8d44\u6e90\u6216\u4f18\u5316\u914d\u7f6e&#039;)<br \/>\n                elif cause &#061;&#061; &#039;application&#039;:<br \/>\n                    recommendations.append(&#039;\u9700\u8981\u6027\u80fd\u4f18\u5316\u548c\u4ee3\u7801\u5ba1\u67e5&#039;)<br \/>\n                elif cause &#061;&#061; &#039;client&#039;:<br \/>\n                    recommendations.append(&#039;\u5efa\u8bae\u6539\u5584\u5ba2\u6237\u7aef\u5b9e\u73b0\u548c\u6dfb\u52a0\u9650\u5236&#039;)<\/p>\n<p>        # \u57fa\u4e8e\u8d85\u65f6\u7387<br \/>\n        if self.statistics[&#039;timeout_rate&#039;] &gt; 0.1:  # \u8d85\u65f6\u7387\u8d85\u8fc710%<br \/>\n            recommendations.append(&#039;\u6574\u4f53\u8d85\u65f6\u7387\u8fc7\u9ad8&#xff0c;\u5efa\u8bae\u5168\u9762\u5ba1\u67e5\u7cfb\u7edf\u67b6\u6784&#039;)<\/p>\n<p>        # \u57fa\u4e8e\u6301\u7eed\u65f6\u95f4<br \/>\n        if self.statistics[&#039;avg_timeout_duration&#039;] &gt; 30:  # \u5e73\u5747\u8d85\u65f6\u8d85\u8fc730\u79d2<br \/>\n            recommendations.append(&#039;\u8d85\u65f6\u6301\u7eed\u65f6\u95f4\u8fc7\u957f&#xff0c;\u5efa\u8bae\u589e\u52a0\u8d85\u65f6\u914d\u7f6e\u6216\u4f18\u5316\u5904\u7406\u903b\u8f91&#039;)<\/p>\n<p>        return list(set(recommendations))[:5]  # \u53bb\u91cd\u5e76\u9650\u5236\u6570\u91cf<\/p>\n<h4>21.5.2 \u5b9e\u65f6\u8d85\u65f6\u76d1\u63a7\u4eea\u8868\u677f<\/h4>\n<p>javascript<\/p>\n<p>\/\/ React\u7ec4\u4ef6&#xff1a;\u5b9e\u65f6\u8d85\u65f6\u76d1\u63a7\u4eea\u8868\u677f<br \/>\nimport React, { useState, useEffect, useRef } from &#039;react&#039;;<br \/>\nimport {<br \/>\n  LineChart, Line, BarChart, Bar, PieChart, Pie, Cell,<br \/>\n  XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer,<br \/>\n  AreaChart, Area, RadarChart, Radar, PolarGrid, PolarAngleAxis, PolarRadiusAxis<br \/>\n} from &#039;recharts&#039;;<br \/>\nimport { AlertTriangle, Clock, Activity, Wifi, Server, Cpu, AlertCircle } from &#039;lucide-react&#039;;<\/p>\n<p>function TimeoutMonitoringDashboard() {<br \/>\n  const [timeRange, setTimeRange] &#061; useState(&#039;1h&#039;);<br \/>\n  const [stats, setStats] &#061; useState(null);<br \/>\n  const [realtimeEvents, setRealtimeEvents] &#061; useState([]);<br \/>\n  const [anomalies, setAnomalies] &#061; useState([]);<br \/>\n  const [causeDistribution, setCauseDistribution] &#061; useState([]);<br \/>\n  const [networkMetrics, setNetworkMetrics] &#061; useState({});<\/p>\n<p>  const wsRef &#061; useRef(null);<\/p>\n<p>  \/\/ \u989c\u8272\u914d\u7f6e<br \/>\n  const COLORS &#061; [&#039;#0088FE&#039;, &#039;#00C49F&#039;, &#039;#FFBB28&#039;, &#039;#FF8042&#039;, &#039;#8884D8&#039;];<\/p>\n<p>  \/\/ \u83b7\u53d6\u7edf\u8ba1\u6570\u636e<br \/>\n  useEffect(() &#061;&gt; {<br \/>\n    const fetchStats &#061; async () &#061;&gt; {<br \/>\n      try {<br \/>\n        const response &#061; await fetch(&#096;\/api\/monitor\/timeout-stats?range&#061;${timeRange}&#096;);<br \/>\n        const data &#061; await response.json();<br \/>\n        setStats(data);<\/p>\n<p>        \/\/ \u5904\u7406\u6839\u56e0\u5206\u5e03\u6570\u636e<br \/>\n        if (data.cause_analysis?.distribution) {<br \/>\n          const distribution &#061; Object.entries(data.cause_analysis.distribution)<br \/>\n            .map(([name, value]) &#061;&gt; ({ name, value }));<br \/>\n          setCauseDistribution(distribution);<br \/>\n        }<br \/>\n      } catch (error) {<br \/>\n        console.error(&#039;Failed to fetch stats:&#039;, error);<br \/>\n      }<br \/>\n    };<\/p>\n<p>    fetchStats();<br \/>\n    const interval &#061; setInterval(fetchStats, 30000); \/\/ \u6bcf30\u79d2\u66f4\u65b0<\/p>\n<p>    return () &#061;&gt; clearInterval(interval);<br \/>\n  }, [timeRange]);<\/p>\n<p>  \/\/ WebSocket\u8fde\u63a5\u63a5\u6536\u5b9e\u65f6\u4e8b\u4ef6<br \/>\n  useEffect(() &#061;&gt; {<br \/>\n    const connectWebSocket &#061; () &#061;&gt; {<br \/>\n      wsRef.current &#061; new WebSocket(&#039;wss:\/\/api.example.com\/monitor\/timeout-events&#039;);<\/p>\n<p>      wsRef.current.onmessage &#061; (event) &#061;&gt; {<br \/>\n        const eventData &#061; JSON.parse(event.data);<\/p>\n<p>        \/\/ \u6dfb\u52a0\u4e8b\u4ef6\u5230\u5b9e\u65f6\u5217\u8868<br \/>\n        setRealtimeEvents(prev &#061;&gt; [<br \/>\n          { &#8230;eventData, id: Date.now() },<br \/>\n          &#8230;prev.slice(0, 49) \/\/ \u4fdd\u7559\u6700\u8fd150\u4e2a<br \/>\n        ]);<\/p>\n<p>        \/\/ \u68c0\u67e5\u662f\u5426\u662f\u5f02\u5e38\u4e8b\u4ef6<br \/>\n        if (eventData.severity &#061;&#061;&#061; &#039;high&#039;) {<br \/>\n          setAnomalies(prev &#061;&gt; [<br \/>\n            {<br \/>\n              &#8230;eventData,<br \/>\n              id: Date.now(),<br \/>\n              acknowledged: false<br \/>\n            },<br \/>\n            &#8230;prev<br \/>\n          ]);<br \/>\n        }<\/p>\n<p>        \/\/ \u66f4\u65b0\u7f51\u7edc\u6307\u6807<br \/>\n        if (eventData.metrics) {<br \/>\n          setNetworkMetrics(prev &#061;&gt; ({<br \/>\n            &#8230;prev,<br \/>\n            &#8230;eventData.metrics,<br \/>\n            lastUpdated: Date.now()<br \/>\n          }));<br \/>\n        }<br \/>\n      };<\/p>\n<p>      wsRef.current.onclose &#061; () &#061;&gt; {<br \/>\n        console.log(&#039;WebSocket closed, reconnecting&#8230;&#039;);<br \/>\n        setTimeout(connectWebSocket, 5000);<br \/>\n      };<\/p>\n<p>      wsRef.current.onerror &#061; (error) &#061;&gt; {<br \/>\n        console.error(&#039;WebSocket error:&#039;, error);<br \/>\n      };<br \/>\n    };<\/p>\n<p>    connectWebSocket();<\/p>\n<p>    return () &#061;&gt; {<br \/>\n      if (wsRef.current) {<br \/>\n        wsRef.current.close();<br \/>\n      }<br \/>\n    };<br \/>\n  }, []);<\/p>\n<p>  \/\/ \u6a21\u62df\u7f51\u7edc\u6307\u6807\u66f4\u65b0<br \/>\n  useEffect(() &#061;&gt; {<br \/>\n    const updateNetworkMetrics &#061; () &#061;&gt; {<br \/>\n      setNetworkMetrics(prev &#061;&gt; ({<br \/>\n        latency: Math.random() * 500,<br \/>\n        throughput: 1000 &#043; Math.random() * 5000,<br \/>\n        packetLoss: Math.random() * 0.05,<br \/>\n        reliability: 0.95 &#043; Math.random() * 0.05,<br \/>\n        lastUpdated: Date.now()<br \/>\n      }));<br \/>\n    };<\/p>\n<p>    const interval &#061; setInterval(updateNetworkMetrics, 10000);<br \/>\n    return () &#061;&gt; clearInterval(interval);<br \/>\n  }, []);<\/p>\n<p>  if (!stats) {<br \/>\n    return (<br \/>\n      &lt;div className&#061;&#034;loading-container&#034;&gt;<br \/>\n        &lt;div className&#061;&#034;spinner&#034;&gt;&lt;\/div&gt;<br \/>\n        &lt;p&gt;\u52a0\u8f7d\u76d1\u63a7\u6570\u636e&#8230;&lt;\/p&gt;<br \/>\n      &lt;\/div&gt;<br \/>\n    );<br \/>\n  }<\/p>\n<p>  return (<br \/>\n    &lt;div className&#061;&#034;dashboard timeout-dashboard&#034;&gt;<br \/>\n      {\/* \u4eea\u8868\u677f\u5934\u90e8 *\/}<br \/>\n      &lt;div className&#061;&#034;dashboard-header&#034;&gt;<br \/>\n        &lt;div className&#061;&#034;header-left&#034;&gt;<br \/>\n          &lt;Clock size&#061;{32} className&#061;&#034;header-icon&#034; \/&gt;<br \/>\n          &lt;h1&gt;\u8bf7\u6c42\u8d85\u65f6\u76d1\u63a7&lt;\/h1&gt;<br \/>\n          &lt;span className&#061;&#034;time-range-label&#034;&gt;<br \/>\n            \u65f6\u95f4\u8303\u56f4: {timeRange &#061;&#061;&#061; &#039;1h&#039; ? &#039;1\u5c0f\u65f6&#039; :<br \/>\n                     timeRange &#061;&#061;&#061; &#039;24h&#039; ? &#039;24\u5c0f\u65f6&#039; :<br \/>\n                     timeRange &#061;&#061;&#061; &#039;7d&#039; ? &#039;7\u5929&#039; : &#039;30\u5929&#039;}<br \/>\n          &lt;\/span&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;header-right&#034;&gt;<br \/>\n          &lt;div className&#061;&#034;time-range-selector&#034;&gt;<br \/>\n            {[&#039;1h&#039;, &#039;24h&#039;, &#039;7d&#039;, &#039;30d&#039;].map(range &#061;&gt; (<br \/>\n              &lt;button<br \/>\n                key&#061;{range}<br \/>\n                className&#061;{&#096;time-range-btn ${timeRange &#061;&#061;&#061; range ? &#039;active&#039; : &#039;&#039;}&#096;}<br \/>\n                onClick&#061;{() &#061;&gt; setTimeRange(range)}<br \/>\n              &gt;<br \/>\n                {range}<br \/>\n              &lt;\/button&gt;<br \/>\n            ))}<br \/>\n          &lt;\/div&gt;<\/p>\n<p>          &lt;button className&#061;&#034;export-btn&#034; onClick&#061;{() &#061;&gt; exportReport()}&gt;<br \/>\n            \u5bfc\u51fa\u62a5\u544a<br \/>\n          &lt;\/button&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u5173\u952e\u6307\u6807\u5361\u7247 *\/}<br \/>\n      &lt;div className&#061;&#034;key-metrics-cards&#034;&gt;<br \/>\n        &lt;div className&#061;&#034;metric-card critical&#034;&gt;<br \/>\n          &lt;div className&#061;&#034;metric-icon&#034;&gt;<br \/>\n            &lt;AlertTriangle size&#061;{24} \/&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n          &lt;div className&#061;&#034;metric-content&#034;&gt;<br \/>\n            &lt;h3&gt;\u8d85\u65f6\u603b\u6570&lt;\/h3&gt;<br \/>\n            &lt;p className&#061;&#034;metric-value&#034;&gt;{stats.summary.timeout_count}&lt;\/p&gt;<br \/>\n            &lt;p className&#061;&#034;metric-trend&#034;&gt;<br \/>\n              \u7387: {(stats.summary.timeout_rate * 100).toFixed(2)}%<br \/>\n            &lt;\/p&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;metric-card warning&#034;&gt;<br \/>\n          &lt;div className&#061;&#034;metric-icon&#034;&gt;<br \/>\n            &lt;Clock size&#061;{24} \/&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n          &lt;div className&#061;&#034;metric-content&#034;&gt;<br \/>\n            &lt;h3&gt;\u5e73\u5747\u8d85\u65f6&lt;\/h3&gt;<br \/>\n            &lt;p className&#061;&#034;metric-value&#034;&gt;<br \/>\n              {stats.summary.avg_timeout_duration?.toFixed(1) || &#039;0.0&#039;}s<br \/>\n            &lt;\/p&gt;<br \/>\n            &lt;p className&#061;&#034;metric-trend&#034;&gt;<br \/>\n              \u9608\u503c: 30.0s<br \/>\n            &lt;\/p&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;metric-card info&#034;&gt;<br \/>\n          &lt;div className&#061;&#034;metric-icon&#034;&gt;<br \/>\n            &lt;Activity size&#061;{24} \/&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n          &lt;div className&#061;&#034;metric-content&#034;&gt;<br \/>\n            &lt;h3&gt;\u603b\u8bf7\u6c42\u6570&lt;\/h3&gt;<br \/>\n            &lt;p className&#061;&#034;metric-value&#034;&gt;<br \/>\n              {stats.summary.total_requests?.toLocaleString() || &#039;0&#039;}<br \/>\n            &lt;\/p&gt;<br \/>\n            &lt;p className&#061;&#034;metric-trend&#034;&gt;<br \/>\n              {stats.summary.total_requests &gt; 0 ? &#039;\u6b63\u5e38&#039; : &#039;\u65e0\u6570\u636e&#039;}<br \/>\n            &lt;\/p&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;metric-card success&#034;&gt;<br \/>\n          &lt;div className&#061;&#034;metric-icon&#034;&gt;<br \/>\n            &lt;Server size&#061;{24} \/&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n          &lt;div className&#061;&#034;metric-content&#034;&gt;<br \/>\n            &lt;h3&gt;\u6b63\u5e38\u7387&lt;\/h3&gt;<br \/>\n            &lt;p className&#061;&#034;metric-value&#034;&gt;<br \/>\n              {((1 &#8211; stats.summary.timeout_rate) * 100).toFixed(1)}%<br \/>\n            &lt;\/p&gt;<br \/>\n            &lt;p className&#061;&#034;metric-trend&#034;&gt;<br \/>\n              \u76ee\u6807: 99.9%<br \/>\n            &lt;\/p&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u7f51\u7edc\u6307\u6807\u5361\u7247 *\/}<br \/>\n      &lt;div className&#061;&#034;network-metrics&#034;&gt;<br \/>\n        &lt;h3&gt;\u7f51\u7edc\u8d28\u91cf\u6307\u6807&lt;\/h3&gt;<br \/>\n        &lt;div className&#061;&#034;network-cards&#034;&gt;<br \/>\n          &lt;div className&#061;&#034;network-card&#034;&gt;<br \/>\n            &lt;Wifi size&#061;{20} \/&gt;<br \/>\n            &lt;span className&#061;&#034;network-label&#034;&gt;\u5ef6\u8fdf&lt;\/span&gt;<br \/>\n            &lt;span className&#061;{&#096;network-value ${networkMetrics.latency &gt; 200 ? &#039;warning&#039; : &#039;&#039;}&#096;}&gt;<br \/>\n              {networkMetrics.latency?.toFixed(0) || &#039;0&#039;}ms<br \/>\n            &lt;\/span&gt;<br \/>\n          &lt;\/div&gt;<\/p>\n<p>          &lt;div className&#061;&#034;network-card&#034;&gt;<br \/>\n            &lt;Activity size&#061;{20} \/&gt;<br \/>\n            &lt;span className&#061;&#034;network-label&#034;&gt;\u541e\u5410\u91cf&lt;\/span&gt;<br \/>\n            &lt;span className&#061;&#034;network-value&#034;&gt;<br \/>\n              {(networkMetrics.throughput \/ 1000).toFixed(1)}kbps<br \/>\n            &lt;\/span&gt;<br \/>\n          &lt;\/div&gt;<\/p>\n<p>          &lt;div className&#061;&#034;network-card&#034;&gt;<br \/>\n            &lt;AlertCircle size&#061;{20} \/&gt;<br \/>\n            &lt;span className&#061;&#034;network-label&#034;&gt;\u4e22\u5305\u7387&lt;\/span&gt;<br \/>\n            &lt;span className&#061;{&#096;network-value ${networkMetrics.packetLoss &gt; 0.02 ? &#039;critical&#039; : &#039;&#039;}&#096;}&gt;<br \/>\n              {(networkMetrics.packetLoss * 100).toFixed(2)}%<br \/>\n            &lt;\/span&gt;<br \/>\n          &lt;\/div&gt;<\/p>\n<p>          &lt;div className&#061;&#034;network-card&#034;&gt;<br \/>\n            &lt;Cpu size&#061;{20} \/&gt;<br \/>\n            &lt;span className&#061;&#034;network-label&#034;&gt;\u53ef\u9760\u6027&lt;\/span&gt;<br \/>\n            &lt;span className&#061;&#034;network-value&#034;&gt;<br \/>\n              {(networkMetrics.reliability * 100).toFixed(1)}%<br \/>\n            &lt;\/span&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u56fe\u8868\u533a\u57df *\/}<br \/>\n      &lt;div className&#061;&#034;charts-section&#034;&gt;<br \/>\n        &lt;div className&#061;&#034;chart-row&#034;&gt;<br \/>\n          &lt;div className&#061;&#034;chart-container&#034;&gt;<br \/>\n            &lt;h3&gt;\u8d85\u65f6\u8d8b\u52bf&lt;\/h3&gt;<br \/>\n            &lt;ResponsiveContainer width&#061;&#034;100%&#034; height&#061;{300}&gt;<br \/>\n              &lt;LineChart data&#061;{Object.entries(stats.time_distribution || {}).map(([hour, count]) &#061;&gt; ({ hour, count }))}&gt;<br \/>\n                &lt;CartesianGrid strokeDasharray&#061;&#034;3 3&#034; \/&gt;<br \/>\n                &lt;XAxis dataKey&#061;&#034;hour&#034; \/&gt;<br \/>\n                &lt;YAxis \/&gt;<br \/>\n                &lt;Tooltip \/&gt;<br \/>\n                &lt;Legend \/&gt;<br \/>\n                &lt;Line<br \/>\n                  type&#061;&#034;monotone&#034;<br \/>\n                  dataKey&#061;&#034;count&#034;<br \/>\n                  stroke&#061;&#034;#ff6b6b&#034;<br \/>\n                  strokeWidth&#061;{2}<br \/>\n                  dot&#061;{{ r: 4 }}<br \/>\n                  activeDot&#061;{{ r: 6 }}<br \/>\n                  name&#061;&#034;\u8d85\u65f6\u6b21\u6570&#034;<br \/>\n                \/&gt;<br \/>\n              &lt;\/LineChart&gt;<br \/>\n            &lt;\/ResponsiveContainer&gt;<br \/>\n          &lt;\/div&gt;<\/p>\n<p>          &lt;div className&#061;&#034;chart-container&#034;&gt;<br \/>\n            &lt;h3&gt;\u6839\u56e0\u5206\u5e03&lt;\/h3&gt;<br \/>\n            &lt;ResponsiveContainer width&#061;&#034;100%&#034; height&#061;{300}&gt;<br \/>\n              &lt;PieChart&gt;<br \/>\n                &lt;Pie<br \/>\n                  data&#061;{causeDistribution}<br \/>\n                  cx&#061;&#034;50%&#034;<br \/>\n                  cy&#061;&#034;50%&#034;<br \/>\n                  labelLine&#061;{false}<br \/>\n                  label&#061;{({ name, percent }) &#061;&gt; &#096;${name}: ${(percent * 100).toFixed(0)}%&#096;}<br \/>\n                  outerRadius&#061;{80}<br \/>\n                  fill&#061;&#034;#8884d8&#034;<br \/>\n                  dataKey&#061;&#034;value&#034;<br \/>\n                &gt;<br \/>\n                  {causeDistribution.map((entry, index) &#061;&gt; (<br \/>\n                    &lt;Cell key&#061;{&#096;cell-${index}&#096;} fill&#061;{COLORS[index % COLORS.length]} \/&gt;<br \/>\n                  ))}<br \/>\n                &lt;\/Pie&gt;<br \/>\n                &lt;Tooltip formatter&#061;{(value) &#061;&gt; [&#096;${value}\u6b21&#096;, &#039;\u6570\u91cf&#039;]} \/&gt;<br \/>\n              &lt;\/PieChart&gt;<br \/>\n            &lt;\/ResponsiveContainer&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;div className&#061;&#034;chart-row&#034;&gt;<br \/>\n          &lt;div className&#061;&#034;chart-container&#034;&gt;<br \/>\n            &lt;h3&gt;\u8d85\u65f6\u7387\u53d8\u5316&lt;\/h3&gt;<br \/>\n            &lt;ResponsiveContainer width&#061;&#034;100%&#034; height&#061;{300}&gt;<br \/>\n              &lt;AreaChart data&#061;{generateRateData()}&gt;<br \/>\n                &lt;CartesianGrid strokeDasharray&#061;&#034;3 3&#034; \/&gt;<br \/>\n                &lt;XAxis dataKey&#061;&#034;time&#034; \/&gt;<br \/>\n                &lt;YAxis label&#061;{{ value: &#039;\u8d85\u65f6\u7387 (%)&#039;, angle: -90, position: &#039;insideLeft&#039; }} \/&gt;<br \/>\n                &lt;Tooltip formatter&#061;{(value) &#061;&gt; [&#096;${value}%&#096;, &#039;\u8d85\u65f6\u7387&#039;]} \/&gt;<br \/>\n                &lt;Area<br \/>\n                  type&#061;&#034;monotone&#034;<br \/>\n                  dataKey&#061;&#034;rate&#034;<br \/>\n                  stroke&#061;&#034;#8884d8&#034;<br \/>\n                  fill&#061;&#034;#8884d8&#034;<br \/>\n                  fillOpacity&#061;{0.3}<br \/>\n                  name&#061;&#034;\u8d85\u65f6\u7387&#034;<br \/>\n                \/&gt;<br \/>\n                &lt;Line<br \/>\n                  type&#061;&#034;monotone&#034;<br \/>\n                  dataKey&#061;&#034;threshold&#034;<br \/>\n                  stroke&#061;&#034;#ff6b6b&#034;<br \/>\n                  strokeDasharray&#061;&#034;5 5&#034;<br \/>\n                  name&#061;&#034;\u8b66\u544a\u9608\u503c&#034;<br \/>\n                  dot&#061;{false}<br \/>\n                \/&gt;<br \/>\n              &lt;\/AreaChart&gt;<br \/>\n            &lt;\/ResponsiveContainer&gt;<br \/>\n          &lt;\/div&gt;<\/p>\n<p>          &lt;div className&#061;&#034;chart-container&#034;&gt;<br \/>\n            &lt;h3&gt;\u6027\u80fd\u6307\u6807\u96f7\u8fbe\u56fe&lt;\/h3&gt;<br \/>\n            &lt;ResponsiveContainer width&#061;&#034;100%&#034; height&#061;{300}&gt;<br \/>\n              &lt;RadarChart data&#061;{generateRadarData()}&gt;<br \/>\n                &lt;PolarGrid \/&gt;<br \/>\n                &lt;PolarAngleAxis dataKey&#061;&#034;subject&#034; \/&gt;<br \/>\n                &lt;PolarRadiusAxis angle&#061;{30} domain&#061;{[0, 100]} \/&gt;<br \/>\n                &lt;Radar<br \/>\n                  name&#061;&#034;\u5f53\u524d&#034;<br \/>\n                  dataKey&#061;&#034;current&#034;<br \/>\n                  stroke&#061;&#034;#8884d8&#034;<br \/>\n                  fill&#061;&#034;#8884d8&#034;<br \/>\n                  fillOpacity&#061;{0.3}<br \/>\n                \/&gt;<br \/>\n                &lt;Radar<br \/>\n                  name&#061;&#034;\u76ee\u6807&#034;<br \/>\n                  dataKey&#061;&#034;target&#034;<br \/>\n                  stroke&#061;&#034;#82ca9d&#034;<br \/>\n                  fill&#061;&#034;#82ca9d&#034;<br \/>\n                  fillOpacity&#061;{0.3}<br \/>\n                \/&gt;<br \/>\n                &lt;Legend \/&gt;<br \/>\n                &lt;Tooltip \/&gt;<br \/>\n              &lt;\/RadarChart&gt;<br \/>\n            &lt;\/ResponsiveContainer&gt;<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u5b9e\u65f6\u4e8b\u4ef6\u6d41 *\/}<br \/>\n      &lt;div className&#061;&#034;realtime-events-section&#034;&gt;<br \/>\n        &lt;h3&gt;\u5b9e\u65f6\u8d85\u65f6\u4e8b\u4ef6&lt;\/h3&gt;<br \/>\n        &lt;div className&#061;&#034;events-table-container&#034;&gt;<br \/>\n          &lt;table className&#061;&#034;events-table&#034;&gt;<br \/>\n            &lt;thead&gt;<br \/>\n              &lt;tr&gt;<br \/>\n                &lt;th&gt;\u65f6\u95f4&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u7aef\u70b9&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u6301\u7eed\u65f6\u95f4&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u6839\u56e0&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u5ba2\u6237\u7aefIP&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u4e25\u91cd\u7a0b\u5ea6&lt;\/th&gt;<br \/>\n                &lt;th&gt;\u64cd\u4f5c&lt;\/th&gt;<br \/>\n              &lt;\/tr&gt;<br \/>\n            &lt;\/thead&gt;<br \/>\n            &lt;tbody&gt;<br \/>\n              {realtimeEvents.map(event &#061;&gt; (<br \/>\n                &lt;tr<br \/>\n                  key&#061;{event.id}<br \/>\n                  className&#061;{&#096;event-row severity-${event.severity || &#039;medium&#039;}&#096;}<br \/>\n                &gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    {new Date(event.timestamp || Date.now()).toLocaleTimeString()}<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td className&#061;&#034;endpoint-cell&#034;&gt;<br \/>\n                    &lt;code title&#061;{event.endpoint}&gt;<br \/>\n                      {event.endpoint?.length &gt; 30 ?<br \/>\n                        event.endpoint.substring(0, 30) &#043; &#039;&#8230;&#039; :<br \/>\n                        event.endpoint}<br \/>\n                    &lt;\/code&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    &lt;span className&#061;&#034;duration-badge&#034;&gt;<br \/>\n                      {event.duration ? &#096;${event.duration.toFixed(2)}s&#096; : &#039;N\/A&#039;}<br \/>\n                    &lt;\/span&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    &lt;span className&#061;{&#096;cause-badge cause-${event.root_cause?.toLowerCase() || &#039;unknown&#039;}&#096;}&gt;<br \/>\n                      {event.root_cause || &#039;\u672a\u77e5&#039;}<br \/>\n                    &lt;\/span&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    &lt;code&gt;{event.client_ip || &#039;N\/A&#039;}&lt;\/code&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    &lt;span className&#061;{&#096;severity-badge severity-${event.severity || &#039;medium&#039;}&#096;}&gt;<br \/>\n                      {event.severity &#061;&#061;&#061; &#039;high&#039; ? &#039;\u9ad8&#039; :<br \/>\n                       event.severity &#061;&#061;&#061; &#039;medium&#039; ? &#039;\u4e2d&#039; : &#039;\u4f4e&#039;}<br \/>\n                    &lt;\/span&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                  &lt;td&gt;<br \/>\n                    &lt;button<br \/>\n                      className&#061;&#034;action-btn view-details&#034;<br \/>\n                      onClick&#061;{() &#061;&gt; viewEventDetails(event)}<br \/>\n                    &gt;<br \/>\n                      \u8be6\u60c5<br \/>\n                    &lt;\/button&gt;<br \/>\n                    &lt;button<br \/>\n                      className&#061;&#034;action-btn acknowledge&#034;<br \/>\n                      onClick&#061;{() &#061;&gt; acknowledgeEvent(event.id)}<br \/>\n                    &gt;<br \/>\n                      \u786e\u8ba4<br \/>\n                    &lt;\/button&gt;<br \/>\n                  &lt;\/td&gt;<br \/>\n                &lt;\/tr&gt;<br \/>\n              ))}<br \/>\n            &lt;\/tbody&gt;<br \/>\n          &lt;\/table&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<\/p>\n<p>      {\/* \u5f02\u5e38\u8b66\u62a5 *\/}<br \/>\n      {anomalies.length &gt; 0 &amp;&amp; (<br \/>\n        &lt;div className&#061;&#034;anomalies-section&#034;&gt;<br \/>\n          &lt;h3&gt;<br \/>\n            &lt;AlertTriangle size&#061;{20} \/&gt;<br \/>\n            \u5f02\u5e38\u8b66\u62a5 ({anomalies.length})<br \/>\n          &lt;\/h3&gt;<br \/>\n          &lt;div className&#061;&#034;anomalies-list&#034;&gt;<br \/>\n            {anomalies.filter(a &#061;&gt; !a.acknowledged).slice(0, 5).map(anomaly &#061;&gt; (<br \/>\n              &lt;div key&#061;{anomaly.id} className&#061;&#034;anomaly-card&#034;&gt;<br \/>\n                &lt;div className&#061;&#034;anomaly-header&#034;&gt;<br \/>\n                  &lt;span className&#061;&#034;anomaly-type&#034;&gt;{anomaly.type}&lt;\/span&gt;<br \/>\n                  &lt;span className&#061;&#034;anomaly-time&#034;&gt;<br \/>\n                    {new Date(anomaly.timestamp).toLocaleTimeString()}<br \/>\n                  &lt;\/span&gt;<br \/>\n                &lt;\/div&gt;<br \/>\n                &lt;div className&#061;&#034;anomaly-content&#034;&gt;<br \/>\n                  &lt;p&gt;{anomaly.message || &#039;\u68c0\u6d4b\u5230\u5f02\u5e38\u6a21\u5f0f&#039;}&lt;\/p&gt;<br \/>\n                  {anomaly.details &amp;&amp; (<br \/>\n                    &lt;div className&#061;&#034;anomaly-details&#034;&gt;<br \/>\n                      &lt;pre&gt;{JSON.stringify(anomaly.details, null, 2)}&lt;\/pre&gt;<br \/>\n                    &lt;\/div&gt;<br \/>\n                  )}<br \/>\n                &lt;\/div&gt;<br \/>\n                &lt;div className&#061;&#034;anomaly-actions&#034;&gt;<br \/>\n                  &lt;button<br \/>\n                    className&#061;&#034;btn btn-primary&#034;<br \/>\n                    onClick&#061;{() &#061;&gt; investigateAnomaly(anomaly)}<br \/>\n                  &gt;<br \/>\n                    \u8c03\u67e5<br \/>\n                  &lt;\/button&gt;<br \/>\n                  &lt;button<br \/>\n                    className&#061;&#034;btn btn-secondary&#034;<br \/>\n                    onClick&#061;{() &#061;&gt; acknowledgeAnomaly(anomaly.id)}<br \/>\n                  &gt;<br \/>\n                    \u786e\u8ba4<br \/>\n                  &lt;\/button&gt;<br \/>\n                  &lt;button<br \/>\n                    className&#061;&#034;btn btn-tertiary&#034;<br \/>\n                    onClick&#061;{() &#061;&gt; muteAnomaly(anomaly.id)}<br \/>\n                  &gt;<br \/>\n                    \u9759\u97f3<br \/>\n                  &lt;\/button&gt;<br \/>\n                &lt;\/div&gt;<br \/>\n              &lt;\/div&gt;<br \/>\n            ))}<br \/>\n          &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n      )}<\/p>\n<p>      {\/* \u5efa\u8bae\u548c\u6d1e\u5bdf *\/}<br \/>\n      &lt;div className&#061;&#034;insights-section&#034;&gt;<br \/>\n        &lt;h3&gt;\u4f18\u5316\u5efa\u8bae&lt;\/h3&gt;<br \/>\n        &lt;div className&#061;&#034;insights-grid&#034;&gt;<br \/>\n          {stats.recommendations?.map((recommendation, index) &#061;&gt; (<br \/>\n            &lt;div key&#061;{index} className&#061;&#034;insight-card&#034;&gt;<br \/>\n              &lt;div className&#061;&#034;insight-icon&#034;&gt;&#x1f4a1;&lt;\/div&gt;<br \/>\n              &lt;div className&#061;&#034;insight-content&#034;&gt;<br \/>\n                &lt;p&gt;{recommendation}&lt;\/p&gt;<br \/>\n                &lt;button<br \/>\n                  className&#061;&#034;insight-action&#034;<br \/>\n                  onClick&#061;{() &#061;&gt; implementRecommendation(recommendation)}<br \/>\n                &gt;<br \/>\n                  \u5b9e\u65bd<br \/>\n                &lt;\/button&gt;<br \/>\n              &lt;\/div&gt;<br \/>\n            &lt;\/div&gt;<br \/>\n          ))}<br \/>\n        &lt;\/div&gt;<br \/>\n      &lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n  );<\/p>\n<p>  \/\/ \u8f85\u52a9\u51fd\u6570<br \/>\n  function generateRateData() {<br \/>\n    \/\/ \u751f\u6210\u6a21\u62df\u7684\u8d85\u65f6\u7387\u6570\u636e<br \/>\n    const data &#061; [];<br \/>\n    for (let i &#061; 0; i &lt; 24; i&#043;&#043;) {<br \/>\n      data.push({<br \/>\n        time: &#096;${i}:00&#096;,<br \/>\n        rate: Math.random() * 15, \/\/ 0-15%\u7684\u8d85\u65f6\u7387<br \/>\n        threshold: 10 \/\/ \u8b66\u544a\u9608\u503c10%<br \/>\n      });<br \/>\n    }<br \/>\n    return data;<br \/>\n  }<\/p>\n<p>  function generateRadarData() {<br \/>\n    \/\/ \u751f\u6210\u96f7\u8fbe\u56fe\u6570\u636e<br \/>\n    return [<br \/>\n      { subject: &#039;\u7f51\u7edc\u8d28\u91cf&#039;, current: 85, target: 95 },<br \/>\n      { subject: &#039;\u670d\u52a1\u5668\u6027\u80fd&#039;, current: 90, target: 98 },<br \/>\n      { subject: &#039;\u5e94\u7528\u54cd\u5e94&#039;, current: 75, target: 95 },<br \/>\n      { subject: &#039;\u5ba2\u6237\u7aef\u4f53\u9a8c&#039;, current: 80, target: 90 },<br \/>\n      { subject: &#039;\u7cfb\u7edf\u7a33\u5b9a\u6027&#039;, current: 88, target: 99 },<br \/>\n      { subject: &#039;\u9519\u8bef\u6062\u590d&#039;, current: 70, target: 85 }<br \/>\n    ];<br \/>\n  }<\/p>\n<p>  function viewEventDetails(event) {<br \/>\n    \/\/ \u67e5\u770b\u4e8b\u4ef6\u8be6\u60c5<br \/>\n    console.log(&#039;Event details:&#039;, event);<br \/>\n    alert(&#096;\u4e8b\u4ef6\u8be6\u60c5:\\\\n${JSON.stringify(event, null, 2)}&#096;);<br \/>\n  }<\/p>\n<p>  function acknowledgeEvent(eventId) {<br \/>\n    \/\/ \u786e\u8ba4\u4e8b\u4ef6<br \/>\n    setRealtimeEvents(prev &#061;&gt;<br \/>\n      prev.map(event &#061;&gt;<br \/>\n        event.id &#061;&#061;&#061; eventId ? { &#8230;event, acknowledged: true } : event<br \/>\n      )<br \/>\n    );<br \/>\n  }<\/p>\n<p>  function acknowledgeAnomaly(anomalyId) {<br \/>\n    \/\/ \u786e\u8ba4\u5f02\u5e38<br \/>\n    setAnomalies(prev &#061;&gt;<br \/>\n      prev.map(anomaly &#061;&gt;<br \/>\n        anomaly.id &#061;&#061;&#061; anomalyId ? { &#8230;anomaly, acknowledged: true } : anomaly<br \/>\n      )<br \/>\n    );<br \/>\n  }<\/p>\n<p>  function investigateAnomaly(anomaly) {<br \/>\n    \/\/ \u8c03\u67e5\u5f02\u5e38<br \/>\n    console.log(&#039;Investigating anomaly:&#039;, anomaly);<br \/>\n    window.open(&#096;\/monitor\/investigate?anomalyId&#061;${anomaly.id}&#096;, &#039;_blank&#039;);<br \/>\n  }<\/p>\n<p>  function muteAnomaly(anomalyId) {<br \/>\n    \/\/ \u9759\u97f3\u5f02\u5e38<br \/>\n    setAnomalies(prev &#061;&gt; prev.filter(a &#061;&gt; a.id !&#061;&#061; anomalyId));<br \/>\n  }<\/p>\n<p>  function implementRecommendation(recommendation) {<br \/>\n    \/\/ \u5b9e\u65bd\u5efa\u8bae<br \/>\n    console.log(&#039;Implementing recommendation:&#039;, recommendation);<br \/>\n    alert(&#096;\u5f00\u59cb\u5b9e\u65bd: ${recommendation}&#096;);<br \/>\n  }<\/p>\n<p>  function exportReport() {<br \/>\n    \/\/ \u5bfc\u51fa\u62a5\u544a<br \/>\n    const reportData &#061; {<br \/>\n      timestamp: new Date().toISOString(),<br \/>\n      timeRange,<br \/>\n      stats,<br \/>\n      networkMetrics<br \/>\n    };<\/p>\n<p>    const blob &#061; new Blob([JSON.stringify(reportData, null, 2)], {<br \/>\n      type: &#039;application\/json&#039;<br \/>\n    });<\/p>\n<p>    const url &#061; URL.createObjectURL(blob);<br \/>\n    const a &#061; document.createElement(&#039;a&#039;);<br \/>\n    a.href &#061; url;<br \/>\n    a.download &#061; &#096;timeout-report-${new Date().toISOString().slice(0, 10)}.json&#096;;<br \/>\n    a.click();<br \/>\n    URL.revokeObjectURL(url);<br \/>\n  }<br \/>\n}<\/p>\n<p>\/\/ CSS\u6837\u5f0f&#xff08;\u5185\u8054\u793a\u4f8b&#xff09;<br \/>\nconst styles &#061; &#096;<br \/>\n.timeout-dashboard {<br \/>\n  padding: 20px;<br \/>\n  max-width: 1600px;<br \/>\n  margin: 0 auto;<br \/>\n  font-family: -apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, sans-serif;<br \/>\n}<\/p>\n<p>.dashboard-header {<br \/>\n  display: flex;<br \/>\n  justify-content: space-between;<br \/>\n  align-items: center;<br \/>\n  margin-bottom: 30px;<br \/>\n  padding-bottom: 20px;<br \/>\n  border-bottom: 1px solid #e0e0e0;<br \/>\n}<\/p>\n<p>.header-left {<br \/>\n  display: flex;<br \/>\n  align-items: center;<br \/>\n  gap: 15px;<br \/>\n}<\/p>\n<p>.header-icon {<br \/>\n  color: #ff6b6b;<br \/>\n}<\/p>\n<p>.time-range-label {<br \/>\n  background: #f0f0f0;<br \/>\n  padding: 4px 12px;<br \/>\n  border-radius: 16px;<br \/>\n  font-size: 14px;<br \/>\n  color: #666;<br \/>\n}<\/p>\n<p>.time-range-selector {<br \/>\n  display: flex;<br \/>\n  gap: 8px;<br \/>\n}<\/p>\n<p>.time-range-btn {<br \/>\n  padding: 8px 16px;<br \/>\n  border: 1px solid #ddd;<br \/>\n  border-radius: 4px;<br \/>\n  background: white;<br \/>\n  cursor: pointer;<br \/>\n  font-size: 14px;<br \/>\n}<\/p>\n<p>.time-range-btn.active {<br \/>\n  background: #007bff;<br \/>\n  color: white;<br \/>\n  border-color: #007bff;<br \/>\n}<\/p>\n<p>.key-metrics-cards {<br \/>\n  display: grid;<br \/>\n  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));<br \/>\n  gap: 20px;<br \/>\n  margin-bottom: 30px;<br \/>\n}<\/p>\n<p>.metric-card {<br \/>\n  background: white;<br \/>\n  border-radius: 8px;<br \/>\n  padding: 20px;<br \/>\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);<br \/>\n  display: flex;<br \/>\n  align-items: center;<br \/>\n  gap: 20px;<br \/>\n  border-left: 4px solid;<br \/>\n}<\/p>\n<p>.metric-card.critical { border-left-color: #ff6b6b; }<br \/>\n.metric-card.warning { border-left-color: #ffa726; }<br \/>\n.metric-card.info { border-left-color: #42a5f5; }<br \/>\n.metric-card.success { border-left-color: #66bb6a; }<\/p>\n<p>.metric-icon {<br \/>\n  background: #f5f5f5;<br \/>\n  padding: 12px;<br \/>\n  border-radius: 8px;<br \/>\n  display: flex;<br \/>\n  align-items: center;<br \/>\n  justify-content: center;<br \/>\n}<\/p>\n<p>.metric-card.critical .metric-icon { background: #ffebee; color: #ff6b6b; }<br \/>\n.metric-card.warning .metric-icon { background: #fff3e0; color: #ffa726; }<br \/>\n.metric-card.info .metric-icon { background: #e3f2fd; color: #42a5f5; }<br \/>\n.metric-card.success .metric-icon { background: #e8f5e8; color: #66bb6a; }<\/p>\n<p>.metric-value {<br \/>\n  font-size: 32px;<br \/>\n  font-weight: bold;<br \/>\n  margin: 8px 0;<br \/>\n}<\/p>\n<p>.metric-trend {<br \/>\n  font-size: 14px;<br \/>\n  color: #666;<br \/>\n}<\/p>\n<p>.network-metrics {<br \/>\n  background: white;<br \/>\n  border-radius: 8px;<br \/>\n  padding: 20px;<br \/>\n  margin-bottom: 30px;<br \/>\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);<br \/>\n}<\/p>\n<p>.network-cards {<br \/>\n  display: grid;<br \/>\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));<br \/>\n  gap: 15px;<br \/>\n  margin-top: 15px;<br \/>\n}<\/p>\n<p>.network-card {<br \/>\n  display: flex;<br \/>\n  align-items: center;<br \/>\n  gap: 10px;<br \/>\n  padding: 15px;<br \/>\n  background: #f8f9fa;<br \/>\n  border-radius: 6px;<br \/>\n  border: 1px solid #e9ecef;<br \/>\n}<\/p>\n<p>.network-label {<br \/>\n  flex: 1;<br \/>\n  font-size: 14px;<br \/>\n  color: #666;<br \/>\n}<\/p>\n<p>.network-value {<br \/>\n  font-weight: bold;<br \/>\n  font-size: 16px;<br \/>\n}<\/p>\n<p>.network-value.warning { color: #ffa726; }<br \/>\n.network-value.critical { color: #ff6b6b; }<\/p>\n<p>.charts-section {<br \/>\n  margin-bottom: 30px;<br \/>\n}<\/p>\n<p>.chart-row {<br \/>\n  display: grid;<br \/>\n  grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));<br \/>\n  gap: 20px;<br \/>\n  margin-bottom: 20px;<br \/>\n}<\/p>\n<p>.chart-container {<br \/>\n  background: white;<br \/>\n  border-radius: 8px;<br \/>\n  padding: 20px;<br \/>\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);<br \/>\n}<\/p>\n<p>.realtime-events-section {<br \/>\n  background: white;<br \/>\n  border-radius: 8px;<br \/>\n  padding: 20px;<br \/>\n  margin-bottom: 30px;<br \/>\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);<br \/>\n}<\/p>\n<p>.events-table-container {<br \/>\n  overflow-x: auto;<br \/>\n  margin-top: 15px;<br \/>\n}<\/p>\n<p>.events-table {<br \/>\n  width: 100%;<br \/>\n  border-collapse: collapse;<br \/>\n}<\/p>\n<p>.events-table th {<br \/>\n  text-align: left;<br \/>\n  padding: 12px;<br \/>\n  background: #f8f9fa;<br \/>\n  border-bottom: 2px solid #dee2e6;<br \/>\n  font-weight: 600;<br \/>\n  color: #495057;<br \/>\n}<\/p>\n<p>.events-table td {<br \/>\n  padding: 12px;<br \/>\n  border-bottom: 1px solid #dee2e6;<br \/>\n}<\/p>\n<p>.event-row.severity-high {<br \/>\n  background: #fff5f5;<br \/>\n}<\/p>\n<p>.event-row.severity-medium {<br \/>\n  background: #fff9db;<br \/>\n}<\/p>\n<p>.event-row.severity-low {<br \/>\n  background: #f8f9fa;<br \/>\n}<\/p>\n<p>.endpoint-cell code {<br \/>\n  background: #f1f3f5;<br \/>\n  padding: 2px 6px;<br \/>\n  border-radius: 4px;<br \/>\n  font-family: monospace;<br \/>\n  font-size: 12px;<br \/>\n}<\/p>\n<p>.duration-badge {<br \/>\n  background: #e7f5ff;<br \/>\n  color: #1971c2;<br \/>\n  padding: 4px 8px;<br \/>\n  border-radius: 4px;<br \/>\n  font-size: 12px;<br \/>\n  font-weight: 500;<br \/>\n}<\/p>\n<p>.cause-badge {<br \/>\n  padding: 4px 8px;<br \/>\n  border-radius: 4px;<br \/>\n  font-size: 12px;<br \/>\n  font-weight: 500;<br \/>\n}<\/p>\n<p>.cause-badge.cause-network { background: #e3f2fd; color: #1976d2; }<br \/>\n.cause-badge.cause-server { background: #f3e5f5; color: #7b1fa2; }<br \/>\n.cause-badge.cause-application { background: #e8f5e8; color: #2e7d32; }<br \/>\n.cause-badge.cause-client { background: #fff3e0; color: #ef6c00; }<\/p>\n<p>.severity-badge {<br \/>\n  padding: 4px 8px;<br \/>\n  border-radius: 4px;<br \/>\n  font-size: 12px;<br \/>\n  font-weight: 500;<br \/>\n}<\/p>\n<p>.severity-badge.severity-high { background: #ffebee; color: #c62828; }<br \/>\n.severity-badge.severity-medium { background: #fff3e0; color: #ef6c00; }<br \/>\n.severity-badge.severity-low { background: #e8f5e8; color: #2e7d32; }<\/p>\n<p>.action-btn {<br \/>\n  padding: 6px 12px;<br \/>\n  margin: 0 4px;<br \/>\n  border: none;<br \/>\n  border-radius: 4px;<br \/>\n  cursor: pointer;<br \/>\n  font-size: 12px;<br \/>\n}<\/p>\n<p>.action-btn.view-details {<br \/>\n  background: #e3f2fd;<br \/>\n  color: #1976d2;<br \/>\n}<\/p>\n<p>.action-btn.acknowledge {<br \/>\n  background: #e8f5e8;<br \/>\n  color: #2e7d32;<br \/>\n}<\/p>\n<p>.anomalies-section {<br \/>\n  background: white;<br \/>\n  border-radius: 8px;<br \/>\n  padding: 20px;<br \/>\n  margin-bottom: 30px;<br \/>\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);<br \/>\n  border: 2px solid #ffebee;<br \/>\n}<\/p>\n<p>.anomaly-card {<br \/>\n  background: #fff5f5;<br \/>\n  border: 1px solid #ffcdd2;<br \/>\n  border-radius: 6px;<br \/>\n  padding: 15px;<br \/>\n  margin-bottom: 10px;<br \/>\n}<\/p>\n<p>.anomaly-header {<br \/>\n  display: flex;<br \/>\n  justify-content: space-between;<br \/>\n  margin-bottom: 10px;<br \/>\n}<\/p>\n<p>.anomaly-type {<br \/>\n  font-weight: bold;<br \/>\n  color: #c62828;<br \/>\n}<\/p>\n<p>.anomaly-time {<br \/>\n  font-size: 12px;<br \/>\n  color: #666;<br \/>\n}<\/p>\n<p>.anomaly-details {<br \/>\n  background: white;<br \/>\n  padding: 10px;<br \/>\n  border-radius: 4px;<br \/>\n  margin-top: 10px;<br \/>\n  font-size: 12px;<br \/>\n  max-height: 200px;<br \/>\n  overflow-y: auto;<br \/>\n}<\/p>\n<p>.anomaly-actions {<br \/>\n  display: flex;<br \/>\n  gap: 10px;<br \/>\n  margin-top: 15px;<br \/>\n}<\/p>\n<p>.btn {<br \/>\n  padding: 8px 16px;<br \/>\n  border: none;<br \/>\n  border-radius: 4px;<br \/>\n  cursor: pointer;<br \/>\n  font-size: 14px;<br \/>\n}<\/p>\n<p>.btn-primary {<br \/>\n  background: #1976d2;<br \/>\n  color: white;<br \/>\n}<\/p>\n<p>.btn-secondary {<br \/>\n  background: #f5f5f5;<br \/>\n  color: #333;<br \/>\n}<\/p>\n<p>.btn-tertiary {<br \/>\n  background: transparent;<br \/>\n  color: #666;<br \/>\n  border: 1px solid #ddd;<br \/>\n}<\/p>\n<p>.insights-section {<br \/>\n  background: white;<br \/>\n  border-radius: 8px;<br \/>\n  padding: 20px;<br \/>\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);<br \/>\n}<\/p>\n<p>.insights-grid {<br \/>\n  display: grid;<br \/>\n  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));<br \/>\n  gap: 20px;<br \/>\n  margin-top: 20px;<br \/>\n}<\/p>\n<p>.insight-card {<br \/>\n  display: flex;<br \/>\n  gap: 15px;<br \/>\n  padding: 15px;<br \/>\n  background: #f8f9fa;<br \/>\n  border-radius: 6px;<br \/>\n  border-left: 4px solid #42a5f5;<br \/>\n}<\/p>\n<p>.insight-icon {<br \/>\n  font-size: 24px;<br \/>\n}<\/p>\n<p>.insight-content {<br \/>\n  flex: 1;<br \/>\n}<\/p>\n<p>.insight-action {<br \/>\n  margin-top: 10px;<br \/>\n  padding: 6px 12px;<br \/>\n  background: #42a5f5;<br \/>\n  color: white;<br \/>\n  border: none;<br \/>\n  border-radius: 4px;<br \/>\n  cursor: pointer;<br \/>\n  font-size: 12px;<br \/>\n}<br \/>\n&#096;;<\/p>\n<p>\/\/ \u6dfb\u52a0\u5230\u6587\u6863<br \/>\nconst styleSheet &#061; document.createElement(&#039;style&#039;);<br \/>\nstyleSheet.textContent &#061; styles;<br \/>\ndocument.head.appendChild(styleSheet)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u7b2c20\u7ae0&#xff1a;405 Method Not Allowed &#8211; \u65b9\u6cd5\u4e0d\u5141\u8bb8\u6df1\u5ea6\u5206\u679020.1 \u5b9a\u4e49\u4e0e\u8bed\u4e49405 Method Not Allowed\u00a0\u72b6\u6001\u7801\u8868\u793a\u8bf7\u6c42\u884c\u4e2d\u6307\u5b9a\u7684\u65b9\u6cd5\u5bf9\u8bf7\u6c42URI\u6807\u8bc6\u7684\u8d44\u6e90\u662f\u5df2\u77e5\u7684&#xff0c;\u4f46\u4e0d\u88ab\u76ee\u6807\u8d44\u6e90\u652f\u6301\u3002\u670d\u52a1\u5668\u5fc5\u987b\u751f\u6210\u4e00\u4e2a\u00a0Allow\u00a0\u54cd\u5e94\u5934\u5b57\u6bb5&#xff0c;\u5305\u542b\u76ee\u6807\u8d44\u6e90\u5f53\u524d\u652f\u6301\u7684\u65b9\u6cd5\u5217\u8868\u3002\u6838\u5fc3\u8bed\u4e49&#xff1a;\u670d\u52a1\u5668\u7406\u89e3\u8bf7\u6c42&#xff0c;\u4f46\u660e\u786e\u62d2\u7edd\u6267\u884c\u8be5\u65b9\u6cd5\u4e0e501&#xff08;\u672a\u5b9e\u73b0&#xff09;\u4e0d\u540c&amp;#xff1a<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[99,43],"topic":[],"class_list":["post-59721","post","type-post","status-publish","format-standard","hentry","category-server","tag-java","tag-43"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.wsisp.com\/helps\/59721.html\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"og:description\" content=\"\u7b2c20\u7ae0&#xff1a;405 Method Not Allowed - \u65b9\u6cd5\u4e0d\u5141\u8bb8\u6df1\u5ea6\u5206\u679020.1 \u5b9a\u4e49\u4e0e\u8bed\u4e49405 Method Not Allowed\u00a0\u72b6\u6001\u7801\u8868\u793a\u8bf7\u6c42\u884c\u4e2d\u6307\u5b9a\u7684\u65b9\u6cd5\u5bf9\u8bf7\u6c42URI\u6807\u8bc6\u7684\u8d44\u6e90\u662f\u5df2\u77e5\u7684&#xff0c;\u4f46\u4e0d\u88ab\u76ee\u6807\u8d44\u6e90\u652f\u6301\u3002\u670d\u52a1\u5668\u5fc5\u987b\u751f\u6210\u4e00\u4e2a\u00a0Allow\u00a0\u54cd\u5e94\u5934\u5b57\u6bb5&#xff0c;\u5305\u542b\u76ee\u6807\u8d44\u6e90\u5f53\u524d\u652f\u6301\u7684\u65b9\u6cd5\u5217\u8868\u3002\u6838\u5fc3\u8bed\u4e49&#xff1a;\u670d\u52a1\u5668\u7406\u89e3\u8bf7\u6c42&#xff0c;\u4f46\u660e\u786e\u62d2\u7edd\u6267\u884c\u8be5\u65b9\u6cd5\u4e0e501&#xff08;\u672a\u5b9e\u73b0&#xff09;\u4e0d\u540c&amp;#xff1a\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.wsisp.com\/helps\/59721.html\" \/>\n<meta property=\"og:site_name\" content=\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"article:published_time\" content=\"2026-01-14T03:33:29+00:00\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"78 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/59721.html\",\"url\":\"https:\/\/www.wsisp.com\/helps\/59721.html\",\"name\":\"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"isPartOf\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\"},\"datePublished\":\"2026-01-14T03:33:29+00:00\",\"dateModified\":\"2026-01-14T03:33:29+00:00\",\"author\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/59721.html#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.wsisp.com\/helps\/59721.html\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/59721.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.wsisp.com\/helps\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\",\"url\":\"https:\/\/www.wsisp.com\/helps\/\",\"name\":\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"description\":\"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"contentUrl\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"caption\":\"admin\"},\"sameAs\":[\"http:\/\/wp.wsisp.com\"],\"url\":\"https:\/\/www.wsisp.com\/helps\/author\/admin\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.wsisp.com\/helps\/59721.html","og_locale":"zh_CN","og_type":"article","og_title":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","og_description":"\u7b2c20\u7ae0&#xff1a;405 Method Not Allowed - \u65b9\u6cd5\u4e0d\u5141\u8bb8\u6df1\u5ea6\u5206\u679020.1 \u5b9a\u4e49\u4e0e\u8bed\u4e49405 Method Not Allowed\u00a0\u72b6\u6001\u7801\u8868\u793a\u8bf7\u6c42\u884c\u4e2d\u6307\u5b9a\u7684\u65b9\u6cd5\u5bf9\u8bf7\u6c42URI\u6807\u8bc6\u7684\u8d44\u6e90\u662f\u5df2\u77e5\u7684&#xff0c;\u4f46\u4e0d\u88ab\u76ee\u6807\u8d44\u6e90\u652f\u6301\u3002\u670d\u52a1\u5668\u5fc5\u987b\u751f\u6210\u4e00\u4e2a\u00a0Allow\u00a0\u54cd\u5e94\u5934\u5b57\u6bb5&#xff0c;\u5305\u542b\u76ee\u6807\u8d44\u6e90\u5f53\u524d\u652f\u6301\u7684\u65b9\u6cd5\u5217\u8868\u3002\u6838\u5fc3\u8bed\u4e49&#xff1a;\u670d\u52a1\u5668\u7406\u89e3\u8bf7\u6c42&#xff0c;\u4f46\u660e\u786e\u62d2\u7edd\u6267\u884c\u8be5\u65b9\u6cd5\u4e0e501&#xff08;\u672a\u5b9e\u73b0&#xff09;\u4e0d\u540c&amp;#xff1a","og_url":"https:\/\/www.wsisp.com\/helps\/59721.html","og_site_name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","article_published_time":"2026-01-14T03:33:29+00:00","author":"admin","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"admin","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"78 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.wsisp.com\/helps\/59721.html","url":"https:\/\/www.wsisp.com\/helps\/59721.html","name":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","isPartOf":{"@id":"https:\/\/www.wsisp.com\/helps\/#website"},"datePublished":"2026-01-14T03:33:29+00:00","dateModified":"2026-01-14T03:33:29+00:00","author":{"@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41"},"breadcrumb":{"@id":"https:\/\/www.wsisp.com\/helps\/59721.html#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.wsisp.com\/helps\/59721.html"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.wsisp.com\/helps\/59721.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.wsisp.com\/helps"},{"@type":"ListItem","position":2,"name":"HTTP \u72b6\u6001\u7801\uff1a\u5ba2\u6237\u7aef\u4e0e\u670d\u52a1\u5668\u7684\u901a\u4fe1\u8bed\u8a00\u2014\u2014\u7b2c\u56db\u90e8\u5206\uff1a\u5ba2\u6237\u7aef\u9519\u8bef\u72b6\u6001\u7801\uff084xx\uff09\u6df1\u5ea6\u89e3\u8bfb\uff08\u4e8c\uff09"}]},{"@type":"WebSite","@id":"https:\/\/www.wsisp.com\/helps\/#website","url":"https:\/\/www.wsisp.com\/helps\/","name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","description":"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41","name":"admin","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/","url":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","contentUrl":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","caption":"admin"},"sameAs":["http:\/\/wp.wsisp.com"],"url":"https:\/\/www.wsisp.com\/helps\/author\/admin"}]}},"_links":{"self":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/59721","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/comments?post=59721"}],"version-history":[{"count":0,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/59721\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media?parent=59721"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/categories?post=59721"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/tags?post=59721"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/topic?post=59721"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}