aws-elixir源码剖析:query、json、rest-json、rest-xml、ec2五大协议请求构建全流程

发布时间:2026/8/28 15:23:13
aws-elixir源码剖析:query、json、rest-json、rest-xml、ec2五大协议请求构建全流程 aws-elixir源码剖析query、json、rest-json、rest-xml、ec2五大协议请求构建全流程【免费下载链接】aws-elixirAWS clients for Elixir项目地址: https://gitcode.com/gh_mirrors/aw/aws-elixiraws-elixir 是一个 Elixir 编写的 AWS 官方风格客户端库几乎覆盖全部 AWS 服务。本文剖析它的五大协议query、json、rest-json、rest-xml、ec2请求构建全流程带你从源码看懂一次 AWS 调用是如何被组装、编码、签名并发出的。先看清五大协议一张表看懂格式差异AWS 每个服务都声明了自己的通信协议aws-elixir 在 lib/aws/request.ex 中只认这五种协议请求格式响应格式代表服务queryPOST 表单Action Version 拼在 bodyXML早期服务如 CloudWatch、SQSjsonPOST JSON靠X-Amz-Target头定位操作JSONKMStarget_prefix 为TrentServicerest-jsonRESTful 路径 JSON bodyJSONAPI Gateway、Lambda 等rest-xmlRESTful 路径 XML bodyXMLS3ec2POST 表单Action Version 拼在 bodyXMLEC2核心规律一句话请求编码看协议走encode!响应解码走decode!。query 和 ec2 的请求体都是表单编码但响应都是 XMLjson 和 rest-json 全程 JSONrest-xml 全程 XML。请求管线总览request_post 的六个阶段所有走 POST 的协议query、json、ec2都汇聚到AWS.Request.request_post/5流程固定为六步① 准备客户端prepare_client把服务元数据里的signing_name如ec2、s3注入 client若服务是全局的如 IAM、Route53region 会被替换为凭证作用域默认us-east-1。② 构建主机名build_host按五种规则拼出域名详见下文「端点如何拼装」一节。③ 组装 URIbuild_uriproto://host:port再 merge 路径POST 类请求路径固定为/。④ 初始化请求头固定两对头Host和Content-Type取自服务元数据如application/x-www-form-urlencoded。⑤ 协议差异化处理源码中最妙的一处分支# lib/aws/request.ex # json 协议用 X-Amz-Target 头定位具体操作 if metadata.protocol json do [{X-Amz-Target, #{metadata.target_prefix}.#{action}} | headers] end # query / ec2 协议把 Action 和 Version 注入请求体 if metadata.protocol in ~w(query ec2) do Map.merge(input, %{Action action, Version metadata.api_version}) end⑥ 编码 → 签名 → 发送 → 解码encode!按协议把 map 转成字符串Signature.sign_v4生成签名头经AWS.Client.request发出最后decode!把响应还原成 map。协议到编解码器的映射规则encode!/decode!用一张简单的映射表消除五协议差异协议请求编码响应解码ec2、query表单 query 编码lib/aws/util.ex 的encode_queryXMLjson、rest-jsonJSONJason见 lib/aws/json.exJSONrest-xmlXMLxmerl见 lib/aws/xml.exXML所以源码里根本没有五种协议的五套编码逻辑只有 query、JSON、XML 三种编解码器协议名只是「路由标签」。REST 家族怎么走request_rest 的九参数rest-json 和 rest-xml 走另一个入口request_rest/9比 POST 多三个关键参数http_method、path、success_status_code。1. 参数分流谁进 URL、谁进请求头、谁进 bodyREST 协议下每个输入字段的「位置」由生成代码决定。build_params/2按映射表把输入参数搬到 header 或 query string例如 S3 的PutObject中Bucket走 URL 路径、Body走二进制体。add_query/3再把 query 参数编码后拼到 URI 上。2. body 的三种形态cond do send_body_as_binary? - Map.fetch!(input, Body) # 二进制直传如 S3 上传 http_method in [:get, :head, :options, :delete] - # 这些方法不带 body true - encode!(client, metadata.protocol, input) # 其余按协议编码 endsend_body_as_binary?是专为 S3 这类「上传原始字节」场景设计的开关此时Content-Type默认application/octet-stream。3. 可选的 SHA256 校验头传append_sha256_content_hash: true时会额外计算 body 的 SHA256 并放入X-Amz-CheckSum-SHA256头用于端到端完整性校验。4. 成功状态码不只有 200success_status_code参数允许服务声明自己的成功码——S3 的 HEAD 请求返回 204、部分删除返回 206源码用status_code success_status_code判断成败其余情况包装为{:error, {:unexpected_response, response}}。SigV4 签名如何完成sign_v4 的四个细节签名统一收口在 lib/aws/signature.ex实际哈希运算委托给 Erlang 的:aws_signatureNIF 模块Elixir 侧只做参数组装时间取整now()截断到秒级 UTC保证签名时间确定性Session Token临时凭证STS自动追加X-Amz-Security-Token头全局服务兜底region 为空时回退到us-east-1S3 特殊规则service s3时传uri_encode_path: false因为 S3 的 object key 允许保留/等字符不能全量百分号编码。另外request_rest支持sign_request?: false跳过签名方便处理预签名场景。端点如何拼装build_host 的五条规则URL 的域名由build_host/3按优先级匹配lib/aws/request.ex本地模式region 为local且未设 endpoint →localhost自定义 endpoint 字符串原样使用如对接 MinIO 等兼容 API自定义 endpoint 函数以%{region:, service:, global?:, endpoint_prefix:, account_id:}调用灵活度最高全局服务直接取元数据里的hostname常规服务host_prefix endpoint_prefix region 默认后缀amazonaws.com用点号连接如s3.us-east-1.amazonaws.com。两个隐藏细节S3 Control 会在最前面插入account_id账号专属端点{AccountId}占位符会被全局替换。想接入 AWS 兼容服务时用 lib/aws/client.ex 的put_endpoint/2传{:keep_prefixes, example.com}即可保留前缀规则最终得到s3.us-east-1.example.com这样的地址。重试策略与可插拔组件三个可换的开关重试AWS.Client.request/6内置指数退避 全抖动FullJitter重试对 500/429 状态码及连接类错误:closed、:connect_timeout、Mint 的:timeout生效默认最多 10 次、间隔上限 5 秒需显式传enable_retries?: true开启。三个可替换组件都在 client 的defstruct里默认值一目了然组件默认实现配置项HTTP 客户端Hackneylib/aws/http_client/hackney.exhttp_client可换 FinchJSON 解析器Jasonjson_moduleXML 解析器xmerl 封装xml_module均遵循AWS.HTTPClient/AWS.JSON/AWS.XML行为契约实现对应回调即可替换。生成代码与源码阅读路线lib/aws/generated/ 下每个服务模块结构一致如 lib/aws/generated/s3.ex一个metadata/0返回服务元数据协议、endpoint_prefix、api_version、signing_name、target_prefix每个 API 一个函数POST 类直接调用Request.request_post(client, meta, action, input, options)REST 类调用Request.request_rest/9并带上方法、路径和成功码。也就是说手写代码只有 lib/aws/ 下那一层「引擎」几百个服务模块全部由 aws-codegen 生成这正是五大协议能统一抽象成两个入口的原因。快速验证协议测试目录想边读边动手推荐看 test/aws/protocol_tests/四个文件正好对应四种测试场景json_post_test.exs—— json 协议的 X-Amz-Target 与 JSON bodyquery_post_test.exs—— query 协议的 Action/Version 注入与 XML 响应rest_json_test.exs—— rest-json 的路径、query 与状态码rest_xml_test.exs—— rest-xml 的 S3 风格调用推荐阅读顺序先看 lib/aws/request.ex 的request_post与request_rest两个函数全文约 370 行是全部协议逻辑的骨架再看 lib/aws/client.ex 理解凭证、endpoint 与重试然后挑一个生成模块KMS 最简单S3 最典型对照元数据看调用签名最后跑一遍 test/aws/protocol_tests/ 中的测试验证理解。读完后你会发现所谓五大协议本质只是「编码格式 × 传输位置body / 路径 / 头× 成功码」的三种组合而 aws-elixir 用两个入口函数 一张映射表就把它们全部收编了。【免费下载链接】aws-elixirAWS clients for Elixir项目地址: https://gitcode.com/gh_mirrors/aw/aws-elixir创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考