netty http2设计深入讲解

本文详细介绍了Netty实现HTTP/2的设计,包括HTTP/2的优势、协商机制,以及Netty中的HTTP/2核心模块、协商模块、帧处理和协议转换。特别强调了协商过程中的ALPN和Upgrade机制,以及Netty的Http2ConnectionHandler在HTTP/2协议处理中的关键作用。
摘要由CSDN通过智能技术生成

前言

近期接到一个任务,把netty http2引入到项目里面。听说过http2,还真没有自己玩过。对看过这篇博客的大家说句: 抱歉。本来想很好的说说http2的。写着写着,发现要写的东西太多了,有一些内容根本就不好写。但是netty http2.0的主要内容,本章博客已经全面的讲述了,需要读者有使用经历,阅读点源码。

了解下http2.0

时代在发展,使用http协议的人越来越多。http1.1的弊端慢慢都被显现出来。

  1. 浏览器方式一些网站频繁发送请求,造成一家独大其他网站无法使用。或者所有网站都频发发送请求造成用户体验差等等问题。限制每个url同时并发数量
  2. 提高请求的响应速度。只有一个连接,只有一次tcp三次握手或者tls的7次握手。一个http1.1请求所用的时间,http2.0可以处理三到四个请求。
  3. 提高服务端与客服端的性能(尤其是大型互联网公司流量很大,如果使用http2.0,可以减少一半的http服务器)

协商

原因 一

http客服端不知道http服务端是否支持http2.0。反过来 http服务端也不知道http客服端是否支持http2.0。为什么出现这种现象,让所有的http服务端与http客服端直接从http1.1过度到http2.0是不可能的事情。甚至在大点的公司内部直接从http1.1直接过度到http2.0也是一件不现实的事情,那么出现一件麻烦的事情有http1客服端,也有http2客服端。有http2服务端,也有http1服务端。这种两个维度,四种情况的共存现象。

原因二

有人会问,只支持http1.1不好吗? 已经支持http2,.0的client肯定不会放弃http2.0优秀的性能与特性,能使用使用http2.0,就要使用。

解决

那么http2.0的设计者为了解决这种麻烦的东西。推出了解决方案:协商。

https 1.1 与https.20的协商

https1.1与https2.0的协商是基于ALPN机制。ALPNS是基于TLS实现。在建立TLS链接的时候,客服端会 在TLS协议里面加入自己支持的协议,服务端在客服端支持的协议里面选中一个自己最合适的。然后把选中的协议通知给客服端。如果客户端没有发送支持的http协议,服务端会默认使用http1.1

http1.1与http2.0的协商

http没有TLS协议,无法基于TLS传递协议。协议制定者使用了Upgrade机制。客户端发送一个空请求,请求里面包含该Upgrade,Connection,HTTP2-Settings请求头。服务端从Upgrade取出支持的协议然后响应请求,在响应的请求头里面包含Upgrade,Connection。这样协商就成功了。下面是http1.1与http2.0的协商流程

请求头示例:

GET / HTTP/1.1
Host: example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>

如果服务端不支持 HTTP/2,它会忽略 Upgrade 字段,直接返回 HTTP/1.1 响应,例如:

HTTP/1.1 200 OK
Content-Length: 243
Content-Type: text/html

如果服务端支持 HTTP/2,那就可以回应 101 状态码及对应头部:

HTTP/1.1 100 Switching Protocols
Connection: Upgrade
Upgrade: h2c
小结
  1. https 1.1 与https.2.0的协商 与 http1.1与http2.0的协商 是两套设计方案。https 1.1 与https.2.0 TLS帮你做了。http1.1与http2.0的协商需要自己做。
  2. 现在的趋势,客服端与服务端都需要同时支持http1.1,http2.0,https1.1,https2.0。真是一件很麻烦的事情

netty http2

netty 的http2模块设计的非常好,实在是很绕,就一个http2的包。实在有点乱。按照功能划分的话http2应该有四个模块。

  1. http2核心模块
  2. http1.1与http2协商模块
  3. http2帧处理模块
  4. http2协议转http1协议模块
