• midori 百日记 at 2017年1月17日

    #28楼 @luikore mruby 下的线程栈还挺小,但生态太糟糕

  • midori 百日记 at 2017年1月16日

    #25楼 @gihnius 很多稳定性问题主要是 eventmachine 本身导致的,而不是异步。我也打算在下一个大版本中把基于 eventmachine 的绑定改成基于 nio4r 的绑定。rust 线程比 gorouting 快很多,但是 rust 上基于的 tokio-rust 的网络通讯会比线程更快。扯到 IO 的时候,只有在完全解决阻塞的情况下,问题才会落在内存、网络、磁盘上,一旦出现线程阻塞,那性能必定是非常糟糕的。异步的做法就是用来解决阻塞问题的,但是异步的做法代码很难看,这才是这几年很多语言设计都在极力克服的问题。高性能的 Web 服务器,从 Apache 到 nginx 没有一个不是在使用 I/O 多路复用技术来解决问题,甚至连使用哪种复用技术都经过了几代的改变,更不要说单纯同步靠创建线程来解决 I/O 导致的线程阻塞,运行效率实在是太低下。

    IO 密集型还是用线程。

    这种表述实在是难以苟同,你基本找不到单机 10k 并发以上的应用是通过纯线程来解决的,因为单纯靠创建新线程,*NIX 线程创建的性能代价是极大的。

  • midori 百日记 at 2017年1月14日

    #23楼 @mizuhashi 在多线程下不会阻塞是指,他们在多个线程连接数据库的时候互不阻塞,但对于当前线程就出于阻塞状态了,我在实现 pg 的 adapter 的时候就发现了这一问题。主要是大家没有共享同一个 event loop

  • 有点奇怪,既然是 Let's encrypt 的证书,为什么不直接申请呢?在又拍云上申请 Let's encrypt 证书有什么区别?

  • 怎么看都像是脱裤啊。。。

  • 这帖子让我想起来不少事情。我自己从 BASIC 开始 Delphi, C++, PHP, Python, Java, JavaScript, Scala, Objective-C, Ruby, Common Lisp, Swift 都先后学过。最后想想语言和框架真的是这么好学的东西吗?是也不是。

    是的部分是语言的语法确实是一通百通的,基本就可以分为类 C 语言和类 Lisp 语言这两大类。不是的部分是,如果大家的设计都这么接近,那么为什么会有这么多语言呢?事实上,理解一门语言的重要部分就是理解这门语言的设计思想。

    如果你会写 C++,学习 Java 的第一天你就可以开始干活了,但你很容易写出 C++ 风格的 Java。这就好像谭浩强的 C 语言书有那么多问题,除了本身的语法问题,一个就是对于语言设计思想本身谭浩强不理解,另一个就是保有了大量谭浩强之前 BASIC 程序员的风格。

    所以如果你学过很多语言,学得好,你优势就会很大。比如你又会写后端,又掌握前端,在你开发时你就能很好的对接,设计出更合适的接口,极大地提高沟通和开发的效率。学得不好,那么这些东西反而会成为阻碍,让你什么事情都没法做好。这是全栈工程师和全栈 Hello World 工程师最大的区别。

    不过反过来说,如果只学一样东西,即使这项技术没有过时,那么你就能专精了吗?其实也不是。很多外包公司的 Java 程序员所谓的 10 年经验难道不是半年经验用九年半吗?

    所以本质上来说,要想有好的职业规划,需要对自己有明确的认识。永远知道自己知识薄弱的地方,永远知道自己要学习要接触新的东西。好的程序员其实吃饭的时候在想怎么更好抽象,排队的时候在想怎么提高性能,洗澡的时候在想怎么优化风格。用框架用语言的同时会提出这些框架语言设计的问题,并思考能不能解决,怎么解决,这样才能对这些东西理解更加深刻,这样才能让自己写起代码来游刃有余。

    其实真的好好思考后,就算是业务代码也并不会繁杂。业务逻辑上的繁杂通常就是由于没有正确的抽象,从一开始就没有好好做好,从而使得复杂度越来越高,永远是打补丁,在补丁上打补丁,代码越来越糟糕,最后让思路越发混乱,越发不能维护。如果是这种情况,别说 Rails,世界上任何一种语言、一个框架都能写出这种垃圾代码出来。

    于是说,如果因为自己买了台 iPhone,就能荒废 Android 开发的技能,这种坚持和努力程度实在是太低了。我很难苟同说楼主现在这样的情况的主要原因是环境造成的。

  • midori 百日记 at 2016年12月25日

    #20楼 @0x005a 已 fix。我最近用这东西写了些实际的项目,跑了下 benchmark 还是相当好的。准备把性能测试写得更完善一些,之后可以更好对比。

  • midori 百日记 at 2016年12月22日

    #18楼 @harryyoung 我觉得我要把 em- 彻底拿掉。。。

  • midori 百日记 at 2016年12月20日

    #16楼 @huacnlee 其实 API 里都没有 em- 前缀,只有 gem 名字有。。。因为 midori 这个 gem 名字被人注册了 QuQ

  • midori 百日记 at 2016年12月18日

    #12楼 @0x005a

    • 请求体较大的请求,耗费 parse 时间
    • 响应体较大的请求,耗时 send_data 时间
    • get 中执行耗时操作时。上述 sleep 1 只是举例,现实场景是,开发者使用的第三方网络请求库,随机性的 CPU 密集型操作等等。

    这样的问题就不是说依靠异步模型本身去解决的,因为这些场景出现的是真正的 CPU 密集运算 需要的是真正的计算力来解决的。如果说我们只有一个核心一个线程可用的情况下,任何 Web 框架,包括 Node 也好,都会出现同样的性能下滑。只有说我们靠多线程多进程来解决,类似于 express 的 clusters;类似于 Ruby 上的 puma。

    另外你给的 benchmark 代码,如果限制 thin 只能在一个核心上运行的话,em-midori 并不会比 thin 慢一倍。(在 Linux 上的性能测试,macOS 上的 em-midori 有一些奇怪的性能问题,还在排查。)这和 em-midori 目前还没有实现 clusters 功能有关,这是下一个版本的工作重点之一。

    另外我不能理解所说的

    难以保证 Promise 的方式是可靠的,且性能不低于常规方式

    因为这个 Promise 是按照 Node 新版本的思路一致实现的,如果说可能存在一些边缘情况,这些之后会通过更完整的单元测试来补充。而使用 Fiber 的封装肯定会比 UNIX 线程方式的性能高,这点应该是毋庸置疑的。

  • midori 百日记 at 2016年12月18日

    #10楼 @0x005a 不,receive_data 一上来就对 Fiber 干了一些特殊的处理。也就是认为 receive_data 里的任何东西都一个 async 函数,

    # Logic of receiving data
    # @param [String] data raw data
    def receive_data(data)
      lambda do
        async_internal(Fiber.new do
                         #...
                         receive_new_request(data)
                         #...
         )
      end.call
    end
    

    async_internal 的实现是

    def async_internal(fiber)
      chain = lambda do |result|
        return unless result.is_a?Promise
        result.then(lambda do |val|
          chain.call(fiber.resume(val))
        end)
      end
      chain.call(fiber.resume)
    end
    

    所以 Fiber.yield 只会阻塞当前请求,同时将处理权让渡给下一个 EventMachine 循环,并不会阻塞主线程。

    之所以你上面给出的

    require 'em-midori'
    class Route < Midori::API
      get '/' do
        sleep 1; ''
      end
    end
    Midori::Runner.new(Route).start
    

    将并发降到了 1,是因为 Kernel.sleep 函数本身没有被封装。

    em-midori 与其说是一个异步的 Web 框架,不如说是提供了一系列无回调非阻塞 I/O 的工具集合。

    事实上,你可以利用 EM.add_ticker 放在 em-midori 定义的 Promise 类里套一下,就可以得到一个不会影响并发的计时器。defer 是个方法,但用来做计时器还是有些重。 em-midori 内置了 Postgres 和 Sequel 的封装,使用样例如下:

    Postgres:

    require 'em-midori'
    require 'em-midori/extension/postgres'
    Midori::Configure.before = proc do
      DB = Midori::Postgres.new('127.0.0.1', 5432)
      DB.connect('dbname', 'username', 'pwd')
    end
    
    class Route < Midori::Route
      get '/' do
         DB.query('SELECT * FROM TEST WHERE A=0')
         ''
      end
    end
    

    Sequel:

    require 'em-midori'
    require 'em-midori/extension/sequel'
    Midori::Configure.before = proc do
      DB = Sequel.connect('postgres://username:pwd@localhost:5432/dbname')
      class User < Sequel::Model
      end
    end
    
    class Route < Midori::Route
      get '/' do
         User.find(a: 0)
         ''
      end
    end
    

    em-synchrony 其实本身写得也很好,我在写这个框架之前仔细读完了 em-synchrony 的代码,但 em-synchrony 的主要目的是利用 Fiber 实现更灵活的异步调用语法。但 em-midori 更大的意义是通过封装,尽可能减少在业务层加入异步相关的语法。虽然实现途径非常类似,但是目标还是有很大区别的。

  • midori 百日记 at 2016年12月17日

    #6楼 @embbnux 对,对于单个请求的响应时间确实是有好处的

  • midori 百日记 at 2016年12月17日

    #4楼 @embbnux 事实上,Promise.all 这样的特性在语言层面上是很高效的,但在运行上,特别是 Web 服务器上并没有想象中的优势。因为 Web 服务器主要都是碎片式的 CGI,这意味着,我 CPU 一旦有空闲的时间我只要去处理别的请求就行,而不必通过压榨一个请求内的时间。本质上说,如果我们能将一个线程的 Idle 率降到 0,那么无论我们采取何种策略,他们的整体效率应该都是接近的。

  • #2楼 @Rei 哈哈哈哈哈哈哈,我也不相信裸写 Lisp,纯 Lisp 更像是在写语法树而不是在写程序。但是我相信基于 Lambda 演算模型做出更好的语法设计,从而又具备 Lisp 那样高度灵活性和高度抽象性的动态语言,但又能让编程有更好的可读性和工程性的东西。这两年 Lisp 社区其实发展得也很迷,不是很懂。

  • 任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、不合规范的、充满程序错误的、运行速度很慢的、只有一半功能的Common Lisp实现。—— 格林斯潘第十定律

    Paul Graham 后来给这个格言加了一个推论。

    包括Common Lisp。

    包括 Erlang、Ruby、JavaScript 在内的高度灵活的动态语言,不免和 Lisp 的设计有很多相似之处。在这些语言的虚拟机上支持 Lisp 本身的语法其实相对来说并不麻烦。

  • shadowsocks 客户端的原理基本就是通过私有协议和远端服务器连接后,转换成 socks5 协议在本地暴露。在配置 proxychains 先要调通 shadowsocks。如果你用的是 Mac 图形化的客户端的话,切换到全局模式,打开网络偏好设置-高级-代理,就能找到 shadowsocks 在本地建出来的 socks5 协议的地址。127.0.0.1:1080 是默认设置,如果端口被占之类的就不是 1080 了。

  • 其实用特殊字符写代码有个比较常见的用途是在 Android 开发中使用一本非 ASCII 字符的字典,比如中文甚至 emoji 来做混淆。。。而反编译工具遇到这些字符变量名会触发异常导致无法反编译成功😆

  • 几个月前买了原版书还没怎么看。。。下了一单中文版。。。

  • 惊了,我才知道搭建 gem 原来也是有脚手架工具了,每次都是手动一个个文件建出来的 0.0