Netty中ChannelHandler共享數據的方式

(一)成員變量

public class DataServerHandler extends SimpleChannelHandler {
    // 成員變量
    private boolean loggedIn;

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        ...
    }
}
  • 如果只是想在當前連接內共享數據,那麼需要針對不同的Channel創建不同的ChannelHandler實例,避免共享範圍擴大至所有連接。
// 所有Channel共用一個ChannelHandler實例 
public class DataServerPipelineFactory implements ChannelPipelineFactory {

     private static final DataServerHandler SHARED = new DataServerHandler();

     public ChannelPipeline getPipeline() {
         return Channels.pipeline(SHARED);
     }
 }
// 每一個Channel創建一個ChannelHandler實例
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
    public ChannelPipeline getPipeline() {
        ChannelPipeline pipeline = pipeline();      
        pipeline.addLast("handler", new TestHandler());
        return pipeline;
    }
});

(二)ChannelHandlerContext

  • ChannelHandlerContext是在Pipline註冊ChannelHandler的時候和其綁定的,因此一個被多次註冊(無論是否是同一個Pipline)的ChannelHandler會同時擁有多個ChannelHandlerContext。
  • 對於ChannelHandlerContext一個常見的誤解是以爲其可以在同一個Pipline的各個Handler之間傳遞數據。如果真要這樣做,應該使用MessageEvent或者ChannelLocal。
@Sharable
public class DataServerHandler extends SimpleChannelHandler {

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        Channel ch = e.getChannel();
        Object o = e.getMessage();
        if (o instanceof LoginMessage) {
            authenticate((LoginMessage) o);
            // 設置共享數據
            ctx.setAttachment(true);
        }
    }
}
  • @Sharable註解只是起到一個標示的作用,說明一個該ChannelHandler實例可以被多次註冊到一個或者多個Pipline中,而且不會導致不同的Channel共享數據出現競爭條件。
  • 基於ChannelHandlerContext同ChannelHandler的綁定時機和機制,很容易理解在本例中是怎麼保證@Sharable的。

(三)Channel

  • 該方法類似ChannelLocal,只是共享數據是直接存儲在Channel實例中的。
@Sharable
public class TestHandler extends SimpleChannelHandler {
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
        // 通過任意一種方式獲取到當前Channel,然後設置共享數據
        ctx.getChannel().setAttachment(true);
        event.getChannel().setAttachment(true);
    }
}
  • 很明顯,本例中的ChannelLocal也是符合@Sharable要求的。

(四)ChannelLocal

  • 如果想在當前連接的其他ChannelHandler或者外部Handler中共享數據,則可以使用ChannelLocal。其設計思想類似於JDK中的ThreadLocal,其內部是一個Channel做Key,共享數據做Value的結構。
public final class DataServerState {

    public static final ChannelLocal<Boolean> loggedIn = new ChannelLocal<>() {
        protected Boolean initialValue(Channel channel) {
            return false;
        }
    }
}

@Sharable
public class DataServerHandler extends SimpleChannelHandler {

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        Channel ch = e.getChannel();
        Object o = e.getMessage();
        if (o instanceof LoginMessage) {
            authenticate((LoginMessage) o);
            // 設置共享數據
            DataServerState.loggedIn.set(ch, true);
        }
    }
}
  • 很明顯,本例中的ChannelLocal也是符合@Sharable要求的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章