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还是十分值得我们去学习的。
相关文章
- 浅谈RPC框架
- Mina框架IoSession详解
- 开源中文分词框架分词效果对比smartcn与IKanalyzer
- HEVC算法和体系结构:编码框架
- 第三百零六节,Django框架,models.py模块,数据库操作——创建表、数据类型、索引、admin后台,补充Django目录说明以及全局配置文件配置
- PHP做Web开发的MVC框架(Smarty使用说明 )
- HTML框架
- Guice框架-DI(依赖注入之作用域)
- 分享一个简易的ORM框架源代码以及基于该框架开发的一个简易论坛源代码
- Go语言web框架beego:目录说明
- Atitit spring注解事务的demo与代码说明 目录 1.1. Spring框架中,要如何实现事务?有一个注解,@EnableTransactionManagement1 1.2. 事务管理
- 【编程实践】为了带你搞懂RPC,我们手写了一个RPC框架
- 深入浅出Flask(24): H-ui前端框架的按钮组件
- httprunner接口自动化测试框架使用说明【保姆级教程】
- Caffe 深度学习框架介绍
- Go实战--通过gin-gonic框架搭建restful api服务(github.com/gin-gonic/gin)
- Quartz定时任务框架使用教程详解