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. JAVA学习之面向对象

    面向对象是相对面向过程而言面向过程:强调的是功能行为面向对象:将功能封装进对象,强调具备了功能的对象 不论面向对象还是面向过程都是一种开发思想而已.举一个例子来理解面向对象和面向过程把大象装进冰箱分三 ...

  2. 数据分析系列篇:玩转excel

    数据分析系列篇:玩转excel 不知道现在怎么也变得这么鸡婆,连excel都要准备写一篇.没办法,还有很多不是做数据的小伙伴们不会excel啊,抱着不抛弃.不放弃的态度,就讲下excel如何玩转.其实 ...

  3. C++——智能指针

    动态内存管理:new-delete——很难正确释放内存——智能指针 使用场景: 1.程序不知道自己要使用多少对象 2.程序不知道自己所需对象的准确类型 3.程序需要在多个对象之间共享数据 4.坚持只使 ...

  4. promise基础用法

    /** * Created by liyinghao on 2016/11/6. */ const fs = require('fs'); /* * 新建一个Promise对象,Promise就是一个 ...

  5. LINUX 安装PHP GD库遇到的坑

    本文借鉴:https://www.cnblogs.com/gaohj/p/3152646.html linux下为php添加GD库的步骤如下: 一.下载 gd-.tar.gz http://www.b ...

  6. PHP定界符<<<的使用方法

    在web编程过程中难免会遇到用echo来输出大段的html和javascript脚本的情况,如果用传统的输出方法——按字符串输出的话,使用PHP肯定要有大量的转义符来对字符串中的引号''/" ...

  7. python2和python3中int整型数据的不同之处

    python2中的除法,结果为整型数字(int型),例如 10/5=2,10/3=3,小数位向下取整 python3中的除法,结果为浮点型数字(float型)结果小数位最多保留16位小数

  8. expect的模式

    expect的模式其实就是对话模式: expect    # 期望什么 send      # 我给你什么 比如: expect "password: "    # 碰到什么了 s ...

  9. 使用Hbuilder 报错The keyword 'export' is reserved

    右击文件 > 验证本文档语法(V)后报错 解决: 项目右键->”属性”->”语法&框架”界面中配置项目的javaScript版本,将ECMAScript5.1 修改为6.

  10. syslog-ng收集日志+ELK平台搭建教程

    syslog-ng部署: 用于接收交换机输出的日志. syslog-ng安装很简单,可以顺便搜一下,文章有很多.我是yum直接安装的. syslog-ng配置: vi /etc/syslog-ng/s ...