package com.hhr.demo; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; 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.HttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpVersion; import io.netty.util.CharsetUtil; //@SpringBootApplication public class Demo1Application { public static void main(String[] args) throws Exception { // SpringApplication.run(Demo1Application.class, args); /** * 定义好EventLoopGroup,定义好Bootstrap(ServerBootstrap)以及使用的channel类型(一般就是NioSocketChannel, * 服务端是NioServerSocketChannel),剩下是业务相关,使用的ChannelInitializer和具体的handler。 * 主要就是2+N(N为自定义的handler数量)个类. * 为了更好的理解和进一步深入Netty,我们先总体认识一下Netty用到的组件及它们在整个Netty架构中是怎么协调工作的。Netty应用中必不可少的组件: Bootstrap or ServerBootstrap EventLoop EventLoopGroup ChannelPipeline Channel Future or ChannelFuture ChannelInitializer ChannelHandler Bootstrap,一个Netty应用通常由一个Bootstrap开始,它主要作用是配置整个Netty程序,串联起各个组件。 Handler,为了支持各种协议和处理数据的方式,便诞生了Handler组件。Handler主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。 ChannelInboundHandler,一个最常用的Handler。这个Handler的作用就是处理接收到数据时的事件,也就是说,我们的业务逻辑一般就是写在这个Handler里面的,ChannelInboundHandler就是用来处理我们的核心业务逻辑。 ChannelInitializer,当一个链接建立时,我们需要知道怎么来接收或者发送数据,当然,我们有各种各样的Handler实现来处理它,那么ChannelInitializer便是用来配置这些Handler,它会提供一个ChannelPipeline,并把Handler加入到ChannelPipeline。 ChannelPipeline,一个Netty应用基于ChannelPipeline机制,这种机制需要依赖于EventLoop和EventLoopGroup,因为它们三个都和事件或者事件处理相关。 EventLoops的目的是为Channel处理IO操作,一个EventLoop可以为多个Channel服务。 EventLoopGroup会包含多个EventLoop。 Channel代表了一个Socket链接,或者其它和IO操作相关的组件,它和EventLoop一起用来参与IO处理。 Future,在Netty中所有的IO操作都是异步的,因此,你不能立刻得知消息是否被正确处理,但是我们可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过Future和ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发。总之,所有的操作都会返回一个ChannelFuture。 */ EventLoopGroup bossGroup = new NioEventLoopGroup(2); EventLoopGroup workerGroup = new NioEventLoopGroup(4);//第一个EventLoopGroup用来专门负责绑定到端口监听连接事件,而把第二个EventLoopGroup用来处理每个接收到的连接 /** * 1、 如果不指定线程数,则线程数为:CPU的核数*2 2、根据线程个数是否为2的幂次方,采用不同策略初始化chooser 3、产生nThreads个NioEventLoop对象保存在children数组中。 可以理解NioEventLoop就是一个线程,线程NioEventLoop中里面有如下几个属性: 1、NioEventLoopGroup (在父类SingleThreadEventExecutor中) 2、selector 3、provider 4、thread (在父类SingleThreadEventExecutor中) 更通俗点就是: NioEventLoopGroup就是一个线程池,NioEventLoop就是一个线程。NioEventLoopGroup线程池中有N个NioEventLoop线程。 */ ServerBootstrap serverBootstrap = new ServerBootstrap(); try { /** * 1、group:workerGroup保存在 ServerBootstrap对象的childGroup属性上。 bossGroup保存在ServerBootstrap对象的group属性上 2、channelFactory:BootstrapChannelFactory类的对象(clazz属性为:NioServerSocketChannel.class) 3、handler:SimpleServerHandler 4、childHandler */ serverBootstrap.group(bossGroup, workerGroup) /** * 这行代码的作用为通过反射产生来一个NioServerSocketChannel类的实例,其中这个NioServerSocketChannel类对象有这样几个属性: * SocketChannel、NioServerSocketChannelConfig 、SelectionKey.OP_ACCEPT事件、NioMessageUnsafe、DefaultChannelPipeline */ .channel(NioServerSocketChannel.class)//当一个连接到达,Netty会注册一个channel,然后EventLoopGroup会分配一个EventLoop绑定到这个channel,在这个channel的整个生命周期过程中,都会由绑定的这个EventLoop来为它服务,而这个EventLoop就是一个线程 .childHandler(new MyChannelInitializer()); ChannelFuture future = serverBootstrap.bind(8999).sync(); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } class MyChannelInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { // pipeline上添加来一个ChannelInitializer对象,其中重写来initChannel方法。该方法通过p.addLast()向serverChannel的流水线处理器中加入了一个 ServerBootstrapAcceptor, // 从名字上就可以看出来,这是一个接入器,专门接受新请求,把新的请求扔给某个事件循环器 ChannelPipeline pipeline = ch.pipeline(); // pipeline.addLast(new HttpRequestDecoder()); // pipeline.addLast(new HttpResponseEncoder()); /** * 由于NioEventLoopGroup中维护着多个NioEventLoop,next方法回调用chooser策略找到下一个NioEventLoop,并执行该对象的register方法进行注册。 */ pipeline.addLast("httpServerCodec", new HttpServerCodec()); System.out.println("---------------------"); pipeline.addLast(new MyHttpHandler()); } } /** * netty自带了对用的codec类比较方便。 pipeline.addLast("httpServerCodec", new HttpServerCodec()); 自己实现的handler最简单的方式用SimpleChannelInboundHandler接收HttpRequest方法即可 class MyHttpHandler extends SimpleChannelInboundHandler<HttpRequest> 这里简单说下SimpleChannelInboundHandler这个类,他是简化处理接受信息并处理的一个类,主要做两件事。 第一件事是根据泛型决定是否处理这个消息,能够处理就自己处理,不行就交给下一个(可以参考acceptInboundMessage和channelRead方法)。 第二件事是消息的自动回收(有构造函数支持 默认是true),消息的引用计数会减一(所以在其他地方还要使用记得再retain一下)。 不过只用这个handler并不能拿到content,还需要配合ChunkedWriteHandler和HttpObjectAggregator得到FullHttpRequest对象 * */ class MyHttpHandler extends SimpleChannelInboundHandler<HttpRequest> { @Override protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception { System.out.println("*********************************************************"); System.out.println(msg.getClass()); System.out.println(msg.uri()); System.out.println(msg.method().name()); System.out.println(ctx.channel().remoteAddress()); System.out.println("headers:"); msg.headers().forEach(System.out::println); ByteBuf buf = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes()); ctx.writeAndFlush(response); // ctx.channel().close(); } /** * 处理WebSocket请求 # 只需要在上面的基础上增加一个WebSocketServerProtocolHandler即可,完整如下: pipeline.addLast("httpServerCodec", new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpObjectAggregator(8096)); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); 自己的处理器可以接收并处理WebSocketFrame的子类。 */ }