前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >netty补充NIO的SelectableChannel和SelectorProvider

netty补充NIO的SelectableChannel和SelectorProvider

作者头像
用户1418372
发布2019-02-25 11:25:37
8350
发布2019-02-25 11:25:37
举报

SelectableChannel作为nio选择器和通道的关键

先看官方描述

/**
 * A channel that can be multiplexed via a {@link Selector}.
 * <p> In order to be used with a selector, an instance of this class must
 * first be <i>registered</i> via the {@link #register(Selector,int,Object)
 * register} method.  This method returns a new {@link SelectionKey} object
 * that represents the channel's registration with the selector.
 *其他略。。。
 */

简单总结:

它通道的注册使用大致过程如下:

  1. 新建通道 open方法 SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
  2. 将通道已经通道感兴趣的事件注册到选择器Selector上
  3. 通过SelectKey获得需要处理的通道,然后对通道进行处理 关闭一个已经注册的SelectableChannel需要两个步骤: *上面channel的父类就是SelectableChannel了 它通道的取消大致过程如下:
  4. 取消注册的key,这个可以通过SelectionKey.cancel方法,也可以通过SelectableChannel.close方法,或者中断阻塞在该channel上的IO操作的线程来做到。
  5. 后续的Selector.selectXXX方法的调用才真正地关闭 本地Socket。 因而,如果,如果在取消SelectionKey后没有调用到selector的select方法(因为Client一般在取消key后, 我们都会终止调用select的循环,当然,server关闭一个注册的channel我们是不会终止select循环的),那么本地socket将进入CLOSE-WAIT 状态(等待本地Socket关闭)。简单的解决办法是在 SelectableChannel.close方法之后调用Selector.selectNow方法,类似: Selector sel; SocketChannel sch; // … sch.close(); sel.selectNow(); 实现类 有关UDP协议的:DatagramChannel 有关SCTP协议的:SctpChannel、SctpMultiChannel、SctpServerChannel [有关TCP协议的:ServerSocketChannel、SocketChannel 有关管道的:SinkChannel、SourceChannel这两个抽象类定义在java.nio.channels.Pipe类中 仅以SocketChannel和ServerSocketChannel分析提供如下类图: SocketChannel和ServerSocketChannel,两者的父类是SelectableChannel,类图结构如下: ServerSocketChannel的类图

图片.png SocketChannel的类图

图片.png


SelectorProvider作为选择器的核心部分

  • 顾名思义作为选择器的生产类,那么他是如何做到呢
  1. 通常我们使用Selector 创建一个新的选择器

Selector selector = Selector.open(); 跟踪selector的源码如下:

    public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }

继续往下跟踪就是我们要介绍的关键了SelectorProvider.provider()

 public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
//AccessController.doPrivileged属于特权操作,下面详说
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
 //loadProviderFromProperty方法是通过JDK的参数//-Djava.nio.channels.spi.SelectorProvider=class设置的class来反射构造SelectorProvider
                            if (loadProviderFromProperty())
                                return provider;
//loadProviderAsService从jar中的目录META-INF/services配置文件中找参数//java.nio.channels.spi.SelectorProvider=class设置的第一个class来反射构造SelectorProvider
                            if (loadProviderAsService())
                                return provider;
//最后都没有则调用不同操作系统版本的JDK里自带的sun.nio.ch.DefaultSelectorProvider来创建SelectorProvider
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

先解释下AccessController访问控制类的三个功能

1、根据当前有效的安全策略,决定是允许还是拒绝对关键系统资源的访问,
2、将代码标记为“特权”,从而影响后续访问确定
3、获取当前调用上下文的“快照”,因此可以针对保存的上下文进行来自不同上下文的访问控制决策。

上面的特权操作主要使用了功能2,部分javadoc中伪代码如下:

//类注释
/**
 * somemethod() {
 *     ...normal code here...
 *     String user = AccessController.doPrivileged(
 *         new PrivilegedAction<String>() {
 *         public String run() {
 *             return System.getProperty("user.name");
 *             }
 *         });
 *     ...normal code here...
 * }}
* 其他略。。
**/
public final class AccessController {
//方法注释
 /**
     * Performs the specified {@code PrivilegedAction} with privileges
     * enabled. The action is performed with <i>all</i> of the permissions
     * possessed by the caller's protection domain.
     * 其他略。。
     */
//简言之就是让调用者有权执行方法代码中的action
    @CallerSensitive
    public static native <T> T doPrivileged(PrivilegedAction<T> action);
//其他略。。
}

总结SelectorProvider的创建分三步进行: (1)由JDK的参数-Djava.nio.channels.spi.SelectorProvider=class设置的class来反射构造SelectorProvider,找不到就跳转到步骤(2) (2)从jar中的目录META-INF/services配置文件中找参数java.nio.channels.spi.SelectorProvider=class设置的第一个class来反射构造SelectorProvider,找不到就跳转到步骤(3) (3)调用不同操作系统版本的JDK里自带的sun.nio.ch.DefaultSelectorProvider来创建SelectorProvider 一般都会走到最后的步骤(3),而这个步骤里创建的SelectorProvider在各个操作系统对应的JDK里各不相同。sun.nio.ch.DefaultSelectorProvider这个类最终编译后放置在JDK的安装根目录下的jre/lib/rt.jar里。

  • 该方法返回系统范围默认选择器提供程序,那么系统默认的选择器和系统有关吗,答案是肯定的

不通系统的DefaultSelectorProvider实现

  • Windows
public class DefaultSelectorProvider {
//
    public static SelectorProvider create() {
        return new WindowsSelectorProvider();
    }
}
  • MAC
public class DefaultSelectorProvider {
 public static SelectorProvider create()
  {
    return new KQueueSelectorProvider();
  }
}
  • Linux
public class DefaultSelectorProvider {
public static SelectorProvider create()
  {
    String str1 = (String)AccessController.doPrivileged(new GetPropertyAction("os.name"));
 
    if ("SunOS".equals(str1)) {
      return new DevPollSelectorProvider();
    }
 
    if ("Linux".equals(str1)) {
      String str2 = (String)AccessController.doPrivileged(new GetPropertyAction("os.version"));
      String[] arrayOfString = str2.split("\\.", 0);
      if (arrayOfString.length >= 2) {
        try {
          int i = Integer.parseInt(arrayOfString[0]);
          int j = Integer.parseInt(arrayOfString[1]);
          if ((i > 2) || ((i == 2) && (j >= 6))) {
            return new EPollSelectorProvider();
          }
        }
        catch (NumberFormatException localNumberFormatException)
        {
        }
      }
    }
    return new PollSelectorProvider();
  }
}

那么为什么会出现几种不同的实现方式呢?原因在于 不同操作系统的I/O多路复用选择器各自内核实现不同,目前有select、poll、epoll、kqueue四种实现

操作系统下JDK

JDK里SelectorProvider实现

windows JDK

sun.nio.cn.WindowsSelectorProvider

MAC JDK

sun.nio.ch.KQueueSelectorProvider

Linux JDK

1)os.name=SunOS时,sun.nio.ch.DevPollSelectorProvider2)os.name=Linux并且os.version版本号中第一个版本大于2或者第一个版本号等于2且第二个版本大于6时,sun.nio.chEPollSelectorProviderLinux kernels 2.6内核版本及以后使用epoll进行支持;Linux kernels 2.6内核版本之前使用poll进行支持;比如Linux version 3.10.0-327.el7.x86_64就是用的

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.01.11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SelectableChannel作为nio选择器和通道的关键
  • SelectorProvider作为选择器的核心部分
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档