本章開始分析ChannelHandler實現代碼。ChannelHandler是netty爲開發者提供的實現定製業務的主要接口,開發者在使用netty時,最主要的工做就是實現本身的ChannelHandler。ChannelHandler在設計上須要和ChannelPipeline配合共同實現pipeline的事件傳遞能力,這要求ChannelHandler須要實現一些固定的基本功能。因爲這個緣由,若是讓用戶本身完整地實現,會顯得比較麻煩。爲此netty實現類一系列的類來幫助開發者以簡單的方式實現本身的ChannelHandler,並且只須要把注意力聚焦在本身業務和定製的部分上。在netty中,ChannelHandler不單單是幾個簡單的接口定義,而是一系列的實現,這些實現針對不一樣的問題,其中包括:git
單一功能性的Handler實現: github
IP過濾,實現IP黑白名單功能。算法
寫日誌。服務器
SSL實現。app
超時處理。異步
編碼和序列化格式的支持:tcp
base64ide
gzip,snappy壓縮算法ui
protoBuf編碼
String
自定義數據包格式。
常見應用層協議:
http/https(後面的版本還支持http2)
haprox
ChannelHandler體系結構
上圖上的全部接口的實現都位於io.netty.channel包中,其中,ChannelHandler是最頂層抽象的接口,ChannelInboundHandler是處理inbound事件的接口,ChannelOutboundHandler是處理outbound事件的接口。ChannelHandlerApapter是與I/O無關的抽象實現。ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter是最簡單的實現,他們什麼都沒作,僅僅使用ChannelHandlerContext把事件傳遞到pipleline中下一個handler,若是你不清楚在正在處理事件以後如何傳遞事件,請參考這兩個類中的代碼。好比,ChannelInboundAdapter中是這樣傳遞read事件的:
1 @Override 2 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 3 ctx.fireChannelRead(msg); 4 }
CombinedChannelDuplexhandler兼具ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter的能力。若是你想讓一個Handler同時處理inbound和outbound事件,請繼承這個類。
ChannelHandler的共享和獨佔模式
netty爲ChannelHandler設計了兩種模式,若是用ChannelHandler.Sharable修飾你的Handler, 那麼這個Handler實例就是可共享的,不然它就是被某個Channel獨佔的。
@ChannelHandler.Sharable public class MyHandler extends ChannelInboundHandler{ }
這個MyHandler的實例是能夠被不一樣的Channel共享的。在初始化Channel的時候能夠這加入到pipeline中:
1 public class HandlerInitializer extends ChannelInitializer<SocketChanne>{ 2 private static MyHandler handler = new MyHandler(); 3 4 @Override 5 protected void initChannel(SocketChannel ch) throws Exception{ 6 ch.pipeline().addLast(handler); 7 } 8 }
若是去掉@ChannelHandler.Sharable,它就是獨佔的,默認的都是獨佔。它能夠這樣用:
public class HandlerInitializer extends ChannelInitializer<SocketChanne>{ @Override protected void initChannel(SocketChannel ch) throws Exception{ ch.pipeline().addLast(new MyHandler()); } }
ChannelHandlerAdapter的isSharable方法負責處理@ChannelHandler.Sharable:
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; }
如今已經清楚了@ChannelHandler.Sharable影響和用法。那麼問題來了,netty爲何要設計這樣一個東西,它解決了什麼問題呢?弄清這個問題以前,咱們先來弄清楚ChannelHandler的做用。每一個ChannelHandler實例都在Channel的pipeline上佔據一個節點,每一個節點帶表明了事件處理流水線上的一道工序,具體每道工序是什麼,由開發者本身定義和實現。整體來講,netty把工序分紅兩種類型: 有狀態的和無狀態的。有狀態的就是獨佔的,無狀態的是能夠共享的。有狀態和無狀態這個兩個概念太過於抽象。舉個例子,http協議是一個基於TCP的應用層協議,實現這個協議的時候有一道工序是從tcp流中讀到一個完整http數據報,tcp不保證每次read都恰好是一個完整的http數據報,頗有多是不完整的,這時候要把不完整的數據保存在緩衝區中, 下次read到數據以後追加到緩衝區,直到獲得一個完整的http數據報。處理這道工序的Handler是有狀態的,必須使用獨佔的Handler。
一個Handler的實例是否能夠共享,由它處理的工序決定的。即便netty沒有設計這種獨佔/共享模式,開發者也應該能夠把握好Handler的用法。
經常使用的功能性Handler
io.netty.handler包中是netty提供的經常使用的功能性的handler:
io.netty.handler.ipfilter.RuleBasedIpFilter 使用IpFilterRule過過濾遠程地址。
io.netty.handler.ipfilter.UniqueIpFilter 一個地址只容許和服務器創建一個鏈接。
io.netty.handler.logging.LoggingHandler 異步讀/寫日誌。
io.netty.handler.stream.ChunkedWriteHandler 異步讀/寫文件。
io.netty.handler.timeout.IdleStateHandler 超時觸發空閒狀態事件。
io.netty.handler.timeout.ReadTimeoutHandler 超時沒有收到數據斷開鏈接。
io.netty.handler.timeout.WriteTimeoutHandler 超時沒有寫數據斷開鏈接。