Netty-ChannelHandle的业务处理

ChannelHandle结构

在这里插入图片描述

ChannelHandler基础接口

在这里插入图片描述
  基础接口里面定义的基础通用方法。增加handler,移除handler,异常处理。

ChannelInboundHandler

public interface ChannelInboundHandler extends ChannelHandler {/*** The {@link Channel} of the {@link ChannelHandlerContext} was registered with its {@link EventLoop}*/void channelRegistered(ChannelHandlerContext ctx) throws Exception;/*** The {@link Channel} of the {@link ChannelHandlerContext} was unregistered from its {@link EventLoop}*/void channelUnregistered(ChannelHandlerContext ctx) throws Exception;/*** The {@link Channel} of the {@link ChannelHandlerContext} is now active*/void channelActive(ChannelHandlerContext ctx) throws Exception;/*** The {@link Channel} of the {@link ChannelHandlerContext} was registered is now inactive and reached its* end of lifetime.*/void channelInactive(ChannelHandlerContext ctx) throws Exception;/*** Invoked when the current {@link Channel} has read a message from the peer.*/void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;/*** Invoked when the last message read by the current read operation has been consumed by* {@link #channelRead(ChannelHandlerContext, Object)}.  If {@link ChannelOption#AUTO_READ} is off, no further* attempt to read an inbound data from the current {@link Channel} will be made until* {@link ChannelHandlerContext#read()} is called.*/void channelReadComplete(ChannelHandlerContext ctx) throws Exception;/*** Gets called if an user event was triggered.*/void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;/*** Gets called once the writable state of a {@link Channel} changed. You can check the state with* {@link Channel#isWritable()}.*/void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;/*** Gets called if a {@link Throwable} was thrown.*/@Override@SuppressWarnings("deprecation")void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}

  可以看到,channelInboundHandler集成channelhandler接口,新增方法:channelRegistered,channelUnregistered,channelActive,channelInactive,channelRead,channelReadComplete,userEventTriggered,channelWritabilityChanged。

ChannelHandlerAdapter

public abstract class ChannelHandlerAdapter implements ChannelHandler {// Not using volatile because it's used only for a sanity check.boolean added;/*** Throws {@link IllegalStateException} if {@link ChannelHandlerAdapter#isSharable()} returns {@code true}*/protected void ensureNotSharable() {if (isSharable()) {throw new IllegalStateException("ChannelHandler " + getClass().getName() + " is not allowed to be shared");}}/*** Return {@code true} if the implementation is {@link Sharable} and so can be added* to different {@link ChannelPipeline}s.*/public boolean isSharable() {/*** Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a* {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different* {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of* {@link Thread}s are quite limited anyway.** See <a href="https://github.com/netty/netty/issues/2289">#2289</a>.*/Class<?> clazz = getClass();Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();Boolean sharable = cache.get(clazz);if (sharable == null) {sharable = clazz.isAnnotationPresent(Sharable.class);cache.put(clazz, sharable);}return sharable;}/*** Do nothing by default, sub-classes may override this method.*/@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {// NOOP}/*** Do nothing by default, sub-classes may override this method.*/@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {// NOOP}/*** Calls {@link ChannelHandlerContext#fireExceptionCaught(Throwable)} to forward* to the next {@link ChannelHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.** @deprecated is part of {@link ChannelInboundHandler}*/@Skip@Override@Deprecatedpublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.fireExceptionCaught(cause);}
}

  ChannelHandlerAdapter 是一个抽象类,实现接口ChannelHandler。exceptionCaught方法被实现。

ChannelInboundHandlerAdapter

