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协议,请求完毕时并不会马上关闭连接,当浏览器关闭时连接断开,服务端就会调用以上两个方法来完成通道的关闭。