当前位置: 首页 > news >正文

http trailer 与 http2

前言

最近做项目,发现一些项目使用 grpc,尤其是容器 K8S下,笔者在很久以前文章:https://blog.csdn.net/fenglllle/article/details/127829481

有一点非常奇怪的现象,在 h2 的返回的结果后面还有一个 header,这个实际上是 http trailer。

准备示例

在 github 为例:https://github.com/ma6174/blog/issues/22

go 语言

package mainimport ("fmt""io""net/http""os""os/signal""syscall"
)func grpcStatus(w http.ResponseWriter, r *http.Request) {fmt.Printf("header: %+v\n", r.Header)fmt.Printf("trailer before read body: %+v\n", r.Trailer)data, _ := io.ReadAll(r.Body)w.Header().Add("Transfer-Encoding", "chunked")
//	w.Header().Add("Content-Length", "-1")w.Header().Add("Trailer", "grpc-status")fmt.Println("------------", string(data))w.Write([]byte("haha"))w.WriteHeader(200)w.Header().Set("grpc-status", "0")
}func main() {go func() {http.HandleFunc("/demo", grpcStatus)err := http.ListenAndServe(":8080", nil)if err != nil {fmt.Println(err)}}()signalCh := make(chan os.Signal, 1)signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)sig := <-signalChfmt.Printf("Received signal: %v\n", sig)fmt.Println("hello")
}

使用 postman 发送请求 localhost:8080/demo

没什么奇特的地方,非常常规,但是当我们抓包时

奇怪的事情发生了,在 chunked 的结束时,发现了 trailer header 数据

这里是返回数据,同理,如果对请求数据进行处理也会出现类似的效果

go 示例

package mainimport ("fmt""io""net/http""os"_ "strconv""strings"
)type headerReader struct {reader io.Readerheader http.Header
}func (r *headerReader) Read(p []byte) (n int, err error) {n, err = r.reader.Read(p) //先读 bodyif err == io.EOF {//写 trailer headerr.header.Set("grpc-status", "0")}return
}func main() {h := &headerReader{reader: strings.NewReader("body"),header: http.Header{},}req, err := http.NewRequest("POST", "http://localhost:8080", h)if err != nil {panic(err)}req.ContentLength = -1req.Trailer = h.header //设置 trailerresp, err := http.DefaultClient.Do(req)if err != nil {panic(err)}fmt.Println(resp.Status)_, err = io.Copy(os.Stdout, resp.Body)if err != nil {panic(err)}
}

mac 下使用 nc -l 8080

nc 命令

-l                            Listen mode, for inbound connects

监听发现 trailer 的 header 在 body 后被读取出来了

因为 postman 没法在发送 body 后,再写 header,所以只能通过代码的方式

通过上面的实践,发现 http trailer 的执行方式:

1. 使用 http chunked

2. header 设置key 为Trailer的header key,多个 key 逗号,分割

3. 先写,或者先读 body,然后设置 trailer 定义的 header key 和 value

使用 h2

回到最初的问题,如果真是 grpc,那么使用的 h2 的方式 trailer?换成 java 写一个springboot demo

@RestController
public class DemoController {@GetMapping("/demo")public void demo(HttpServletResponse response) throws IOException {response.addHeader("Transfer-Encoding", "chunked");response.addHeader("Content-Length", String.valueOf(-1));response.addHeader("Trailer", "grpc-status");PrintWriter printWriter = response.getWriter();printWriter.print("body");printWriter.flush();response.addHeader("grpc-status", "0");printWriter.close();}
}server.http2.enabled = true

然后强制发送 http2 的请求

huahua@huahuadeMac-mini ~ % curl --http2 http://localhost:8080/demo
curl: (92) Invalid HTTP header field was received: frame type: 1, stream: 1, name: [transfer-encoding], value: [chunked]

根据 http2 的标准:https://datatracker.ietf.org/doc/html/rfc7540#section-8.1

不能使用 chunked 编码,其实 http2 本身就是支持分块传输

但是 trailer 需要 chunked 传输,所以暂时还没有头绪解决,怎么使用 http2 执行 chunked http trailer

直到一篇文章:https://carlmastrangelo.com/blog/why-does-grpc-insist-on-trailers

讲述了 grpc 的设计,到是怎么失控的,在 http2 中分帧,所以不需要 chunked 编码了,可以直接发送 trailer

@RestController
public class DemoController {@GetMapping("/demo")public void demo(HttpServletResponse response) throws IOException {
//        response.addHeader("Transfer-Encoding", "chunked");
//        response.addHeader("Content-Length", String.valueOf(-1));response.addHeader("Trailer", "grpc-status");PrintWriter printWriter = response.getWriter();printWriter.print("body");//        response.addHeader("grpc-status", "0");response.setTrailerFields(()->{Map<String, String> map = new HashMap<>();map.put("grpc-status", "0");return map;});printWriter.flush();printWriter.close();}
}

 发送请求,抓包,成功还原 http2 grpc 的设计理念

总结

经过实践还原了 grpc 的通信内容,通过 springboot 的 h2 达到 grpc 的 trailer 的方式,以后在 springboot 中试试,算是解决上一篇文章的遗留问题。不过居然有 http trailer 这玩意,笔者以前没来没用过,也没听到哪里有使用,算是对这个设计有了深刻的了解了。

http://www.dtcms.com/a/523716.html

相关文章:

  • 有没有会计做兼职的网站wordpress获取文章
  • 中国人在国外做网站网站代理网站群建设 会议 主持
  • 在Ubuntu Linux安装brew 使用brew安装llama.cpp 运行文心Ernie大模型
  • 基于MATLAB/Simulink的风光储联合系统经M3C接入电网的低电压穿越仿真研究
  • CNCF Kepler与MCP:开启云原生绿色计算的人机协作新纪元
  • 昇腾NPU部署GPT-OSS-20B混合专家模型:从环境配置到性能优化的完整实践指南
  • java8中的‘+‘的使用注意事项
  • 德国莱茵金属公司使用Varjo XR-4创建虚拟现实培训解决方案
  • STM32的GPIOx_ODR,GPIOx_BSRR,GPIOx_BRR寄存器的区别与使用
  • 网站建设指南 菜鸟教程简历模板做的最好的是哪个网站
  • Prometheus + Alertmanager + 钉钉告警
  • 基于 Spring Boot + RabbitMQ 实现应用通信
  • docker一键部署prometheus和grafana
  • 《深入剖析TCP Socket API:从连接到断开的全链路解读》
  • 数据库连接池 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下
  • Flink Watermark(水位线)机制详解
  • wordpress wpadmin东莞seo网站建设公司
  • 刷赞网站怎么做WordPress编辑器加载慢
  • 【知识图谱】图神经网络(GNN)核心概念详解:从消息传递到实战应用
  • 系统与网络安全------弹性交换网络(5)
  • 车联网车云架构_信息分享01
  • 纯css实现任务头像叠加
  • B2122 单词翻转
  • Tailwind CSS Next.js实战(官方)Tailwind Demo、Tailwind教程
  • 建设个人博客网站做网站页面设计报价
  • 告别显卡焦虑:Wan2.1+cpolar让AI视频创作走进普通家庭
  • 浙人医创新开新篇——用KingbaseES数据库开创首个多院区异构多活容灾架构
  • openstock部署
  • 平替 MongoDB 实践指南 | 金仓多模数据库助力电子证照系统国产化改造
  • android三方调试几个常用命令