• 我的经验教训,订单和订单项的创建是主业务逻辑,不建议放到 callbacks 中,放到 callbacks 里代码不直观,可读性差很多。 至于你提到的作为一个事务处理,很多处理的方法,比如:

    • 1 最简单的方法建立 Order 和 OrderItem 关联,order has_many order_items ,通过 order.order_items.build(...) 构造 order_items,order.save! 是放在一个数据库事务中处理。
    • 2 另外一种方法显式使用 Order. transaction {}

    而 destory 使用 关联的 dependent: :destory 选项处理最简单。

  • 感谢支持!微信群里最近对 Web 实时通信讨论挺多的,现在实时通信的应用场景比较广泛,Rails 5 的 ActionCable 是解决方案之一,但是我觉得基于 nginx nchan 高效实现 Web 实时通信的解决方案要比它好不少,薄荷在实际项目中有相应实践,和大家分享一下。

  • 我参加 3 月 7 日的上海 Ruby Tuesday 活动 https://ruby-china.org/topics/32404,有一个主题分享,有兴趣者也可以参加一下,到时可以当面聊一聊,:)

  • 期待!:)

  • 感谢支持!:)

  • 感谢支持!

  • 感谢支持

  • Ruby Rack 及其应用 (上) at 2016年11月15日

    写得不错,👍

  • RubyConf China 2016 视频 at 2016年11月06日

    #39 楼 @novtopro 嗯 simple_comand 做的是一样的事情

  • RubyConf China 2016 视频 at 2016年10月31日

    #30 楼 @jicheng1014 另外一点,我认为虽然 ServiceObject 中可以有多个实例方法,但是基于单一责任原则,服务主要动作建议只有一个,这是我们只使用一个 call 方法的原因。如果有多个主要动作还不如剥离成多个 ServiceObject,如果它们之间有关联逻辑要复用,可以通过类继承或模块 mix 达成,这也是用实例方法的好处之一。

  • RubyConf China 2016 视频 at 2016年10月31日

    #33 楼 @betterthornbird 感谢聆听!跨服务数据访问是实施微服务化后面临的一个麻烦问题,解决的方式多种多样,共享存储是一种变通方法。

    微服务化的一个显著标志就是每个微服务都有自己独立的存储(包括数据库和缓存),独立开发和独立部署。每个微服务应该是独立的,通常情况一个服务需要使用另外一个服务的数据应该通过服务的 API(也就是服务间通信)来完成,但是有些特殊情景走 API 的话,会变得特别麻烦和低效,所以可以通过共享数据存储变通一下。

    比如验证用户安全。用户会话建立和安全认证是由账号系统 Passport 统一负责的,但每个微服务也都有验证用户安全的需求,如果每次请求都要访问 Passport 一次十分麻烦和低效,我们的做法是所有微服务之间共享用户会话数据(放在 Redis 中),只有 Passport 有写入权限,其它微服务只有读权限。另外一种做法是使用 API 网关,所有验证都在网关完成,每个微服务都不用管用户验证,我们没有使用这种方法。

    另外一个例子是某些使用频繁的公共数据库也使用共享存储。比如我们系统中食物数据使用特别频繁,我们直接把这部分的数据和模型以只读方式共享给其它微服务,这样使用食物数据就简单多了。

  • RubyConf China 2016 视频 at 2016年10月30日

    #30 楼 @jicheng1014 确实,包在 class 的 private 可以把类方法私有,但是这种方法和对象的私有方法相比,没法很简便的共享对象的状态,每个方法都要传递所有参数。

  • RubyConf China 2016 视频 at 2016年10月30日

    #21 楼 @jicheng1014 我是薄荷的 vincent,欢迎探讨这个问题,😀

    对于 ServiceObject,我认为使用类方法还是使用对象方法是两种看待(抽象)问题的方式。

    # 类方法形式
    class SuperService
       def self.call(params)
          # bla bla...
          # process it with params
       end
    end
    
    # call service
    result = SuperService.call params
    
     # 对象方法形式
     class SuperService
        def initialize(params)
           @params = params
        end
    
        def call
           # bla bla...
           # process it with @params
        end
    end
    
    # call service
     service = SuperService.new params
     result = service.call 
    
    • 如果使用类方法,相当于把 ServiceObject 看做服务的提供者/管理者,每次服务过程,相当给服务提供者发送一个消息,请求相应结果。
    • 如果使用对象方法,相当于把 ServiceObject 看做是服务的模板,每次服务过程,需要先生成一个具体的服务对象,然后发消息给该服务对象,请求相应结果。

    从代码上看起来,对象方法要比类方法要啰嗦一些,而且可能有的同学还担心使用对象方法性能要差一些,因为多生成了一个 service 对象,另外 params 还需要在对象中使用实例变量传递。

    我们最初也使用类方法,后来才慢慢转变为使用对象方法。如果 service 很简单,使用类方法更简单直接的,但是当 service 变得复杂之后,我们发现使用对象方法的处理方式要好很多。

    首先遇到的方法重构问题。当 service 逻辑变得复杂之后,在一个 call 方法里容纳所有的代码并不合适,这个时候要从中抽离方法,如果使用对象方法的话重构比较简单,剥离一些代码形成私有方法,然后在 call 方法中进行总调度就可以了。如果使用类方法的话,麻烦的是让类方法成为私有不那么直观,另外需要每个调用过程中传递所有参数。

    其次是处理复用问题,对象方法形式的 ServiceObject 更容易处理。本质上对象方法形式的 ServiceObject 的方式更 OOP,所以可以很方便的使用一些面向对象的手法,包括继承和多态等等。类方法本质上是一种带了命名空间的全局方法,虽然类方法也能继承使用,但是相比对象方法的处理方式,没有那么优雅。

    最后,对于有同学担心的性能问题,相比它带来的易扩展和优雅性,我觉得可以不值一提,一次 Rails request 生成的对象成千上万,一个极其轻量的 ServiceObject 对象生成可以忽略不计。

    另外,每次都 new 一个 service 对象在 call 的方式确实有点累赘,本着 DRY 的原则,我们写一个 Servable module,只要 mix 这个 module,就可以使用简洁的直接 call 方式。

    代码如下所示:

    module Servable
       def call(*args)
         new(*args).call
       end
    end
    
    class SuperService
       extend Servable
    
       def initialize(params)
         @params = params
       end
    
       def call
          # bla bla ...
       end
    end
    
    # call service
    result = SuperService.call params
    
    

    The end.

  • 为什么用 RPC at 2016年07月25日

    #26 楼 @jayliud 今年打算在 RubyConf China 做一个 Rails 微服务化的主题演讲,敬请期待!

  • 为什么用 RPC at 2016年07月17日

    #23 楼 @wangder 没有事务支持,分布式事务都比较复杂,如果真的需要,需要自己做接口支持,也就是说提供恢复操作接口,由自己控制。

  • 为什么用 RPC at 2016年07月17日

    #19 楼 @jayliud 其实去年做过两次的 topic 分享,都是关于这个主题的,:)

  • 为什么用 RPC at 2016年07月15日

    薄荷是很早(4 年前)就开始进行应用拆分和微服务化改造,最初完全是以 RESTful API 完成不同微服务之间通信的。 在一年前开始引入 RPC,但用的不是传统的 RPC,而是基于消息中间件 RabbitMQ 的 RPC , 这样做的主要的原因有:

    • 1.安全,内部 API 和外部 API 完全隔离,避免混淆;
    • 2.统一,统一处理异步任务,广播和同步调用三类最常见消息模式,都走 MQ;
    • 3.性能,有一些改善。

    没有用传统 RPC 是因为:和 Java 比起来 Ruby RPC 这块的基础组件弱爆了,即使看似最成熟的 Apache Thift rubygem 都象是玩具,其它就更不用提了。用 Ruby 实现传统 RPC 麻烦的地方不在于消息协议处理,而在于一系列配套基础设施,比如服务并发,高可用,部署和监控一堆东西都没有现成的,需要自己造。

    我刚开始原本打算用 sidekiq 更改出一套支持三类消息通信的方案,后来发觉这样做的话服务间耦合太紧了,另外过于绑定在 Ruby 语言上,所以放弃了。后来发现一个叫 Sneakers 的东东,其实和 Sidekiq 非常类似,提供完整的异步任务处理机制,只不过把 Redis 换成了 RabbitMQ,因为用了专业的消息中间件,增强处理广播和 RPC 变得比较简单。我就在它的基础上开发了 SneakersPacker 完成一套统一异步任务,广播和同步调用(RPC)的方案。

    这个组件是开源的,项目代码在 http://github.com/xiewenwei/sneakers_packer,目前已经在我们生产跑了比较长时间,还是比较稳定的。

    虽然我们现在用的方案中有 RPC,但是依然认为绝大部分情况用 RESTful API 就够了的,这样简单方便,除非有充分理由才用,才需要引入更复杂的方案。

  • @jicheng1014 加一下 QQ(在我博客里有),我直接发给你好了。

  • Ruby SH 薄荷聚会有感 at 2016年05月23日

    目前 Ruby 大部分应用以 Web 开发为主,而 Web 开发中以使用 Rails 为主,这个现状导致每一次 Ruby 的会议或者活动通常需要有 Rails 一席之地,不过这次活动的 topic 并不全是 Rails 啊,:)

    但是 Rails 并不是 Ruby 的全部,还有很多其他选择。首先使用 Ruby 做 Web 开发,除了 Rails,还有 Sinatra,Grape,Padrino 和 Hanami 等等供选择。另外不做 Web 开发,Ruby 在测试,系统管理等等也有成熟应用,比如 Cucumber,Chef,Puppnet,Homebrew,CocoaPods,不过确实不多。

  • 今天在薄荷举办的 Ruby 交流大会圆满结束了。今天天气不太好,下着不大不小的雨,稀稀拉拉让人心烦,但是活动依然来了近 80 人,超出了所有人的预期。

    主题演讲很精彩:Larry 与 Spam 斗争的故事跌宕起伏,让人敬佩;Teng Bao 关于 Strikingly 的工程师产品文化和方法让人很受启发;我是想尽量把薄荷架构上的一些经验教训分享出来,想讲东西太多,没能控制好节奏,有点小遗憾。圆桌会议讨论是一次新的尝试,挺有意思的,大家意犹未尽的感觉。

    这次活动来了不少新面孔,算是 Ruby 的新生力量吧,让人对 Ruby 的未来充满了信心。活动临近结束,Danel 宣告今年的 RubyConf China 2016 将于 9 月份在成都举行,让大家充满了期待。

    最后特别感谢所有嘉宾,感谢组织者,感谢与会的每一个人。

  • 报了名直接来就好了,没有邮件和短信确认。

  • Ruby ETL 工具漫谈 at 2016年05月16日

    我们在用这个 forklift 蛮轻量级的,更新也比较频繁

  • #18 楼 @rails_on_ll 不需要电话确认了

  • #17 楼 @chenzhong 报名了就想,没有电话确认了

  • #12 楼 @awking 抱歉,没有录视频计划,但是演讲的 slide 应该会分享出来。

  • #9 楼 @jerorry #11 楼 @rails_on_ll 活动完全免费,需要报名,名额有限,所以确定要参加请速速报名哦。

  • #11 楼 @huacnlee 噢,原来是这样,API 请求访问也是个麻烦,国内连海外不通畅,不稳定,经常在 API 访问环节出问题,这部分也能走国内网络就好了。

  • 支持!经常和 @larryzhao 一起聊一些技术问题,非常靠谱。