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);
}
}
请求方法
当然啦,还有和请求方法相关的
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
相关的request
和response
都全了。
不过只能说是都支持,netty
对于http
的支持是够的,不过都必须手动去设置,没有像tomcat
之类的全。
两次请求
如果在控制台上打印请求路径,不仅有你访问的,还有一个
/favicon.ico
。这个是由于浏览器的尿性所致,毕竟标题栏上有个小图片的确挺骚气的。
并非出错,想要一次的话,可以使用
curl
工具直接进行访问。
处理周期
再论流水线
从数据流向来说,有两个:
request
和response
。也就是进和出。对于
netty
而言,同样有两个:ChannelInboundHandler
和ChannelOutboundHandler
。我们所使用的
SimpleChannelInboundHandler
也只是其中一个分支。
除了流向以外,两个数据流都是一致的,所以他们的大致行为和生命周期是一样的。
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
工具发起单次访问,结果如下
把重复的去掉,顺序如此
这算得上是一个handler
的声明周期了
- 注册
- **
- 数据获取
- 数据处理
- 处理完毕
- 失活
- 注销
不过呢,有三个目前没有看到
userEventTriggered
channelWritabilityChanged
exceptionCaught
第三个不必说了,netty
抓到异常的时候就会执行这个方法了。
第二个自我理解一下呢,应该是缓冲数据变化了,毕竟Buffer
不是以前的死数据,而是缓冲
。
第一个我知道,自定义事件
,心跳上常用的,不过会用不懂底层结构。
逻辑顺序
原先
逐个的向下管理,现在的生命周期就是针对
Handler
的了,具体干什么呢,分时候。如果知道什么时候想干什么,这下基本就大概知道了。
结构上的流水线,就是这幅图。
而
handler
又以时间为流水线划分了声明周期。
小结
userEventTriggered
这个东西见过不少,但老实说来,很多东西似懂非懂。现在也不想什么预习,反倒是把路子走歪了,一步步来,只要够远,总会遇到。
事件传播什么的,也不心花了,慢慢的认知清楚,到时候立刻明晰即可。
基础扎实是目前第一步。
-
netty
实现http
必须全手写 -
handler
自有生命周期