我应该让我的REST网关成为一个库吗?

时间:2016-10-08 15:17:45

标签: java rest dependencies

假设有一个包含多个客户端的REST服务。每个客户端必须大致相同才能调用REST服务:构造URL,发送请求,反序列化响应,将HTTP返回代码映射到异常等等。

这似乎是重复的代码,所以将REST网关代码放入可重用的库中可能是个好主意。在Java中,这可能如下所示:

  • 有一个jar文件依赖于Jersey并包含其余的客户端代码。
  • 其余客户端是一个简单的类或CDI bean。
  • 所有客户只需依赖此jar并致电上述类别的成员。

因此,没有客户担心REST,只能调用方法。

这是个好主意吗?

(此问题来自this other question

1 个答案:

答案 0 :(得分:2)

这可能会让您遇到依赖项问题。这是一个非常典型的依赖关系问题,并没有绑定到REST或Jersey。但是,让我们来看看以下情况:

依赖关系

假设有两个服务器(S1和S2),两个客户端(C1和C2)和两个包含用于访问服务器(L1和L2)的其余客户端代码的库。两个客户端都需要查询两个服务器,因此调用结构如下所示:

C1 ---> L1 ---> S1
  \   ^
   \ /
    X
   / \
  /   v
C2 ---> L2 ---> S2

此外,L1和L2都依赖泽西岛。此外,容器(可能是在Glassfish或Wildfly应用程序服务器上运行您的应用程序)依赖于Jersey(或至少在jax-rs API [1]上)。

C1的简化依赖结构如下所示:

              C1
            /  | \
         /     |   \
       <       v     >
Container     L1       L2
   |           |        |
   v           v        v
Jersey      Jersey    Jersey

只要Jersey的三个版本都相同,一切都很好。什么都没有问题。但如果版本不同,您可能会遇到讨厌的NoClassDefFoundError s,MethodNotFoundError等等[2]。

刚性结构

如果版本足够相似,您将不会直接遇到这些错误。也许它会工作很长一段时间。但随后可能发生以下情况:

  • 您想要更新容器。这会更新可能不兼容的Jersey容器版本。 ==&GT;繁荣。因此,使用L1和L2会阻止您更新。只要L1和L2未更新,您就会遇到旧版本。
  • L2更新为Jersey的新版本。只有在L1和容器也更新时才能使用它。所以你坚持使用旧版本(你可以这样做,因为REST的松散耦合)。但是随后新的功能被添加到S2,只能用于新版本的L2,而且你再次陷入困境。

请记住,错误可能会也可能不会发生。无法保证您遇到麻烦,并且无法保证它会起作用。这是一个风险,一个定时炸弹。

另一方面,这只是一个简单的例子,只有两个服务和两个客户端。当有更多时,风险正在增加。

稳定的依赖原则

依赖关系应遵循Stable Dependencies Principle(SDP),其中&#34;取决于稳定性。&#34;鲍勃叔叔使用数字传入和传出依赖来定义它,但SDP可以从更广泛的意义上理解。

当我们查看给定的依赖结构并用zeava替换Jersey时,与SDP的连接变得很明显。这个问题在番石榴中很常见。问题是番石榴通常不向下兼容。它是一个相当不稳定的库,可以在应用程序中使用(不稳定),但是你永远不应该在可重用的库中使用它(它应该是稳定的)。

对于泽西来说,这并不是那么明显,因为你可能会认为泽西岛很稳定。事实上,就Bob Martin的定义而言,它非常稳定。但L2也可能非常稳定。也许它是由另一个团队开发的,一些人离开了公司,没有人敢去触摸它,因为去年有人试图这样做,导致C2团队有依赖性问题。也许L2也是稳定的,因为在开发C1和L2的团队的管理者之间存在一些持续的争吵,所以L2经理声称没有资源,没有预算或其他什么,所以更新L2只能在明年年底完成。 / p>

因此,根据您的代码库,您的组织结构等库可以比您的库更稳定。

例外和补救措施

  • 有些语言具有&#34;隔离的依赖关系&#34;,这意味着您可以在同一个应用程序中拥有同一个库的多个版本。
  • 您可以重新打包泽西岛(复制源,更改包名称并自行维护)。然后你可以拥有版本X中的普通Jersey和版本Y中的重新打包版本的Jersey。这当然有效但当然这有其自身的问题。它需要做很多工作,你必须维护不是由你编写的软件。
  • 如果所有内容都是在同一个团队中开发的,那么这个问题可以忽略不计。然后你可以控制整个事情,而且你不依赖其他团队做某事。然而,一旦你的代码库增长,更新到新版本可能会有很多工作,因为你不能再逐步完成它,但你必须立即将许多服务移动到新版本。
  • 像maven这样的构建工具可以让您控制所依赖的版本。您可以考虑在L1和L2中标记Jersey依赖optional,在C1和C2中标记provided。然后它不包含在你的war文件中,因此只有一个版本的Jersey(容器中的那个)。只要此版本与接口兼容,它就可以正常工作[3]。

做什么

我建议将您的表示类(即您的DTO)放入客户端jar中,让每个客户端决定使用哪个库来进行REST调用。通常那些REST gateways非常简单,在应用程序之间共享它们通常不值得冒这个风险。至少在没有了解的情况下考虑这里提到的问题,然后再进入这些问题。

[1]为了简化说明,我们忽略了Jersey和jax-rs API之间的区别。论点是一样的。

[2]实际上它有点复杂。通常,您的构建工具(maven等)将选择一个版本的Jersey并将其放入war文件中。泽西岛的另一个版本是&#34;因冲突而被省略&#34;与其他版本。只要版本兼容,这就没问题。因此,您最终会在容器中使用两个版本,并在war文件中使用一个版本。虽然前一种情况下的接口兼容性应该足够,但对于剩下的两个版本来说,它还不够。

[3]因为只有一个版本,所以[2]中提到的问题不会发生。接口兼容性问题当然存在。