public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {/*** Calls {@link ChannelHandlerContext#fireChannelRegistered()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelRegistered(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelRegistered();}/*** Calls {@link ChannelHandlerContext#fireChannelUnregistered()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelUnregistered(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelUnregistered();}/*** Calls {@link ChannelHandlerContext#fireChannelActive()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelActive();}/*** Calls {@link ChannelHandlerContext#fireChannelInactive()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelInactive();}/*** Calls {@link ChannelHandlerContext#fireChannelRead(Object)} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ctx.fireChannelRead(msg);}/*** Calls {@link ChannelHandlerContext#fireChannelReadComplete()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelReadComplete();}/*** Calls {@link ChannelHandlerContext#fireUserEventTriggered(Object)} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {ctx.fireUserEventTriggered(evt);}/*** Calls {@link ChannelHandlerContext#fireChannelWritabilityChanged()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelWritabilityChanged();}/*** Calls {@link ChannelHandlerContext#fireExceptionCaught(Throwable)} to forward* to the next {@link ChannelHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Override@SuppressWarnings("deprecation")public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)throws Exception {ctx.fireExceptionCaught(cause);}
}

  ChannelInboundHandlerAdapter 是入站的一个落地实现。继承ChannelHandlerAdapter 实现接口ChannelInboundHandler 。默认反应方法实现调用ChannelHandlerContext中的事件发布。

ChannelHandlerContext

public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {/*** Return the {@link Channel} which is bound to the {@link ChannelHandlerContext}.*/Channel channel();/*** Returns the {@link EventExecutor} which is used to execute an arbitrary task.*/EventExecutor executor();/*** The unique name of the {@link ChannelHandlerContext}.The name was used when then {@link ChannelHandler}* was added to the {@link ChannelPipeline}. This name can also be used to access the registered* {@link ChannelHandler} from the {@link ChannelPipeline}.*/String name();/*** The {@link ChannelHandler} that is bound this {@link ChannelHandlerContext}.*/ChannelHandler handler();/*** Return {@code true} if the {@link ChannelHandler} which belongs to this context was removed* from the {@link ChannelPipeline}. Note that this method is only meant to be called from with in the* {@link EventLoop}.*/boolean isRemoved();@OverrideChannelHandlerContext fireChannelRegistered();@OverrideChannelHandlerContext fireChannelUnregistered();@OverrideChannelHandlerContext fireChannelActive();@OverrideChannelHandlerContext fireChannelInactive();@OverrideChannelHandlerContext fireExceptionCaught(Throwable cause);@OverrideChannelHandlerContext fireUserEventTriggered(Object evt);@OverrideChannelHandlerContext fireChannelRead(Object msg);@OverrideChannelHandlerContext fireChannelReadComplete();@OverrideChannelHandlerContext fireChannelWritabilityChanged();@OverrideChannelHandlerContext read();@OverrideChannelHandlerContext flush();/*** Return the assigned {@link ChannelPipeline}*/ChannelPipeline pipeline();/*** Return the assigned {@link ByteBufAllocator} which will be used to allocate {@link ByteBuf}s.*/ByteBufAllocator alloc();/*** @deprecated Use {@link Channel#attr(AttributeKey)}*/@Deprecated@Override<T> Attribute<T> attr(AttributeKey<T> key);/*** @deprecated Use {@link Channel#hasAttr(AttributeKey)}*/@Deprecated@Override<T> boolean hasAttr(AttributeKey<T> key);
}

  ChannelHandlerContext 继承ChannelInboundInvoker,ChannelOutboundInvoker。可以实现入站,出站事件的处理。

ChannelInboundInvoker

public interface ChannelInboundInvoker {/*** A {@link Channel} was registered to its {@link EventLoop}.** This will result in having the  {@link ChannelInboundHandler#channelRegistered(ChannelHandlerContext)} method* called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelRegistered();/*** A {@link Channel} was unregistered from its {@link EventLoop}.** This will result in having the  {@link ChannelInboundHandler#channelUnregistered(ChannelHandlerContext)} method* called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelUnregistered();/*** A {@link Channel} is active now, which means it is connected.** This will result in having the  {@link ChannelInboundHandler#channelActive(ChannelHandlerContext)} method* called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelActive();/*** A {@link Channel} is inactive now, which means it is closed.** This will result in having the  {@link ChannelInboundHandler#channelInactive(ChannelHandlerContext)} method* called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelInactive();/*** A {@link Channel} received an {@link Throwable} in one of its inbound operations.** This will result in having the  {@link ChannelInboundHandler#exceptionCaught(ChannelHandlerContext, Throwable)}* method  called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireExceptionCaught(Throwable cause);/*** A {@link Channel} received an user defined event.** This will result in having the  {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)}* method  called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireUserEventTriggered(Object event);/*** A {@link Channel} received a message.** This will result in having the {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)}* method  called of the next {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelRead(Object msg);/*** Triggers an {@link ChannelInboundHandler#channelReadComplete(ChannelHandlerContext)}* event to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.*/ChannelInboundInvoker fireChannelReadComplete();/*** Triggers an {@link ChannelInboundHandler#channelWritabilityChanged(ChannelHandlerContext)}* event to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.*/ChannelInboundInvoker fireChannelWritabilityChanged();
}

  入站事件触发

