简介

无论是什么协议,如果要真正被使用的话,需要将该协议转换成为对应的语言才好真正的进行应用,本文将从http2消息的结构出发,探讨一下netty对http2消息的封装,带大家领略一下真正的框架应该做到什么程度。

http2消息的结构

http2和http1.1不同的是它使用了新的二进制分帧,通过客户端和服务器端建立数据流steam来进行客户端和服务器端之间消息的交互。其中数据流是一个双向字节流,用来发送一条或者多条消息。

消息是客户端和服务端发送的一个逻辑上完整的数据。根据数据大小的不同,可以将消息划分为不同的帧Frame。也就是说message是由不同的frame组成的。

frame就是http2中进行通信的最小单位,根据上一节的介绍,我们知道frame有这样几种:

  • DATA frame
  • HEADERS frame
  • PRIORITY frame
  • RST_STREAM frame
  • SETTINGS acknowledgment frame
  • SETTINGS frame
  • PING frame
  • PING acknowledgment
  • PUSH_PROMISE frame
  • GO_AWAY frame
  • WINDOW_UPDATE frame
  • Unknown Frame

我们看一下http2中stream和frame的一个大体的结构:

在http2中,一个TCP连接,可以承载多个数据流stream,多个stream中的不同frame可以交错发送。

每个frame通过stream id来标记其所属的stream。

有了上面的http2的基本概念,我们接下来就看下netty对http2的封装了。

netty对http2的封装

Http2Stream

作为一个TCP连接下面的最大的单位stream,netty中提供了接口Http2Stream。注意,Http2Stream是一个接口,它有两个实现类,分别是DefaultStream和ConnectionStream。

Http2Stream中有两个非常重要的属性,分别是id和state。

id前面已经介绍了,是stream的唯一标记。这里要注意由客户端建立的 Stream ID 必须是奇数,而由服务端建立的 Stream ID 必须是偶数。另外Stream ID 为 0 的流有特殊的作用,它是CONNECTION_STREAM_ID,1 表示HTTP_UPGRADE_STREAM_ID。

state表示stream的状态,具体而言,stream有下面几个状态:

        IDLE(false, false),
RESERVED_LOCAL(false, false),
RESERVED_REMOTE(false, false),
OPEN(true, true),
HALF_CLOSED_LOCAL(false, true),
HALF_CLOSED_REMOTE(true, false),
CLOSED(false, false);

为什么状态需要区分local和remote呢?这是因为stream连接的两端,所以有两端的状态。

和stream状态相对应的就是http2的生命周期了。netty提供了Http2LifecycleManager来表示对http2生命周期的管理:

    void closeStreamLocal(Http2Stream stream, ChannelFuture future);
void closeStreamRemote(Http2Stream stream, ChannelFuture future);
void closeStream(Http2Stream stream, ChannelFuture future);
ChannelFuture resetStream(ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise);
ChannelFuture goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
ByteBuf debugData, ChannelPromise promise);
void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause);

分别是关闭stream,重置stream,拒绝新建stream:goAway,和处理出错状态这几种。

Http2Frame

stream之后,就是真实承载http2消息的Http2Frame了。在netty中,Http2Frame是一个接口,它有很多具体的实现。

Http2Frame的直接子类包括HTTP2GoAwayFrame、HTTPPingFrame、Http2SettingsFrame和HTTP2SettingsAckFrame。

其中goAway表示不接受新的stream,ping用来进行心跳检测。SETTINGS用来修改连接或者 Stream 流的配置。

netty中专门有一个Http2Settings类和其对应。

在这个类中定义了一些特别的setting名字:

SETTINGS 名字 含义
SETTINGS_HEADER_TABLE_SIZE 对端索引表的最大尺寸
SETTINGS_ENABLE_PUSH 是否启用服务器推送功能
SETTINGS_MAX_CONCURRENT_STREAMS 接收端允许的最大并发 Stream 数量
SETTINGS_INITIAL_WINDOW_SIZE 发送端的窗口大小,用于 Stream 级别流控
SETTINGS_MAX_FRAME_SIZE 设置帧的最大大小
SETTINGS_MAX_HEADER_LIST_SIZE 对端头部索引表的最大尺寸

除了上面讲的4个frame之外,其他的frame实现都继承自Http2StreamFrame,具体而言有PriorityFrame,ResetFrame,HeadersFrame,DataFrame,WindowUpdateFrame,PushPromiseFrame和UnknownFrame。

各个frame分别代表了不同的功能。这里最重要的就是Http2HeadersFrame和Http2DataFrame。

Http2HeadersFrame主要是客户端发送给服务器端的http2请求。

