使用 Unix 管道将数据流式传输到 REST 服务

Ram*_*gil 4 pipe wget curl

基于对另一个问题的回答,我使用 curl 将stdout一个进程的流作为POST请求的实体:

myDataGeneratingApp \
| curl -H "Content-Type: application/json" -H "Transfer-Encoding: chunked" -X POST -d @- http://localhost:12000
Run Code Online (Sandbox Code Playgroud)

不幸的是,curlEOF在开始发送数据之前正在等待来自标准输出。我知道这一点是因为我可以独立运行我的应用程序并且数据会立即输出到控制台,但是当我通过管道进行 curl 时,在服务开始接收数据之前会有一个显着的延迟。

当数据从应用程序的标准中可用时,如何使用 curl 立即流式传输数据?如果在 curl 中不可能,那么是否有另一种解决方案(例如 wget)?

meu*_*euh 7

查看 curl 代码transfer.c似乎该程序能够使用分块协议重新打包请求数据(从 curl 到服务器),其中每个数据块都以 ascii 十六进制的块长度为前缀,并后缀由\r\n.

在连接到服务器后,似乎使它以流式方式使用它的方法是使用-T -. 考虑这个例子:

for i in $(seq 5)
do date
   sleep 1
done | 
dd conv=block cbs=512 |
strace -t -e sendto,read -o /tmp/e \
 curl --trace-ascii - \
 -H "Transfer-Encoding: chunked" \
 -H "Content-Type: application/json" \
 -X POST -T -  http://localhost/...
Run Code Online (Sandbox Code Playgroud)

此脚本将 5 个数据块发送dd到管道,在管道中strace运行curl -T -以读取管道,每个数据块都以日期开头并由,填充为 512 字节。在终端我们可以看到

== Info: Connected to localhost (::1) port 80 (#0)
=> Send header, 169 bytes (0xa9)
0000: POST /... HTTP/1.1
001e: Host: localhost
002f: User-Agent: curl/7.47.1
0048: Accept: */*
0055: Transfer-Encoding: chunked
0071: Content-Type: application/json
0091: Expect: 100-continue
00a7: 
<= Recv header, 23 bytes (0x17)
0000: HTTP/1.1 100 Continue
Run Code Online (Sandbox Code Playgroud)

它显示了连接和发送的标头。特别是curl没有提供Content-length:标题,而是Expect:服务器(apache)回复的标题Continue。紧随其后的是前 512 个字节(十六进制中的 200 个)数据:

=> Send data, 519 bytes (0x207)
0000: 200
0005: Fri Sep 14 15:58:15 CEST 2018                                   
0045:                                                                 
0085:                                                                 
00c5:                                                                 
0105:                                                                 
0145:                                                                 
0185:                                                                 
01c5:                                                                 
=> Send data, 519 bytes (0x207)
Run Code Online (Sandbox Code Playgroud)

查看strace输出文件,我们看到read来自管道的每个时间戳,并sendto写入连接:

16:00:00 read(0, "Fri Sep 14 16:00:00 CEST 2018   "..., 16372) = 512
16:00:00 sendto(3, "200\r\nFri Sep 14 16:00:00 CEST 20"..., 519, ...) = 519
16:00:00 read(0, "Fri Sep 14 16:00:01 CEST 2018   "..., 16372) = 512
16:00:01 sendto(3, "200\r\nFri Sep 14 16:00:01 CEST 20"..., 519, ...) = 519
16:00:01 read(0, "Fri Sep 14 16:00:02 CEST 2018   "..., 16372) = 512
16:00:02 sendto(3, "200\r\nFri Sep 14 16:00:02 CEST 20"..., 519, ...) = 519
16:00:02 read(0, "Fri Sep 14 16:00:03 CEST 2018   "..., 16372) = 512
16:00:03 sendto(3, "200\r\nFri Sep 14 16:00:03 CEST 20"..., 519, ...) = 519
16:00:03 read(0, "Fri Sep 14 16:00:04 CEST 2018   "..., 16372) = 512
16:00:04 sendto(3, "200\r\nFri Sep 14 16:00:04 CEST 20"..., 519, ...) = 519
16:00:04 read(0, "", 16372)             = 0
16:00:05 sendto(3, "0\r\n\r\n", 5, ...) = 5
Run Code Online (Sandbox Code Playgroud)

如您所见,它们间隔 1 秒,表明数据在接收时正在发送。您必须至少有 512 个字节要发送,因为数据正在被fread().