SeaTunnel Http Source 连接器完整指南:HTTP 数据采集、分页抓取、JSON 抽取与二进制下载
2026/9/19 17:23:46 网站建设 项目流程

SeaTunnel Http Source 连接器完整指南:HTTP 数据采集、分页抓取、JSON 抽取与二进制下载

【免费下载链接】seatunnelSeaTunnel is a multimodal, high-performance, distributed, massive data integration tool.项目地址: https://gitcode.com/GitHub_Trending/se/seatunnel

Http Source 是 SeaTunnel 中用于从 HTTP API 读取数据的源连接器,支持 GET/POST 请求、JSON/text/binary 三种响应格式、基于页码与游标的分页抓取,以及流式轮询,是打通外部 HTTP 服务与 SeaTunnel 数据集成管道的最常用入口。阅读本文后,你将掌握 Http Source 的全部配置参数、分页与占位符替换机制、content_field/json_field的 JSON 抽取技巧,以及底层源码实现原理,能够独立完成从 HTTP 接口同步数据到各类 Sink 的实战作业。

Http 源连接器概述

Http 源连接器用于从 HTTP 接口读取数据,在 SeaTunnel 中通过Http插件标识注册(对应源码 HttpSourceFactory.java 中的factoryIdentifier()返回值)。

支持的引擎

Spark
Flink
SeaTunnel Zeta

主要特性

特性支持情况
批处理✅ 支持
流处理✅ 支持(配合poll_interval_millis周期性轮询)
精确一次❌ 不支持
列投影❌ 不支持
并行度❌ 不支持(连接器为单分片读取,即 AbstractSingleSplitReader 的定位)
支持用户自定义分片❌ 不支持

特性说明详见 Connector V2 特性文档。

依赖安装

数据源支持的版本依赖
Http通用connector-http(可通过install-plugin.sh脚本安装,或从 Maven 中央仓库下载)

源选项速查表

以下为 Http 源连接器的完整源选项,其中部分选项在源码 HttpSourceOptions.java 与 HttpCommonOptions.java 中定义:

名称类型是否必须默认值描述
urlString-HTTP 请求 URL,唯一必填项,由HttpSourceFactory声明为 required 选项
schemaConfig-HTTP 响应与 SeaTunnel 数据结构映射,详见 Schema 特性
schema.fieldsConfig-上游数据的 schema 字段定义
json_fieldConfig-辅助配置 schema,必须与schema一起使用,通过 JSONPath 从响应中抽取字段
pageingConfig-分页查询配置(注意:这是连接器现有的参数名,拼写保持为pageing
pageing.page_fieldStringpage请求中分页字段名,可在 headers、params 或 body 中使用${page}占位符
pageing.use_placeholder_replacementBooleanfalse为 true 时对 headers、params、body 的值使用${field}占位符替换;为 false 时使用基于 key 的整值替换
pageing.total_page_sizeLong0控制总页数;0表示总页数未知,连接器会在单次返回行数小于pageing.batch_size时停止
pageing.batch_sizeInt100每个请求返回的批量大小,用于总页数未知时判断是否继续翻页
pageing.start_page_numberInt1指定开始同步的页码
pageing.page_typeStringPageNumber分页类型,仅支持PageNumberCursor(对应枚举 HttpPaginationType.java)
pageing.cursor_fieldString-请求参数中游标字段的名称
pageing.cursor_response_fieldString-从响应中检索游标的字段(支持 JSONPath,如$.paging.cursors.next
content_fieldString-抽取 JSON 数据片段,如content_field = "$.store.book.*"只取 book 部分
formatStringtext上游数据格式,支持jsontextbinarybinary时响应体作为原始字节处理,用于下载文件(PDF、图片、ZIP 等)
binary_chunk_sizeLong10485760format = binary时的分片大小(字节),大文件拆分为多行,默认 10MB,仅在 BATCH 模式下生效
methodStringgetHTTP 请求方法,连接器文档说明仅支持 GET、POST;底层 HttpClientProvider.java 同时实现了 PUT、DELETE 分支(DELETE 通过_method=delete的 POST 模拟)
headersMap-HTTP 头信息
paramsMap-HTTP 查询参数
bodyString-HTTP 请求体,程序自动添加Content-Type: application/json,body 为 JSON body
poll_interval_millisInt-流模式下请求 HTTP API 的间隔(毫秒)
retryInt-请求返回IOException时的最大重试次数
retry_backoff_multiplier_msInt100请求失败时的重试退避时间(毫秒)乘数
retry_backoff_max_msInt10000请求失败时的最大重试退避时间(毫秒)
enable_multi_linesBooleanfalse是否将 HTTP 响应文本按行拆分(每行作为一条记录输出)
connect_timeout_msInt12000连接超时设置,默认 12 秒(源码默认值为6000 * 2
socket_timeout_msInt60000Socket 超时设置,默认 60 秒(源码默认值为6000 * 10
common-options--源插件通用参数,详见 Source Common Options
keep_params_as_formBooleanfalse是否按表单提交参数,用于兼容旧行为;为 true 时 params 的值通过表单提交
keep_page_param_as_http_paramBooleanfalse是否将分页参数直接写入 params,用于兼容旧行为
json_filed_missed_return_nullBooleanfalseJSON 字段缺失时,为 true 返回 null,否则返回错误

注意:json_filed_missed_return_null沿用了源码中的拼写(Filed而非Field),作业配置中需保持该拼写,对应 HttpSourceOptions.java 中的JSON_FILED_MISSED_RETURN_NULL定义。

参数说明与易错点

  • pageing是连接器现有的参数名,作业配置中必须保持这个拼写,不要写成paging
  • format = json通常需要配置schema。当字段来自嵌套 JSON 路径时,可以配合json_field使用;如果想直接抽取某个 JSON 数组或对象片段,可以使用content_fieldHttpSourceFactoryoptionRule()中通过conditional规则将schema声明为format = json时的条件必填项。
  • format = binary会输出固定字段(data: bytes, relativePath: string, partIndex: long),只支持批模式,且不能与pageing一起使用——源码 HttpSourceReader.java 的构造函数会在binaryMode && pageInfo != null时直接抛出CONFIG_VALIDATION_FAILED异常。

快速上手:创建 Http 数据同步作业

以下是一个完整的批处理作业:从 HTTP 接口读取 JSON 数据并按声明 schema 解析,最后输出到控制台:

env { parallelism = 1 job.mode = "BATCH" } source { Http { plugin_output = "http" url = "http://mockserver:1080/example/http" method = "GET" format = "json" schema = { fields { c_map = "map<string, string>" c_array = "array<int>" c_string = string c_boolean = boolean c_tinyint = tinyint c_smallint = smallint c_int = int c_bigint = bigint c_float = float c_double = double c_bytes = bytes c_date = date c_decimal = "decimal(38, 18)" c_timestamp = timestamp c_row = { C_MAP = "map<string, string>" C_ARRAY = "array<int>" C_STRING = string C_BOOLEAN = boolean C_TINYINT = tinyint C_SMALLINT = smallint C_INT = int C_BIGINT = bigint C_FLOAT = float C_DOUBLE = double C_BYTES = bytes C_DATE = date C_DECIMAL = "decimal(38, 18)" C_TIMESTAMP = timestamp } } } } } # 控制台打印读取的 Http 数据 sink { Console { parallelism = 1 } }

从源码 HttpClientProvider.java 的实现看,连接器使用 Apache HttpClient 执行请求,并将connect_timeout_mssocket_timeout_ms注入RequestConfig。响应状态码在 200~207 之间视为成功,否则抛出REQUEST_FAILED异常。

format 详解:json / text / binary

format = json

当指定formatjson时,应同时指定schema选项。例如上游数据如下:

{ "code": 200, "data": "get success", "success": true }

对应的 schema 配置:

schema { fields { code = int data = string success = boolean } }

连接器将生成如下数据:

codedatasuccess
200get successtrue

format = text

当指定formattext时,连接器不会对上游数据做任何处理,整段响应文本作为一行content输出。同样输入上述 JSON,生成的数据为:

content
{"code": 200, "data": "get success", "success": true}

配合enable_multi_lines = true时,HttpSourceReader.pollAndCollectData 会将响应文本按行拆分为多条记录。

format = binary

当指定formatbinary时,HTTP 响应体作为原始字节处理,用于下载文件(PDF、图片、ZIP 等)。输出 schema 固定为(data: bytes, relativePath: string, partIndex: long)。大文件会根据binary_chunk_size自动拆分为多行,文件名优先从响应头Content-Disposition提取(对应源码 FilenameExtractor.java)。仅支持 BATCH 模式,且不支持分页(pageing)。

示例:通过 HTTP 下载文件并写入 LocalFileSink:

env { parallelism = 1 job.mode = "BATCH" } source { Http { url = "http://example.com/files/report.pdf" method = "GET" format = "binary" binary_chunk_size = 10485760 # 每个分片 10MB schema = { fields { data = bytes relativePath = string partIndex = long } } } } sink { LocalFile { path = "/tmp/download" file_format = "binary" } }

二进制模式在底层通过 HttpClientProvider.executeBinaryStreaming 以流式分块方式消费响应体,每读到chunkSize字节即回调一次,避免整个大文件驻留内存。

请求组装细节:params、body、headers 与兼容开关

keep_params_as_form

用于兼容旧版本的 http 连接器行为:

  • 当设置为true时,paramspageing以表单形式提交。
  • 当设置为false时,params添加到 URL 路径中,而pageing不会添加到 body 或表单中,只会替换 params 和 body 中的占位符。

从源码 HttpClientProvider.execute 看:POST +keepParamsAsForm = true时,若params非空,会自动补上Content-Type: application/x-www-form-urlencoded,并将bodyparams合并为表单 body;若两者出现同名键,params的值会覆盖body中的同名键。

keep_page_param_as_http_param

控制分页参数是否写入params

  • 当设置为true时,pageing写入params
  • 当设置为false时,当页面字段已存在于bodyparams中时替换其值。

当设置为false时,配置示例:

body="""{"id":1,"page":"${page}"}"""
params={ page: "${page}" }

对应 HttpSourceReader.updateRequestParam:keepPageParamAsHttpParam = true时直接向 params 写入分页字段并立即返回;否则按“headers → params → body”的顺序,仅更新已存在的同名键或占位符,不会凭空新增分页字段。

params

默认情况下,参数将添加到 URL 路径(查询串)中。如果需要保持旧版本行为,请参考keep_params_as_form

body

HTTP body 用于在请求或响应中携带实际数据,包括 JSON、表单提交。参考格式:

body="""{"id":1,"name":"seatunnel"}"""

对于表单提交,请按如下设置 content-type:

headers { Content-Type = "application/x-www-form-urlencoded" }

源码中有一个值得注意的实现细节:对于 POST 请求,若未开启keepParamsAsForm且未显式将Content-Type设为 form 类型,body 会以原始字符串(StringEntityContent-Type: application/json)直接发送,从而保留嵌套 JSON 结构(如data.type不会被展平);仅当显式声明 form Content-Type 或开启keepParamsAsForm时,body 才会走 HOCON 解析并转为表单参数。

分页与最终请求形态排查

下面几条规则最容易混淆,建议先按“最终发出的 HTTP 请求长什么样”来理解:

  1. GET请求:params一定会被拼到 URL 查询串里。
  2. POSTkeep_params_as_form = false
    • params仍然会拼到 URL 查询串里;
    • 在默认的非 form 分支上,body会作为 JSON body 发送;
    • 如果没有配置body,并且请求仍然走这个默认非 form 分支,运行时会发送一个空 JSON 对象{}作为请求体;
    • 如果显式把Content-Type设为application/x-www-form-urlencoded,运行时会改走 form-body 分支,而不是默认 JSON 分支。
  3. POSTkeep_params_as_form = true
    • params会并入表单 body;
    • 如果未显式设置Content-Type,SeaTunnel 会自动补application/x-www-form-urlencoded
    • 如果bodyparams出现同名键,params的值会覆盖body中同名键。
  4. keep_page_param_as_http_param = true:分页字段会直接写入params
  5. keep_page_param_as_http_param = false:SeaTunnel 只会更新 headers、params、body 里已经存在的同名键或占位符,不会凭空新增分页字段。
  6. pageing.use_placeholder_replacement = true:支持${page}${cursor}占位符,也支持"10${page}" -> "105"这种带前后缀的替换;为false时只做按 key 的整值替换(对应源码replacePlaceholderprocessPageMap的两条分支)。

示例 1:GET 分页,页码写入查询参数

source { Http { url = "https://api.example.com/orders" method = "GET" params = { page = "${page}" size = "100" } pageing = { page_field = "page" page_type = "PageNumber" start_page_number = 3 use_placeholder_replacement = true } } }

当页码推进到3时,最终请求为:

GET https://api.example.com/orders?page=3&size=100

示例 2:POST JSON(默认非 form 分支),请求参数进 URL,分页字段留在 body

source { Http { url = "https://api.example.com/orders/search" method = "POST" keep_params_as_form = false params = { tenant = "acme" } body = """{"page":"${page}","pageSize":100}""" pageing = { page_field = "page" page_type = "PageNumber" start_page_number = 3 use_placeholder_replacement = true } } }

当页码推进到3时,最终请求为:

POST https://api.example.com/orders/search?tenant=acme Content-Type: application/json Body: {"page":"3","pageSize":100}

示例 3:POST 表单,请求参数和分页字段都写入表单 body

source { Http { url = "https://api.example.com/orders/search" method = "POST" keep_params_as_form = true keep_page_param_as_http_param = true params = { size = "100" } pageing = { page_field = "page" page_type = "PageNumber" start_page_number = 3 } } }

当页码推进到3时,最终请求为:

POST https://api.example.com/orders/search Content-Type: application/x-www-form-urlencoded Body: size=100&page=3

content_field:直接抽取 JSON 片段

content_field可以获取部分 JSON 数据。如果只需要book部分的数据,配置content_field = "$.store.book.*"。假设返回数据如下:

{ "store": { "book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 } ], "bicycle": { "color": "red", "price": 19.95 } }, "expensive": 10 }

配置content_field = "$.store.book.*"后返回结果:

[ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 } ]

然后可以使用更简单的 schema 获取所需结果:

Http { url = "http://mockserver:1080/contentjson/mock" method = "GET" format = "json" content_field = "$.store.book.*" schema = { fields { category = string author = string title = string price = string } } }

参考实现:

  • 测试数据:mockserver-config.json
  • 任务配置:http_contentjson_to_assert.conf

实现原理上,HttpSourceReader.collect 通过JsonPath.compile(contentJson)编译 JSONPath 表达式并截取响应片段,再交给下游 deserialization 处理。

json_field:按 JSONPath 映射字段

json_field用于辅助配置 schema,因此必须与schema一起使用。对于上面的同一份数据,可以通过如下配置获取book的内容:

source { Http { url = "http://mockserver:1080/jsonpath/mock" method = "GET" format = "json" json_field = { category = "$.store.book[*].category" author = "$.store.book[*].author" title = "$.store.book[*].title" price = "$.store.book[*].price" } schema = { fields { category = string author = string title = string price = string } } } }

参考实现:

  • 测试数据:mockserver-config.json
  • 任务配置:http_jsonpath_to_assert.conf

json_fieldcontent_field的区别在于:content_field把整个数组片段交给 schema 逐字段解析;而json_field为每个字段单独指定 JSONPath,连接器在 JsonPathProcessorImpl.java / ArrayJsonPathProcessor.java 中按路径取值并做行列转置(dataFlip)后映射成行。当 JSON 字段缺失时,json_filed_missed_return_null = true会返回 null 而非报错。

pageing 分页详解:PageNumber 与 Cursor

当前支持的分页类型是PageNumberCursor(定义见 HttpPaginationType.java)。默认分页类型为PageNumber。分页信息载体为 PageInfo.java,包含totalPageSizebatchSizepageFieldpageIndexpageTypecursorpageCursorFieldNamepageCursorResponseFieldusePlaceholderReplacement等字段。

1. PageNumber

使用PageNumber分页时,页面参数可放在 HTTP 请求的不同部分:

  • 在 URL 参数中:将页面参数添加到params部分;
  • 在请求体中:在bodyJSON 中包含页面参数;
  • 在头信息中:将页面参数添加到headers部分。

可使用${page}占位符配合use_placeholder_replacement = true动态更新这些值,占位符支持多种格式:

  • 作为独立值:"${page}"
  • 带前缀/后缀:"10${page}""page-${page}"
  • 作为不带引号的数字:${page}(在 JSON 体中)
  • 在嵌套 JSON 结构中:{"pagination":{"page":${page}}}
示例 1:在 body 和 params 中使用页面参数
source { Http { url = "http://localhost:8080/mock/queryData" method = "POST" format = "json" body="""{"id":1,"page":"${page}"}""" content_field = "$.data.*" params={ page: "${page}" } pageing={ #你可以不设置此参数,默认值是 PageNumber page_type="PageNumber" total_page_size=20 page_field=page use_placeholder_replacement=true #当不知道 total_page_size 时使用 batch_size,如果读取大小<batch_size 则完成,否则继续 #batch_size=10 } schema = { fields { name = string age = string } } } }
示例 2:在 headers 中使用页面参数
source { Http { url = "http://localhost:8080/mock/queryData" method = "GET" format = "json" headers={ Page-Number = "${pageNo}" Authorization = "Bearer token-123" } pageing={ page_field = pageNo start_page_number = 1 batch_size = 10 use_placeholder_replacement = true } schema = { fields { name = string age = string } } } }
示例 3:使用基于键的替换(不使用占位符)
source { Http { url = "http://localhost:8080/mock/queryData" method = "GET" format = "json" params={ page = "1" } pageing={ page_field = page start_page_number = 1 batch_size = 10 use_placeholder_replacement = false } schema = { fields { name = string age = string } } } }
示例 4:在 headers 中使用带前缀的页码
source { Http { url = "http://localhost:8080/mock/queryData" method = "GET" format = "json" headers = { Page-Number = "10${page}" # 当 page=5 时将变为 "105" Authorization = "Bearer token-123" } pageing = { page_field = page start_page_number = 5 batch_size = 10 use_placeholder_replacement = true } schema = { fields { name = string age = string } } } }
示例 5:在 body 中使用不带引号的页码
source { Http { url = "http://localhost:8080/mock/queryData" method = "POST" format = "json" body = """{"a":${page},"limit":10}""" # 不带引号的数字 pageing = { page_field = page start_page_number = 1 batch_size = 10 use_placeholder_replacement = true } schema = { fields { name = string age = string } } } }
示例 6:使用带页面参数的嵌套 JSON 结构
source { Http { url = "http://localhost:8080/mock/queryData" method = "POST" format = "json" body = """{"pagination":{"page":${page},"size":10},"filters":{"active":true}}""" # 嵌套结构 pageing = { page_field = page start_page_number = 1 total_page_size = 20 use_placeholder_replacement = true } schema = { fields { name = string age = string } } } }

2. Cursor(游标分页)

pageing.page_type必须设置为Cursorcursor_field是请求参数中游标的字段名称;cursor_response_field是响应数据中分页令牌字段的名称(支持 JSONPath),连接器会将其值写入后续请求的分页字段中。

source { Http { plugin_output = "http" url = "http://localhost:8080/mock/cursor_data" method = "GET" format = "json" content_field = "$.data.*" keep_page_param_as_http_param = true pageing ={ page_type="Cursor" cursor_field ="cursor" cursor_response_field="$.paging.cursors.next" } schema = { fields { content=string id=int name=string } } json_field = { content = "$.data[*].content" id = "$.data[*].id" name = "$.data[*].name" } } }

游标分页的停止条件在 HttpSourceReader.collect 中实现:从响应 JSON 中读取cursor_response_field对应的首个值作为新游标;当新游标为空或与当前游标相同(无法推进)时停止翻页。实际可运行的分页用例可参考 e2e 测试资源,例如 http_page_increase_page_num.conf、http_page_increase_no_page_num.conf、http_page_cursor_num_assert.conf。

源码级原理:连接器内部如何工作

参数模型

所有源选项以Option常量形式集中在 HttpSourceOptions.java(源专属)与 HttpCommonOptions.java(url/headers/params/retry 系列,与 Http Sink 共用)中,再由 HttpSourceFactory.java 组装成OptionRule完成参数校验(url必填、format=jsonschema条件必填)。

读取主循环

HttpSourceReader.internalPollNext 是核心循环:

  • 未配置pageing时,单次请求后结束(批模式)或按poll_interval_millis睡眠后继续(流模式);
  • PageNumber 分页时,每轮updateRequestParam更新请求参数,pollAndCollectData拉取数据后页码自增 1;
  • 结束条件两条路径:配置了total_page_size > 0时,pageIndex >= totalPageSize即停止;未配置时,若当页读取行数< batch_size则停止(否则继续翻页);
  • 批模式下noMoreElementFlag为 true 时调用context.signalNoMoreElement()结束作业。

请求执行与重试

HttpClientProvider 基于 Apache HttpClient 实现:GET通过URIBuilder拼接查询参数;POST依据keepParamsAsForm与 Content-Type 在 JSON body 与表单 body 之间切换;重试使用guava-retryingRetryerBuilder,仅对IOException生效,退避策略为斐波那契退避,下限retry_backoff_multiplier_ms(默认 100ms)、上限retry_backoff_max_ms(默认 10s),停止策略为retry指定的最大尝试次数。

参考资料

  • 连接器变更日志:connector-http.md
  • e2e 测试用例目录:connector-http-e2e/src/test/resources,内含http_json_to_assert.confhttp_binary_to_assert.confhttp_multilinejson_to_assert.confhttp_formrequestbody_to_assert.confhttp_streaming_json_to_postgresql.conf等覆盖各类格式与分页场景的作业示例
  • 源码位置:connector-http-base

【免费下载链接】seatunnelSeaTunnel is a multimodal, high-performance, distributed, massive data integration tool.项目地址: https://gitcode.com/GitHub_Trending/se/seatunnel

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询