您好,登錄后才能下訂單哦!
今天小編給大家分享一下netty中pipeline的handler怎么添加刪除的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>(){ @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new MyServerHandler()); } }); ChannelFuture channelFuture = serverBootstrap.bind(8899).sync(); channelFuture.channel().closeFuture().sync();
分析pipeline.addLast(new MyServerHandler())
中的addLast
。
首先通過channel
拿到當前的pipline
, 拿到pipeline
之后再為其添加handler
, 因為channel
初始化默認創建的是DefualtChannelPipeline
public final ChannelPipeline addLast(ChannelHandler... handlers) { return addLast(null, handlers); }
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) { if (handlers == null) { throw new NullPointerException("handlers"); } for (ChannelHandler h: handlers) { if (h == null) { break; } addLast(executor, null, h); } return this; }
這里的handlers
只有一個
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) { final AbstractChannelHandlerContext newCtx; synchronized (this) { //判斷handler是否被重復添加(1) checkMultiplicity(handler); //創建一個HandlerContext并添加到列表(2) newCtx = newContext(group, filterName(name, handler), handler); //添加HandlerContext(3) addLast0(newCtx); //是否已注冊 if (!registered) { newCtx.setAddPending(); callHandlerCallbackLater(newCtx, true); return this; } EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { newCtx.setAddPending(); //回調用戶事件 executor.execute(new Runnable() { @Override public void run() { callHandlerAdded0(newCtx); } }); return this; } } //回調添加事件(4) callHandlerAdded0(newCtx); return this; }
分為四個步驟:
重復添加驗證
創建一個HandlerContext
并添加到列表
添加context
回調添加事件
private static void checkMultiplicity(ChannelHandler handler) { if (handler instanceof ChannelHandlerAdapter) { ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler; if (!h.isSharable() && h.added) { throw new ChannelPipelineException( h.getClass().getName() + " is not a @Sharable handler, so can't be added or removed multiple times."); } //滿足條件設置為true, 代表已添加 h.added = true; } }
首先判斷是不是ChannelHandlerAdapter
類型, 因為我們自定義的handler
通常會直接或者間接的繼承該接口, 所以這里為true
拿到handler
之后轉換成ChannelHandlerAdapter
類型。
然后進行條件判斷 if (!h.isSharable() && h.added)
代表如果不是共享的handler
, 并且是未添加狀態, 則拋出異常。
public boolean isSharable() { Class<?> clazz = getClass(); Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache(); Boolean sharable = cache.get(clazz); if (sharable == null) { //如果這個類注解了Sharable.class, 說明這個類會被多個channel共享 sharable = clazz.isAnnotationPresent(Sharable.class); cache.put(clazz, sharable); } return sharable; }
首先拿到當前handler
的class
對象。
然后再從netty
自定義的一個ThreadLocalMap
對象中獲取一個盛放handler
的class
對象的map
, 并獲取其value
。
如果value
值為空, 則會判斷是否被Sharable
注解, 并將自身handler
的class
對象和判斷結果存入map
對象中, 最后返回判斷結果。
這說明了被Sharable
注解的handler
是一個共享handler
。
從這個邏輯我們可以判斷, 共享對象是可以重復添加的。
回到DefaultChannelPipeline.addLast
,如果是共享對象或者沒有被添加, 則將ChannelHandlerAdapter
的added
設置為true
, 代表已添加分析完了重復添加驗證, 回到addLast
方法中, 我們看第二步, 創建一個HandlerContext
并添加到列表
newCtx = newContext(group, filterName(name, handler), handler)
首先看filterName(name, handler)方法, 這個方法是判斷添加handler的name是否重復
首先看filterName(name, handler)
方法, 這個方法是判斷添加handler
的name
是否重復
private String filterName(String name, ChannelHandler handler) { if (name == null) { //沒有名字創建默認名字 return generateName(handler); } //檢查名字是否重復 checkDuplicateName(name); return name; }
因為我們添加handler
時候, 不一定會給handler
命名, 所以這一步name
有可能是null
, 如果是null
, 則創建一個默認的名字, 這里創建名字的方法就不分析了
private void checkDuplicateName(String name) { //不為空 if (context0(name) != null) { throw new IllegalArgumentException("Duplicate handler name: " + name); } }
繼續跟進分析context0(name)
方法
private AbstractChannelHandlerContext context0(String name) { //遍歷pipeline AbstractChannelHandlerContext context = head.next; while (context != tail) { //發現name相同, 說明存在handler if (context.name().equals(name)) { //返回 return context; } context = context.next; } return null; }
這里的邏輯就是將pipeline
中, 從head
節點往下遍歷HandlerContext
, 一直遍歷到tail
, 如果發現名字相同則會認為重復并返回HandlerContext
對象。
繼續跟到newContext(group, filterName(name, handler), handler)
方法中。
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) { return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler); }
可以看到創建了一個DefaultChannelHandlerContext
對象, 構造方法的參數中, 第一個this
代表當前的pipeline
對象, group
為null
, 所以childExecutor(group)
也會返回null
, name
為handler
的名字, handler
為新添加的handler
對象
DefaultChannelHandlerContext( DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) { super(pipeline, executor, name, isInbound(handler), isOutbound(handler)); if (handler == null) { throw new NullPointerException("handler"); } this.handler = handler; }
首先調用了父類的構造方法, 之后將handler
賦值為自身handler
的成員變量, HandlerConext
和handler
關系在此也展現了出來, 是一種組合關系
父類的構造方法, 有這么兩個參數:isInbound(handler)
, isOutbound(handler)
, 這兩個參數意思是判斷需要添加的handler
是inboundHandler
還是outBoundHandler
private static boolean isInbound(ChannelHandler handler) { return handler instanceof ChannelInboundHandler; }
這里通過是否實現ChannelInboundHandler
接口來判斷是否為inboundhandler
private static boolean isOutbound(ChannelHandler handler) { return handler instanceof ChannelOutboundHandler; }
通過判斷是否實現ChannelOutboundHandler
接口判斷是否為outboundhandler
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, boolean inbound, boolean outbound) { this.name = ObjectUtil.checkNotNull(name, "name"); this.pipeline = pipeline; this.executor = executor; this.inbound = inbound; this.outbound = outbound; ordered = executor == null || executor instanceof OrderedEventExecutor; }
之前tail
節點和head
節點創建的時候也執行到了這里,初始化了name
, pipeline
, 以及標識添加的handler
是inboundhanlder
還是outboundhandler
。
回到DefaultChannelPipeline.addLast
,分析完了創建HandlerContext
的相關邏輯, 我們繼續跟第三步, 添加HandlerContext
private void addLast0(AbstractChannelHandlerContext newCtx) { //拿到tail節點的前置節點 AbstractChannelHandlerContext prev = tail.prev; //當前節點的前置節點賦值為tail節點的前置節點 newCtx.prev = prev; //當前節點的下一個節點賦值為tail節點 newCtx.next = tail; //tail前置節點的下一個節點賦值為當前節點 prev.next = newCtx; //tail節點的前一個節點賦值為當前節點 tail.prev = newCtx; }
做了一個指針的指向操作, 將新添加的handlerConext
放在tail
節點之前, 之前tail
節點的上一個節點之后, 如果是第一次添加handler
, 那么添加后的結構入下圖所示
添加完handler
之后, 這里會判斷當前channel
是否已經注冊, 這部分邏輯之后再進行分析,先接著繼續執行。
之后會判斷當前線程線程是否為eventLoop
線程, 如果不是eventLoop
線程, 就將添加回調事件封裝成task
交給eventLoop
線程執行, 否則, 直接執行添加回調事件callHandlerAdded0(newCtx)
。
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) { try { ctx.handler().handlerAdded(ctx); ctx.setAddComplete(); } catch (Throwable t) { /** * 省略 * */ } }
分析ctx.handler().handlerAdded(ctx)
,其中ctx
是我們新創建的HandlerContext
, 通過handler()
方法拿到綁定的handler
, 也就是新添加的handler
, 然后執行handlerAdded(ctx)
方法, 如果我們沒有重寫這個方法, 則會執行父類的該方法。
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // NOOP }
沒做任何操作, 也就是如果我們沒有重寫該方法時, 如果添加handler
之后將不會做任何操作, 這里如果我們需要做一些業務邏輯, 可以通過重寫該方法進行實現
刪除的邏輯和添加的邏輯相同,區別刪除是將pipeline
的雙向鏈表的節點去掉。這里就不詳細的分析。
以上就是“netty中pipeline的handler怎么添加刪除”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。