netty之Http服务器

1. netty可以做的事情

  • netty可以作为RPC的通信框架
  • netty作为长连接服务器(websockt)
  • netty也可以作为http服务器(不是基于servlet规范)

2. netty的Hello World(Http服务器)

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;

public class TestServer {

    public static void main(String[] args) {
        // bossGroup 不断的从客户端接受链接,但不会做任何处理,直接转给 workerGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // workerGroup 从 bossGroup 接收到链接,然后获取参数等进行处理
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new TestServerInitializer());
        try {
            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast("httpServerCodec",new HttpServerCodec());
        pipeline.addLast("testHttpServerHandler",new TestHttpServerHandler());
    }
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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;

public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    /**
     * 读取客户端发起的请求,并向客户端返回响应的方法
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
        if (msg instanceof HttpRequest) {
            ByteBuf content = Unpooled.copiedBuffer("Hello Word", CharsetUtil.UTF_8);
            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);
        }
    }
}

3. netty使用的基本流程

  • 启动一个Bootstrap服务器,Bootstrap里面会关联两个事件循环组(bossGroup、workerGroup)
  • bossGroup 用于获取链接
  • workerGroup 用于处理链接
  • 服务器启动时关联一个处理器(ChannelHandler)
  • 在ChannelHandler中添加若干个自定义或者netty提供的处理器
  • 在自定义的处理器(ChannelHandler)中返回响应
  • 整个请求处理完毕
netty处理请求进入的类一般是xxxInbound(ChannelInboundHandler)
netty处理响应输出的类一般是xxxOutbound(ChannelOutboundHandler)

4. TestHttpServerHandler分析

我们复写一下父类中的一些方法,这些方法在事件发生时会被回调

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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;

public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    /**
     * 读取客户端发起的请求,并向客户端返回响应的方法
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
        if (msg instanceof HttpRequest) {
            ByteBuf content = Unpooled.copiedBuffer("Hello Word", CharsetUtil.UTF_8);
            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);
        }
    }

    // 3. 管道活跃
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
        super.channelActive(ctx);
    }

    // 2. 管道注册
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
        super.channelRegistered(ctx);
    }

    // 1. 新的管道添加
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded");
        super.handlerAdded(ctx);
    }

    // 4. 管道不活跃
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive");
        super.channelInactive(ctx);
    }

    // 5. 管道取消注册
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered");
        super.channelUnregistered(ctx);
    }
}

我们使用curl访问,发现控制台打印信息如下:

curl "http://localhost:8899"
handlerAdded
channelRegistered
channelActive
channelInactive
channelUnregistered
  • handlerAdded在父级接口(ChannelHandler)中的doc如下:
/**
     * Gets called after the {@link ChannelHandler} was added to the actual context and it's ready to handle events.
     */
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;

意思是handlerAdded将在ChannelHandler被添加到真实的上下文后被调用,这时表示上下文已经准备好处理事件了。

  • channelRegistered在父级接口(ChannelInboundHandler)中的doc如下:
The {@link Channel} of the {@link ChannelHandlerContext} was registered with its {@link EventLoop}

意思是ChannelHandlerContext(上下文)的Channel(管道)注册了它的EventLoop(事件循环)

  • channelActive在父级接口(ChannelInboundHandler)中的doc如下:
The {@link Channel} of the {@link ChannelHandlerContext} is now active

意思是ChannelHandlerContext(上下文)的Channel(通道)现在处于活跃状态

  • channelInactive在父级接口(ChannelInboundHandler)中的doc如下:
The {@link Channel} of the {@link ChannelHandlerContext} was registered is now inactive and reached its end of lifetime.

意思是ChannelHandlerContext(上下文)注册的Channel(通道)当前处于不活跃状态,并且到达了它生命周期的末尾

  • channelUnregistered在父级接口(ChannelInboundHandler)中的doc如下:
The {@link Channel} of the {@link ChannelHandlerContext} was unregistered from its {@link EventLoop}

意思是ChannelHandlerContext(上下文)的Channel(通道)从它的EventLoop(事件循环)中取消了注册

我们使用curl请求这五个方法都会被调用,但是当我们使用浏览器去请求时channelInactive和channelUnregistered可能不会被调用,但当我们关闭浏览器时这两个方法就会被调用。这是应该目前的浏览器时基于HTTP1.1协议,请求完毕时并不会马上关闭连接,当浏览器关闭时连接断开,服务端就会调用以上两个方法来完成通道的关闭。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值