netty系列之:手持framecodec神器,创建多路复用http2客户端

简介: netty系列之:手持framecodec神器,创建多路复用http2客户端

目录



简介


在之前的文章中,我们实现了支持http2的netty服务器,并且使用支持http2的浏览器成功的进行访问。虽然浏览器非常通用,但是有时候我们也需要使用特定的netty客户端去和服务器进行通信。


今天我们来探讨一下netty客户端对http2的支持。


配置SslContext


虽然http2并不强制要求支持TLS,但是现代浏览器都是需要在TLS的环境中开启http2,所以对于客户端来说,同样需要配置好支持http2的SslContext。客户端和服务器端配置SslContext的内容没有太大的区别,唯一的区别就是需要调用

SslContextBuilder.forClient()而不是forServer()方法来获取SslContextBuilder,创建SslContext的代码如下:


SslProvider provider =
                    SslProvider.isAlpnSupported(SslProvider.OPENSSL)? SslProvider.OPENSSL : SslProvider.JDK;
            sslCtx = SslContextBuilder.forClient()
                  .sslProvider(provider)
                  .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                  // 因为我们的证书是自生成的,所以需要信任放行
                  .trustManager(InsecureTrustManagerFactory.INSTANCE)
                  .applicationProtocolConfig(new ApplicationProtocolConfig(
                          Protocol.ALPN,
                          SelectorFailureBehavior.NO_ADVERTISE,
                          SelectedListenerFailureBehavior.ACCEPT,
                          ApplicationProtocolNames.HTTP_2,
                          ApplicationProtocolNames.HTTP_1_1))
                  .build();


如果使用SSL,那么ssl handler必须是pipline中的第一个handler,所以将SslContext加入到pipline中的代码如下:


ch.pipeline().addFirst(sslCtx.newHandler(ch.alloc()));


客户端的handler


使用Http2FrameCodec


netty的channel默认只能接收ByteBuf消息,对于http2来说,底层传输的是一个个的frame,直接操作底层的frame对于普通程序员来说并不是特别友好,所以netty提供了一个Http2FrameCodec来对底层的http2 frame进行封装成Http2Frame对象,方便程序的处理。


在服务器端我们使用Http2FrameCodecBuilder.forServer()来创建Http2FrameCodec,在客户端我们使用Http2FrameCodecBuilder.forClient()来创建Http2FrameCodec:


Http2FrameCodec http2FrameCodec = Http2FrameCodecBuilder.forClient()
            .initialSettings(Http2Settings.defaultSettings())
            .build();


然后将其加入到pipline中即可使用:


ch.pipeline().addLast(http2FrameCodec);


Http2MultiplexHandler和Http2MultiplexCodec


我们知道对于http2来说一个TCP连接中可以创建多个stream,每个stream又是由多个frame来组成的。考虑到多路复用的情况,netty可以为每一个stream创建一个单独的channel,对于新创建的每个channel来说,都可以使用netty的ChannelInboundHandler来对channel的消息进行处理,从而提升netty处理http2的效率。


而这个对stream创建新channel的支持,在netty中有两个专门的类,他们是Http2MultiplexHandler和Http2MultiplexCodec。


他们的功能是一样的,Http2MultiplexHandler继承自Http2ChannelDuplexHandler,它必须和 Http2FrameCodec一起使用。而Http2MultiplexCodec本身就是继承自Http2FrameCodec,已经结合了Http2FrameCodec的功能。


public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler
@Deprecated
public class Http2MultiplexCodec extends Http2FrameCodec


但是通过检查源代码,我们发现Http2MultiplexCodec是不推荐使用的API,所以这里我们主要介绍Http2MultiplexHandler。


对于Http2MultiplexHandler来说,每次新创建一个stream,都会创建一个新的对应的channel,应用程序使用这个新创建的channel来发送和接收Http2StreamFrame。


新创建的子channel会被注册到netty的EventLoop中,所以对于一个有效的子channel来说,并不是立刻就会被匹配到HTTP/2 stream上去,而是当第一个Http2HeadersFrame成功被发送或者接收之后,才会触发Event事件,进而进行绑定操作。


因为是子channel,所以对于connection level的事件,比如Http2SettingsFrame 和 Http2GoAwayFrame会首先被父channel进行处理,然后再广播到子channel中进行处理。


同时,虽然Http2GoAwayFrame 和 Http2ResetFrame表示远程节点已经不再接收新的frame了,但是因为channel本身还可能有queue的消息,所以需要等待Channel.read()为空之后,才会进行关闭操作。