http2核心模块

作为核心的模块主要是负责http2协议的解码与编码,帧的解析与处理,http2请求头子模块,streamId的管理,http2链接管理

Http2ConnectionHandler

Http2ConnectionHandler 是 netty核心设计hadler的实现,也是http2模块的出发点。

负责http2模块的组合
 protected Http2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
                                     Http2Settings initialSettings) {
        this.initialSettings = checkNotNull(initialSettings, "initialSettings");
        this.decoder = checkNotNull(decoder, "decoder");
        this.encoder = checkNotNull(encoder, "encoder");
        if (encoder.connection() != decoder.connection()) {
            throw new IllegalArgumentException("Encoder and Decoder do not share the same connection object");
        }
    }
负责http2协议下handler生命周期处理
 @Override
 public void flush(ChannelHandlerContext ctx) {
	 try {
		 // Trigger pending writes in the remote flow controller.
		 encoder.flowController().writePendingBytes();
		 ctx.flush();
	 } catch (Http2Exception e) {
		 onError(ctx, true, e);
	 } catch (Throwable cause) {
	 	onError(ctx, true, connectionError(INTERNAL_ERROR, cause, "Error flushing"));
	 }
 }

public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        // Initialize the encoder, decoder, flow controllers, and internal state.
        encoder.lifecycleManager(this);
        decoder.lifecycleManager(this);
        encoder.flowController().channelHandlerContext(ctx);
        decoder.flowController().channelHandlerContext(ctx);
        byteDecoder = new PrefaceDecoder(ctx);
    }

    @Override
    protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
        if (byteDecoder != null) {
            byteDecoder.handlerRemoved(ctx);
            byteDecoder = null;
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        if (byteDecoder == null) {
            byteDecoder = new PrefaceDecoder(ctx);//当链接创建的时候创建PrefaceDecoder对象
        }
        byteDecoder.channelActive(ctx);
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // Call super class first, as this may result in decode being called.
        super.channelInactive(ctx);
        if (byteDecoder != null) {
            byteDecoder.channelInactive(ctx);
            byteDecoder = null;
        }
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        // Writability is expected to change while we are writing. We cannot allow this event to trigger reentering
        // the allocation and write loop. Reentering the event loop will lead to over or illegal allocation.
        try {
            if (ctx.channel().isWritable()) {
                flush(ctx);
            }
            encoder.flowController().channelWritabilityChanged();
        } finally {
            super.channelWritabilityChanged(ctx);
        }
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        byteDecoder.decode(ctx, in, out);
    }
负责http2 Lifecycle Manager(http2生命周期的管理)
public interface Http2LifecycleManager {

    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);

 

closeStreamLocal

关闭本地stream。local stream是指客服端发送headers帧与data帧到服务端,客服端会创建一个local stream。同样服务端发送headers帧与data帧给客服端,服务端也会创建一个 local stream

closeStreamRemote

关闭远程stream。 remote stream是值当客服端接受服务端发的headers帧与data帧 ,客服端会创建一个remote stream。同样服务端接受到客服端发送的headers帧与data帧,服务端也会创建一个 remote stream

closeStream

当接受到 resetStream 帧的时候就用调用改方法。发送方发送一个错误的流,想后悔的时候,就发送resetStream帧这个后悔药

resetStream

对resetStream帧进行处理

负责校验协议行为
public void onHttpClientUpgrade() throws Http2Exception {
        if (connection().isServer()) {
            throw connectionError(PROTOCOL_ERROR, "Client-side HTTP upgrade requested for a server");
        }
        if (!prefaceSent()) {
            // If the preface was not sent yet it most likely means the handler was not added to the pipeline before
            // calling this method.
            throw connectionError(INTERNAL_ERROR, "HTTP upgrade must occur after preface was sent");
        }
        if (decoder.prefaceReceived()) {
    
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值