几十行代码基于Netty搭建一个 HTTP Server

目前正在写的一个叫做 jsoncat 的轻量级 HTTP 框架内置的 HTTP 服务器是我自己基于 Netty 写的,所有的核心代码加起来不过就几十行。这得益于 Netty 提供的各种开箱即用的组件,为我们节省了太多事情。

这篇文章我会手把手带着小伙伴们实现一个简易的 HTTP Server。

如果文章有任何需要改善和完善的地方,欢迎在评论区指出,共同进步!

开始之前为了避免有小伙伴不了解 Netty ,还是先来简单介绍它!

什么是 Netty?
简单用 3 点来概括一下 Netty 吧!

Netty 是一个基于 NIO 的 client-server(客户端服务器)框架,使用它可以快速简单地开发网络应用程序。
Netty 极大地简化并优化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面都要更好。
Netty 支持多种协议 如 FTP,SMTP,HTTP 以及各种二进制和基于文本的传统协议。本文所要写的 HTTP Server 就得益于 Netty 对 HTTP 协议(超文本传输协议)的支持。
Netty 应用场景有哪些?
凭借自己的了解,简单说一下吧!理论上来说,NIO 可以做的事情 ,使用 Netty 都可以做并且更好。

不过,我们还是首先要明确的是 Netty 主要用来做网络通信 。

实现框架的网络通信模块 : Netty 几乎满足任何场景的网络通信需求,因此,框架的网络通信模块可以基于 Netty 来做。拿 RPC 框架来说! 我们在分布式系统中,不同服务节点之间经常需要相互调用,这个时候就需要 RPC 框架了。不同服务指点的通信是如何做的呢?那就可以使用 Netty 来做了!比如我调用另外一个节点的方法的话,至少是要让对方知道我调用的是哪个类中的哪个方法以及相关参数吧!
实现一个自己的 HTTP 服务器 :通过 Netty ,我们可以很方便地使用少量代码实现一个简单的 HTTP 服务器。Netty 自带了编解码器和消息聚合器,为我们开发节省了很多事!
实现一个即时通讯系统 : 使用 Netty 我们可以实现一个可以聊天类似微信的即时通讯系统,这方面的开源项目还蛮多的,可以自行去 Github 找一找。
实现消息推送系统 :市面上有很多消息推送系统都是基于 Netty 来做的。

那些开源项目用到了 Netty?
我们平常经常接触的 Dubbo、RocketMQ、Elasticsearch、gRPC 、Spring Cloud Gateway 等等都用到了 Netty。

可以说大量的开源项目都用到了 Netty,所以掌握 Netty 有助于你更好的使用这些开源项目并且让你有能力对其进行二次开发。

实际上还有很多很多优秀的项目用到了 Netty,Netty 官方也做了统计,统计结果在这里:https://netty.io/wiki/related-projects.html 。

实现 HTTP Server 必知的前置知识
既然,我们要实现 HTTP Server 那必然先要回顾一下 HTTP 协议相关的基础知识。

HTTP 协议
超文本传输协议(HTTP,HyperText Transfer Protocol)主要是为 Web 浏览器与 Web 服务器之间的通信而设计的。

当我们使用浏览器浏览网页的时候,我们网页就是通过 HTTP 请求进行加载的,整个过程如下图所示。

HTTP请求过程

https://www.seobility.net/en/wiki/HTTP_headers

HTTP 协议是基于 TCP 协议的,因此,发送 HTTP 请求之前首先要建立 TCP 连接也就是要经历 3 次握手。目前使用的 HTTP 协议大部分都是 1.1。在 1.1 的协议里面,默认是开启了 Keep-Alive 的,这样的话建立的连接就可以在多次请求中被复用了。

