netty初步认识

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的子类。
     */
     
}

 

posted on 2018-05-04 10:34  wonder2636  阅读(146)  评论(0编辑  收藏  举报

导航