【JAVA新生】拿协程开始写个异步io应用
来源:互联网 发布:linux本地安装包 编辑:程序博客网 时间:2024/04/29 14:17
前面已经准备好了greenlet对应的Java版本了,一个删减后的kilim(http://segmentfault.com/blog/taowen/1190000000697487)。接下来,就看怎么用协程来实现异步io了。首先,拿一段最最简单的tcp socket accept的代码:
Selector selector = Selector.open();ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(9090));serverSocketChannel.configureBlocking(false);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("listening...");selector.select();scheduler.accept(serverSocketChannel);System.out.println("hello");
这里使用的是java 6的NIO1的selector模型。直接拿NIO的原始api写代码会死人的。引入协程就是为了把上下连续的业务逻辑放在一个协程里,把与业务关系不大的selector的处理部分放到框架的ioloop里。也就是把一段交织的代码,分成两个关注点不同的组成部分。
改造之后的代码在这里: https://github.com/taowen/daili/tree/1e319f929678213a8d8f63ee5e8b8cf016637317
这是改造之后的效果:
Scheduler scheduler = new Scheduler();DailiTask task = new DailiTask(scheduler) { @Override public void execute() throws Pausable, Exception { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(9090)); serverSocketChannel.configureBlocking(false); System.out.println("listening..."); scheduler.accept(serverSocketChannel); System.out.println("hello"); }};scheduler.callSoon(task);scheduler.loop();
其中最关键的一行是 scheduler.accept(serverSocketChannel); 这个调用是阻塞的。但是只阻塞调用它的Task协程。如果有多个Task并行的话,别的Task可以在这个时候被运行。那么scheduler.accept是如何做到把NIO的selector api转换成这样的形式的呢?
public SocketChannel accept(ServerSocketChannel serverSocketChannel) throws IOException, Pausable { SocketChannel socketChannel = serverSocketChannel.accept(); if (null != socketChannel) { return socketChannel; } SelectionKey selectionKey = serverSocketChannel.keyFor(selector); if (null == selectionKey) { selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT, new WaitingSelectorIO()); } else { selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_ACCEPT); } WaitingSelectorIO waitingSelectorIO = (WaitingSelectorIO) selectionKey.attachment(); waitingSelectorIO.acceptBlockedAt = System.currentTimeMillis(); waitingSelectorIO.acceptTask = (Task) Task.getCurrentTask(); selectionKey.attach(waitingSelectorIO); Task.pause(waitingSelectorIO); return serverSocketChannel.accept();}
这个函数分成四部分:第一部分是尝试去accept,如果有戏就不用NIO了。第二部分是注册selection key,说明我希望知道什么时候可以accept了,并把task作为附件加上去。第三部分是Task.pause放弃掉执行权。第四部分是task被回调了,说明等待的accept已经ok了,可以去调用了。
但是Task.pause了之后,是谁在把这个暂停的task重新拉起来执行的呢?这个就是scheduler的loop干的活了
public void loop() throws IOException { while (true) { executeReadyTasks(); selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); for (SelectionKey selectionKey : selectionKeys) { WaitingSelectorIO waitingSelectorIO = (WaitingSelectorIO) selectionKey.attachment(); if (selectionKey.isAcceptable()) { Task taskToCall = waitingSelectorIO.acceptTask; waitingSelectorIO.acceptBlockedAt = 0; waitingSelectorIO.acceptTask = null; callSoon(taskToCall); } } }}
在循环中调用selector.select获得网络事件的通知。如果selection key就绪了,就把附件里的task取出来回调。具体的回调发生在executeReadyTasks内部,其实就是调用一下resume而已。
private void executeReadyTasks() { Task task; while((task = readyTasks.poll()) != null) { executeTask(task); }}private void executeTask(Task task) { try { task.resume(); } catch (Exception e) { LOGGER.error("failed to execute task: " + task, e); }}
这样一个只能接收telnet 127.0.0.1 9090打印一行hello的异步io应用就写好了。
- 【JAVA新生】拿协程开始写个异步io应用
- Java 异步IO(1) 文件异步IO
- Java 异步io
- Java 异步IO
- Java伪异步IO
- Java 异步 IO
- Java 异步 IO
- 写了个异步日志
- 开始写第一个程序。。
- 开始写Java项目
- 异步IO java中非阻塞IO
- java之”伪异步“IO
- Java 异步IO(2) 文件异步IO Callback
- Linux异步IO的简单应用
- 开始第一个javaFX应用
- java 异步 同步应用
- java 异步 同步应用
- java 异步 同步应用
- 「知乎 Card」更新日志(20141024)
- 数据结构——算法之(028)( 寻找其中的一个子字符串个数)
- 制作python模块安装包
- Ubuntu设置NAT连接上网
- 破解VCD无法复制方法 提取加密vcd中的DAT文件的解决方案
- 【JAVA新生】拿协程开始写个异步io应用
- A*算法
- 【随手写】偷懒新技能:Applescript
- 一个小坑,ccui.ScrollView和cc.ScrollView不是一回事,TMD
- perl 脚本学习笔记
- 【c++程序】乘法口诀公式显示
- Android学习笔记(二)--------浅谈Broadcast 广播
- 一张图告诉你,只会jQuery还不够!!!!!
- 使用Java发送Email