ChannelOutboundInvoker

public interface ChannelOutboundInvoker {/*** Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation* completes, either because the operation was successful or because of an error.* <p>* This will result in having the* {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method* called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture bind(SocketAddress localAddress);/*** Request to connect to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation* completes, either because the operation was successful or because of an error.* <p>* If the connection fails because of a connection timeout, the {@link ChannelFuture} will get failed with* a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException}* will be used.* <p>* This will result in having the* {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture connect(SocketAddress remoteAddress);/*** Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of* an error.* <p>* This will result in having the* {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress);/*** Request to disconnect from the remote peer and notify the {@link ChannelFuture} once the operation completes,* either because the operation was successful or because of an error.* <p>* This will result in having the* {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture disconnect();/*** Request to close the {@link Channel} and notify the {@link ChannelFuture} once the operation completes,* either because the operation was successful or because of* an error.** After it is closed it is not possible to reuse it again.* <p>* This will result in having the* {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture close();/*** Request to deregister from the previous assigned {@link EventExecutor} and notify the* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of* an error.* <p>* This will result in having the* {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.**/ChannelFuture deregister();/*** Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation* completes, either because the operation was successful or because of an error.** The given {@link ChannelPromise} will be notified.* <p>* This will result in having the* {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method* called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);/*** Request to connect to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation* completes, either because the operation was successful or because of an error.** The given {@link ChannelFuture} will be notified.** <p>* If the connection fails because of a connection timeout, the {@link ChannelFuture} will get failed with* a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException}* will be used.* <p>* This will result in having the* {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);/*** Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of* an error.** The given {@link ChannelPromise} will be notified and also returned.* <p>* This will result in having the* {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);/*** Request to disconnect from the remote peer and notify the {@link ChannelFuture} once the operation completes,* either because the operation was successful or because of an error.** The given {@link ChannelPromise} will be notified.* <p>* This will result in having the* {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture disconnect(ChannelPromise promise);/*** Request to close the {@link Channel} and notify the {@link ChannelFuture} once the operation completes,* either because the operation was successful or because of* an error.** After it is closed it is not possible to reuse it again.* The given {@link ChannelPromise} will be notified.* <p>* This will result in having the* {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture close(ChannelPromise promise);/*** Request to deregister from the previous assigned {@link EventExecutor} and notify the* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of* an error.** The given {@link ChannelPromise} will be notified.* <p>* This will result in having the* {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture deregister(ChannelPromise promise);/*** Request to Read data from the {@link Channel} into the first inbound buffer, triggers an* {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)} event if data was* read, and triggers a* {@link ChannelInboundHandler#channelReadComplete(ChannelHandlerContext) channelReadComplete} event so the* handler can decide to continue reading.  If there's a pending read operation already, this method does nothing.* <p>* This will result in having the* {@link ChannelOutboundHandler#read(ChannelHandlerContext)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelOutboundInvoker read();/*** Request to write a message via this {@link ChannelHandlerContext} through the {@link ChannelPipeline}.* This method will not request to actual flush, so be sure to call {@link #flush()}* once you want to request to flush all pending data to the actual transport.*/ChannelFuture write(Object msg);/*** Request to write a message via this {@link ChannelHandlerContext} through the {@link ChannelPipeline}.* This method will not request to actual flush, so be sure to call {@link #flush()}* once you want to request to flush all pending data to the actual transport.*/ChannelFuture write(Object msg, ChannelPromise promise);/*** Request to flush all pending messages via this ChannelOutboundInvoker.*/ChannelOutboundInvoker flush();/*** Shortcut for call {@link #write(Object, ChannelPromise)} and {@link #flush()}.*/ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);/*** Shortcut for call {@link #write(Object)} and {@link #flush()}.*/ChannelFuture writeAndFlush(Object msg);/*** Return a new {@link ChannelPromise}.*/ChannelPromise newPromise();/*** Return an new {@link ChannelProgressivePromise}*/ChannelProgressivePromise newProgressivePromise();/*** Create a new {@link ChannelFuture} which is marked as succeeded already. So {@link ChannelFuture#isSuccess()}* will return {@code true}. All {@link FutureListener} added to it will be notified directly. Also* every call of blocking methods will just return without blocking.*/ChannelFuture newSucceededFuture();/*** Create a new {@link ChannelFuture} which is marked as failed already. So {@link ChannelFuture#isSuccess()}* will return {@code false}. All {@link FutureListener} added to it will be notified directly. Also* every call of blocking methods will just return without blocking.*/ChannelFuture newFailedFuture(Throwable cause);/*** Return a special ChannelPromise which can be reused for different operations.* <p>* It's only supported to use* it for {@link ChannelOutboundInvoker#write(Object, ChannelPromise)}.* </p>* <p>* Be aware that the returned {@link ChannelPromise} will not support most operations and should only be used* if you want to save an object allocation for every write operation. You will not be able to detect if the* operation  was complete, only if it failed as the implementation will call* {@link ChannelPipeline#fireExceptionCaught(Throwable)} in this case.* </p>* <strong>Be aware this is an expert feature and should be used with care!</strong>*/ChannelPromise voidPromise();
}

  出站事件触发

