• @bhuztez 在 read/write 碰到 EWOULDBLOCK 的时候插 yield 进去,就不用手动写 yield 了

    马克一下两个链接: http://golang.org/src/pkg/runtime/proc.c http://state-threads.sourceforge.net/docs/st.html

    @reus goroutine 里大概也是在 read/write 里用长跳转把控制权交回调度器,相当于 yield 了吧?

  • 一个优雅的快排 at August 24, 2013

    #8 楼 @blacktulip 用了就是这样:

    def qs x, *xs
      l, r = xs.partition {|e| e <= x }
      x ? [*qs(l), x, *qs(r)] : []
    end
    
  • 看外文资料,官方最新文档是捷径... 熟悉 unix/linux C 的话很多标准库的用法你一下就上手了,和很多 C 库都是一一对应的。例如 Date.strptimeman 3 strptime 的格式占位符是一样的。

    web 搜索尽量加时间限定,不要被过时的内容坑了

  • 事件框架的问题 1 是,破坏了对象的封装...

    本来大家都是有主观能动性的,obj.write 现在变成了 obj.send_data, 只是改了个名字本质没什么区别,obj.read 却变成了被动读 def obj.receive_data. 两个对象只要相互收发数据,就得参照对方的实现去写。

    问题 2 是,数据流不是自然按照写代码的顺序呈现出来,而是通过手动拼接回调接起来的。

    这里不得不提下 Haskell ... Haskell 有专门的语法糖把这种回调套回调变成一个和顺序写法很相似的东西。例如一个 do-notation

    f x = do
      a <- g x
      b <- h a
      ...
    

    相当于翻译成用 >>= 串起来的样子

    f x = return x >>= \x -> return (g x) >>= \a -> return (h a) >>= ...
    

    >>= 虽然是个左结合的二元运算符,但坑爹的地方是 lambda 语法是能往右边延伸多长就多长的,其实 do-notation 每一行都是回调有木有...

    f x = return x >>= (\x ->
      return (g x) >>= (\a ->
        return (h a) >>= ...
      )
    )
    

    所以实际上这个丑陋的回调套回调,通过语法糖变成了优美的 do-notation...

    不过 Fiber 走的是另一条路... 线程的特点是每线程都有自己的栈,读写共享数据的时候需要锁。而环保线程/Fiber 是 cooperative 的调度,自己在跑的时候就是老大,不用担心什么时候被人抢占,所以不用加锁,不用做全栈拷贝,只用在建立出分个叉就可以了。Fiber 的调度权在于自己 (Fiber.yield), 而不是 VM 或者计时器插一脚进来的。

    Fiber 的用法举例:

    f = Fiber.new{ i = 0; loop{ Fiber.yield i += 1 }}
    f.resume #=> 1
    f.resume #=> 2
    

    既然 Fiber 可以暂停和重启,那么我们可以利用这个特性把 def obj.receive_data 改造成像 obj.read 一般人性友好的 API.

    我们可以这么实现 read:

    def read
      while not eof
        read_nonblock # 当然在 EM 里你就要对 receive_data 做点很搅脑的操作了...
        Fiber.yield
      end
    end
    

    client 对应的 EM::Connection 可以用 Fiber 包起来,当捕捉到事件的时候,就调用 fiber.resume 去继续操作。于是就实现了 em-synchrony.

  • # 例如歪了
    

    我来说明下 EM 的原理吧

    一个 blocking server 是长这个样子的:

    # blocking loop, 没事干的时候会安静的 sleep
    loop {
      client = server.accept # 接受请求, 可以 ri accept 看看它的功效.
      client.read # 其实 ruby 做了点处理, 和系统函数 read(blocking_fd) 不同, 只会塞住本线程
      client.write
      ...
    }
    

    一个 nonblocking server 是长这样子的:

    # busy loop, 没事干的时候还会吃 CPU 100%
    loop {
      # 把存着的 client 都跑一趟
      client = server.try_accept # 伪码... 不过 kgio 里是有个 try_accept 的, 反正就是不塞住
      client.read_nonblock # 不塞住, 所以就不一定成功...
      client.write_nonblock
      ...
      # 没跑完? 把 client 存起来
    }
    

    (以 linux 为例) 一个 event server 是长这样子的:

    # event loop, 没事干的时候也很安静
    loop {
      event = epoll(timeout) # 当然也可以 poll 多个, 但这里就简简单单的 poll 一个
      if event means server readable
        client = server.accept
        ...
      end
    }
    

    系统事件包括 fd (文件描述符) 可以读 (EPOLLIN), fd 可以写 (EPOLLOUT), 定时器跳跳,文件系统被改,信号...

    系统提供了:

    • 建立事件队列的函数:epoll_create
    • 注册事件的函数:epoll_ctl
    • 获取事件的函数:epoll

    最后你写的服务器就变成 loop { epoll ; ...} 的样子

    EM 做的事情,就是把上面 loop 中的 client 包装成 EM::Connection 对象,然后碰到 "fd 可读" 事件时,回调 Connection 对象的 receive_data 方法。

    所以 EM.start 是长这样子的:

    # 各种初始化后...
    loop do
      event = epoll(timeout)
      conn = find or create connection by event
      conn.receive_data(read_nonblock)
      schedule thread # 触发线程调度和信号处理
    end
    

    multiplexing 没问题了,读也是非阻塞了,那写数据 send_data 是怎么处理的呢?

    • 先往 socket 中写,如果出现写不下的状况,就把剩下的部分拷贝到一个 buffer 队列中
    • 当那个 socket 变成可写的时候 (收到 EPOLLOUT 事件), 就从 buffer 队列中取出来写进去

    至此,EM 就产生了两个问题:

    • 关闭连接的时候,如果 buffer 队列没写完怎么办?解决办法是 close_connection_after_writing
    • 在回调函数中注册了触发本回调的事件,无限循环怎么办?解决方法是 EM.next_tick{ ... }

    还有一点,EM::Connection 对象除了来源于 server accept 以外,还可以来自 socket connect

  • 鲍叔真是硅谷的开心果

  • #8 楼 @bhuztez 你说的这两种和我说的 隔三岔五塞调度点 难道不是一个意思...

  • #1 楼 @bhuztez

    抢占式调度有两种意思。一种意思是多个进程抢着 accept 同一个 file descriptor , 和 event loop 可以完全协同工作啊,nyara 的 production server 就是这样整的。另一种意思是人为的在字节码/机器码中隔三岔五的塞进类似于 yield point 的调度点 (例如 JVM 里有几十种迷你锁,锁和锁之间都是可调度的), 细到一定程度就出现了抢占的效果,多用于支持线程的虚拟机中,塞太多对于 web server 没什么好处,反而增加了 context switch 的开销... 塞少一点或者只塞到 EWOULDBLOCK 的时候,就和 event 类框架差不多了,再把控制权交给 OS, 就变成 event loop 了...

    stackless 迷你线程的做法挺好的啊,不过为什么很多人都转用 gevent 了?

  • #22 楼 @hooopo 被你发现了

  • iTerm 几个问题? at August 23, 2013

    \1.3. iTerm 重启后能记住 iTerm tab 布局和颜色,但记不住目录。用 tmux 的话重启 iTerm 可以记住一个 iTerm tab 内各屏的上下文,但重启机器后还是记不住... 如果 tmux 的 tab 在 ssh 服务器上,那 tmux 也可以恢复会话,本地鸡怎么重启都没关系

    \1. zsh 的配置 s host 产生绿色的 tab

    # change tab color for ssh
    tab-color() {
      echo -ne "\033]6;1;bg;red;brightness;$1\a"
      echo -ne "\033]6;1;bg;green;brightness;$2\a"
      echo -ne "\033]6;1;bg;blue;brightness;$3\a"
    }
    tab-reset() {
      echo -ne "\033]6;1;bg;*;default\a"
    }
    # Change the color of the tab when using SSH
    # reset the color after the connection closes
    s() {
      if [[ -n "$ITERM_SESSION_ID" ]]; then
        trap "tab-reset" INT EXIT
        if [[ "$*" =~ "production|ec2-.*compute-1" ]]; then
          tab-color 255 0 0
        else
          tab-color 0 255 0
        fi
      fi
      ssh $*
    }
    compdef _ssh s=ssh
    
  • #33 楼 @wjch 哦,不包括模型 400 行还算正常啦...

  • @wjch 400 行 java 你没有漏算 import 吧?还有页面 jsp (相信你不会把查数据库之类的放 jsp 里吧?), 还有各种 web.xml, pom.xml 配置文件...

  • PDFKit bug at August 22, 2013

    #15 楼 @liker 去掉 turbolinks 还不行?那就不知道了...

  • PDFKit bug at August 22, 2013
  • PDFKit bug at August 22, 2013

    #10 楼 @liker 木有扣扣..

  • 测试都用 RSpec? at August 22, 2013

    rspec 还是有点好处的

    it "..." 测试的描述可以更清楚,而且不会因为意外方法重名而把前面的测试丢了

    before :eachbefore :all 容易记住

    可以选择 彩色输出 (-c), 文档输出 (-f d), 编辑器专用输出 (-f textmate), 还有 nyancat formatter...

    should == 把哪个是 expect, 哪个是 actual 分得很清楚,还可以少写一些括号 (assert_equal({}, output) 就不能省括号)

    == 是最自然不会写错的,如果一段时间没写代码,就会忘记是该写 assert_equal 还是 assert_equals 了...

  • #4 楼 @yakczh activerecord?

  • 1.9 以后没 rdoc 了。。 at August 21, 2013

    可以考虑搬到 thor 上...

  • windows 不能用 shotgun, 用这个:

    http://www.sinatrarb.com/contrib/reloader.html

  • PDFKit bug at August 21, 2013

    #8 楼 @liker 没用过 pdfkit... 可能要给 qt 打补丁 wkhtmltopdf 才能正常运作 ...

  • 注意那骗人的坐标轴,最低和最高差别只有 20% 左右 (可能还不到)

  • PDFKit bug at August 21, 2013

    #6 楼 @liker 好吧,搞不懂了 ... 程序有没有挂起或者退出代码 != 0 ?

  • PDFKit bug at August 21, 2013

    那这个能成功么?

    echo foo | wkhtmltopdf --page-size Letter --margin-top 0.75in --margin-right 0.75in --margin-bottom 0.75in --margin-left 0.75in --encoding UTF-8 --print-media-type --quiet - - > a.pdf
    
  • PDFKit bug at August 21, 2013

    #2 楼 @liker

    命令行

    echo hello | wkhtmltopdf - - > a.pdf
    

    能生成文件么?

  • Setting.upload_url ?

  • PDFKit bug at August 21, 2013

    可能是没有 gem install wkhtmltopdf

    如果装了,在命令行下面试试那个命令,看看错误信息是什么

  • 要在 devkit 控制台下面安装

  • 终于抢到沙发了...