如何提供流式(chunked)接口
流式(chunked)概念
-
Chunked流式接口是一种用于在网络传输中分块发送数据的方法。在传统的请求/响应模式中,数据通常以完整的实体(例如,完整的HTML页面或文件)的形式发送。而在Chunked流式接口中,数据被分成较小的块(chunks),每个块都带有自己的长度信息,并逐个发送。
-
使用Chunked流式接口,发送方可以将数据划分为较小的块,然后逐个发送,而不需要等待整个实体的完整性。这样可以提供更高的传输效率和更快的响应时间,尤其在处理大文件或大量数据时。接收方则可以逐块接收数据,并立即处理,而不需要等待完整的数据传输完成。
-
在HTTP协议中,Chunked流式传输通常用于在不知道整个响应内容长度的情况下传输数据,或者在需要逐步处理响应内容的情况下。通过使用Chunked编码,服务器可以动态生成和发送数据块,而无需事先将整个响应内容存储在内存中。
-
使用Chunked流式接口时,HTTP头部中的Transfer-Encoding字段会设置为"chunked",并且每个数据块都会在发送前带有一个表示其长度的十六进制数值。数据块之间使用特定的分隔符进行分隔。
-
总之,Chunked流式接口是一种将数据分块发送的方法,可以提高传输效率和响应速度,特别适用于处理大文件或大量数据的情况。
流式(chunked)实现思路:
流式(chunked)传输是通过在HTTP协议中使用Chunked编码来实现的。下面是流式传输的基本实现过程:
-
服务器生成数据块:服务器根据需要生成数据块,并确定每个数据块的大小。
-
数据块编码:每个数据块都以十六进制表示其大小,并在数据块内容之前添加该大小值。例如,如果一个数据块的大小为10字节,那么编码后的大小值为"A"(十六进制10),然后跟着10个字节的数据。
-
发送数据块:编码后的数据块被发送到客户端。
-
接收数据块:客户端接收到数据块后,解码数据块的大小,并根据大小值读取相应数量的字节数据。
-
处理数据块:客户端可以立即处理接收到的数据块,而不需要等待完整的响应内容。
-
重复步骤3-5:服务器继续生成和发送后续的数据块,直到所有数据块都被发送完毕。
-
结束传输:当所有数据块都发送完毕后,服务器会发送一个空的数据块,用于表示传输结束。这个空的数据块以单个的"0"(十六进制0)表示。
通过这种方式,数据可以以逐块的方式进行传输,而不需要等待完整的响应内容。客户端可以根据需要逐块接收和处理数据,从而实现流式传输的效果。
示例代码演示(服务端选择其一即可)
springboot(服务器端)中如何搭建
import org.springframework.http.ResponseEntity;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/chunked")
@Slf4j
public class TestController {@GetMapping("/chunked-test")public ResponseEntity<StreamingResponseBody> getData() {// 创建一个发送句柄StreamingResponseBody responseBody = outputStream -> {// 模拟真实场景下连续发送数据for (int i = 0; i < 10; i++) {// 延迟等待:模拟真实场景try {Thread.sleep(1000);}catch (Exception e){}// 数据块编码String data = "{\"name\": \"name-"+i+"\"}";// 写入数据outputStream.write(data.getBytes());// 发送数据outputStream.flush();}};// 响应到客户端return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).body(responseBody);}
}
注意: 不需要引入额外的包,正常的springboot项目即可(本文使用的egg版本为:2.6.1)
egg(服务器端)中如何搭建
- EGG代码: controller中的一个方法
import { Controller } from 'egg';// 创建一个sleep方法,进行延迟等待
const sleep = function (ms) {return new Promise((resolve) => setTimeout(resolve, ms));
};export default class TestController extends Controller {/*** chunked发送数据*/public async chunkedSendData() {const { ctx } = this;// 设置HTTP头部信息ctx.set('Transfer-Encoding', 'chunked');ctx.set('Content-Type', 'application/octet-stream');try {// 一定要设置响应状态ctx.status = 200;// 使用egg中的响应作为发送句柄const rawResponse = ctx.res;// 模拟真实场景下连续发送数据for (const item of [1, 2, 3, 4, 5, 6, 7, 8]) {// 延迟等待:模拟真实场景await sleep(1000);// 发送数据块rawResponse.write(JSON.stringify({ name: 'name-' + item }));}// 结束请求rawResponse.end();} catch {ctx.status = 500;}}
}
- 路由文件中写法(正常书写就行): router.js
import { Application } from 'egg';
export default (app: Application) => {const { controller, router } = app;// chunked发送数据测试:router.get('/chunked/chunked-test', controller.test.chunkedSendData);
};
注意: 不需要任何的插件,正常的Egg项目即可(本文使用的egg版本为:2.6.1)
使用nginx作为代理
- 将
/api/chunked/chunked-test请求代理到http://127.0.0.1:7001/chunked/chunked-test
server {...location /api/chunked/ {proxy_set_header Host $http_host;proxy_set_header X-Forwarded-Proto $scheme;// 设置不需要缓存,也就是实时输出proxy_buffering off;// 设置http版本,支持实时输出proxy_http_version 1.1;// chunked打开(下面两句话作用不大)chunked_transfer_encoding on;proxy_set_header Connection "";proxy_pass http://127.0.0.1:7001/chunked/;}
}
注1:Nginx自版本1.3.9起开始支持Chunked传输。此版本以及之后的版本都包含对Chunked传输的支持,无需安装模块即可支持
使用场景
- 实时输出场景(GPT)
- 数据实时要求比较高(商场首页)
注意事项
- 微信小程序作为chunked客户端,请查阅:《微信小程序之流式(chunked)响应》
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