AbstractChannelHandlerContext

  AbstractChannelHandlerContext是一个抽象类,实现ChannelHandlerContext。
  在这个抽象类里面,fire开头方法和invoke开头方法。
  1. fire开头方法,实现也是调用invoke开头方法。只不过入参的channelHandlerContext通过方法findContextInbound查询下一个入站或者出站的方法。例如FireChannelRead方法:

   @Overridepublic ChannelHandlerContext fireChannelRead(final Object msg) {invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);return this;}private AbstractChannelHandlerContext findContextInbound(int mask) {AbstractChannelHandlerContext ctx = this;EventExecutor currentExecutor = executor();do {ctx = ctx.next;} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));return ctx;}

  2. invoke开头方法是静态方法,执行对应事件的方法。里面有个逻辑,获取当前context中的EventExecutor是不是NioEventLoop线程,是的话直接执行方法调用,不是的话,添加到NioEventLoop任务队列中。例如invokeChannelRead方法:

    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);EventExecutor executor = next.executor();if (executor.inEventLoop()) {next.invokeChannelRead(m);} else {executor.execute(new Runnable() {@Overridepublic void run() {next.invokeChannelRead(m);}});}}

ChannelPipeline

  ChannelPipeline也实现了ChannelInboundInvoker,ChannelOutboundInvoker接口。具有入站出站事件执行的能力。ChannelHandlerContext也实现同样的接口。他们的区别是。ChannelHandlerContext方法执行是file开头对应的方法。ChannelPipeline方法执行调用的是ChannelHandlerContext中的invoke方法,入站是方法传入ChannelPipeline的head节点,出站方法传入ChannelPipeline的tail节点。
  以下是ChannelPipeline的默认实现DefaultChannelPipeline,中fire方法实现。可以看到调用的就是ChannelHandlerContext中的静态invoke开头方法。
在这里插入图片描述

