说说我用netty的经验和教训

pelp1451 6年前
   <p><img src="https://simg.open-open.com/show/2d68ae65a82ea2cac819389a5255a9bf.png"></p>    <h2>简单梳理图中的红线过程</h2>    <ol>     <li>客户端连接</li>     <li>boss channel接受连接,创建或者找到client channel,并且将处理过程delegate到child handlers</li>     <li>所谓delegate到child handlers,意思是child Eventloop会用这个channel的handlers来进行业务逻辑处理</li>     <li>事件是netty用来通知的方式,如果client的通道上有事件发生,比如通道上有数据了,那么netty会触发channelRead方法;如果handler在处理过程抛了异常,那么netty会触发exceptionCaught()方法执行</li>     <li>调用write方法往通道上发送数据</li>    </ol>    <h2>ServerBootStrap</h2>    <ul>     <li>理清ServerBootStrap要干的几件事情      <ul>       <li>2个线程池,很重。</li>       <li>channel类型指定,NioServerChannel</li>       <li>childHandler的指定</li>       <li>一些参数的指定</li>       <li>bind</li>      </ul> </li>     <li>为啥要干这些事情      <ul>       <li>线程池是用来执行方法的</li>       <li>Handler用来执行业务逻辑</li>      </ul> </li>     <li>注意点      <ul>       <li>一般我们把整个Server的逻辑拆成2块,1)start;2)stop;</li>       <li>start最终目的是要执行bind,并且等待bind成功,所以一般我们会在ChannelFuture上加上sync()方法进行同步等待。</li>       <li>stop最终要释放所有的资源,主要是2个线程池,close所有的child channels和boss channel</li>       <li>可以用 Runtime.getRuntime().addShutdownHook(new ShutdownThread(this)); 结束server,注意ShutdownHook接收的是15信号,所以优雅关闭的命令是 kill -15 pid</li>      </ul> </li>    </ul>    <h2>EventLoop</h2>    <ul>     <li>Eventloop就是一个运行时的载体,它会执行每个channel上的handlers</li>     <li>Eventloop扩展自 ScheduledExecutorService ,对于在Chile通道上执行一些定时任务很合适</li>     <li>inEventLoop()方法可以用来判断是否当前线程可以马上执行,如果true,那就直接do();如果false,那就 executors().schedule(new Runnable(){do();}, delayTime)</li>     <li>在做代理的时候,new BootStrap().group()应该要复用当前channel.eventloop(),这样效率是最高的</li>    </ul>    <h2>Channel & ChannelContext</h2>    <ul>     <li>每个连接用一个Channel来抽象,有10w个连接那么就有10w个Channel实体,并且每个Channel有一个对应的context</li>     <li>上面这句话的意思是,Channel和ChannelContext都是线程安全的,因为每个通道一份,你不会在不同的通道上还要共享啥东西吧。。。</li>     <li>channel.write()会导致从最后一个outboundhandler开始往前write;ctx.write()会从当前这个outboundhandler开始往前write,所以如果你在inboundhandler里面write,那么效果和channel.write()是一样的</li>    </ul>    <h2>Handler & Pipeline</h2>    <ul>     <li>我一开始一直理解错了,以为内存中只有一份Handlers和Pipeline;其实不是,每个Channel都有一份自己的Handlers和Pipeline。这样就可以理解为啥在netty中不需要去处理共享资源了,因为每次Eventloop都是去拿到一个channel,及其handlers,pipeline。然后你在业务逻辑中也不用去考虑锁之类的东西,因为每个通道都是独立的。</li>     <li>每个inboundhandler都有如下方法需要实现      <ul>       <li>void channelRegistered(ChannelHandlerContext ctx) throws Exception;</li>       <li>void channelUnregistered(ChannelHandlerContext ctx) throws Exception;</li>       <li>void channelActive(ChannelHandlerContext ctx) throws Exception;</li>       <li>void channelInactive(ChannelHandlerContext ctx) throws Exception;</li>       <li>void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;</li>       <li>void channelReadComplete(ChannelHandlerContext ctx) throws Exception;</li>       <li>void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;</li>       <li>void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;</li>       <li>void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;</li>      </ul> </li>     <li>每个outboundHandler有如下方法需要实现      <ul>       <li>void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;</li>       <li>`void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception;</li>       <li>void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;</li>       <li>void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;</li>       <li>void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;</li>       <li>void read(ChannelHandlerContext ctx) throws Exception;</li>       <li>void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;</li>       <li>void flush(ChannelHandlerContext ctx) throws Exception;</li>      </ul> </li>     <li>我为啥滔滔不绝写上面那么多?肯定不是蛋疼,对不对。我是要告诉你,你在用别人的handler的时候,千万记住这些方法肯定都实现了,你没看到的话,去超类里面找!</li>     <li>你没看到 channelRead() 方法?快去父类里面找,不然你怎么知道整个的逻辑是怎么处理的?decode()其实是包含在channelread()方法中的</li>     <li>你只需要关注某些已经实现的Codec类的部分方法?握草,不知道整个的逻辑是怎么处理的你怎么会用?</li>     <li>并且!!!!channel上产生事件以后,每个handler中的对应方法都会走到的,为什么?因为默认的adaptor里面用的处理方法是ctx.fireXXX()这样就把时间传递到下一个handler去处理了,并且肯定会调用下一个handler中的channelXXX()</li>    </ul>    <p> </p>    <p>来自:https://blog.huachao.me/2016/7/说说我用netty的经验和教训/</p>    <p> </p>