• 推荐算法老师 labuladong at 2020年05月06日

    我那句提反转二叉树了?贴个老梗显得自己懂得多?一个极端特例当论据是不是抬扛?一直用反问句回复别人是不是自我感觉很良好?

  • 推荐算法老师 labuladong at 2020年05月05日

    工程师也会 “相轻” 的,跟不同背景的同事吹逼,聊到算法,设计模式以及 “众所周知” 的 pattern 一定不能露怯,否则将会被暗暗鄙视(其实大部分人都是半桶水,所以都会很默契的不会聊的很深入)

    面试中算法有点像义务教育里那些感觉没用的课程,一是能快速筛掉学习能力差的人,二是算法确实对编码有影响。

  • 三楼的需要在方法外定义 param

    不需要,写里面也一样。

  • 还不是很理解,为啥 broadcast 的时候要用 thread pool,因为 io 已经 异步了

    ActionCable 的实现是在内存里维护一个订阅列表的 hash,key 是 channel,value 是一个订阅者回调 lambda 的 array,

    回调 lambda : https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/channel/streams.rb#L146-L154

    def worker_pool_stream_handler(broadcasting, user_handler, coder: nil)
      handler = stream_handler(broadcasting, user_handler, coder: coder)
    
      -> message do
        connection.worker_pool.async_invoke handler, :call, message, connection: connection
      end
    end
    

    broadcast 的时候: https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/subscription_adapter/subscriber_map.rb#L36-L45

    def broadcast(channel, message)
      list = @sync.synchronize do
        return if !@subscribers.key?(channel)
        @subscribers[channel].dup
      end
    
      list.each do |subscriber|
        invoke_callback(subscriber, message)
      end
    end
    

    invoke_callback最终是上面那段 lambda

    -> message do
      connection.worker_pool.async_invoke handler, :call, message, connection: connection
    end
    

    worker_pool是一个 thread pool (Concurrent::ThreadPoolExecutor),async_invoke是往 thread pool 里塞任务,任务异步执行,所以遍历 subscribers 时是不会阻塞的,而消息何时向全部 client 发完取决于 thread pool 消化任务是的速度了。而且整个 ActionCable 是公用一个 thread pool 的。

  • 可以看一下 https://hashrocket.com/blog/posts/websocket-shootout 粗略比较了各种编程语言/框架 websocket 的指标。

    结论

    When it comes to websockets, Clojure, Elixir, and Go are all choices that balance great performance with ease of development. NodeJS is in a lower performance tier, but is still good enough for many uses. Rails can only be recommended when part of an existing Rails application with few concurrent clients. C++ offers the best performance, but it is hard to recommend due to the complexity and difficulty of development.

    需要指出的是做测试的时候 Rails 6 刚出,puma 还没有用上 nio4r,所以 Rails 支持的连接数看起来弱爆了,但是不代表现在 Rails 的真实水平。

    ActionCable 比较大的问题是 broadcast 是基于线程池的,线程池大小默认是 5(可以通过 config.action_cable.worker_pool_size 调整),换句话说一个 puma worker 同一时间只能同时向 5 个 websocket client 发消息 😂

  • 装饰器可以对原方法进行修饰,返回一个修改之后的方法,在打印日志,切面编程中运用广泛,你说 ruby 开放那你可以试一下在不改变函数名的情况下对原函数进行修改

    module LogDecortor
      def log_method *method_names
        mod = Module.new do
          method_names.each do |method_name|
            define_method(method_name) do |*args, &blk|
              puts "Method #{method_name}: start"
              ret = super(*args, &blk)
              puts "Method #{method_name}: end"
              ret
            end
          end
        end
        self.prepend(mod)
      end
    end
    
    class Foo
      extend LogDecortor
    
      log_method :bar
      def bar
        return 1
      end
    end
    
    p Foo.new.bar
    

    另外 Ruby 即使有不改变方法代码本身就改变方法行为的能力,但是实践中还是比较克制,像加日志、打点一类的需求有另外的方法,就不展开说了。

    第二 javascript 只是我用来举例,可能你没懂我在说什么

    只是没懂你的举的例子有什么太大的好处。

    第三纯函数式语言是写不出复杂的系统的,或者说是不容易写出复杂的系统

    纯函数式编程不容易写出复杂系统是因为程序员要求高一点。

    所以这里的函数式编程就是一个概念,允许方法有副作用

    "函数式编程就是一个概念"不是我的原话吗?我本意是现在市面上绝大部分面向对象语言都可以写 “函数式编程” 的代码,只是前端生态很推崇,别的领域还没有一要使用 “函数式编程” 的场景。

    Ruby 2.7 曾经有过管道符 https://bugs.ruby-lang.org/issues/15799 ,你对函数式编程那么了解肯定知道管道符对函数式编程的意义,虽然种种原因取消了,但是可以反映出 Ruby 是拥抱函数式编程的,你可以期待一下 ❤

  • 比如增加 python 的 AOP 特性 (装饰器)

    Ruby 足够开放,不需要语言层面装饰器。

    def 定义的方法名也是一个变量,可以覆盖(就是重复定义)类似 javascript,

    javascript 还没有好到需要别的语言模仿吧 😅 覆盖方法不就是 override 么……

    然后支持函数式编程,高阶函数(支持传入函数,传出函数),现在来看还不能算真正支持函数式。

    高阶函数核心在与 “穿作用域”,而在 “穿作用域” 领域,Ruby 比大多数语言强很多。函数式编程也仅仅是个概念:要不你就用纯函数式编程语言,写不出违反函数式编程 pattern 的代码;要不你就用其他语言假装在函数式编程:有些领域大家都在假装,有些领域大家还没开始装,并不是说不能装,只是还没遇到需要装的场景。

    学新语言还是要有空杯的心态。

  • Rails 原生的 websocket 比较弱,这种全靠 websocket 的框架还是观望一下比较好(实在忍不住可以用 phoenix live view)

    看过Samuel Williams的 Ruby 处理百万 websocket 连接The Journey to One Million by Samuel Williams后一度对 Ruby 在稍微高一点的并发场景产生了幻想。

    Samuel Williams 主推的是基于FiberAsync做 ruby 的 websocket server,然而 Rails 团队似乎并没有打算用 Aysnc https://github.com/rails/rails/issues/35657

    关于 websocket 另一条路是用 Anycable,用 golang 或者 erlang 做 websocket server,再通过一另一个 gRPC 服务跟 Rails 应用代码交互。Stimulusrelex 文档上说已经支持 Anycable 了,但是用 Anycable 增加了部署的复杂度,会不会违背了 Stimulusrelex 的初心 😅

    With StimulusReflex, you'll ship projects faster, with smaller teams and re-discover the joy of programming.

    另外分享几个事情:

    • 好多年前就有一个基于 websocket 渲染的 Ruby web 框架 https://pakyow.com/ ,开发者也不用自己写 js 代码,跟 Rails + Stimulusrelex 体验很像,停更了几年后最近半年突然 release 1.0,用上了 Async,我猜可能是 Async 解决了他们 websocket scale 的问题。
    • Anycable 的作者做的benchmark表明, async 并没有比 puma + actioncable 好太多 😢
  • xxx.call(10)可以写成xxx.(10)或者xxx[10],都是语法糖,用多了讨人厌

  • param = []
    define_method(:method1) do
      define_method(:method2) do |x|
          param.push(x)
      end
      return method(:method2)
    end
    
    f = method1
    f.(10)
    
    p param