回顾与架构设计

最后更新于:2022-04-01 14:31:39

[TOC] 在我开始接触架构设计的时候,我对于这个知识点觉得很奇怪。因为架构设计看上去是一个很复杂的话题,然而他是属于设计的一部分。如果你懂得什么是美、什么是丑,那么我想你也是懂得设计的。而设计是一件很有意思的事——刚开始写字时,我们被要求去临摹别人的字体,到了一定的时候,我们就可以真正的去设计。 ## 自我总结 总结在某种意义上相当于自己对自己的反馈: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db0104c2.png) Output is Input 当我们向自己输入更多反馈的时候,我们就可以更好地调整我们的方向。它属于输出的一部分,而我们也在不断调整我们的输入的时候,我们也在导向更好地输出。 ### 吾日三省吾身 > 为什么你不看不到自己的方向? ## Retro Retro,又可以称为回顾,它的目的是对团队的激励、改进。它的模式的特点就是让我们更关注于 Less Well,即不好的地方。当我们无法变得更好的时候,它可以帮助我们反观团队自身,即不要让现状变得更差,避免让破窗效应[2](http://growth.phodal.com/#fn2)难以发生。 在敏捷团队里,Retro 通常会发生一个迭代的结束与下一个迭代的开始之间,这看上去就是我们的除旧迎新。相信很多人都会对自我进行总结,随后改进。而 Retro 便是对团队进行改进,即发生了一些什么不好的事,而这些事可以变好,那么我们就应该对此进行改进。 Retro 是以整个团队为核心去考虑问题的,通常来说没有理由以个人为对象。因为敏捷回顾有一个最高指导原则,即: > 无论我们发现了什么,考虑到当时的已知情况、个人的技术水平和能力、可用的资源,以及手上的状况,我们理解并坚信:每个人对自己的工作都已全力以赴。 下面就让我们来看看在一个团队里是如何 Retro 的。 ### Retro 的过程 它不仅仅可以帮助我们发现团队里的问题,也可以集思广益的寻找出一些合适的解决方案。Retro 的过程和我们之前说的数据分析是差不多的,如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db719228.jpg) Retro 流程 即: 1. 设定会议目标。在会议最开始的时候我们就应该对会议的内容达成一种共识,我们要回顾的主题是啥,我们要回顾哪些内容。如果是一般性的迭代 Retro,那么我们的会议主题就很明显了。如果是针对某一个特定项目的 Retro,那么主题也很明显。 2. Retro 的回顾。即回顾上一个 Retro 会议的 Action 情况,并进行一个简单的小结。 3. 收集数据。收集数据需要依赖于我们收集数据的模式,要下面将会说到的四种基本维度,或者是雷达图等等。不同的收集数据的形式有不同的特别,团队里的每个人都应该好好去参与。 4. 激发灵感。当我们寻找到团队中一个值得去庆祝的事,或者一个出了问题的事,我们就应该对这个问题进行讨论。并且对其展开了解、调查,让大家进一步看到问题,看到问题的根源。 5. 决定做什么。现在我们已经做了一系列的事,最重要的来了,就是决定我们去做什么。我们应该对之前的问题做出怎样的改进。 6. 总结和收尾。记录会议成果,更新文档等等。 ### 三个维度 以我们为例,我们以下面的三个维度去进行 Retro: 1. Well. 2. Less Well. 3. Suggestion 当然最后还会有一个Action: 1. Action 该模式的特点是会让我们更多的关注 Less Well,关注我们做的不好的那些。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db7b1754.jpg) Retro **Well**。我们在 Well 里记录一些让我们开心的事,如最近天气好、迭代及时完成、没有加班等等,这些事从理论上来说应该继续保持(KEEP)下去。 **Less Well**。关注于在这个迭代的过程中,发生了一些什么不愉快的事。一般来说,我们就会对 Less Well 加以细致的讨论,找出问题的根源,并试图找到一个解决方案。换句话来说,就是改变(CHANGE)。 **Suggestion/Puzzle**。如果我们可以直接找到一些建议,那么我们就可以直接提出来。并且如果我们对当前团队里的一些事情,有一些困惑那么也应该及早的提出来。 **Action**。当我们对一些事情有定论的时候,我们就会提出相应的 Action。这些 Action 应该有相应的人去执行,并且由团队来追踪。 ## 架构模式 > 模式就是最好的架构。 #### 架构的产生 在我开始接触架构设计的时候,我买了几本书然后我就开始学习了。我发现在这些书中都出现了一些相似的东西,如基本的分层设计、Pipe and Filters 模式、MVC 模式。然后,我开始意料到这些模式本身就是最好的架构。 MVC 模式本身也是接于分层而设计的,如下图是 Spring MVC 的请求处理过程: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db7ca44b.png) Spring MVC 而这个框架只是框架本身的架构,这一类也是我们预先设计好的框架。 在框架之上,我们会有自己本身的业务所带来的模式。如下图是我的网上搜罗到的一个简单的发送邮件的架构: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db7e8e5d.png) 发送邮件中的 Pipe and Filters 模式 这样的模式则是由业务发展的过程中演进出来的。 ### 预设计式架构 在我们日常使用的框架多数是预先设计的构架,因为这个架构本身的目标是明确的。系统会围绕一定的架构去构建,并且在这个过程中架构会帮助我们更好地理解系统。如下图所示的是 Emacs 的架构: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db8094a4.png) Emacs 架构 它采用的是交互式应用程序员应用广泛的模型-视图-控制器模式。 无论是瀑布式开发——设计好系统的框架,然后对系统的每个部分进行独立的完善和设计,最后系统再集成到一起。还是敏捷式开发——先做出 MVP,再一步步完善。他们都需要一定的预先式设计,只是传统的开发模式让两者看上去是等同的。 在过去由于 IT 技术变革小,新技术产生的速率也比较低,预先设计系统的架构是一种很不错的选择。然而,技术的发展趋势是越来越快,现有的设计往往在很短的一些时间里就需要推倒重来。 ### 演进式架构:拥抱变化 演进式架构则是我们日常工作的业务代码库演进出来的。由于业务本身在不断发展,我们不断地演进系统的架构。也就是这样模式下产生的架构系统会更加稳定,也更加优美。仅仅依赖于事先的设计,而不考虑架构在后期业务中的变化是一种不可取的设计模式。 这不并意味着不采用预先式设计,而是不一味着去靠原先系统的架构。 ## 浮现式设计 设计模式不是一开始就有的,好的软件也不是一开始就设计成现在这样的,好的设计亦是如此。 导致我们重构现有系统的原因有很多,但是多数是因为原来的代码变得越来越不可读,并且重构的风险太大了。在实现业务逻辑的时候,我们快速地用代码实现,没有测试,没有好的设计。 而下图算是最近两年来想要的一个答案: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db81bbce.jpg) 浮现式设计 浮现式设计是一种敏捷技术,强调在开发过程中不断演进。软件本身就不应该是一开始就设计好的,他需要经历一个演化的过程。 ### 意图导向 就和 Growth 一样在最开始的时候,我不知道我想要的是怎样的——我只有一个想法以及一些相对应的实践。接着我便动手开始做了,这是我的风格。不得不说这是结果导向编程,也是大部分软件开发采用的方法。 所以在一开始的时候,我们就有了下面的代码: ~~~ if (rating) { $scope.showSkillMap = true; skillFlareChild[skill.text] = [rating]; $scope.ratings = $scope.ratings + rating; if (rating >= 0) { $scope.learnedSkills.push({ skill: skill.text, rating: rating }); } if ($scope.ratings > 250) { $scope.isInfinite = true; } } ~~~ 代码在不经意间充斥着各种 Code Smell: 1. Magic Number 2. 超长的类 3. 等等 ### 重构 还好我们在一开始的时候写了一些测试,这让我们可以有足够的可能性来重构代码,而使得其不至于变成遗留代码。而这也是我们推崇的一些基本实践: > 红 -> 绿 -> 重构 测试是系统不至于腐烂的一个后勤保障,除此我们还需要保持对于 Code Smell 的嗅觉。如上代码: ~~~ if ($scope.ratings > 250) { $scope.isInfinite = true; } ~~~ 上面代码中的“250”指的到底是?这样的数字怎么能保证别人一看代码就知道250到底是什么? 如下的代码就好一些: ~~~ var MAX_SKILL_POINTS = 250; if ($scope.ratings > MAX_SKILL_POINTS) { $scope.isInfinite = true; } ~~~ 而在最开始的时候我们想不到这样的结果。最初我们的第一直觉都是一样的,然而只要我们保持着对 Code Smell 的警惕,情况就会发生更多的变化。 重构是区分普通程序员和专业程序员的一个门槛,而这也是练习得来的一个结果。 ### 模式与演进 如果你还懂得一些设计模式,那么想来,软件开发这件事就变得非常简单——我们只需要理解好需求即可。 从一开始就使用模式,要么你是专家,要么你是在自寻苦恼。模式更多的是一些实现的总结,对于多数的实现来说,他们有着诸多的相似之处,他们可以使用相同的模式。 而在需求变化的过程中,一个设计的模式本身也是在不断的改变。如果我们还固执于原有的模式,那么我们就会犯下一个又一个的错误。 在适当的时候改变原有的模式,进行一些演进变显得更有意义一些。如果我们不能在适当的时候引进一些新的技术来,那么旧有的技术就会不断累积。这些技术债就会不断往下叠加,那么这个系统将会接近于崩塌。而我们在一开始所设定的一些业务逻辑,也会随着系统而逝去,这个公司似乎也要到尽头了。 而如果我们可以不断地演进系统——抽象服务、拆分模块等等。业务在技术不断演进地过程中,得以保留下来。 ## 每个人都是架构师 每一个程序员都是架构师。平时在我们工作的时候,架构师这个 Title 都被那些非常有经历的开发人员占据着。然而,如果你喜欢刷刷 Github,喜欢做一些有意思的东西,那么你也将是一个架构师。 ### 如何构建一个博客系统 #### 如果你需要帮人搭建一个博客你先会想到什么? 先问一个问题,如果要让你搭建一个博客你会想到什么技术解决方案? 1. 静态博客(类似于 GitHub Page) 2. 动态博客(可以在线更新,如 WordPress) 3. 半动态的静态博客(可以动态更新,但是依赖于后台构建系统) 4. 使用第三方博客 这只是基本的骨架。因此如果只有这点需求,我们无法规划出整体的方案。现在我们又多了一点需求,我们要求是独立的博客,这样我们就把第4个方案去掉了。但是就现在的过程来说,我们还是有三个方案。 接着,我们就需要看看 Ta 需要怎样的博客,以及他有怎样的更新频率?以及他所能接受的价格? 先说说价格——从价格上来说,静态博客是最便宜的,可以使用 AWS S3 或者国内的云存储等等。从费用上来说,一个月只需要几块钱,并且快速稳定,可以接受大量的流量访问。而动态博客就贵了很多倍——我们需要一直开着这个服务器,并且如果用户的数量比较大,我们就需要考虑使用缓存。用户数量再增加,我们就需要更多地服务器了。而对于半动态的静态博客来说,需要有一个 Hook 检测文章的修改,这样的 Hook 可以是一个客户端。当修改发生的时候,运行服务器,随后生成静态网页。最后,这个网页接部署到静态服务器上。 从操作难度上来说,动态博客是最简单的,静态博客紧随其后,半动态的静态博客是最难的。 整的性价比考虑如下: | x | 动态博客 | 静态博客 | 半动态的静态博客 | | --- | --- | --- | --- | | 价格 | 几十到几百元 | 几元 | 依赖于更新频率 几元~几十元 | | 难度 | 容易 | 稍有难度 | 难度稍大 | | 运维 | 不容易 | 容易 | 容易 | | 数据存储 | 数据库 | 无 | 基于 git 的数据库 | 现在,我们已经达到了一定的共识。现在,我们已经有了几个方案可以提用户选择。而这时,我们并不了解进一步的需求,只能等下面的结果。 客户需要可以看到文章的修改变化,这时就去除了静态博客。现在还有第1和第3种方案可以选,考虑到第3种方案实现难度比较大,不易短期内实现。并且第3种方案可以依赖于第1种方案,就采取了动态博客的方案。 但是,问题实现上才刚刚开始。 #### 我们使用怎样的技术? 作为一个团队,我们需要优先考虑这个问题。使用怎样的技术解决方案?而这是一个更复杂的问题,这取决于我们团队的技术组成,以及未来的团队组成。 如果在现有的系统中,我们使用的是 Java 语言。并不意味着,每个人都喜欢使用 Java 语言。因为随着团队的变动,做这个技术决定的那些人有可能已经不在这个团队里。并且即使那些人还在,也不意味着我们喜欢在未来使用这个语言。当时的技术决策都是在当时的环境下产生的,在现在看来很扯的技术决策,有可能在当时是最好的技术决策。 对于一个优秀的团队来说,不存在一个人对所有的技术栈都擅长的情况——除非这个团队所从事的范围比较小。在一个复杂的系统里,每个人都负责系统的相应的一部分。尽管到目前为止并没有好的机会去构建自己的团队,但是也希望总有一天有这样的机会。在这样的团队里,只需要有一个人负责整个系统的架构。其中的人可以在自己擅长的层级里构建自己的架构。因此,让我们再回到我们的博客中去,现在我们已经决定使用动态的博客。然后呢? 作为一个博客我们至少有前后台,这样我们可能就需要两个开发人员。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db83658d.png) 前后台 (PS:当然,我们也可以使用 React,但是在这里先让我们忽略掉这个框架,紧耦合会削弱系统的健壮性。) 接着,作为一个前端开发人员,我们还需要考虑的两个问题是: 1. **我们的博客系统是否是单页面应用?**。 2. **要不要做成响应式设计**。 第二个问题不需要和后台开发人员做沟通就可以做决定了。而第一个问题,我们则需要和后台开发人员做决定。单页面应用的天然优势就是:由于系统本身是解耦的,他与后台模板系统脱离。这样在我们更换前端或者后台的时候,我们都不需要去考虑使用何种技术——因为我们使用 API 作为接口。现在,我们决定做成单页面应用,那么我们就需要定义一个 API。而在这时,我们就可以决定在前台使用何种框架: AngularJS、Backbone、Vue.js、jQuery,接着我们的架构可以进一步完善: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db84853e.png) 含前端的架构 在这时,后台人员也可以自由地选择自己的框架、语言。后台开发人员只需要关注于生成一个 RESTful API 即可,而他也需要一个好的 Model 层来与数据库交付。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db85ac05.png) 含前端后台的架构 现在,我们似乎已经完成了大部分的工作?我们还需要: 1. 部署到何处操作系统 2. 使用何处数据库 3. 如何部署 4. 如何去分析数据 5. 如何做测试 6. 。。。 相信看完之前的章节,你也有了一定的经验了,你也可以成为一个架构师了。 ### 相关阅读资料 -《程序员必读之软件架构》 ## 架构解耦 解耦是一件很有意思的过程,它也能反应架构的变迁。 ### 从 MVC 与微服务 在我初识架构是什么的时候,我看到了 MVC 模式架构。这种模式是基于分层的结构,要理解起逻辑也很简单。这个模式如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db7ca44b.png) Spring MVC 由我们的 Front controller 来处理由客户端(浏览器)发过来的请求,实际上这里的 Front controller 是 DispatcherServlet。 DispatcherServlet 负责将请求派发到特定的 handler,接着交由对应的Controller来处理这个请求。依据请求的内容,Controller 将创建相应 model。随后这个 model 将传到前端框架中渲染,最后再返回给浏览器。 但是这样的架构充满了太多的问题,如 view 与 controller 的紧密耦合、controller 粒度难以把控的问题等等。 #### Django MTV 我使用 Django 差不多有四年了,主要是用在我的博客上。与 MVC 模式一对比,我发现 Django 在分层上还是很有鲜明特性的: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db877f04.png) Django MTV架构 在 Django 中没有 Controller 的概念,Controller 做的事都交由 URL Dispatcher,而这是一个高级的 URL Dispatcher。它使用正则表达式匹配 URL,然后调用合适的 Python 函数。然后这个函数就交由相应的 View 层来处理,而这个 View 层则是处理业务逻辑的地方。处理完后,Model 将传到 Template 层来处理。 对比如下图如示: | 传统的MVC架构 | Django 架构 | | --- | --- | | Model | Model(Data Access Logic) | | View | Template(Presentation Logic) | | View | View(Business Logic) | | Controller | Django itself | 从上面的对比中,我们可以发现 Django 把 View 分层了。以 Django 对于 MVC 的解释来说,视图用来描述要展现给用户的数据。 而在 ROR 等其他的 MVC 框架中,控制器负责决定向用户展现哪些数据,而视图决定如何展现数据。 联想起我最近在学的 Scala 中的 Play 框架,我发现了其中诸多的相似之处: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db8916bc.png) Play 框架异步请求 虽然在 Play 中,也有 Controller 的概念。但是对于 URL 的处理先交给了 Routes 来处理,随后再交给 Controller 中的函数来处理。 不过与一般 MVC 架构的最大不同之处,怕是在于 Django 的 APP 架构。Django 中有一个名为 APP 的概念,它是实现某种功能的Web 应用程序。如果我们要设计一个博客系统的话,那么在这个项目中,Blogpost 是一个 APP、评论是一个 APP、用户管理是一个 APP等等。每个 APP 之中,都会有自己的 Model、View 和 Controller。其架构如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-28_5680cac0e0f9e.jpg) Django APP 架构 当我们需要创建一个新的功能的时候,我们只需要创建一个新的 APP 即可——为这个 APP 配置新的 URL、创建新的 Model 以及新的 View。如果功能上没有与原来的代码重复的话,那么这就是一个独立的 APP,并且我们可以将这个 APP 的代码 Copy/Paste 到一个新的项目中,并且不需要做修改。 与一般的 MVC 架构相比,我们会发现我们细化了这些业务逻辑原来的三层结构,会随着 APP 的数量发生变化。如果我们有三个 APP 的话,那么我们相当于有3*三层,但是他不是等于九层。这样做可以从代码上直接减少逻辑的思考,让我们可以更加集中注意力于业务实现,同时也利于我们后期维护。 虽是如此,后来我意识到了这样的架构并没有太多的先进之处。而这实际上是一个美好但是不现实的东西,因为我们还是使用同一个数据库。 #### 微服务与 Reactive 在微服务架构中,它提倡将单一应用程序划分成一组小的服务,这些服务之间互相协调、互相配合。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通。每个服务都应该有自己独立的数据库来存储数据。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db8b4a53.png) 分散数据 Django 从某种意义上有点接近微服务的概念,只是实际上并没有。因为它没有实现 Play 框架的异步请求机制。抱句话来说,应用很容易就会在调用 JDBC、Streaming API、HTTP 请求等一系列的请求中发生阻塞。 这些服务都是独立的,对于服务的请求也是独立的。使用微服务来构建的应用,不会因为一个服务的瘫痪让整个系统瘫痪。最后,这一个个的微服务将合并成这个系统。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db8d365d.png) Combined List 我们将我们后台的服务变成微服务的架构,在我们的前台使用 Reactive 编程,这样我们就可以结合两者的优势,解耦出更好的架构模式。然而,这其中还有一个让人不爽的问题,即数据库。如果我们使用多个数据库,那么维护成本也随着上升。而如果我们可以在后台使用类似于微服务的 Django MTV 架构,并且它可以支持异步请求的话,并在前台使用 Reactive 来编程,是不是就会更爽一点? ### CQRS 对于复杂的系统来说,上面的做法做确实很不错。但是对于一个简单地系统来说,这样做是不是玩过火了?如果我们要设计一个博客系统的话,那么我们是不是可以考虑将 Write/Read 分离就可以了? > 命令和查询责任分离 Command Query Responsibility Segregation(CQRS)是一种将系统的读写操作分离为两种独立模型的架构模式。 #### CQS 对于这个架构的深入思考是起源于之前在理解 DDD。据说在 DDD 领域中被广泛使用。理解 CQRS 可以用分离 Model 和 API 集合来处理读取和写入请求开始,即 CQS(Command Query Separation,命令查询分离)模式。CQS 模式最早由软件大师Bertrand Meyer(Eiffel语言之父,面向对象开-闭原则 OCP 提出者)提出。他认为,对象的行为仅有两种:命令和查询。 这个类型的架构如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db8ecf0d.png) CQS Basic > 除了编写优化的查询类型,它可以让我们轻松换 API 的一部分读一些缓存机制,甚至移动读取 API 的请求到另一台服务器。 对于读取和写入相差不多的应用来说,这种架构看起来还是不错的。而这种架构还存在一个瓶颈问题,使用同一个 RDBMS。对于写入多、读取少的应用来说,这种架构还是存在着不合理性。 为了解决这个问题,人们自然是使用缓存来解决这个问题了。我们在我们的应用服务外有一个 HTTP 服务器,而在 HTTP 服务器之外有一个缓存服务器,用于缓存用户常驻的一些资源。如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db90d950.png) 带缓存的 Web 架构 而实际上这样的服务器可能是多余的——我们为什么不直接生成HTML就好了? #### 编辑-发布分离 或许你听过 Martin Folwer 提出的编辑-发布分享式架构:即文章在编辑时是一个形式,而发表时是另一个形式,比如用 Markdown 编辑,而用 HTML 发表。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db921617.jpg) 编辑-发布分离 而最典型的应用就是流行于 GitHub 的 Hexo、Jekyll 框架之类的静态网站。如下图所示的是 Hexo 的工作流: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db93f401.png) Hexo 站点工作流 我们在本地生成我们的项目,然后可以创建一个新的博客、开始编写内容等等。接着,我们可以在本地运行起这个服务,除了查看博客的内容,还可以修改样式等等。完成上面的工作后,我们就可以生成静态内容,然后部署我们的应用到GitHub Page上。这一切看上去都完美,我们有两个不同的数据源——一个是 md 格式的文本,一个是最后生成的 HTML。它们已经实现了读写/分离: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db96a257.png) CQRS 进阶 但是作为一个前端开发人员,没有 JSON,用不了 Ajax 请求,我怎么把我的博客做成一个单页面应用? #### 编辑-发布-开发分离 因为我们需要交我们的博客转为 JSON,而不是一个 hexo 之类的格式。有了这些 JSON 文件的存在,我们就可以把 Git 当成一个 NoSQL 数据库。同时这些 JSON 文件也可以直接当成 API 来 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db983ce6.png) Git As NoSQL DB 其次,这些博客还需要 hexo 一样生成 HTML。 并且,开发人员在开发的时候不会影响到编辑的使用,于是就有了下面的架构: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-28_5680cac138d0d.png) 基于 Git 的编辑-发布分离 在这其中我们有两种不同的数据形式,即存储着 Markdown 数据的 JSON 文件和最后生成的 HTML。 对博客数量不是很大的网站,或者说一般的网站来说,用上面的技术都不是问题。然而有大量数据的网站怎么办?使用 EventBus: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db9a3074.png) CQRS 和 EventBus 在我之前玩的一个 Demo 中,使用 Python 中的 Scrapy 爬虫来抓取现有的动态网站,并将其变成静态网站部署到 AWS S3上。 但是上面仅仅只是实现了文章的显示,我们还存在一些问题: 1. 搜索功能 2. AutoComplete 等等的这些服务是没有用静态 API 来实现的。 ### CQRS 结合微服务 既然可以有这么多分法,并且我们都已经准备好分他们了。那么分了之后,我们就可以把他们都合到一起了。 #### Nginx as Dispatcher 最常见的解耦应用的方式中,就有一种是基于 Nginx 来分发 URL 请求。在这种情况下,对于 API 的使用者,或者最终用户来说,他们都是同一个 API。只是在后台里,这个 API 已经是不同的几个 API 组成,如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-16_57398db9b5ecd.png) Nginx 解耦微服务 客户端的请求来到 API Gateway,根据不同的请求类型,这些 URL 被分发到不同的 Service,如 Review Service、Order Service 等等。 对于我们想要设计的系统来说也是如此,我们可以通过这个 Dispatcher 来解耦我们的服务。 #### CQRS 结合微服务 现在,我们想要的系统的雏形已经出现了。 从源头上来说,我们把能缓存的内容变成了静态的 HTML,通过 CDN 来分发。并且,我们还可以将把不同的服务独立出来。 从实现上来说,我们将博客的数据变成了两部分: 一个以 Git + JSON 格式存在的 API,它除了可以用于生成 HTML,另外一部分作为 API 来使用。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-17_573a9919e50ac.png) CQRS 结合微服务 最后,我们可以通过上面说到的 Nginx 或者 Apache 来当这里的 Request Dispatcher。 * * * 1. [基于 Jenkins 快速搭建持续集成环境](https://www.ibm.com/developerworks/cn/java/j-lo-jenkins/)[↩](http://growth.phodal.com/#fnref1) 2. 以一幢有少许破窗的建筑为例,如果那些窗不被修理好,可能将会有破坏者破坏更多的窗户。最终他们甚至会闯入建筑内,如果发现无人居住,也许就在那里定居或者纵火。又或想像一条人行道有些许纸屑,如果无人清理,不久后就会有更多垃圾,最终人们会视为理所当然地将垃圾顺手丢弃在地上。因此破窗理论强调着力打击轻微罪行有助减少更严重罪案,应该以零容忍的态度面对罪案。[↩](http://growth.phodal.com/#fnref2)
';