将Java EE单体应用打造成微服务


【编者的话】如何将单体应用拆分成微服务相信是很多人共同的疑问,本文作者就技术和组织结构等方面为我们提供了一个思路,一起来看看吧~

紧接着上篇为什么微服务应该是事件驱动的介绍博客,我还想再花一些文章和篇幅就这块为我即将参加的一些演讲做些准备(与你相约jBCNconf在旧金山的Red Hat峰会)。你可以通过在twitter上关注我@christianposta来跟进这个项目的最新进展。在本文里,我们将讨论第一部分,即如何拆分一个单体应用。

这些文章里我所深入探索的单体应用,教程案例取自Ticket Monster,它已经在很长一段时间内被当成是讲解如何使用Java EE和Red Hat技术来构建一个出色应用的经典例子。我们选用Ticket Monster是因为它是一个精心编写的应用,它在“作为教程不够详尽”和“作为例子来讲太过复杂”这两条线之间权衡的很好。它很适用于教学用途,而且我们可以具体以它为蓝本,就真实的样例代码来探讨某些方法的利弊。在进一步讨论之前,请先仔细看一下相关域和当前架构

tm-arch.png


观看上述的现有架构,我们可以看到各项事物事先都已经被很好的拆分开来。我们有UI组件,业务服务,以及从其他各块很好地分离和解耦并且被打包成一个单独可部署文件(在这里是一个WAR文件)的长期持久化存储。如果我们仔细看下源代码的话,我们会看到它也拥有类似的结构。倘若我们想要部署这个应用的话,任何组件有任何变动都意味着整个部署包的一次重新构建,测试和发布。推进微服务的其中一个先决条件便是组件的自治,这样一来他们便能够相对独立地进行开发,测试和部署,而无需中断系统的其他部分。因此,要是我们在这里就只是打造出不同层,然后将它们独立地部署下去会怎样?那样我们能否实现一些自治能力呢?

过去我们花了大量的时间论证这一类型的架构,而这些工作似乎是有意义的。我们希望能够按照各个组件自己的需求来进行扩展。如果我们需要处理更多的web请求那就向外扩展web层就好了。如果那些服务开始陷入瓶颈,那就扩展业务服务层。与数据库以及数据访问层打交道和管理维护对于整个应用/服务的其他部分而言是相对独立的。从中间层和数据访问中“解耦”出来UI逻辑是一个不错的指导原则,但是不要把它和必需的分层搞混淆了。

实践中的真实情况是所有这些“分层”的架构化组件,针对所有对它的单独关注来看很容易殒命于数据和数据库引发的怪问题。我们可以尽情地添加所需的CPU,中间件和UI,但是无论我们的网络、计算、内存等等变得如何快速,对于这类系统的瓶颈往往都在于域模型和最终的数据库的相互竞争。这里面的压力便是“域模型”———互联网公司所践行的微服务可能不会拥有像FSI或者保险、零售商那样复杂、模糊而又矛盾的域模型。比如,推特有一个简单的域:发布和展示推文。但是这一案例也会在大规模场景下变得复杂———一些企业正开始同时遭遇这两方面问题的困扰———域模型及其复杂性与如何扩展它同等重要(并且常常会
在努力扩展时有所妨碍)。所以如今你单纯想着“我们只要用一个像MongoDB这样的NoSQL数据库就能实现后端的扩展性”的话———你现在恐怕会遇到更多问题。

那说说我们的团队?像这样架构一个系统的另外一部分是因为这样我们就能在这些分层上分配各个专业的团队以不同的效率,在不同地方,用不同的工具等相对独立地进行工作。他们只需要和其他人共享一个接口,然后便能自主进行他们的工作。这里用到一点康威定律:


设计系统的组织,其产生的设计和架构等价于组织间的沟通结构。
不幸的是,我感觉它与事实恰好相反。这并不是说通过搭建这个架构,我们就可以创造这个机会来给团队专业化分工和提高效率。因为我们这样的组织结构反而会倒逼我们沿着这个系统架构演进。就像我们有很多独立的数据库团队,UI团队,安全,运维,QA,构建,以及发布等等。这正是我们过去十年来的组织结构。然而,如果你看下一些企业践行微服务的成功案例的话,你会发现他们所组织的结构有些不太不一样

让我们一起来看看到底发生了什么。以Ticket Monster为例,业务要求我们改变所处理网站的管理方式。他们要求我们添加一些相关的额外字段来追踪音乐会在网站上添加和删除的频繁程度,因为他们想据此添加一些预测分析,基于时间,位置,天气等决定在未来添加该活动是否是一个好主意。如果业务想要给管理用户展示这个预测分析的话,这可能还会涉及到UI团队。这也必将涉及改变应用的业务逻辑层,并且它肯定会导致数据库的变动。我们想给我们的应用添加一些功能特性,而这引发了所有分层的波动效应,而更为重要的是,涉及到了整个团队。如今我们不得不需要项目经理为所有相关团队协调和跟进会议。我们需要在创建票单的时候保证UI和团队做什么事情不会让QA,安全,运维等全部介入。所有这一切造就了我们各个团队之间的复杂同步处境,而如今我们不得不协调我们分层的全部变动,构建和发布(并且一切都同时部署!)。这不是我们想要的那种自治。我们无法相互独立地做出变动,事实上,我们已经变得相当脆弱。

针对我们的Ticket Monster应用,让我们改为将功能切分成可拼接的各个“垂直频道”而不是以技术或者组织层面。每一个垂直频道有它自己的“UI”(或者UI组件),“业务服务”和“数据库”,它们用于管理网站的特定功能。(然而,在第一步里,我们将把UI留作一个单体,然后将它背后的模块切分开来。我们会回过头来切分UI,尽管这有其自己的挑战。)Ticket Monster还允许用户查看和预订音乐会票。让我们将这块功能切分到它自己的垂直频道里。它可能也有忠诚度,推荐,搜索,广告,个性化等等。我们将这些都切分到它们自己的垂直频道里,每个都拥有它们自己的数据库,UI,和集成点(REST服务,后端,等等)。如果我们需要对网站忠诚度这块功能做出变动的话,我不需要去重新部署整个单体的业务服务层或是任何相关的服务,比如搜索。我可以从UI到DB部署忠诚度这块的功能而无需再被迫触发其他服务的变动。在理想情况下,一个团队拥有和运维的每个服务都将如此。

next-arch.png


这给我们各服务之间的代码注入了更强的凝聚力,以及更多的自主权。而一旦你开始努力去理解切分业务功能到各个垂直频道的意义时,我们便可以探索一下针对每个垂直频道而言,什么才看起来像是它的上下文边界;比如它在一个上下文边界里是否适用CQRS。又或者基于它的读/写模型(文档?关系型?图)应该适用哪种类型的数据库以及你是否偏向于一致性或者能够容忍数据的丢失/数据的不一致。再者可能看上去像的什么事务啊,补偿啊,认错啊,等等,以及诸如此类的...现在我们可以就单个服务怎样才是最好的这一问题作出这些决策,它不是单个分层或者一个单体的共同分母最小化。这也是我们在下一篇文章里将继续探索的内容!敬请关注!

原文链接:Carving the Java EE Monolith Into Microservices(翻译:吴佳兴)

0 个评论

要回复文章请先登录注册