具体而言除了标准的http1.1的header之外,http2还支持下面的header:

      METHOD(":method", true),

        SCHEME(":scheme", true),

        AUTHORITY(":authority", true),

        PATH(":path", true),

        STATUS(":status", false),

        PROTOCOL(":protocol", true);

对于Http2DataFrame来说,他本身是一个ByteBufHolder,用来传递具体的数据信息。data frame的Payload直接存储在ByteBuf中。

总结

以上就是netty对http2消息的封装了。

本文的例子可以参考:learn-netty4

本文已收录于 http://www.flydean.com/28-netty-wrap-http2/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

netty系列之:netty对http2消息的封装的更多相关文章

  1. 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?

    [读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...

  2. Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化

    Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...

  3. netty系列之:netty实现http2中的流控制

    目录 简介 http2中的流控制 netty对http2流控制的封装 Http2FlowController Http2LocalFlowController Http2RemoteFlowContr ...

  4. Netty系列之Netty线程模型

    Reference: http://www.infoq.com/cn/articles/netty-threading-model 1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 ...

  5. netty系列之:netty初探

    目录 简介 netty介绍 netty的第一个服务器 netty的第一个客户端 运行服务器和客户端 总结 简介 我们常用浏览器来访问web页面得到相关的信息,通常来说使用的都是HTTP或者HTTPS协 ...

  6. netty系列之:netty架构概述

    目录 简介 netty架构图 丰富的Buffer数据机构 零拷贝 统一的API 事件驱动 其他优秀的特性 总结 简介 Netty为什么这么优秀,它在JDK本身的NIO基础上又做了什么改进呢?它的架构和 ...

  7. Netty 系列之 Netty 高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用 Netty4 + Thrift 压缩二进制编解码技术,他们实现了 10 W TPS(1 K 的复杂 POJO 对象)的跨 ...

  8. Netty系列之Netty高性能之道

    转载自http://www.infoq.com/cn/articles/netty-high-performance 1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Ne ...

  9. 转:Netty系列之Netty高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用 ...

随机推荐

  1. 提交Spark作业遇到的NoSuchMethodError问题总结

    测试应用说明 测试的Spark应用实现了同步hive表到kafka的功能.具体处理流程: 从 ETCD 获取 SQL 语句和 Kafka 配置信息 使用 SparkSQL 读取 Hive 数据表 把 ...

  2. SpringBoot整合定时任务----Scheduled注解实现(一个注解全解决)

    一.使用场景 定时任务在开发中还是比较常见的,比如:定时发送邮件,定时发送信息,定时更新资源,定时更新数据等等... 二.准备工作 在Spring Boot程序中不需要引入其他Maven依赖 (因为s ...

  3. 在node节点部署kubectl管理k8s集群

    感谢!原文链接:https://blog.csdn.net/sinat_35930259/article/details/79994078 kubectl是k8s的客户端程序,也是k8s的命令行工具, ...

  4. Spring Boot 入门系列(二十五)读取配置文件的几种方式详解!

    在项目开发中经常会用到配置文件,之前介绍过Spring Boot 资源文件属性配置的方法,但是很多朋友反馈说介绍的不够详细全面.所以, 今天完整的分享Spring Boot读取配置文件的几种方式! S ...

  5. 第九章 Net 5.0 快速开发框架 YC.Boilerplate --定时服务 Quartz.net

    在线文档:http://doc.yc-l.com/#/README 在线演示地址:http://yc.yc-l.com/#/login 源码github:https://github.com/linb ...

  6. K8S_第一课作业_20200407

    (1)通过cgroup来限制memory ##消耗内存脚本 /tmp/xmem.sh #!/bin/bash mkdir /tmp/memory mount -t tmpfs -o size=2048 ...

  7. Vue 路由跳转报错 Error: Avoided redundant navigation to current location: "/XXX".

    在router文件夹下的index.js中加入红色字体代码即可解决 import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(V ...

  8. vue项目 'node-sass'问题

    Cannot find module 'node-sass' 解决办法: 运行命令:cnpm install node-sass@latest 即可解决,( 网络差的同学可以选择重新下载no-modu ...

  9. SQL语句分组获取记录的第一条数据的方法

    使用Northwind 数据库 首先查询Employees表 查询结果: city列里面只有5个城市 使用ROW_NUMBER() OVER(PARTITION BY COL1 ORDER BY CO ...

  10. Docker安装mysql镜像并进行主从配置

    Docker安装mysql镜像并进行主从配置 1.下载需要的mysql版本镜像 docker pull mysql:5.6 2.启动mysql服务实例(基本启动) #启动主mysql docker r ...