第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Netty 教程 Netty 教程 Netty ChannelHandler性能優(yōu)化

Netty ChannelHandler 性能優(yōu)化

1. 前言

本節(jié)我們主要來繼續(xù)講解 ChannelHandler 的其它特性,主要講解如何去進行 ChannelHandler 業(yè)務(wù)鏈表的常見性能優(yōu)化。

2. 優(yōu)化途徑

通常情況下為了提高自定義業(yè)務(wù) Handler 的性能需要進行一定的優(yōu)化策略,常見的優(yōu)化方案分別是縮短傳播路徑、Handler 單利等。

  1. 傳播路徑: 如果業(yè)務(wù)很復雜的情況,由很多的 Handler 組成的時候,鏈條過長會消耗性能,因此,一般都是動態(tài)的刪除一些沒用的 Handler。
  2. Handler 單利: 每個客戶端進來,都會為每個 Channel 創(chuàng)建一輪 Handler 并且加入到 Pipeline 進行管理,new 的過程是消耗性能的。

圖片描述

3. 熱插拔

上節(jié)我們學習了 ChannelHandler 的生命周期,其中有一個關(guān)鍵的方法是 handlerRemoved (),在 handler 被移除的時候觸發(fā)該事件,針對該事件,其實我們可以靈活的擴展自己的業(yè)務(wù)功能。

需求:客戶端和服務(wù)端之間通信,必須需要先認證。

實例:

serverBootstrap
    .group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .childHandler(new ChannelInitializer<NioSocketChannel>() {
        protected void initChannel(NioSocketChannel ch) {
            //1.登錄認證Handler
            ch.pipeline().addLast(new LoginHandler());
            //2.其他業(yè)務(wù)Handler
            ch.pipeline().addLast(new OtherHandler());
        }
    });

通過以上的代碼,我們就能很好的解決了客戶端登錄認證問題,但是我們會發(fā)現(xiàn),在登錄認證成功之后,客戶端發(fā)起其他類型請求的時候,每次請求 LoginHandler 都會被執(zhí)行,那么應(yīng)該怎么去解決這個問題呢?

解決思路:在客戶端第一次連接服務(wù)端時,進行賬號認證,認證成功之后,把 LoginHandler 給移除掉。

實例:

public class LoginHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //1.省略了部分代碼(轉(zhuǎn)換ByteBuf,對象流反序列化)
        //2.獲取Map
        Map<String,String> map=(Map<String,String>)iss.readObject();
        //3.認證賬號、密碼,并且響應(yīng)
        String username=map.get("username");
        String password=map.get("password");
        if(username.equals("admin")&&password.equals("123456")){
            //3.1.給客戶端響應(yīng)
            ctx.channel().writeAndFlush(Unpooled.copiedBuffer("success".getBytes()));
            //3.2.移除該Handler,這樣下次請求就不會再執(zhí)行該Handler了
            ctx.pipeline().remove(this);
        }else{
            ctx.channel().writeAndFlush(Unpooled.copiedBuffer("error".getBytes()));
            ctx.channel().closeFuture();
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        System.out.println("LoginHandler被移除");
    }
}

總結(jié),動態(tài)新增和移除 Handler,也稱之為熱插拔,在真實項目開發(fā)當中其實非常的有用。

4. Handler 單利

4.1 @Shareable

ch.pipeline().addLast(new LoginHandler()); 添加鏈表節(jié)點的時候,我們是手工 new 一個對象,其實也就是說,每個客戶端連接進來的時候,都需要組建一條雙向鏈表,并且都是 new 每個節(jié)點的對象,我們都知道每次 new 性能肯定是不高。

Spring 的 IOC 其實就是解決手工 new 對象的,項目啟動的時候把所有對象創(chuàng)建完放到 Spring 容器,后面每次使用的時候無需再創(chuàng)建,而是直接從容器里面獲取,這種方式可以提高性能。同樣道理,Netty 也提供類似的功能,那就是 @Shareable 注解修飾的 Handler,只要用該注解修飾之后,那么該 Handler 就會變成共享,也就是說被所有的客戶端所共享,無需每次都創(chuàng)建,自然性能會得到提升。

實例:

//使用注解修飾
@ChannelHandler.Sharable
public class ServerLoginHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        
    }
}
public class NettyServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        //提前創(chuàng)建好
        final ServerLoginHandler serverLoginHandler=new ServerLoginHandler();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) {
                        //這里無需再創(chuàng)建,只需要傳遞實例即可
                    	ch.pipeline().addLast(serverLoginHandler);
                    }
                });

        serverBootstrap.bind(80);
    }
}

4.2 @Shareable 線程不安全

對于共享的 Handler,很容易就會出現(xiàn)線程安全問題,多個線程同時訪問同一個對象不會出現(xiàn)任何的線程安全問題,但是有讀有寫,則就會產(chǎn)生線程安全問題,因此需要特別注意,因此,如果使用了 @Shareable 修飾了 Handler,那么千萬不要包含全局變量、全局靜態(tài)變量,否則就會出現(xiàn)線程安全問題。

實例:

@ChannelHandler.Sharable
public class ServerLoginHandler extends ChannelInboundHandlerAdapter {
    //全局變量
    private int count;
        
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //遞增
        count++;
    }
}

疑問:為什么以上的代碼在并發(fā)情況下是不安全的呢?

原因是,每個線程內(nèi)部都會開辟一個內(nèi)存空間,從主內(nèi)存中拷貝 count 值,在線程中遞增之后,再把結(jié)果寫到主內(nèi)存當中。并發(fā)情況下,多個線程之間可能取得的值是一樣,然后線程之間又不可見性,因此就會導致線程不安全。

解決:如果開發(fā)過程中遇到類似的問題,應(yīng)該如何解決呢?

直接使用 AtomicXxx 去代替,AtomicXxx 是 J.U.C 下提供的工具類,底層是通過 CAS 無鎖機制去控制,保證線程安全。

4.3 集成 Spring 容器

其實,在真實開發(fā)項目當中,一般都是把 Handler 直接交給 Spring 容器進行管理,也就是說在 Handler 類上添加 Spring 提供的 @Component 注解即可。

主要目的:

  1. 統(tǒng)一把 Handler 交給 Spring 來管理;
  2. Handler 一般都是需要和底層的數(shù)據(jù)庫進行交互的,真實項目當中一般都是使用 Spring 來管理 ORM 組件,如果 Handler 不交給 Spring 管理,那么操作數(shù)據(jù)庫的時候就會相對麻煩。

實例:

//交給Spring容器管理
@Component
public class ServerLoginHandler extends ChannelInboundHandlerAdapter {
    //注入dao
    @Autowired
    private UserDao userDao;
        
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        
    }
}
@Autowired
private ServerLoginHandler serverLoginHandler;

//這里無需再創(chuàng)建,只需要傳遞實例即可
ch.pipeline().addLast(serverLoginHandler);

5. 小結(jié)

本內(nèi)容主要是從兩個方面去進行業(yè)務(wù) Handler 性能上面的優(yōu)化,分別是

  1. 熱插拔: 在執(zhí)行過程中動態(tài)的刪除無用的 Handler, 縮短 Handler 的傳播距離;
  2. 單例: 避免每個客戶端的連接進來時都重復創(chuàng)建 Handler,使用單利的集中方式以及線程安全問題。