最近中文字幕高清中文字幕无,亚洲欧美高清一区二区三区,一本色道无码道dvd在线观看 ,一个人看的www免费高清中文字幕

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

Netty ChannelHandler 性能優(yōu)化

1. 前言

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

2. 優(yōu)化途徑

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

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

圖片描述

3. 熱插拔

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

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

實(shí)例:

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

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

解決思路:在客戶(hù)端第一次連接服務(wù)端時(shí),進(jìn)行賬號(hào)認(rèn)證,認(rèn)證成功之后,把 LoginHandler 給移除掉。

實(shí)例:

public class LoginHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //1.省略了部分代碼(轉(zhuǎn)換ByteBuf,對(duì)象流反序列化)
        //2.獲取Map
        Map<String,String> map=(Map<String,String>)iss.readObject();
        //3.認(rèn)證賬號(hào)、密碼,并且響應(yīng)
        String username=map.get("username");
        String password=map.get("password");
        if(username.equals("admin")&&password.equals("123456")){
            //3.1.給客戶(hù)端響應(yīng)
            ctx.channel().writeAndFlush(Unpooled.copiedBuffer("success".getBytes()));
            //3.2.移除該Handler,這樣下次請(qǐng)求就不會(huì)再執(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é),動(dòng)態(tài)新增和移除 Handler,也稱(chēng)之為熱插拔,在真實(shí)項(xiàng)目開(kāi)發(fā)當(dāng)中其實(shí)非常的有用。

4. Handler 單利

4.1 @Shareable

ch.pipeline().addLast(new LoginHandler()); 添加鏈表節(jié)點(diǎn)的時(shí)候,我們是手工 new 一個(gè)對(duì)象,其實(shí)也就是說(shuō),每個(gè)客戶(hù)端連接進(jìn)來(lái)的時(shí)候,都需要組建一條雙向鏈表,并且都是 new 每個(gè)節(jié)點(diǎn)的對(duì)象,我們都知道每次 new 性能肯定是不高。

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

實(shí)例:

//使用注解修飾
@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) {
                        //這里無(wú)需再創(chuàng)建,只需要傳遞實(shí)例即可
                    	ch.pipeline().addLast(serverLoginHandler);
                    }
                });

        serverBootstrap.bind(80);
    }
}

4.2 @Shareable 線(xiàn)程不安全

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

實(shí)例:

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

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

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

解決:如果開(kāi)發(fā)過(guò)程中遇到類(lèi)似的問(wèn)題,應(yīng)該如何解決呢?

直接使用 AtomicXxx 去代替,AtomicXxx 是 J.U.C 下提供的工具類(lèi),底層是通過(guò) CAS 無(wú)鎖機(jī)制去控制,保證線(xiàn)程安全。

4.3 集成 Spring 容器

其實(shí),在真實(shí)開(kāi)發(fā)項(xiàng)目當(dāng)中,一般都是把 Handler 直接交給 Spring 容器進(jìn)行管理,也就是說(shuō)在 Handler 類(lèi)上添加 Spring 提供的 @Component 注解即可。

主要目的:

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

實(shí)例:

//交給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;

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

5. 小結(jié)

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

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