当前位置:首页 > CN2资讯 > 正文内容

netty 代理 HttpPostRequestDecoder

9小时前CN2资讯


一. 代码下载

Netty代码下载和编译参考前一篇Netty文章
https://blog.皇冠云.com/483181/2112163

二. 服务器代码分析

2.1 服务器代码编写

一般Netty服务器端这样编写

EventLoopGroup bossGroup = new NioEventLoopGroup(); //1. 实例化NioEventLoopGroup对象 EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); //2. b.group(bossGroup, workerGroup) //3. .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new FixedLengthFrameDecoder(20)); } }); ChannelFuture f = b.bind(port).sync(); //4. f.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }

2.2 NioEventLoopGroup

2.2.1 NioEventLoopGroup继承关系

一步步来看,首先看第一个注释,初始化NioEventLoopGroup对象

EventLoopGroup bossGroup = new NioEventLoopGroup(); //1. 实例化NioEventLoopGroup对象

下图是NioEventLoopGroup的类继承图,包含类成员和方法,比较详细。 这个功能是IntelliJ 自带的。
右击NioEventLoopGroup类名,选择Diagrams->Show Diagram->上面有f,m的按钮,分别对应field和method。

如下:

2.2.2 NioEventLoopGroup构造函数

public NioEventLoopGroup() { this(0); } public NioEventLoopGroup(int nThreads) { this(nThreads, (Executor) null); } public NioEventLoopGroup(int nThreads, Executor executor) { this(nThreads, executor, SelectorProvider.provider()); } public NioEventLoopGroup( int nThreads, Executor executor, final SelectorProvider selectorProvider) { this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE); } public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider, final SelectStrategyFactory selectStrategyFactory) { super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject()); }

