【netty框架面试】java中Netty框架中的@Skip使用详解

更新时间:2019-10-22    来源:php应用    手机版     字体:

【www.bbyears.com--php应用】

最近在学习Netty框架,对着教程上写了个简单的netty应用,可是死活调试不成功,对着程序跟教程上看了几遍也找不到原因,后来又重新写了一遍,服务端程序终于调试成功,原因出在了那个@Skip注释上了,代码如下:

package com.chris.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler.Skip;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPromise;
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.example.discard.DiscardServerHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.ReferenceCountUtil;

import java.net.SocketAddress;
import java.sql.Date;


/**
 * @author Chris
 * @date 2015-4-12
 */
public class NettyTimerServer {
 
 
 public void bind(int port) throws Exception{
  
  EventLoopGroup bossGroup = new NioEventLoopGroup();
  EventLoopGroup workGroup = new NioEventLoopGroup();
  
  try {
   ServerBootstrap b = new ServerBootstrap();
   b.group(bossGroup, workGroup)
   .channel(NioServerSocketChannel.class)
   .option(ChannelOption.SO_BACKLOG, 1024)
   .handler(new LoggingHandler(LogLevel.INFO))
   .childHandler(new ChildChannelHandler());
   System.out.println("server bind 8888");
   ChannelFuture f = b.bind(port).sync();
   System.out.println("finish bind");
   f.channel().closeFuture().sync();
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }finally{
   bossGroup.shutdownGracefully();
   workGroup.shutdownGracefully();
  }
  
  
 }
 
 private class ChildChannelHandler extends ChannelInitializer{

  /* (non-Javadoc)
   * @see io.netty.channel.ChannelInitializer#initChannel(io.netty.channel.Channel)
   */
  @Override
  protected void initChannel(SocketChannel arg0) throws Exception {
   System.out.println("server initChannel");
   arg0.pipeline().addLast(new TimeServerHandler());
   //arg0.pipeline().addl
   
  }
  
 }

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  
  try {
   new NettyTimerServer().bind(8888);
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }

}

class TimeServerHandler extends ChannelHandlerAdapter {

 @Override
 @Skip
 public void channelActive(ChannelHandlerContext ctx) throws Exception {
  super.channelActive(ctx);
 }

 @Override
 @Skip
 public void channelRead(ChannelHandlerContext ctx, Object msg)
   throws Exception {
  ByteBuf buf = (ByteBuf)msg;
  
  byte[] bytes = new byte[buf.readableBytes()];
  
  buf.readBytes(bytes);
  
  String body = new String(bytes,"UTF-8");
  System.out.println("the server receive order:"+body);
  
  String currentTIme = "QUERY CURRENT TIME".equalsIgnoreCase(body)?(new Date(System.currentTimeMillis())).toString():"receive error order";
  
  ByteBuf resp = Unpooled.copiedBuffer(currentTIme.getBytes());
  
  ctx.write(resp);
 }

 @Override
 @Skip
 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
  ctx.flush();
 }

 @Override
 @Skip
 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
   SocketAddress localAddress, ChannelPromise promise)
   throws Exception {
  // TODO Auto-generated method stub
  super.connect(ctx, remoteAddress, localAddress, promise);
 }
 
 
 
 
 
 
}
 

这个实现类的每个方法上都有一个@Skip注释,去掉注释之后,程序调试成功,使用netty开发的服务端程序可以正常接收和处理客户端连接。

被这个注释坑了一天了,于是特地去看了netty的源码,以下是关于@Skip源码的说明:

/**
     * Indicates that the annotated event handler method in {@link ChannelHandler} will not be invoked by
     * {@link ChannelPipeline}.  This annotation is only useful when your handler method implementation
     * only passes the event through to the next handler, like the following:
     *
     *


     * {@code @Skip}
     * {@code @Override}
     * public void channelActive({@link ChannelHandlerContext} ctx) {
     *     ctx.fireChannelActive(); // do nothing but passing through to the next handler
     * }
     *

     *
     * {@link #handlerAdded(ChannelHandlerContext)} and {@link #handlerRemoved(ChannelHandlerContext)} are not able to
     * pass the event through to the next handler, so they must do nothing when annotated.
     *
     *

     * {@code @Skip}
     * {@code @Override}
     * public void handlerAdded({@link ChannelHandlerContext} ctx) {
     *     // do nothing
     * }
     *

     *
     *


     * Note that this annotation is not {@linkplain Inherited inherited}.  If you override a method annotated with
     * {@link Skip}, it will not be skipped anymore.  Similarly, you can override a method not annotated with
     * {@link Skip} and simply pass the event through to the next handler, which reverses the behavior of the
     * supertype.
     *


     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Skip {
        // no value
    }
大概意思就是说@Skip注释用来在实现了Handler的实现类中的方法上,程序运行过程中如果某个handler实现中的方法被@Skip注释了,则此方法不会被 ChannelPipeline 对象调用,所以,这就是为什么我的服务端程序死活调试不成功的原因。我们可以看看netty内部执行过程中是如何处理@Skip注释的,通过对源码文件全文扫苗,找到了对@Skip注释的处理都集中在了AbstractChannelHandlerContext中,下面贴出处理@Skip相关的方法源码:

/**
     * Returns an integer bitset that tells which handler methods were annotated with {@link Skip}.
     * It gets the value from {@link #skipFlagsCache} if an handler of the same type were queried before.
     * Otherwise, it delegates to {@link #skipFlags0(Class)} to get it.
     */
    static int skipFlags(ChannelHandler handler) {
        WeakHashMap, Integer> cache = skipFlagsCache.get();
        Class handlerType = handler.getClass();
        int flagsVal;
        Integer flags = cache.get(handlerType);
        if (flags != null) {
            flagsVal = flags;
        } else {
            flagsVal = skipFlags0(handlerType);
            cache.put(handlerType, Integer.valueOf(flagsVal));
        }

        return flagsVal;
    }
 

 /**
     * Determines the {@link #skipFlags} of the specified {@code handlerType} using the reflection API.
     */
    static int skipFlags0(Class handlerType) {
        int flags = 0;
        try {
            if (isSkippable(handlerType, "handlerAdded")) {
                flags |= MASK_HANDLER_ADDED;
            }
            if (isSkippable(handlerType, "handlerRemoved")) {
                flags |= MASK_HANDLER_REMOVED;
            }
            if (isSkippable(handlerType, "exceptionCaught", Throwable.class)) {
                flags |= MASK_EXCEPTION_CAUGHT;
            }
            if (isSkippable(handlerType, "channelRegistered")) {
                flags |= MASK_CHANNEL_REGISTERED;
            }
            if (isSkippable(handlerType, "channelUnregistered")) {
                flags |= MASK_CHANNEL_UNREGISTERED;
            }
            if (isSkippable(handlerType, "channelActive")) {
                flags |= MASK_CHANNEL_ACTIVE;
            }
            if (isSkippable(handlerType, "channelInactive")) {
                flags |= MASK_CHANNEL_INACTIVE;
            }
            if (isSkippable(handlerType, "channelRead", Object.class)) {
                flags |= MASK_CHANNEL_READ;
            }
            if (isSkippable(handlerType, "channelReadComplete")) {
                flags |= MASK_CHANNEL_READ_COMPLETE;
            }
            if (isSkippable(handlerType, "channelWritabilityChanged")) {
                flags |= MASK_CHANNEL_WRITABILITY_CHANGED;
            }
            if (isSkippable(handlerType, "userEventTriggered", Object.class)) {
                flags |= MASK_USER_EVENT_TRIGGERED;
            }
            if (isSkippable(handlerType, "bind", SocketAddress.class, ChannelPromise.class)) {
                flags |= MASK_BIND;
            }
            if (isSkippable(handlerType, "connect", SocketAddress.class, SocketAddress.class, ChannelPromise.class)) {
                flags |= MASK_CONNECT;
            }
            if (isSkippable(handlerType, "disconnect", ChannelPromise.class)) {
                flags |= MASK_DISCONNECT;
            }
            if (isSkippable(handlerType, "close", ChannelPromise.class)) {
                flags |= MASK_CLOSE;
            }
            if (isSkippable(handlerType, "deregister", ChannelPromise.class)) {
                flags |= MASK_DEREGISTER;
            }
            if (isSkippable(handlerType, "read")) {
                flags |= MASK_READ;
            }
            if (isSkippable(handlerType, "write", Object.class, ChannelPromise.class)) {
                flags |= MASK_WRITE;
            }
            if (isSkippable(handlerType, "flush")) {
                flags |= MASK_FLUSH;
            }
        } catch (Exception e) {
            // Should never reach here.
            PlatformDependent.throwException(e);
        }

        return flags;
    }
 

 @SuppressWarnings("rawtypes")
    private static boolean isSkippable(
            Class handlerType, String methodName, Class... paramTypes) throws Exception {

        Class[] newParamTypes = new Class[paramTypes.length + 1];
        newParamTypes[0] = ChannelHandlerContext.class;
        System.arraycopy(paramTypes, 0, newParamTypes, 1, paramTypes.length);

        return handlerType.getMethod(methodName, newParamTypes).isAnnotationPresent(Skip.class);
    }
 

相信不少netty初学者都会碰到此类问题吧,希望这篇文章能对大家有点帮助。

本文来源:http://www.bbyears.com/jiaocheng/74733.html

热门标签

更多>>

本类排行