Inbound入站事件生命周期

  在Netty中,ChannelHandler的生命周期与Channel的状态紧密相关,主要涉及到以下几个回调方法:

  1. handlerAdded: 当一个新的ChannelHandler被添加到ChannelPipeline时调用。
  2. channelRegistered: 当Channel成功注册到EventLoop上时调用。
  3. channelActive: 当Channel激活,可以开始接收和发送数据时调用。
  4. channelRead: 当从Channel中读取到数据时调用。
  5. channelReadComplete: 当一次读取操作完成时调用。
  6. channelInactive: 当Channel变为非激活状态时调用。
  7. channelUnregistered: 当Channel从EventLoop上注销时调用。
  8. handlerRemoved: 当ChannelHandler从ChannelPipeline中移除时调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/656406.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

猫突然不吃东西没精神?性价比高可以迅速恢复精神的生骨肉冻干推荐

猫突然不吃东西没精神怎么办&#xff1f;当猫咪不吃东西、精神不振时&#xff0c;可能是由于健康问题、环境因素或食物原因所引起。首先应进行身体检查&#xff0c;观察是否有其他并发症&#xff0c;如无则可排除健康问题。为猫咪提供安全舒适的环境、给予关爱&#xff0c;可改…

亚信安慧AntDB:AntDB-M元数据锁(七)

5.4.5 慢路径锁的授予条件 当且仅当满足如下两个条件时&#xff0c;才可以授予锁。 1. 其他线程没有持有不兼容类型锁。 2. 当前申请的锁的优先级高于请求等待列表中的。 首先通过锁位图判断等待队列&#xff0c;不兼容则不能授予锁。再判断快速路径&#xff0c;不兼容则不…

状态码400以及状态码415

首先检查前端传递的参数是放在header里边还是放在body里边。 此图前端传参post请求&#xff0c;定义为’Content-Type’&#xff1a;‘application/x-www-form-urlencoded’ 此刻他的参数在FormData中。看下图 后端接参数应为&#xff08;此刻参数前边什么都不加默认为requestP…

Qt QScrollArea 不显示滚动条 不滚动

使用QScrollArea时&#xff0c;发现添加的控件超出QScrollArea 并没有显示&#xff0c;且没有滚动条效果 原因是 scrollArea指的是scrollArea控件本身的大小&#xff0c;肉眼能看到的外形尺寸。 scrollAreaWidgetContents指的是scrollArea控件内部的显示区域&#xff0c;里面可…

windows server 开启远程连接RDP连接

windows server 开启远程连接&#xff0c;RDP连接windows server 打开gpedit.msc, 找到计算机配置-管理模板-windows组件-远程桌面服务-远程桌面会话主机-授权 1 使用指定的远程桌面许可证服务器 2 设置远程桌面授权模式 3 重启windows server服务器生效 4使用mstsc命令连接…

未来每家公司都需要有自己的大模型- Hugging Face创始人分享

自ChatGPT发布以来&#xff0c;有人称其是统治性一切的模型。Hugging Face创始人兼首席执行官Clem Delangue介绍&#xff0c;Hugging Face平台已经有15000家公司分享了25万个开源模型&#xff0c;当然这些公司不会为了训练模型而训练模型&#xff0c;因为训练模型需要投入大量资…

Springboot自定义线程池实现多线程任务

1. 在启动类添加EnableAsync注解 2.自定义线程池 package com.bt.springboot.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTask…

记录 | ubuntu nm命令的基本使用

什么是nm命令 nm命令是linux下针对某些特定文件的分析工具&#xff0c;能够列出库文件&#xff08;.a、.lib&#xff09;、目标文件&#xff08;*.o&#xff09;、可执行文件的符号表。 nm命令的常用参数 -A 或 -o 或 --print-file-name&#xff1a;打印出每个符号属于的文件…

webassembly003 TTS BARK.CPP

TTS task TTS&#xff08;Text-to-Speech&#xff09;任务是一种自然语言处理&#xff08;NLP&#xff09;任务&#xff0c;其中模型的目标是将输入的文本转换为声音&#xff0c;实现自动语音合成。具体来说&#xff0c;模型需要理解输入的文本并生成对应的语音输出&#xff0…

c++学习记录 多态—案例2—电脑组装

