- 浏览: 1582998 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
jsrgzhangzhiyong:
关于null值的转换还是感觉不太友好,就像 mapstruct ...
我也造了个轮子:BeanMapping(属性拷贝) -
he037:
a417930422 写道引用使用EPHEMERAL会引出一个 ...
基于zookeeper的分布式lock实现 -
seancheer:
qianshangding 写道首先节点启动后,尝试读取本地的 ...
zookeeper学习记录三(session,watcher,persit机制) -
雪夜归人:
您好,我想咨询一下,开源的canal都能支持mysql的哪些版 ...
Canal BinlogChange(mysql5.6) -
zhoudengyun:
copy 一份做记录,后续学习,请知悉
阿里巴巴开源项目: 基于mysql数据库binlog的增量订阅&消费
背景
前一段时间一直在关注一些nio的相关技术,也和公司的架构师交流了一下,学到了一些相关原理,比如nio的优势和劣势。以及一些排查nio bug问题的方式,受益量多。为自己做一下技术储备,以后可以多玩玩nio的相关技术
知识点
unix网络编程第6章: 几种unix下的I/O模型。
- 阻塞I/O模型 (java io)
- 非阻塞I/O模型
- I/O复用 (java 5的nio)
- 信号驱动I/O
- 异步I/O (java7的aio)
#include<sys/select.h> int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds,fd_set *restrict exceptfds, struct timeval* restrict tvptr);
#include <sys/select.h> int pselect(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds,fd_set *restrict exceptfds, const struct timespec *restrict tsptr,const sigset_t *restrict sigmask);
#include <poll.h> int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
struct pollfd { int fd; /* file descriptor to check, or <0 to ignore */ short events; /* events of interest on fd */ short revents; /* events that occurred on fd */ };
2. nfds代表pollfd的长度
返回值:0超时,-1出错, 正数代表准备好的描述符
同样变种的有ppoll函数,具体可以见man ppoll,两者的区别和select/pselect区别一样,多了时间精度的支持+信号屏蔽字
和select系列的区别点,poll不再受限于select中位数组的长度限制,我们可以将关心的描述符添加到poolfd中。
再看epoll函数,是对select/poll的一个增强版:
1. 因为它不会复用文件描述符集合来传递结果而迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合
2. 另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
epoll的除了提供select/poll 那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
电平触发(Level Triggered): select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息
边沿触发(Edge Triggered: 只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发。
几个接口:
int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 1. 第一个参数是epoll_create()的返回值. 2. 第二个参数表示动作,用三个宏来表示: EPOLL_CTL_ADD:注册新的fd到epfd中; EPOLL_CTL_MOD:修改已经注册的fd的监听事件; EPOLL_CTL_DEL:从epfd中删除一个fd; 3. 第三个参数是需要监听的fd 4. 第四个参数是告诉内核需要监听什么事 events可以是以下几个宏的集合: EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭); EPOLLOUT:表示对应的文件描述符可以写; EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来); EPOLLERR:表示对应的文件描述符发生错误; EPOLLHUP:表示对应的文件描述符被挂断; EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 1. 等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size, 2. 参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有 说法说是永久阻塞)。 该函数返回需要处理的事件数目,如返回0表示已超时。
再看一下jdk中对nio的使用:
public static SelectorProvider create() { PrivilegedAction pa = new GetPropertyAction("os.name"); String osname = (String) AccessController.doPrivileged(pa); if ("SunOS".equals(osname)) { return new sun.nio.ch.DevPollSelectorProvider(); } // use EPollSelectorProvider for Linux kernels >= 2.6 if ("Linux".equals(osname)) { pa = new GetPropertyAction("os.version"); String osversion = (String) AccessController.doPrivileged(pa); String[] vers = osversion.split("\\.", 0); if (vers.length >= 2) { try { int major = Integer.parseInt(vers[0]); int minor = Integer.parseInt(vers[1]); if (major > 2 || (major == 2 && minor >= 6)) { return new sun.nio.ch.EPollSelectorProvider(); } } catch (NumberFormatException x) { // format not recognized } } } return new sun.nio.ch.PollSelectorProvider(); }
比较明显: 如果当前是sunos系统,直接使用DevPoll,在linux 2.6内核下,使用Epoll模型,否则使用Poll。
DevPoll估计是sunos自己整的一套poll模型,公司一般使用redhat系列,内核2.6.18,64位主机。所以就介绍下Epoll的实现
java实现类: EPollSelectorImpl
// wake up使用的两描述符 protected int fd0; protected int fd1; // The poll object , native的实现 EPollArrayWrapper pollWrapper; // Maps from file descriptors to keys , 文件描述符和SelectKey的关系 private HashMap fdToKey;
看下select()的实现:
PollSelectorImpl类: ============== protected int doSelect(long timeout) //具体执行epoll调用 throws IOException { if (closed) throw new ClosedSelectorException(); processDeregisterQueue(); try { begin(); pollWrapper.poll(timeout); } finally { end(); } processDeregisterQueue(); int numKeysUpdated = updateSelectedKeys(); if (pollWrapper.interrupted()) { // Clear the wakeup pipe pollWrapper.putEventOps(pollWrapper.interruptedIndex(), 0); synchronized (interruptLock) { pollWrapper.clearInterrupted(); IOUtil.drain(fd0); interruptTriggered = false; } } return numKeysUpdated; } 继续看下EPollArrayWrappe : ========================== int poll(long timeout) throws IOException { updateRegistrations(); updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd); for (int i=0; i<updated; i++) { // 这里判断下当前响应的描述符是否为fd0,后面再细说 if (getDescriptor(i) == incomingInterruptFD) { interruptedIndex = i; interrupted = true; break; } } return updated; } 继续看下EPollArrayWrapper 的native实现: epollWait(): ====================JNIEXPORT jint JNICALL Java_sun_nio_ch_EPollArrayWrapper_epollWait(JNIEnv *env, jobject this, jlong address, jint numfds, jlong timeout, jint epfd) { struct epoll_event *events = jlong_to_ptr(address); int res; if (timeout <= 0) { /* Indefinite or no wait */ RESTARTABLE((*epoll_wait_func)(epfd, events, numfds, timeout), res); } else { /* Bounded wait; bounded restarts */ res = iepoll(epfd, events, numfds, timeout); } if (res < 0) { JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed"); } return res; } static int iepoll(int epfd, struct epoll_event *events, int numfds, jlong timeout) { jlong start, now; int remaining = timeout; struct timeval t; int diff; gettimeofday(&t, NULL); start = t.tv_sec * 1000 + t.tv_usec / 1000; //转化为ns单位 for (;;) { int res = (*epoll_wait_func)(epfd, events, numfds, timeout); if (res < 0 && errno == EINTR) { //处理异常 if (remaining >= 0) { gettimeofday(&t, NULL); now = t.tv_sec * 1000 + t.tv_usec / 1000; diff = now - start; remaining -= diff; if (diff < 0 || remaining <= 0) { return 0; } start = now; } } else { return res; } } }
看下wakeup的实现 :
EPollSelectorImpl类: EPollSelectorImpl(SelectorProvider sp) { super(sp); int[] fdes = new int[2]; IOUtil.initPipe(fdes, false); fd0 = fdes[0]; fd1 = fdes[1]; pollWrapper = new EPollArrayWrapper(); pollWrapper.initInterrupt(fd0, fd1); // 设置中断的两个描述符 fdToKey = new HashMap(); } public Selector wakeup() { synchronized (interruptLock) { if (!interruptTriggered) { pollWrapper.interrupt(); //调用warpper进行中断 interruptTriggered = true; } } return this; } 继续看下EPollArrayWrapper : void initInterrupt(int fd0, int fd1) { outgoingInterruptFD = fd1; //保存pipeline的描述符 incomingInterruptFD = fd0; epollCtl(epfd, EPOLL_CTL_ADD, fd0, EPOLLIN); //注册到epoll上。 } public void interrupt() { interrupt(outgoingInterruptFD); //调用native方法 } 继续看下EPollArrayWrapper.c native实现: JNIEXPORT void JNICALL Java_sun_nio_ch_EPollArrayWrapper_interrupt(JNIEnv *env, jobject this, jint fd) { int fakebuf[1]; fakebuf[0] = 1; if (write(fd, fakebuf, 1) < 0) { //发送一字节的内容,让epoll_wait()能得到及时响应 JNU_ThrowIOExceptionWithLastError(env,"write to interrupt fd failed"); } }
实现方式也是挺简单的,弄了两个fd,一个往另一个写1byte的内容,促使epoll_wait能得到响应。
异步I/O模型:
暂时还未真实用过,只是大致看过其api,有兴趣可以自己baidu。
最后
后续会起一篇,单独针对nio在服务端和客户端使用上的注意点,主要是吸收了一些大牛门的经验,需要做个总结,消化一下。
评论
最近忙着做项目,都没太多时间写blog。
等项目走上正轨后,一定补上
发表评论
-
yugong QuickStart
2016-03-05 01:52 0几点说明 a. 数据迁移的方案可参见设计文档,oracl ... -
阿里巴巴开源项目: 阿里巴巴去Oracle数据迁移同步工具
2016-03-05 18:29 6334背景 08年左右,阿里巴巴开始尝试MySQL的相关 ... -
愚公performance
2016-03-02 17:29 0性能测试 全量测试 场景1 (单主键, ... -
yugong AdminGuide
2016-03-02 16:40 0环境要求 操作系统 数据库 迁移方案 部署 ... -
Tddl_hint
2014-01-27 13:52 0背景 工作原理 Hint格式 direct模 ... -
tddl5分库规则
2014-01-26 14:41 0背景 工作原理 构建语法树 元数据 基于 ... -
tddl5优化器
2014-01-22 15:12 0背景 工作原理 构建语法树 元数据 抽象语 ... -
Canal BinlogChange(mariadb5/10)
2014-01-20 17:25 4439背景 先前开源了一个 ... -
asynload quickstart
2013-10-08 22:49 0几点说明: 1. asyncload是做为一个j ... -
网友文档贡献
2013-09-18 15:50 01. Otter源代码解析系列 链接:http://e ... -
Manager配置介绍
2013-09-16 13:00 0通道配置说明 多种同步方式配置 a. 单向同步 ... -
canal&otter FAQ
2013-09-05 17:30 0常见问题 1. canal和 ... -
阿里巴巴开源项目:分布式数据库同步系统otter(解决中美异地机房)
2013-08-22 16:48 40199项目背景 阿里巴巴B2B公司,因为业务的特性 ... -
Otter AdminGuide
2013-08-19 11:06 0几点说明 otter系统自带了manager,所以简化了一 ... -
Otter高可用性
2013-08-17 23:41 0基本需求 网络不可靠,异地机房尤为明显. man ... -
Otter数据一致性
2013-08-17 23:39 0技术选型分析 需要处理一致性的业务场景: 多地修改 ( ... -
Otter扩展性
2013-08-17 22:20 0扩展性定义 按照实现不同,可分为两类: 数据处理自定 ... -
Otter双向回环控制
2013-08-17 21:37 0基本需求 支持mysql/oracle的异构数据库的双 ... -
Otter调度模型
2013-08-17 20:13 0背景 在介绍调度模型之前,首先了解一下otter系统要解 ... -
Otter Manager介绍
2013-08-16 11:16 0背景 otter4.0发布至 ...
相关推荐
ABB[a]-J-4ABB 机器人的 IO 通信 4.1 任务目标... 4.3 知识储备 4.3.1ABB 机器人 I/O 通信种类 机器人提供了丰富的 I/O 通信接口,可以轻松地实现与周边设备进行通信。 ABB 机器人 "PC "现场总线 "ABB 标准 " " "Device
ABB[a]-J-4ABB 机器人的 IO 通信 4.1 任务目标... 4.3 知识储备 4.3.1ABB 机器人 I/O 通信种类 机器人提供了丰富的 I/O 通信接口,可以轻松地实现与周边设备进行通信。 ABB 机器人 "PC "现场总线 "ABB 标准 " " "Device
【文档】机器人教师知识储备.pdf
软件开发知识储备 书籍 持续更新。 内包含数据库优化 设计模式 高并发等书籍
游戏设计者所需的知识储备清单. 一 游戏的定义思考 要揭开“游戏性”的虚伪面纱,我们首先有必要弄清楚游戏的定义到底是什么。电子(本文中,电视游戏和电脑游戏统称为电子游戏,以下不再赘述)游戏从“空间大战”...
知识储备库.docx
算法相关知识储备 LeetCode with Python
web.xml应用,websphere的应用部署与安装实例,网站设计与开发流程管理,Android开发环境搭建演示等参考文档
盘盖类零件知识储备.doc
主要讲解实施需要的IT(Information Technology)基础知识,旨在指引一线同事进行相应的计算机基础知识储备。对于具体没了解或没接触过的,建议网上多查一些详细资料进行深入了解。 第一二三章分别结合公司产品以及...
TPlink 公司RD部门入职前知识储备
作为数据科学家,需要掌握的知识储备相对更广泛和深入,需要具备以下几个方面的知识: 1、统计学和概率论:数据科学家需要掌握统计学和概率论的基本知识,包括概率分布、假设检验、方差分析、回归分析等内容。这些...
java 基础知识储备(csdn)————程序
一款不错的小软件,通过测试的方法来开阔我们的视野,增长我们的知识。
算法相关知识储备 来源 leetCode 和 其他算法书
初中语文---文学作品复习知识储备.doc