zl程序教程

您现在的位置是:首页 >  前端

当前栏目

Netty框架中的@Skip使用说明

框架 说明 Netty skip 使用
2023-09-11 14:16:13 时间

最近在学习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;


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();
/* (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:

 * pre 

 * {@code @Skip}

 * {@code @Override}

 * public void channelActive({@link ChannelHandlerContext} ctx) {

 * ctx.fireChannelActive(); // do nothing but passing through to the next handler

 * /pre 

 * {@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.

 * pre 

 * {@code @Skip}

 * {@code @Override}

 * public void handlerAdded({@link ChannelHandlerContext} ctx) {

 * // do nothing

 * /pre 

 * p 

 * 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.

 * /p 

 @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 Class ? , Integer cache = skipFlagsCache.get();

 Class ? extends ChannelHandler 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 ? extends ChannelHandler 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初学者都会碰到此类问题吧,希望这篇文章能对大家有点帮助。

转载自 并发编程网 - ifeve.com
netty框架的学习笔记 + 一个netty实现websocket通信案例 一、前言 1.什么是netty? 2.netty的使用场景。 3.学习目录 二.java io通信 三.netty入门 四.websocket入门 五.netty实现websocket通信案例。 1.
真的够可以的,基于Netty实现了RPC框架 RPC全称Remote Procedure Call,即远程过程调用,对于调用者无感知这是一个远程调用功能。目前流行的开源RPC 框架有阿里的Dubbo、Google 的 gRPC、Twitter 的Finagle 等。本次RPC框架的设计主要参考的是阿里的Dubbo,这里Netty 基本上是作为架构的技术底层而存在的,主要完成高性能的网络通信,从而实现高效的远程调用。
网络开发的最强大框架:Netty快速入门 Netty是一个异步的,基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端。Netty的应用十分广泛,可以说主流的框架中,如果有网络方面的需求,一般用的都是netty框架。比如Dubbo、ES、Zookeeper中都用到了Netty。因此即使在平常工作中没有Netty的使用场景,Netty还是十分值得我们去学习的。