netty-http和handler生命周期

http

之前我们好像用netty去完成了http接口的实现,不过其实存在很多坑。

很多http的特性并没有真正的识别和处理。

请求路径

首先不论如何路径,只要是localhost:8989,后面不论跟上什么的确能够触发逻辑。

必须这样改

protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if(msg instanceof HttpRequest){
        HttpRequest request = (HttpRequest)msg;
        URI uri = URI.create(request.uri());
        String path = uri.getPath();
        System.out.println(path);
        if(!"/message".equals(path)){
            return;
        }
        ByteBuf content = Unpooled.copiedBuffer("hello , world", 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);
    }
}

其实就改了一点点

 HttpRequest request = (HttpRequest)msg;
 URI uri = URI.create(request.uri());
 String path = uri.getPath();
 System.out.println(path);
 if(!"/message".equals(path)){
     return;
 }

每次打印了访问路径,你可以进行对比一下,看看是否符合测试结果。

这就涉及到了一个http始终逃不过的问题----路由

平时我们都是配置页面访问路径,殊不知底层来说,也仅仅是个分支条件选择。

或许这样更能体会一些

protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if(msg instanceof HttpRequest){
        HttpRequest request = (HttpRequest)msg;
        URI uri = URI.create(request.uri());
        String path = uri.getPath();
        ByteBuf content = Unpooled.copiedBuffer(path, 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);
    }
}

netty-http和handler生命周期

请求方法

当然啦,还有和请求方法相关的

protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if(msg instanceof HttpRequest){
        HttpRequest request = (HttpRequest)msg;
        HttpMethod method = request.method();
        URI uri = URI.create(request.uri());
        String path = uri.getPath();
        String contentString = "method\t: " + method.toString() + "\npath\t:" + path;
        ByteBuf contentBuf = Unpooled.copiedBuffer(contentString, CharsetUtil.UTF_8);
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,contentBuf);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH,contentBuf.readableBytes());
        ctx.writeAndFlush(response);
    }
}

这样一来,和http相关的requestresponse都全了。

不过只能说是都支持,netty对于http的支持是够的,不过都必须手动去设置,没有像tomcat之类的全。

两次请求

如果在控制台上打印请求路径,不仅有你访问的,还有一个/favicon.ico

这个是由于浏览器的尿性所致,毕竟标题栏上有个小图片的确挺骚气的。

并非出错,想要一次的话,可以使用curl工具直接进行访问。

处理周期

再论流水线

从数据流向来说,有两个:requestresponse。也就是进和出。

对于netty而言,同样有两个:ChannelInboundHandlerChannelOutboundHandler

我们所使用的SimpleChannelInboundHandler也只是其中一个分支。

netty-http和handler生命周期

除了流向以外,两个数据流都是一致的,所以他们的大致行为和生命周期是一样的。

package com.godme.example.first;

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.*;
import io.netty.util.CharsetUtil;

import java.net.URI;

public class MyHandler  extends SimpleChannelInboundHandler<HttpObject> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        System.out.println("channelRead0");
        if(msg instanceof HttpRequest){
            HttpRequest request = (HttpRequest)msg;
            HttpMethod method = request.method();
            URI uri = URI.create(request.uri());
            String path = uri.getPath();
            String contentString = "method\t: " + method.toString() + "\npath\t:" + path;
            ByteBuf contentBuf = Unpooled.copiedBuffer(contentString, CharsetUtil.UTF_8);
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,contentBuf);
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH,contentBuf.readableBytes());
            ctx.writeAndFlush(response);
            ctx.channel().close();
        }
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
        super.channelRegistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
        super.channelActive(ctx);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelReadComplete");
        super.channelReadComplete(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("userEventTriggered");
        super.userEventTriggered(ctx, evt);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelWritabilityChanged");
        super.channelWritabilityChanged(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("exceptionCaught");
        super.exceptionCaught(ctx, cause);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered");
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive");
        super.channelInactive(ctx);
    }
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("channelRead");
        super.channelRead(ctx, msg);
    }
}

利用curl工具发起单次访问,结果如下
netty-http和handler生命周期

把重复的去掉,顺序如此

channelRegistered
channelActive
channelRead
channelRead0
channelReadComplete
channelInactive
channelUnregistered

这算得上是一个handler的声明周期了

  1. 注册
  2. **
  3. 数据获取
  4. 数据处理
  5. 处理完毕
  6. 失活
  7. 注销

不过呢,有三个目前没有看到

  1. userEventTriggered
  2. channelWritabilityChanged
  3. exceptionCaught

第三个不必说了,netty抓到异常的时候就会执行这个方法了。

第二个自我理解一下呢,应该是缓冲数据变化了,毕竟Buffer不是以前的死数据,而是缓冲

第一个我知道,自定义事件,心跳上常用的,不过会用不懂底层结构。

逻辑顺序

原先

ServerMain
Initializer
Handler

逐个的向下管理,现在的生命周期就是针对Handler的了,具体干什么呢,分时候。

如果知道什么时候想干什么,这下基本就大概知道了。

结构上的流水线,就是这幅图。

handler又以时间为流水线划分了声明周期。

小结

userEventTriggered这个东西见过不少,但老实说来,很多东西似懂非懂。

现在也不想什么预习,反倒是把路子走歪了,一步步来,只要够远,总会遇到。

事件传播什么的,也不心花了,慢慢的认知清楚,到时候立刻明晰即可。

基础扎实是目前第一步。

  • netty实现http必须全手写
  • handler自有生命周期