• netty学习


    在项目中使用到netty框架,因此,想对其进行一个梳理。
    最好还是结合具体的代码来展开思考和阐述吧。

    从 关于HTTP(S)说起

    // Configure SSL.
    final SslContext sslCtx;
    if (Config.SSL) {
        SelfSignedCertificate ssc = new SelfSignedCertificate();
        sslCtx = SslContext.newServerContext(SslProvider.JDK, ssc.certificate(), ssc.privateKey());
    } else {
        sslCtx = null;
    }
    
    public static SslContext newServerContext(
            SslProvider provider, File certChainFile, File keyFile) throws SSLException {
        return newServerContext(provider, certChainFile, keyFile, null, null, null, 0, 0);
    }
    

    以上只是给出了代码中的概要,通过查看详细的代码之后,可以发现,这个功能是为了HTTPS加密传输做准备工作,比如生成公钥和私钥,然后存储到指定目录之后初始化环境配置。

    ServerBootstrap b = new ServerBootstrap();
    b.option(ChannelOption.SO_BACKLOG, 1024);
    b.option(ChannelOption.SO_REUSEADDR, true);
    b.group(bossGroup, workerGroup)
     .channel(NioServerSocketChannel.class)
     .handler(new LoggingHandler(LogLevel.INFO))
     .childHandler(new RiskServerInitializer(sslCtx));
    

    上面的代码段中生成了秘钥并进行了相关的环境配置,最终产生了一个SslContext 实例对象。这个对象很快就被使用到了,b.childHandler(new RiskServerInitializer(sslCtx));这里就使用到了。

    
    public class RiskServerInitializer extends ChannelInitializer<SocketChannel> {
        
        private final SslContext sslCtx;
    
        public RiskServerInitializer(SslContext sslCtx) {
            this.sslCtx = sslCtx;
        }
    
        @Override
        public void initChannel(SocketChannel ch) throws Exception {
    			// X-Requested-With在文件上传时会用到
    		String[] headers = {"Origin", "X-Requested-With", "Content-Type", "Accept", "x-user-token", "X-User-Mobile"};
    	    CorsConfig corsConfig = CorsConfig.withAnyOrigin().allowedRequestHeaders(headers).build();    	
            ChannelPipeline pipeline = ch.pipeline();
            if (sslCtx != null) {
                pipeline.addLast(sslCtx.newHandler(ch.alloc()));
            }
            pipeline.addLast("decoder", new HttpRequestDecoder());
            pipeline.addLast("encoder", new HttpResponseEncoder());
            // inserted HttpObjectAggregator into the pipeline, we get FullHttpRequest object with all the contnet of the payload
            // 这个handler是了文件上传用的.
            pipeline.addLast("aggregator", new HttpObjectAggregator(8388608)); // 8MB
            pipeline.addLast("deflater", new SmartHttpContentCompressor());
            
            pipeline.addLast("cors", new CorsHandler(corsConfig));
            pipeline.addLast("uploads", new RiskUploadFileServerHandler());        
            pipeline.addLast("handler", new RiskServerHandler());
        }
    }
    

    从上面的代码中可以看到,sslCtx对象被传给了RiskServerInitializer 类之后作为了一个成员变量来存储。并在其initChannel()方法中被使用到了。这个方法体内的代码,仍然是针对HTTP/S请求展开的操作,比如服务端对客户端的HTTP请求进行解码,发送返回时进行编码,还需要设置内容的聚合与分段压缩,跨域资源请求(CORS)的配置,文件上传功能等。此外,还核心的业务功能RiskServerHandler进行了加载和初始化。
    这里的sslCtx.newHandler(ch.alloc()),ch.alloc()的方法原型为ByteBufAllocator alloc();,返回一个ByteBufAllocator 对象,以便分配ByteBuf容器来存储数据。外层的sslCtx.newHandler的方法原型是:

    public final SslHandler newHandler(ByteBufAllocator alloc) {
        return newHandler(newEngine(alloc));
    }
    

    显然,这样做的目的是将SslHandler添加到ChannelPipeline中。
    但是,initChannel()方法又是如何被调用的呢?我们从它的父类ChannelInitializer中可以看到,在channelRegistered方法中对其进行了调用。

    
    @Sharable
    public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {
    
        private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class);
    
        /**
         * This method will be called once the {@link Channel} was registered. After the method returns this instance
         * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
         *
         * @param ch            the {@link Channel} which was registered.
         * @throws Exception    is thrown if an error occurs. In that case the {@link Channel} will be closed.
         */
        protected abstract void initChannel(C ch) throws Exception;
    
        @Override
        @SuppressWarnings("unchecked")
        public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            ChannelPipeline pipeline = ctx.pipeline();
            boolean success = false;
            try {
                initChannel((C) ctx.channel());
                pipeline.remove(this);
                ctx.fireChannelRegistered();
                success = true;
            } catch (Throwable t) {
                logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
            } finally {
                if (pipeline.context(this) != null) {
                    pipeline.remove(this);
                }
                if (!success) {
                    ctx.close();
                }
            }
        }
    }
    
    

    我没打算使用所谓的“各个击破”战略,也没打算使用“自定向下”的策略,但是我想适当的去看待一个问题,或者说从某个角度去看一个问题。然后,见好就收,回到总体上来,然后继续从另一个角度去看,去思考这个新的事物。
    前面是从HTTP(S)的角度去看,下面回到总体来看一下代码。

    整体梳理

    // Configure the server.
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup(Config.threadNum);
            try {
                ServerBootstrap b = new ServerBootstrap();
                b.option(ChannelOption.SO_BACKLOG, 1024);
                b.option(ChannelOption.SO_REUSEADDR, true);
                b.group(bossGroup, workerGroup)
                 .channel(NioServerSocketChannel.class)
                 .handler(new LoggingHandler(LogLevel.INFO))
                 .childHandler(new RiskServerInitializer(sslCtx));
    
                Channel ch = b.bind(Config.PORT).sync().channel();
    
                ch.closeFuture().sync();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
    
    

    首先是ServerBootstrap 。Bootstrap 的含义是引导,而ServerBootstrap 的含义是服务端引导,既然有服务端引导,就会有客户端引导,而在netty中,客户端引导就是Bootstrap。

  • 相关阅读:
    .NET5都来了,你还不知道怎么部署到linux?最全部署方案,总有一款适合你
    一款基于.NET Core的认证授权解决方案-葫芦藤1.0开源啦
    开源项目葫芦藤:IdentityServer4的实现及其运用
    MySQL大表优化方案
    Sec-Fetch-*请求头,了解下?
    前端开发快速入门
    从零开始打造专属钉钉机器人
    打造钉钉事件分发平台之钉钉审批等事件处理
    React中的高阶组件
    浏览器本地存储方案
  • 原文地址:https://www.cnblogs.com/ioveNature/p/7940562.html
Copyright © 2020-2023  润新知