我们可以看到几点

  • NioEventLoopGroup采用的是构造函数重载的方式,以适应不同的初始化场景
  • Executor传的是null
  • SelectorProvider用的是SelectorProvider.provider()
  • 然后把构造好的参数都传给父类MultithreadEventLoopGroup (继承关系可以看上图)
  • 2.2.3 SelectorProvider.provider()

    private static SelectorProvider provider = null; public static SelectorProvider provider() { synchronized (lock) { if (provider != null) return provider; return AccessController.doPrivileged( new PrivilegedAction<SelectorProvider>() { public SelectorProvider run() { if (loadProviderFromProperty()) return provider; if (loadProviderAsService()) return provider; provider = sun.nio.ch.DefaultSelectorProvider.create(); return provider; } }); } } public class DefaultSelectorProvider { private DefaultSelectorProvider() { } public static SelectorProvider create() { return new KQueueSelectorProvider(); } } public class KQueueSelectorProvider extends SelectorProviderImpl { public KQueueSelectorProvider() { } public AbstractSelector openSelector() throws IOException { return new KQueueSelectorImpl(this); } }

    这段代码我们也可以看到几点:

  • SelectorProvider provider是一个单例,static类型
  • SelectorProvider.provider的实现,产生了一个KQueueSelectorProvider
  • KQueueSelectorProvider的openSelector会生成一个KQueueSelectorImpl
  • 这个先记下来,也许后面分析会有用,继续分析MultithreadEventLoopGroup的构造函数。

    2.2.4 MultithreadEventLoopGroup

    protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) { super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args); } private static final int DEFAULT_EVENT_LOOP_THREADS; static { DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt( "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); }

    上面这段代码我们可以看到这几点:

  • 如果我们实例化NioEventLoopGroup没有传入参数,也就是没有nThreads,那么就会采用默认的DEFAULT_EVENT_LOOP_THREADS
    DEFAULT_EVENT_LOOP_THREADS如果没有配置io.netty.eventLoopThreads的话,一般是cpu核数*2
  • MultithreadEventLoopGroup的实例化方法是继续调用父类的初始化方法。
  • 继续父类MultithreadEventExecutorGroup

    2.2.5 MultithreadEventExecutorGroup

    protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { ... children = new EventExecutor[nThreads]; //1. 实例化children数组 for (int i = 0; i < nThreads; i ++) { //2. 循环初始化children boolean success = false; try { children[i] = newChild(executor, args); success = true; } catch (Exception e) { throw new IllegalStateException("failed to create a child event loop", e); } finally { ... } } chooser = chooserFactory.newChooser(children); //3. 实例化chooser final FutureListener<Object> terminationListener = new FutureListener<Object>() { @Override public void operationComplete(Future<Object> future) throws Exception { if (terminatedChildren.incrementAndGet() == children.length) { terminationFuture.setSuccess(null); } } }; for (EventExecutor e: children) { e.terminationFuture().addListener(terminationListener); } Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length); Collections.addAll(childrenSet, children); readonlyChildren = Collections.unmodifiableSet(childrenSet); }

    上面这段代码可以从下面几个点分析:

    private final EventExecutor[] children;
  • children - EventExecutor数组,大小是nThreads,线程数目。
  • newChild初始化
    实例类是NioEventLoopGroup.java,返回NioEventLoop对象
  • protected EventLoop newChild(Executor executor, Object... args) throws Exception { return new NioEventLoop(this, executor, (SelectorProvider) args[0], ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]); }

    NioEventLoop的继承关系是这样的,继承于SingleThreadEventLoop,别忘了上面我们看到NioEventLoopGroup继承自MultithreadEventLoopGroup.(看名字是单线程和多线程的区别?)

    继续看NioEventLoop的构造函数

    2.2.6 NioEventLoop

    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider, SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) { super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler); provider = selectorProvider; final SelectorTuple selectorTuple = openSelector(); selector = selectorTuple.selector; unwrappedSelector = selectorTuple.unwrappedSelector; selectStrategy = strategy; } private SelectorTuple openSelector() { final Selector unwrappedSelector; try { unwrappedSelector = provider.openSelector(); } catch (IOException e) { throw new ChannelException("failed to open a new selector", e); } if (DISABLE_KEYSET_OPTIMIZATION) { return new SelectorTuple(unwrappedSelector); } ... }

    从上面这段代码我们可以看出这几点

  • NioEventLoop里面保存了SelectorProvider selectorProvider, Selector selector, unwrappedSelector(类型是KQueueSelectorImpl)
  • selector, unwrappedSelector是通过provider.openSelector()打开的.根据2.3段的介绍,provider之前介绍的类型是KQueueSelectorProvider,然后它的openSelector会生成一个KQueueSelectorImpl所以provider.openSelector()得到是KQueueSelectorImpl,KQueueSelectorImpl的继承关系如下:
  • 继续往回看,看MultithreadEventExecutorGroup的构造函数。

    2.2.7 newChooser

    EventExecutorChooserFactory.EventExecutorChooser chooser; protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) { this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args); } chooser = chooserFactory.newChooser(children);

    上面代码我们可以看到:

  • chooserFactory的类型是DefaultEventExecutorChooserFactory,所以newChooser调用的是DefaultEventExecutorChooserFactory.newChooser方法。
    如下:
  • public EventExecutorChooser newChooser(EventExecutor[] executors) { if (isPowerOfTwo(executors.length)) { return new PowerOfTwoEventExecutorChooser(executors); } else { return new GenericEventExecutorChooser(executors); } }
  • 传入的参数是children,也就是NioEventLoop数组
  • DefaultEventExecutorChooserFactory INSTANCE是一个static final类型对象,也就是一种饿汉式的单例模式,如下:
  • public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();

    继续看newChooser的实现

    2.2.8 newChooser

    newChooser的代码就不贴了,上面就有,从上面代码可以看到:

  • isPowerOfTwo是用来判断一个整数是否是2的幂,比如(2,4, 8,16,32等等),它的实现方式如下:
  • private static boolean isPowerOfTwo(int val) { return (val & -val) == val; } private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser { private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; PowerOfTwoEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } @Override public EventExecutor next() { return executors[idx.getAndIncrement() & executors.length - 1]; } } private static final class GenericEventExecutorChooser implements EventExecutorChooser { private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; GenericEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } @Override public EventExecutor next() { return executors[Math.abs(idx.getAndIncrement() % executors.length)]; } }

    这种实现方法感觉比较优雅和高效,首先拿到-val,也就是val的二进制倒转,然后+1。再做&运算。
    大家自己可以拿到数字举个例子,比较巧妙。后续自己写代码可以借鉴,这是读源码的一个好处,可以学习到别人很多优秀的写法。

  • PowerOfTwoEventExecutorChooser和GenericEventExecutorChooser的不同之处在于next方法的算法不一样,作用都是从NioEventLoop数组里面选出一个NioEventLoop对象来。
  • 但是说实话,我没有想到这两种算法有什么区别,如果谁知道,请告诉我,谢谢。

    return executors[idx.getAndIncrement() & executors.length - 1]; return executors[Math.abs(idx.getAndIncrement() % executors.length)];

    继续往回走,MultithreadEventExecutorGroup的构造函数就基本看完了。

    三. 总结

    我们来总结下NioEventLoopGroup的实例化过程,可以得到以下几点。

    1. NioEventLoopGroup的父类MultithreadEventExecutorGroup包含一个NioEventLoop数组children,数组的大小等于nThreads线程数目。如果没有指定,默认一般是cpu核数 x 2

    2. NioEventLoopGroup和NioEventLoop一样都是继承自Executor,但是NioEventLoopGroup又包含多个NioEventLoop(children数组),这种关系有点像android里面ViewGroup和View的关系。或者装饰者模式?

    3. NioEventLoopGroup继承自MultithreadEventLoopGroup,而NioEventLoop继承自SingleThreadEventLoop,从名字看,不知道和多线程,单线程有没有关系。

    4. MultithreadEventLoopGroup有个chooser,执行next方法的时候,会选择下一个NioEventLoop对象,虽然并不知道两个chooser算法有何区别。

    5. NioEventLoopGroup里面重写了newChild方法,里面实例化NioEventLoop。

    6. NioEventLoop里面包含了Selector,类型是KQueueSelectorImpl

    SelectorProvider provider
    SelectStrategy selectStrategy

    SelectStrategy这个我们上面我们没有关注,其实它是NioEventLoopGroup构造函数传进去的,如下:

    public NioEventLoopGroup( int nThreads, Executor executor, final SelectorProvider selectorProvider) { this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE); } public final class DefaultSelectStrategyFactory implements SelectStrategyFactory { public static final SelectStrategyFactory INSTANCE = new DefaultSelectStrategyFactory(); private DefaultSelectStrategyFactory() { } @Override public SelectStrategy newSelectStrategy() { return DefaultSelectStrategy.INSTANCE; } } final class DefaultSelectStrategy implements SelectStrategy { static final SelectStrategy INSTANCE = new DefaultSelectStrategy(); private DefaultSelectStrategy() { } @Override public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception { return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT; } }

    所以SelectStrategy的实现类是DefaultSelectStrategy.

    在理清楚NioEventLoopGroup实例化的过程之后,我们下一篇继续按照源代码分析Netty服务器端的源代码。


    转载于:https://blog.皇冠云.com/483181/2118817

      你可能想看:

      扫描二维码推送至手机访问。

      版权声明:本文由皇冠云发布,如需转载请注明出处。

      本文链接:https://www.idchg.com/info/32134.html

      分享给朋友:

      “netty 代理 HttpPostRequestDecoder” 的相关文章

      全面指南:在Linux上使用dd命令安装Windows系统的教程

      在当今的技术环境中,许多用户都希望能够在Linux系统上安装Windows。这不仅能帮助开发者和测试人员多平台间的快速切换,还能让个人用户享受到两个操作系统的优点。dd命令成为了实现这一目标的一个重要工具,通过它,可以将Windows操作系统的映像文件直接写入到一个虚拟专用服务器上。这篇教程将为你提...

      HostYun:高性价比VPS服务的理想选择

      HostYun,最早被称作主机分享,成立于2008年,专注于提供性价比极高的VPS服务。在众多IDC品牌中,HostYun凭借其低价策略迅速占领了一席之地。作为一个以KVM和XEN虚拟化技术为基础的平台,HostYun不仅满足了用户对低成本服务的需求,也为学习、测试和小型项目的部署提供了理想的选择。...

      RackNerd Windows VPS的硬件条件与性能评测

      在选择虚拟服务器服务商时,硬件条件是我最关注的部分。RackNerd作为一家提供多种配置Windows VPS的服务商,其硬件条件非常吸引。接下来,我将详细介绍RackNerd在硬件配置方面的一些关键特点。 处理器配置 RackNerd使用的AMD Ryzen 3900X处理器,让人印象深刻。这个处...

      甲骨文云免费IPv6服务详解:轻松配置与应用技巧

      甲骨文云,作为一个综合性的云服务提供商,正迅速崛起于众多的云技术平台之中。它不仅拥有强大的数据处理能力,还提供了多种免费的云服务选项,让个人和企业都能以更低的成本探索并使用云计算的强大功能。首先,我对甲骨文云的快速适应能力和多种灵活服务感到印象深刻,尤其是它的免费套餐项目,吸引了不少用户前来试用。...

      Debian 修改 DNS 的详细步骤与常见问题解决方案

      在讨论 Debian 中的 DNS 修改前,我想先和大家分享一些关于 DNS 的基本信息。DNS(Domain Name System)是互联网的“电话簿”,它将我们可读的网站地址(如 www.example.com)转换为计算机能够理解的 IP 地址。这一过程对于我们浏览网页、发送邮件等操作至关重...

      甲骨文云无法选择ARM架构的原因及解决方案

      在如今这个信息化的时代,云计算技术已经成为了各类企业和个人用户的重要工具。而甲骨文云服务器正是众多云服务中的一员,凭借其强大的计算能力和灵活的可扩展性,吸引了越来越多的用户。甲骨文云服务器为用户提供了一种高效、灵活的解决方案,让他们能够在不同的业务需求下,快速部署和管理自己的应用程序。 当谈到甲骨文...