NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制

Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)

SelectorProvider 定义了创建 Selector、ServerSocketChannel、SocketChannel 等方法,采用 JDK 的 Service Provider Interface (SPI) 方式实现。

  1. public static ServerSocketChannel open() throws IOException {
  2. return SelectorProvider.provider().openServerSocketChannel();
  3. }

一、SelectorProvider SPI

SelectorProvider 是一个抽象类,需要子类实现。主要方法如下:

  1. public abstract DatagramChannel openDatagramChannel() throws IOException;
  2. public abstract DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException;
  3. public abstract ServerSocketChannel openServerSocketChannel() throws IOException;
  4. public abstract SocketChannel openSocketChannel() throws IOException;
  5. public abstract AbstractSelector openSelector() throws IOException;
  6. public abstract Pipe openPipe() throws IOException;

总结: SelectorProvider 相当于一个工厂类,提供了对 DatagramChannel、ServerSocketChannel、SocketChannel、Selector 了创建方法。

java.nio.channels.spi 中提供了一系列的抽象类,由具体的厂商实现,当然我们一般使用的都是 JDK 自己的实现。相关的 SPI 接口如下:

  1. AbstractInterruptibleChannel -> SocketChannelImpl/ServerSocketChannelImpl
  2. AbstractSelectableChannel
  3. AbstractSelectionKey -> SelectionKeyImpl
  4. AbstractSelector -> WindowsSelectorImpl/PollSelectorImpl/EpollSelectorImpl
  5. SelectorProvider -> DefaultSelectorProvider

二、SelectorProvider 加载过程

2.1 SelectorProvider 加载

  1. private static SelectorProvider provider = null;
  2. public static SelectorProvider provider() {
  3. synchronized (lock) {
  4. if (provider != null)
  5. return provider;
  6. return AccessController.doPrivileged(
  7. new PrivilegedAction<SelectorProvider>() {
  8. public SelectorProvider run() {
  9. // 1. java.nio.channels.spi.SelectorProvider 属性指定实现类
  10. if (loadProviderFromProperty())
  11. return provider;
  12. // 2. SPI 指定实现类
  13. if (loadProviderAsService())
  14. return provider;
  15. // 3. 默认实现,Windows 和 Linux 下不同
  16. provider = sun.nio.ch.DefaultSelectorProvider.create();
  17. return provider;
  18. }
  19. });
  20. }
  21. }

总结: SelectorProvider 提供了三种方式来自定义 SelectorProvider 的实现类。

  1. java.nio.channels.spi.SelectorProvider 属性指定实现类
  2. 采用 SPI 方法创建 SelectorProvider
  3. 默认实现 DefaultSelectorProvider,Windows 和 Linux 下具体的实现不同。

  1. public abstract class SelectorProviderImpl extends SelectorProvider {
  2. public DatagramChannel openDatagramChannel() throws IOException {
  3. return new DatagramChannelImpl(this);
  4. }
  5. public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException {
  6. return new DatagramChannelImpl(this, family);
  7. }
  8. public Pipe openPipe() throws IOException {
  9. return new PipeImpl(this);
  10. }
  11. public abstract AbstractSelector openSelector() throws IOException;
  12. public ServerSocketChannel openServerSocketChannel() throws IOException {
  13. return new ServerSocketChannelImpl(this);
  14. }
  15. public SocketChannel openSocketChannel() throws IOException {
  16. return new SocketChannelImpl(this);
  17. }
  18. }

总结: SelectorProviderImpl 提供了 ServerSocketChannel、SocketChanne 的创建,至于 Selector 在不同的平台下有不同的实现。

2.2 Windows 下 DefaultSelectorProvider

  1. public class DefaultSelectorProvider {
  2. public static SelectorProvider create() {
  3. return new sun.nio.ch.WindowsSelectorProvider();
  4. }
  5. }
  6. public class WindowsSelectorProvider extends SelectorProviderImpl {
  7. public AbstractSelector openSelector() throws IOException {
  8. return new WindowsSelectorImpl(this);
  9. }
  10. }

2.3 Unix 下 DefaultSelectorProvider

  1. public class DefaultSelectorProvider {
  2. public static SelectorProvider create() {
  3. String osname = AccessController
  4. .doPrivileged(new GetPropertyAction("os.name"));
  5. if (osname.equals("SunOS"))
  6. return createProvider("sun.nio.ch.DevPollSelectorProvider");
  7. if (osname.equals("Linux"))
  8. return createProvider("sun.nio.ch.EPollSelectorProvider");
  9. return new sun.nio.ch.PollSelectorProvider();
  10. }
  11. }

总结: Unix 平台下需要根据不同的操作系统选择不同的 Selector,例如 Linux 下是 EPollSelectorProvider。

  1. public class EPollSelectorProvider extends SelectorProviderImpl {
  2. public AbstractSelector openSelector() throws IOException {
  3. return new EPollSelectorImpl(this);
  4. }
  5. public Channel inheritedChannel() throws IOException {
  6. return InheritedChannel.getChannel();
  7. }
  8. }

