美文网首页『互联网架构』码农的世界程序猿阵线联盟-汇总各类技术干货
『互联网架构』软件架构-netty之http协议应用实践(58)

『互联网架构』软件架构-netty之http协议应用实践(58)

作者: IT人故事会 | 来源:发表于2019-05-23 08:57 被阅读4次

    原创文章,欢迎转载。转载请注明:转载自IT人故事会,谢谢!
    原文链接地址:『互联网架构』软件架构-netty之http协议应用实践(58)

    做了多年web开发,http真是熟悉的陌生人(经常在用,但是从头到尾理清楚真的是很有难度的),其实http概述和netty中的http应用真有必要说下,一起聊聊netty实现tomcat的功能,请求服务的功能。
    源码:https://github.com/limingios/netFuture/tree/master/源码/『互联网架构』软件架构-io与nio线程模型reactor模型(上)(53)/nio

    (一)Http协议概述

    • 1.什么是Http协议

    HTTP是一个属于【应用层】的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。

    • 2.HTTP协议的主要特点

    支持客户/服务器模式。

    简单快速
    客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与
    服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

    灵活
    HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。

    无连接
    无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

    无状态
    HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。从HTTP协议来讲是无状态的,其实在应用的时候,很多情况通过回话的方式还是有状态的。

    (二)Http协议交互过程

    协议交互本质是指协议两端(客户端、服务端),互联网上没有协议是混乱的,正如如果现实中没有法律也会打乱一样。

    1.传输数据

    传输数据一般基于TCP/IP 实现,体现到开发语言上就是我们所熟悉的Socket 编程。

    2.交换数据

    交换数据本质是指,两端(客户端、服务端)能各自识别对方所发送的数据。那么这就需要制定一套【报文编码】格式,双方以该格式编码数据发送给对方。

    Http 对应的Request 与Response报文

    注: 我们可以通过抓包工具(fiddler)可以直接看到该报文格式。

    报文约定好以后两端都需要对其进行解码和编码操作

    3.Http协议内容组成

    请求方法

    方法 描述
    GET 请求指定的页面信息,并返回实体主体。
    HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
    POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
    PUT 从客户端向服务器传送的数据取代指定的文档的内容。
    DELETE 请求服务器删除指定的页面。
    CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
    OPTIONS 允许客户端查看服务器的性能。
    TRACE 回显服务器收到的请求,主要用于测试或诊断。

    部分请求头

    请求头 说明
    Host 接受请求的服务器地址,可以是IP:端口号,也可以是域名
    User-Agent 发送请求的应用程序名称
    Connection 指定与连接相关的属性,如Connection:Keep-Alive
    Accept-Charset 通知服务端可以发送的编码格式
    Accept-Encoding 通知服务端可以发送的数据压缩格式
    Accept-Language 通知服务端可以发送的语言

    部分响应头

    响应头 说明
    Server 服务器应用程序软件的名称和版本
    Content-Type 响应正文的类型(是图片还是二进制字符串)
    Content-Length 实体报头域用于指明实体正文的长度,以字节方式存储的十进制数字来表示响应正文长度
    Content-Charset 响应正文使用的编码
    Content-Encoding 响应正文使用的数据压缩格式
    Content-Language 响应正文使用的语言

    部分响应状态

    状态码 说明
    200 响应成功
    302 跳转,跳转地址通过响应头中的Location属性指定(JSP中Forward和Redirect之间的区别)
    400 客户端请求有语法错误,不能被服务器识别
    403 服务器接收到请求,但是拒绝提供服务(认证失败)
    404 请求资源不存在
    500 服务器内部错误

    (二)基于Netty 实现Http协议过程分析

    Http协议分为三部分:

    1.远程数据传输
    2.报文编解码
    3.业务处理

    但如果是开发基于Http普通应用,完全没有必要重复造轮子,,我们只需实现业务即可。现比较成熟的中间件有:Tomcat、Jetty、Jboos。这些中间有个缺点是较重,如果需要轻量实现可采用:netty 或JDK自还http 实现JDK Http源码参见:com.sun.net.httpserver.HttpServer

    • netty 实现http

    源码:nio/http中。

    1.初始ServerBootstrap
    2.通过ChannelInitializer 初始 pipeline
    3.基于SimpleChannelInboundHandler HttpServer处理类

    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.ServerSocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.codec.http.*;
    import io.netty.util.concurrent.Future;
    import io.netty.util.concurrent.GenericFutureListener;
    
    import java.net.ContentHandler;
    import java.util.concurrent.ThreadFactory;
    
    /**
     * Created by idig8.com
     */
    public class HttpSimpleServer {
        //open 启动服务
        public void openServer() {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.channel(NioServerSocketChannel.class);
            EventLoopGroup boss = new NioEventLoopGroup(1);
            EventLoopGroup work = new NioEventLoopGroup(8);
            bootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                    ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
                    ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
                    ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
                    ch.pipeline().addLast("http-server", new HttpServerHandler());
                }
            });
            bootstrap.group(boss, work);
            try {
                ChannelFuture future = bootstrap.bind(8080).sync();
                System.out.println("服务启动:8080");
                future.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                boss.shutdownGracefully();
                work.shutdownGracefully();
            }
        }
    
        private static class HttpServerHandler extends SimpleChannelInboundHandler {
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
                FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
                response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
                String html = "<!DOCTYPE html>\n" +
                        "<html lang=\"en\">\n" +
                        "<head>\n" +
                        "    <meta charset=\"UTF-8\">\n" +
                        "    <title>hello idig8.com</title>\n" +
                        "</head>\n" +
                        "<body>\n" +
                        "hello idig8.com\n" +
                        "</body>\n" +
                        "</html>";
                response.content().writeBytes(html.getBytes("UTF-8"));
                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            }
        }
    
        public static void main(String[] args) {
            HttpSimpleServer simpleServer = new HttpSimpleServer();
            simpleServer.openServer();
        }
    
    
    }
    
    

    实现过程分析

    1. 建立连接读取消息流
    2. 解码Request
    3. 业务处理
    4. 编码Response
    5. 返回消息关闭连接

    Channel 与 ChannelPipeline

    1.Channel:
    a. ServerSocketChannel
    b. SocketChannel

    2.pipeline:一个pipeline 当中包含了多个ChandlerHandler,而且是有顺序的

    3.ChandlerHandler
    a. HttpRequestDecode:解码请求
    b. HttpResponseEncode :编码返回结果
    在 Netty 中每个 Channel 都有且仅有一个 ChannelPipeline 与之对应, 它们的组成关系如下:

    一个 Channel 包含了一个 ChannelPipeline, 而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表. 这个链表的头是 HeadContext, 链表的尾是 TailContext,并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler

    bootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel ch) throws Exception {
    ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
    ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
    ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
    ch.pipeline().addLast("http-server", new HttpServerHandler());
    }
    });
    

    HttpRequest 在netty 当中的表示结构

    HttpResponse在netty 当中的结构

    PS:说了下http协议和如何通过netty完成http服务。下次一起说说通过netty完成websocket。

    相关文章

      网友评论

        本文标题:『互联网架构』软件架构-netty之http协议应用实践(58)

        本文链接:https://www.haomeiwen.com/subject/rgvqwqtx.html