【问题标题】:developing a maintainable RPC system开发可维护的 RPC 系统
【发布时间】:2011-05-14 00:49:35
【问题描述】:

我正在开发一个 Web 应用程序,该应用程序将广泛使用 AJAX 技术进行客户端/服务器通信...特别是 JSON-RPC。 Zend Framework 正在服务器端使用,它提供了一个不错的 JSON-RPC 服务器,我想使用它。

我的目标是构建一个可维护的系统,该系统将大量的服务器端功能子集暴露给客户端 (javascript),而不需要不必要的代码重复。我看过很多关于如何使用 ZF 的 JSON-RPC 服务器的博客文章和教程(请参阅 herehere),但它们似乎都旨在公开一个小型的、可公开使用的 API。代码重复很常见,例如一篇博文暴露了以下方法:

public static function setTitle($bookId, $title) {
    $book = new Nickel_Model_Book($bookId);
    $book->setTitle($title);
    $book->update();
    return true;
}

我不喜欢有两个setTitle 方法的事实。如果一个方法签名发生变化,另一个必须保持同步......如果您的 API 很广泛,这似乎是可维护性的噩梦。在我看来应该有一个Book 类,一个setTitle 方法。

我最初的想法是在我想要公开的方法/类中添加一个 docblock 注释 @export。当我决定公开setTitle 方法时,我只是添加注释而不是新方法。

我看到的一个潜在问题涉及对象持久性。在服务器端,setTitle 设置对象的 title 属性是有意义的……但在调用 update() 之前不要将其保存在数据库中。客户端,调用setTitle 应该会立即影响数据库。一种可能的解决方案是修改所有访问器,以便它们采用可选的第二个参数,表示修改应立即更新数据库:

function setTitle($title, $persist = false) {
    $this->title = $title;

    if ($persist) $this->update();
}

某种代理类可以确保为所有客户端 RPC 调用设置 $persist 标志。

另一个问题是 PHP 对象的序列化。在服务器端,进行 OO 风格的 $book->setTitle("foo") 调用是有意义的,但由于缺少状态,客户端 book.setTitle(1234, "foo") 是有意义的(其中 1234 是书的 ID)。我对此的解决方案是让上述代理类负责以某种方式将book.setTitle(1234, "foo") 变成:

$book = new Book();
$book->load(1234);
return $book->setTitle($title);

我觉得这个问题之前一定已经解决或讨论过......但我在网上找不到很多资源。这看起来是一个合理的解决方案吗?

【问题讨论】:

  • 你解决了这个问题还是仍然存在?您能否发布您的解决方案或删除问题或告诉我们开始思考!
  • 有时当您认为应该存在的某事物的模式不存在时,这是因为您设想的解决方案不正确。您不想将大量方法公开为 Web 服务的原因是因为客户端应该获得尽可能粗略的方法。您的服务设计应该来自客户端需求。客户应该可以访问理想的简单界面。创建课程粒度服务,并根据组合需要使它们更细粒度。与公共 api 和您的库分离是值得付出额外努力的。
  • 我正在用nowjs.comexpressjs.com 解决这个确切的问题
  • 如果我理解得很好:你想摆脱控制器,而是直接用你的视图调用你的模型。没有?

标签: javascript ajax zend-framework rpc json-rpc


【解决方案1】:

我仔细考虑了你的问题几分钟。如果你想尝试,并且它是在 PHP 中的,你可以制作一些形式为

的魔术方法

设置{对象}的{属性}。

您可以通过神奇的 __call 方法来做到这一点。

这样您就不必显式定义方法,但仍会设置属性。

魔术方法将识别它必须实例化的对象类型以及要在其上设置的属性,然后持久化。

您必须找到某种方法来区分远程调用和本地调用,这样如果您正在进行本地库调用并且您确实坚持远程调用,您就不会错误地坚持。

我觉得我以某种方式误解了你的问题,但我试过了。

【讨论】:

    【解决方案2】:

    嗯,

    您想要做的事情在纸面上听起来确实不错,并且您希望代码是可维护的,并且根据您的实际系统,您所要求的甚至可能有些安全,但是它已接近危险且容易出错。

    无论如何,您可能必须自己完成大部分工作,或者只是在所有方法调用中插入某种拦截器,这是一个简单的示例:

    class Book {
      public $title = '';
      public $id = 0;
      public function setTitle($string) {
        $this->title = $string;
        echo "title called with $string\n";
      }
      public function twoArgs($arg1,$arg2) {
        echo "Multi-call: $arg1,$arg2\n";
      }
    }
    class Api {
      public function parse($json) {
        $jsonobj = json_decode($json,true);
        foreach ($jsonobj as $key=>$value) {
          $class_name = $key;
          $obj = new $class_name();
          foreach ($value as $vkey=>$vvalue) {
            if (method_exists($obj,$vkey)) {
              call_user_func_array(array($obj,$vkey),$vvalue);
            } else if (isset($obj->$vkey)) {
              echo "Setting $vkey\n";
              $obj->$vkey = $vvalue;
            }
          }
        }
      }
    }
    
    $json = json_encode(array('Book' => array('id' => 1234, 'setTitle' => array('The new title'))));
    $api = new Api();
    $api->parse($json);
    $json = json_encode(array('Book' => array('id' => 1234, 'twoArgs' => array('arg1 :) ', 'arg2 :]'))));
    $api->parse($json);
    

    显然,您希望添加逻辑来处理持久标记,并加载或允许它们传入构造函数:[args] 并处理它。等等。当涉及到公开函数时,你可以在文档中公开它们,只要它们从那时起都是公共函数,它们都是可以访问的。

    【讨论】:

      【解决方案3】:

      您要查找的是 Service Layer

      它们的实体应该是纯粹的数据容器(除非您使用 Active Record),您应该只公开您的服务层,而这反过来又可以访问它们的实体和它们各自的方法。

      你的服务等级应该是这样的:

      class BookService extends Service {
      
          //...
      
          public function changeBookTitle( $title, Book $book )
          {
              //verify if the $title is correct and valid
              $book->setTitle( $title );
              $this->methodToGetThePersistenceManager()->save( $book );
      
              //fire events, create a result object, etc...
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-03
        • 2021-11-25
        • 1970-01-01
        • 2021-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多