netty入门HttpServer实例

古城微笑少年丶 2022-04-12 03:59 364阅读 0赞

好久没更了,由于目前项目要用到websocket,于是看了下netty。Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。

1、回顾BIO、NIO、AIO

关于这几个,估计在面试中有很大可能会被问到。我首先解释一下,然后举例说明。

BIO:同步阻塞

NIO:同步非阻塞

AIO:异步非阻塞

专业说明:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM2Mzk5Nw_size_16_color_FFFFFF_t_70

通俗举例:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM2Mzk5Nw_size_16_color_FFFFFF_t_70 1

2、netty的三大概念

这里引用原文:https://www.jianshu.com/p/b9f3f6a16911描述得非常清楚了。

  • Channel
    数据传输流,与channel相关的概念有以下四个,上一张图让你了解netty里面的Channel。
  1. ![Image 1][]
  2. Channel一览
  3. * Channel,表示一个连接,可以理解为每一个请求,就是一个Channel
  4. * **ChannelHandler**,核心处理业务就在这里,用于处理业务请求。
  5. * ChannelHandlerContext,用于传输业务数据。
  6. * ChannelPipeline,用于保存处理过程需要用到的ChannelHandlerChannelHandlerContext
  • ByteBuf
    ByteBuf是一个存储字节的容器,最大特点就是使用方便,它既有自己的读索引和写索引,方便你对整段字节缓存进行读写,也支持get/set,方便你对其中每一个字节进行读写,他的数据结构如下图所示:

![Image 1][]

ByteBuf数据结构

他有三种使用模式:

  1. Heap Buffer 堆缓冲区
    堆缓冲区是ByteBuf最常用的模式,他将数据存储在堆空间。
  2. Direct Buffer 直接缓冲区
    直接缓冲区是ByteBuf的另外一种常用模式,他的内存分配都不发生在堆,jdk1.4引入的nio的ByteBuffer类允许jvm通过本地方法调用分配内存,这样做有两个好处

    • 通过免去中间交换的内存拷贝, 提升IO处理速度; 直接缓冲区的内容可以驻留在垃圾回收扫描的堆区以外。
    • DirectBuffer 在 -XX:MaxDirectMemorySize=xxM大小限制下, 使用 Heap 之外的内存, GC对此”无能为力”,也就意味着规避了在高负载下频繁的GC过程对应用线程的中断影响.
  3. Composite Buffer 复合缓冲区
    复合缓冲区相当于多个不同ByteBuf的视图,这是netty提供的,jdk不提供这样的功能。

    • Codec
      Netty中的编码/解码器,通过他你能完成字节与pojo、pojo与pojo的相互转换,从而达到自定义协议的目的。
      在Netty里面最有名的就是HttpRequestDecoder和HttpResponseEncoder了。

3、HttpServer实例

直接上代码了,直接看main方法,步骤都注释了。具体工程请自行用IDE创建。

主类HelloHttpServer.java







  1. package com.soleil.netty;

    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;

    /*
  2. @Description: 实现客户端发送一个请求,服务器会返回 hello netty
    */
    public class HelloServer {
    public static void main(String[] args) throws Exception {
    // 定义一对线程组
    // 主线程组, 用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    // 从线程组, 老板线程组会把任务丢给他,让手下线程组去做任务
    EventLoopGroup workerGroup = new NioEventLoopGroup();

    try {
    // netty服务器的创建, ServerBootstrap 是一个启动类
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    serverBootstrap.group(bossGroup, workerGroup) // 设置主从线程组
    .channel(NioServerSocketChannel.class) // 设置nio的双向通道
    .childHandler(new HelloServerInitializer()); // 子处理器,用于处理workerGroup
    // 启动server,并且设置8088为启动的端口号,同时启动方式为同步
    ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();

    // 监听关闭的channel,设置位同步方式
    channelFuture.channel().closeFuture().sync();
    } finally {
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
    }
    }
    }
  1. 服务器初始类HelloServerInitializer.java






  1. package com.soleil.netty;

    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.handler.codec.http.HttpServerCodec;

    /*
  2. @Description: 初始化器,channel注册后,会执行里面的相应的初始化方法
    */
    public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
    // 通过SocketChannel去获得对应的管道
    ChannelPipeline pipeline = channel.pipeline();

    // 通过管道,添加handler
    // HttpServerCodec是由netty自己提供的助手类,可以理解为拦截器
    // 当请求到服务端,我们需要做解码,响应到客户端做编码
    pipeline.addLast(“HttpServerCodec”, new HttpServerCodec());

    // 添加自定义的助手类,返回 “hello netty~”
    pipeline.addLast(“customHandler”, new CustomHandler());
    }
    }

助手类CustomHandler.java







  1. package com.soleil.netty;

    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    import io.netty.handler.codec.http.FullHttpResponse;
    import io.netty.handler.codec.http.HttpHeaderNames;
    import io.netty.handler.codec.http.HttpObject;
    import io.netty.handler.codec.http.HttpRequest;
    import io.netty.handler.codec.http.HttpResponseStatus;
    import io.netty.handler.codec.http.HttpVersion;
    import io.netty.util.CharsetUtil;

    /*
  2. @Description: 创建自定义助手类
    */
    // SimpleChannelInboundHandler: 对于请求来讲,其实相当于[入站,入境]
    public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg)
    throws Exception {
    // 获取channel
    Channel channel = ctx.channel();

    if (msg instanceof HttpRequest) {
    // 显示客户端的远程地址
    System.out.println(channel.remoteAddress());

    // 定义发送的数据消息
    ByteBuf content = Unpooled.copiedBuffer(“Hello netty~”, CharsetUtil.UTF_8);

    // 构建一个http response
    FullHttpResponse response =
    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
    HttpResponseStatus.OK,
    content);
    // 为响应增加数据类型和长度
    response.headers().set(HttpHeaderNames.CONTENT_TYPE, text/plain”);
    response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());

    // 把响应刷到客户端
    ctx.writeAndFlush(response);
    }

    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“channel。。。注册”);
    super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“channel。。。移除”);
    super.channelUnregistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“channel。。。活跃”);
    super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“channel。。。不活跃”);
    super.channelInactive(ctx);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“channeld读取完毕。。。”);
    super.channelReadComplete(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    System.out.println(“用户事件触发。。。”);
    super.userEventTriggered(ctx, evt);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“channel可写更改”);
    super.channelWritabilityChanged(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    System.out.println(“捕获到异常”);
    super.exceptionCaught(ctx, cause);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“助手类添加”);
    super.handlerAdded(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“助手类移除”);
    super.handlerRemoved(ctx);
    }

    }

运行主类,在浏览器中看到:

20181130204050218.png

在控制台看到:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM2Mzk5Nw_size_16_color_FFFFFF_t_70 2

[Image 1]:

发表评论

表情:
评论列表 (有 0 条评论,364人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Netty入门实例 TCP服务

    基于Netty开发一个入门程序,主要功能:服务端启动后监听一个端口,客户端启动后会给服务端发送一条消息,服务端收到后通过Handler读取消息,进行处理,读取完通过Handle