另外对于子channel来说,因为不能知道connection-level流控制window,所以如果有溢出的消息会被缓存在父channel的buff中。


有了Http2MultiplexHandler,将其加入client的pipline就可以让客户端支持多路的channel了:


ch.pipeline().addLast(new Http2MultiplexHandler(new SimpleChannelInboundHandler() {
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
                // 处理inbound streams
                log.info("Http2MultiplexHandler接收到消息: {}",msg);
            }
        }))


使用子channel发送消息


从上面的介绍我们知道,一旦使用了Http2MultiplexHandler,那么具体的消息处理就是在子channel中了。那么怎么才能从父channel中获取子channel,然后使用子channel来发送信息呢?


netty提供Http2StreamChannelBootstrap类,它提供了open方法,来创建子channel:


final Http2StreamChannel streamChannel;
        try {
            if (ctx.handler() instanceof Http2MultiplexCodec) {
                streamChannel = ((Http2MultiplexCodec) ctx.handler()).newOutboundStream();
            } else {
                streamChannel = ((Http2MultiplexHandler) ctx.handler()).newOutboundStream();
            }


我们要做的就是调用这个方法,来创建子channel:


final Http2StreamChannel streamChannel = streamChannelBootstrap.open().syncUninterruptibly().getNow();


然后将自定义的,专门处理Http2StreamFrame的Http2ClientStreamFrameHandler,添加到子channel的pipline中即可:


final Http2ClientStreamFrameHandler streamFrameResponseHandler =
                    new Http2ClientStreamFrameHandler();
streamChannel.pipeline().addLast(streamFrameResponseHandler);


准备完毕,构建http2消息,使用streamChannel进行发送:


// 发送HTTP2 get请求
            final DefaultHttp2Headers headers = new DefaultHttp2Headers();
            headers.method("GET");
            headers.path(PATH);
            headers.scheme(SSL? "https" : "http");
            Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers, true);
            streamChannel.writeAndFlush(headersFrame);


总结



以上就是使用netty的framecode构建http2的客户端和服务器端进行通信的基本操作了。

相关文章
|
3月前
|
JSON 中间件 Go
Go 网络编程:HTTP服务与客户端开发
Go 语言的 `net/http` 包功能强大,可快速构建高并发 HTTP 服务。本文从创建简单 HTTP 服务入手,逐步讲解请求与响应对象、URL 参数处理、自定义路由、JSON 接口、静态文件服务、中间件编写及 HTTPS 配置等内容。通过示例代码展示如何使用 `http.HandleFunc`、`http.ServeMux`、`http.Client` 等工具实现常见功能,帮助开发者掌握构建高效 Web 应用的核心技能。
215 61
|
1月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
4月前
|
算法 Java 容器
Netty源码—4.客户端接入流程
本文主要介绍了关于Netty客户端连接接入问题整理、Reactor线程模型和服务端启动流程、Netty新连接接入的整体处理逻辑、新连接接入之检测新连接、新连接接入之创建NioSocketChannel、新连接接入之绑定NioEventLoop线程、新连接接入之注册Selector和注册读事件、注册Reactor线程总结、新连接接入总结
|
6月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
570 29
|
11月前
|
网络协议 前端开发
netty的TCP服务端和客户端实现
本文介绍了使用Netty框架实现TCP服务端和客户端的步骤,包括添加Netty依赖、编写服务端和客户端的代码,涉及NioEventLoopGroup、ServerBootstrap、Bootstrap、ChannelInitializer等核心组件,以及如何启动服务端监听和客户端连接。
736 4
|
11月前
使用Netty实现文件传输的HTTP服务器和客户端
本文通过详细的代码示例,展示了如何使用Netty框架实现一个文件传输的HTTP服务器和客户端,包括服务端的文件处理和客户端的文件请求与接收。
239 1
使用Netty实现文件传输的HTTP服务器和客户端
|
开发者 Python
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
231 1
|
Go 开发者
golang的http客户端封装
golang的http客户端封装
337 0
|
安全 NoSQL Java
网络安全-----Redis12的Java客户端----客户端对比12,Jedis介绍,使用简单安全性不足,lettuce(官方默认)是基于Netty,支持同步,异步和响应式,并且线程是安全的,支持R
网络安全-----Redis12的Java客户端----客户端对比12,Jedis介绍,使用简单安全性不足,lettuce(官方默认)是基于Netty,支持同步,异步和响应式,并且线程是安全的,支持R
|
数据采集 Java API
Java HTTP客户端工具的演变之路
Java HTTP客户端工具的演变之路