总结: 无论是 WindowsSelectorProvider 还是 EPollSelectorImpl,它们都继承 SelectorProviderImpl,关于 ServerSocketChannel、SocketChanne 的创建都是一样的,区别是 Selector 有兼容性问题。难道 Socket 在 Windows 和 Linux 下就没有区别吗,肯定也是有兼容性问题的。

  1. ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
  2. super(sp);
  3. this.fd = Net.serverSocket(true); // 创建 socket,这个 Net 工具本身是跨平台的
  4. this.fdVal = IOUtil.fdVal(fd);
  5. this.state = ST_INUSE;
  6. }

Socket 的创建是在 sun.nio.ch.Net 工具类的 socket0 完成的,这个类很多方法都是 native 方法,在不同的平台有不同的实现。


每天用心记录一点点。内容也许不重要,但习惯很重要!

NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制的更多相关文章

  1. NIO 源码分析(05) Channel 源码分析

    目录 一.Channel 类图 二.begin 和 close 是什么 2.1 AbstractInterruptibleChannel 中的 begin 和 close 2.2 Selector 中 ...

  2. NIO 源码分析(02-1) BIO 源码分析

    目录 一.BIO 最简使用姿势 二.ServerSocket 源码分析 2.1 相关类图 2.2 主要属性 2.3 构造函数 2.4 bind 方法 2.5 accept 方法 2.6 总结 NIO ...

  3. NIO 源码分析(03) 从 BIO 到 NIO

    目录 一.NIO 三大组件 Channels.Buffers.Selectors 1.1 Channel 和 Buffer 1.2 Selector 1.3 Linux IO 和 NIO 编程的区别 ...

  4. NIO 源码分析(02-2) BIO 源码分析 Socket

    目录 一.BIO 最简使用姿势 二.connect 方法 2.1 Socket.connect 方法 2.2 AbstractPlainSocketImpl.connect 方法 2.3 DualSt ...

  5. NIO 源码分析(01) NIO 最简用法

    目录 一.服务端 二.客户端 NIO 源码分析(01) NIO 最简用法 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) J ...

  6. 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 百篇博客分析OpenHarmony源码 | v41.03

    百篇博客系列篇.本篇为: v41.xx 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...

  7. jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)

    在第一篇jQuery源码分析中,简单分析了jQuery对象的构造过程,里面提到了jQuery.fn.jQuery.prototype.jQuery.fn.init.prototype的关系. 从代码中 ...

  8. NIO源码分析:SelectionKey

    SelectionKey SelectionKey,选择键,在每次通道注册到选择器上时都会创建一个SelectionKey储存在该选择器上,该SelectionKey保存了注册的通道.注册的选择器.通 ...

  9. 8. SOFAJRaft源码分析— 如何实现日志复制的pipeline机制?

    前言 前几天和腾讯的大佬一起吃饭聊天,说起我对SOFAJRaft的理解,我自然以为我是很懂了的,但是大佬问起了我那SOFAJRaft集群之间的日志是怎么复制的? 我当时哑口无言,说不出是怎么实现的,所 ...

随机推荐

  1. Chosen 的 optgroup 第一级单击的时候选择二级的全部

    相关环境 及 版本 Chosen (v1.6.2) https://harvesthq.github.io/chosen/ jQuery (v1.8.3) 官网 http://jquery.com/ ...

  2. datetime timestamp使用

    #coding=utf-8 import time import datetime def yes_time(): #获取当前时间 now_time = datetime.datetime.now() ...

  3. angular5引入sass

    angular/cli支持使用sass新建工程:如果是新建一个angular工程采用sass:ng new My_New_Project --style=sass这样所有样式的地方都将采用sass样式 ...

  4. 迭代器,生成器,yield,yield from理解

    迭代器 说到迭代器就得想说可迭代对象Iterable,实现了__iter__()方法的对象都是可迭代对象,例如很多容器,list ,set, tuples.使用iter方法可以把一个可迭代对象变成迭代 ...

  5. [USACO14MAR]浇地Watering the Fields

    题目描述 Due to a lack of rain, Farmer John wants to build an irrigation system tosend water between his ...

  6. KiCAD差分布线

    KiCAD差分布线方法 KiCAD在进行差分布线的时候,会自动按照网路名称生成差分对,所以差分对的名称必须是以_P_N或+/-结束,这样才能找到一对差分对,比如说CAN网络,可以定义为CAN_P/CA ...

  7. oracle入门学习之oracle数据库结构

    1. oracle数据库结构 1.1 Oracle可以看做是一个大的数据库???,里面可以创建很多实例; 一个实例对应多个表空间.多个用户; 一个用户只能有一个表空间; 一个表空间可以有多个用户; 一 ...

  8. 转帖 eclipse Web项目WebContent目录修改

    最近在做Web 项目时,新建了一个WEB 项目,如webdemo,eclipse默认的build路径为build,WEB-INF存放于WebContent下面,今改了一个build路径和WebCont ...

  9. express 的路由学习

    使用步骤 - :获取路由中间件对象 `let router = express.Router();` - :配置路由规则 `router.请求方式(URL,fn事)` - fn中参数有req,res, ...

  10. nodejs 模板引擎ejs的简单使用

    ejs1.js /** * Created by ZXW on 2017/11/9. */ var ejs=require('ejs'); ejs.renderFile("},functio ...