了解了 HTTP 协议之后,我们再来看一下 HTTP 报文的内容,这部分内容很重要!(参考图片来自:https://iamgopikrishna.wordpress.com/2014/06/13/4/)

HTTP 请求报文:

HTTP 请求报文

HTTP 响应报文:

HTTP 响应报文

我们的 HTTP 服务器会在后台解析 HTTP 请求报文内容,然后根据报文内容进行处理之后返回 HTTP 响应报文给客户端。

Netty 编解码器
如果我们要通过 Netty 处理 HTTP 请求,需要先进行编解码。所谓编解码说白了就是在 Netty 传输数据所用的 ByteBuf 和 Netty 中针对 HTTP 请求和响应所提供的对象比如 HttpRequest 和 HttpContent之间互相转换。

Netty 自带了 4 个常用的编解码器:

HttpRequestEncoder (HTTP 请求编码器):将 HttpRequest 和 HttpContent 编码为 ByteBuf 。
HttpRequestDecoder (HTTP 请求解码器):将 ByteBuf 解码为 HttpRequest 和 HttpContent
HttpResponsetEncoder (HTTP 响应编码器):将 HttpResponse 和 HttpContent 编码为 ByteBuf 。
HttpResponseDecoder(HTTP 响应解码器):将 ByteBuf 解码为 HttpResponst 和 HttpContent
网络通信最终都是通过字节流进行传输的。 ByteBuf 是 Netty 提供的一个字节容器,其内部是一个字节数组。 当我们通过 Netty 传输数据的时候,就是通过 ByteBuf 进行的。

HTTP Server 端用于接收 HTTP Request,然后发送 HTTP Response。因此我们只需要 HttpRequestDecoder 和 HttpResponseEncoder 即可。

我手绘了一张图,这样看着应该更容易理解了。

Netty 对 HTTP 消息的抽象
为了能够表示 HTTP 中的各种消息,Netty 设计了抽象了一套完整的 HTTP 消息结构图,核心继承关系如下图所示。

HttpObject : 整个 HTTP 消息体系结构的最上层接口。HttpObject 接口下又有 HttpMessage 和HttpContent两大核心接口。
HttpMessage: 定义 HTTP 消息,为HttpRequest和HttpResponse提供通用属性
HttpRequest : HttpRequest对应 HTTP request。通过 HttpRequest 我们可以访问查询参数(Query Parameters)和 Cookie。和 Servlet API 不同的是,查询参数是通过QueryStringEncoder和QueryStringDecoder来构造和解析查询查询参数。
HttpResponse : HttpResponse 对应 HTTP response。和HttpMessage相比,HttpResponse 增加了 status(相应状态码) 属性及其对应的方法。
HttpContent : 分块传输编码(Chunked transfer encoding)是超文本传输协议(HTTP)中的一种数据传输机制(HTTP/1.1 才有),允许 HTTP 由应用服务器发送给客户端应用( 通常是网页浏览器)的数据可以分成多“块”(数据量比较大的情况)。我们可以把 HttpContent 看作是这一块一块的数据。
LastHttpContent : 标识 HTTP 请求结束,同时包含 HttpHeaders 对象。
FullHttpRequest 和 FullHttpResponse : HttpMessage 和 HttpContent 聚合后得到的对象。

HTTP 消息聚合器
HttpObjectAggregator 是 Netty 提供的 HTTP 消息聚合器,通过它可以把 HttpMessage 和 HttpContent 聚合成一个 FullHttpRequest 或者 FullHttpResponse(取决于是处理请求还是响应),方便我们使用。

另外,消息体比较大的话,可能还会分成好几个消息体来处理,HttpObjectAggregator 可以将这些消息聚合成一个完整的,方便我们处理。

使用方法:将 HttpObjectAggregator 添加到 ChannelPipeline 中,如果是用于处理 HTTP Request 就将其放在 HttpResponseEncoder 之后,反之,如果用于处理 HTTP Response 就将其放在 HttpResponseDecoder 之后。

因为,HTTP Server 端用于接收 HTTP Request,对应的使用方式如下。

ChannelPipeline p = …;
p.addLast(“decoder”, new HttpRequestDecoder())
.addLast(“encoder”, new HttpResponseEncoder())
.addLast(“aggregator”, new HttpObjectAggregator(512 * 1024))
.addLast(“handler”, new HttpServerHandler());
基于 Netty 实现一个 HTTP Server
通过 Netty,我们可以很方便地使用少量代码构建一个可以正确处理 GET 请求和 POST 请求的轻量级 HTTP Server。

源代码地址:https://github.com/Snailclimb/netty-practical-tutorial/tree/master/example/http-server 。

添加所需依赖到 pom.xml
第一步,我们需要将实现 HTTP Server 所必需的第三方依赖的坐标添加到 pom.xml中。

io.netty netty-all 4.1.42.Final org.slf4j slf4j-api 1.7.25 org.slf4j slf4j-simple 1.7.25 org.projectlombok lombok 1.18.8 provided commons-codec commons-codec 1.14 创建服务端 @Slf4j public class HttpServer {
private static final int PORT = 8080;

public void start() {
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                // TCP默认开启了 Nagle 算法,该算法的作用是尽可能的发送大数据快,减少网络传输。TCP_NODELAY 参数的作用就是控制是否启用 Nagle 算法。
                .childOption(ChannelOption.TCP_NODELAY, true)
                // 是否开启 TCP 底层心跳机制
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁,服务器处理创建新连接较慢,可以适当调大这个参数
                .option(ChannelOption.SO_BACKLOG, 128)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast("decoder", new HttpRequestDecoder())
                                .addLast("encoder", new HttpResponseEncoder())
                                .addLast("aggregator", new HttpObjectAggregator(512 * 1024))
                                .addLast("handler", new HttpServerHandler());
                    }
                });
        Channel ch = b.bind(PORT).sync().channel();
        log.info("Netty Http Server started on port {}.", PORT);
        ch.closeFuture().sync();
    } catch (InterruptedException e) {
        log.error("occur exception when start server:", e);
    } finally {
        log.error(&#
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值