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

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

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

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

一、SelectorProvider SPI

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

public abstract DatagramChannel openDatagramChannel() throws IOException;
public abstract DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException;
public abstract ServerSocketChannel openServerSocketChannel() throws IOException;
public abstract SocketChannel openSocketChannel() throws IOException; public abstract AbstractSelector openSelector() throws IOException;
public abstract Pipe openPipe() throws IOException;

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

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

AbstractInterruptibleChannel    -> SocketChannelImpl/ServerSocketChannelImpl
AbstractSelectableChannel
AbstractSelectionKey -> SelectionKeyImpl
AbstractSelector -> WindowsSelectorImpl/PollSelectorImpl/EpollSelectorImpl
SelectorProvider -> DefaultSelectorProvider

二、SelectorProvider 加载过程

2.1 SelectorProvider 加载

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() {
// 1. java.nio.channels.spi.SelectorProvider 属性指定实现类
if (loadProviderFromProperty())
return provider;
// 2. SPI 指定实现类
if (loadProviderAsService())
return provider;
// 3. 默认实现,Windows 和 Linux 下不同
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}

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

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

public abstract class SelectorProviderImpl extends SelectorProvider {
public DatagramChannel openDatagramChannel() throws IOException {
return new DatagramChannelImpl(this);
}
public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException {
return new DatagramChannelImpl(this, family);
} public Pipe openPipe() throws IOException {
return new PipeImpl(this);
} public abstract AbstractSelector openSelector() throws IOException; public ServerSocketChannel openServerSocketChannel() throws IOException {
return new ServerSocketChannelImpl(this);
}
public SocketChannel openSocketChannel() throws IOException {
return new SocketChannelImpl(this);
}
}

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

2.2 Windows 下 DefaultSelectorProvider

public class DefaultSelectorProvider {
public static SelectorProvider create() {
return new sun.nio.ch.WindowsSelectorProvider();
}
} public class WindowsSelectorProvider extends SelectorProviderImpl {
public AbstractSelector openSelector() throws IOException {
return new WindowsSelectorImpl(this);
}
}

2.3 Unix 下 DefaultSelectorProvider

public class DefaultSelectorProvider {
public static SelectorProvider create() {
String osname = AccessController
.doPrivileged(new GetPropertyAction("os.name"));
if (osname.equals("SunOS"))
return createProvider("sun.nio.ch.DevPollSelectorProvider");
if (osname.equals("Linux"))
return createProvider("sun.nio.ch.EPollSelectorProvider");
return new sun.nio.ch.PollSelectorProvider();
}
}

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

public class EPollSelectorProvider extends SelectorProviderImpl {
public AbstractSelector openSelector() throws IOException {
return new EPollSelectorImpl(this);
} public Channel inheritedChannel() throws IOException {
return InheritedChannel.getChannel();
}
}

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

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

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. Config JAVA evironment for LoadRunner

    1. Install jdk 2. Set system variables eg. JAVA_HOME = C:\Program Files (x86)\Java\jdk1.6.0_43 class ...

  2. java 重新学习 (一)

    一 垃圾回收特点 1.垃圾回收的工作目标是回收物对象的内存空间,这些内存空间都是jvm堆内存里的空间,垃圾回收器值回收内存资源,对他的物理资源:数据库连接,磁盘I/O等资源则无能为力 2.更快进行垃圾 ...

  3. jmeter beanshell 变量传递

    如果写成这样会报错: ${__BeanShell(${__threadNum}*2,ToatlAmount)}; ${__BeanShell(${__Random(1,99999,)},DayNum) ...

  4. Spring Boot国际化支持

    本章将讲解如何在Spring Boot和Thymeleaf中做页面模板国际化的支持,根据系统语言环境或者session中的语言来自动读取不同环境中的文字. 国际化自动配置 Spring Boot中已经 ...

  5. SqlServer 跨库访问

    同实例跨库 只需要 库名.dbo.表 dbo可省略 如: use Test select * from rdrecords select * from oa.dbo.UserInfo 不同实例与不同i ...

  6. zabbix监控linux内存

    通过free -m查看当前内存 可用内存:Available memory=free+buffers+cached,即31068=759+66+30243 已用内存:Used memory=used- ...

  7. maven工程的下载及其环境配置

    Maven是一个项目管理工具,它给我们提供了好多有用的组件和工具. Maven下载: Maven下载载地址:http://maven.apache.org/download.cgi (1)进入下载界面 ...

  8. Shell 变量的分类

  9. Linux的一些命令总结

    启动终端: ctr+alt+t 终端字体放大: ctr+shift+'+',终端字体缩小: ctr+'-' ls: 查看当前目录下的文件信息 pwd: 查看目录所在的路径 touch: 创建文件 mk ...

  10. vue css动画原理

    从隐藏到显现 从显现到隐藏 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...