#include<iostream> using namespace std;//抽象不同的零件//抽象的cpu类 class Cpu { public://抽象的计算函数virtual void calculate() 0; };//抽象的显卡类 class VideoCard { public://抽象的显示函数virtual void display() 0; };//抽象的内存条类 class Memory …

Nginx启用WebSocket支持

报错内容nginx.conf proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 问题解决WebSocket跨域 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true;

常用芯片学习——AMS1117芯片

AMS1117 1A 低压差线性稳压器 使用说明 AMS1117 是一款低压差线性稳压电路&#xff0c;该电路输出电流能力为1A。该系列电路包含固定输出电压版本和可调输出电压版本&#xff0c;其输出电压精度为士1.5%。为了保证芯片和电源系统的稳定性&#xff0c;XBLWAMS1117 内置热保护和…

对鸢尾花进行分类预测-----pycharm

项目说明 #项目&#xff1a; 对鸢尾花进行分类预测 #实例数量150个(3类各50个) #属性数量&#xff1a;4(数值型&#xff0c;数值型&#xff0c;帮助预测的属性和类) #特征&#xff1a;花萼长度&#xff0c;花萼宽度&#xff0c;花瓣长度&#xff0c;花瓣宽度 单位&#xff1…

什么是事务?

事务 是一组操作的集合&#xff0c;它是一个不可分割的工作单位。事务会把所有的操作作为一个整体&#xff0c;一起向数据库提交或者是撤销操作请求。所以这组操作要么同时成功&#xff0c;要么同时失败。 1. 事务管理 怎么样来控制这组操作&#xff0c;让这组操作同时成功或…

机器学习 强化学习 深度学习的区别与联系

机器学习 强化学习 深度学习 机器学习 按道理来说&#xff0c; 这个领域&#xff08;机器学习&#xff09;应该叫做 统计学习 &#xff08;Statistical Learning&#xff09;&#xff0c;因为它的方法都是由概率统计领域拿来的。这些人中的领军人物很有商业头脑&#xff0c; 把…

亚马逊测评:卖家如何操作测评,安全高效(自养号测评)

亚马逊测评的作用在于让用户更真实、清晰、快捷地了解产品以及产品的使用方法和体验。通过买家对产品的测评&#xff0c;也可以帮助厂商和卖家优化产品缺陷&#xff0c;提高用户的使用体验。这进而帮助他们获得更好的销量&#xff0c;并更深入地了解市场需求。亚马逊测评在满足…

【获奖必看2.0】美赛小技巧之一秒输入一个公式

大家好呀&#xff0c;美赛开赛还有四天的时间&#xff0c;今天给大家带来的是美赛论文写作时非常实用的一个小技巧——快速输入任何复杂公式。 相信很多小伙伴在论文写作的时候都有一个小烦恼&#xff0c;那就是在面对比较复杂的公式的时候&#xff0c;应该怎么进行快速输入呢…

vue3项目中让echarts适应div的大小变化,跟随div的大小改变图表大小

目录如下 我的项目环境如下利用element-resize-detector插件监听元素大小变化element-resize-detector插件的用法完整代码如下&#xff1a;结果如下 在做项目的时候&#xff0c;经常会使用到echarts&#xff0c;特别是在做一些大屏项目的时候。有时候我们是需要根据div的大小改…

【Three.js】Layers图层的使用

目录 前言 创建图层对象 启用图层 关闭图层 其他 前言 Layers 对象为Object3D对象分配了1-32个图层&#xff0c;编号为0-31。在内部实现上&#xff0c;每个图层对象被存储为一个bit mask&#xff0c; 默认所有 Object3D 对象都存储在第 0 个图层上。 图层对象可以用于控制…

美国将限制中国,使用Azure、AWS等云,训练AI大模型

1月29日&#xff0c;美国商务部在Federal Register&#xff08;联邦公报&#xff09;正式公布了&#xff0c;《采取额外措施应对与重大恶意网络行为相关的国家紧急状态》提案。 该提案明确要求美国IaaS&#xff08;云服务&#xff09;厂商在提供云服务时&#xff0c;要验证外国…