其实,很难对微服务下一个准确的定义。就像NoSQL,我们谈论了好几年的NoSQL, 知道NoSQL的大致含义,也可以根据不同的应用场景选择不同的NoSQL数据库,但是我 们还是很难对它下一个准确的定义。类似的,关于什么是“函数式编程”,也或多或少存在 同样的窘境。我们可以轻松地选择不同的函数式编程语言,可以轻松地写出函数式编程风 格的代码,但很难对什么是函数式编程下一个准确的定义。
实际上,从业界的讨论来看,微服务本身并没有一个严格的定义。不过,Thoughtworks 的首席科学家——马丁 •福勒(Martin Fowler)先生,对微服务的这段描述,似乎更加通俗易懂。
实际上,从业界的讨论来看,微服务本身并没有一个严格的定义。不过,Thoughtworks 的首席科学家——马丁 •福勒(Martin Fowler)先生,对微服务的这段描述,似乎更加通 俗易懂:
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务, 服务之间互相协调、互相配合,为用户提供最终价值 每个服务运行在其独立 的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP 的RESTfbl API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部 署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制对具体的
微服务架构通过对特定业务领域的分析与建模,将复杂的应用分解成小而专一、耦合 度低并且高度自治的一组服务,每个服务都是很小的应用。那么,微服务中提到的“微”, 到底是个什么样的“微”呢?
实际上,关于多微的服务才合适,是一个非常有趣的话题。有人觉得使用代码行数来 作为“微”的衡量标准比较合适,而有些人认为,既然是微服务,就应该简单,应该在很 短的时间内,譬如2周内,能够容易地重写该服务,这样才符合微的定义。
我们知道,不同的语言有不同的特点。静态类型语言的主要优点在于其结构规范,存 在编译期的语法检查、便于调试、类型安全性高,通常其继承关系简洁明了,IDE对其支 持也更加友好;但其缺点是为此需要写更多的类型相关代码。因此如果要实现同样的功能, 代码量相对稍多,这类语言的典型代表有Java、C++等。
动态语言,其灵活性较高,运行时可以改变内存结构,无类型检査,无须写较多的类 型相关的代码;但缺点是不方便调试,无编译期检查,因此对个体的能力要求较高。尤其 是项目复杂度高或者代码量较大的项目,如果没有足够高的测试覆盖率,维护起来更是举 步维艰。典型的代表如JavaScript、Ruby或者Python等。
譬如,对于经典的康威生命游戏而言,游戏开始时,细胞随机地被指定为存活或者 死亡状态(黑色表示存活,白色表示死亡),每个细胞都会不断演进,并且在演进的过程 中,每个细胞下一代的状态由该细胞当前周围8个细胞的状态所决定,其具体规则如图2-1 所示。
・规则1,如果一个细胞周围有少于2个存活细胞,则该细胞无论存活或者死亡,下 一代将死亡。
・规则2,如果一个细胞周围有多于3个存活细胞,则该细胞无论存活或者死亡,下 一代将死亡。
・规则3,如果一个细胞周围有3个存活细胞,则该细胞下一代将存活。
-规则4,如果一个细胞周围且仅有2个存活细胞,则该细胞下一代状态保持不变。
感兴趣的读者,可以尝试使用自己熟悉的开发语言来解决这个问题。通常,由于语言 特性的差异,静态语言实现的代码量会略大于动态语言。譬如用Java实现的版本,代码量 会略多于用Ruby实现的版本。
对于实现同样的功能,选择不同的语言,代码的行数会千差万别。因此,代码 行数这种量化的数字显然无法成为衡量微服务是否够“微”的决定因素。
从我们接触编程的第一天起,老师就教我们,编写代码的原则要符合“高内聚、低耦 合”。所谓高内聚,是一个模块内各个元素彼此结合的紧密程度高。而低耦合,则是指对于 一个完整的系统,模块与模块之间,尽可能独立存在。换句话说,对于每个模块,尽可能 独立完成某个特定的子功能。髙内聚、低耦合的系统有什么好处呢?在系统持续发展的过 程中,高内聚、低耦合的系统具有更好的重用性、可维护性和扩展性。
在面向对象的设计中,更是有放之四海而皆准的“SOLID原则”。熟悉的读者一定知 道,SOLID原则中的S表示的是SRP (Single Responsibility Principle,单一职责原则):即 一个对象应该只有一个发生变化的原因,如果一个对象可被多个原因改变,那么就说明这 个对象承担了多个职责。更多关于单一职责原则的解释,请参考罗伯特・C •马丁(Robert C. Martin)的《敏捷软件开发:原则、模式和实践》一书。
实际上,UNIX的设计是这一原则的完美体现者:在UNIX中,各个命令都独立负责 一个单一的功能,但命令和命令之间,可以通过管道连接起来,组合实现更强大的功能。 譬如,将du, sort以及sed几个命令用管道连接起来,能方便地获取当前目录下占用空间 最多的5个资源,如下代码所示:
du -s * | sort -nr I sed 5q
类似的,对于每个服务而言,我们希望它处理的业务逻辑能够单一,在服务架构层面 遵循单一职责原则。也就是说,微服务架构中的每个服务,都是具有业务逻辑的,符合高 内聚、低耦合原则以及单一职责原则的单元,不同的服务通过"管道”的方式灵活组合, 从而构建出庞大的系统。
服务之间应通过轻量级的通信机制,实现彼此间的互通互联,互相协作。所谓轻量级 通信机制,通常指语言无关、平台无关的交互方式。譬如,某些复杂的系统,由Java、Ruby 以及Node等不同开发语言实现的部分组成,不同部分之间能够釆用语言无关、平台无关 的方式进行交互,如图2-2所示。
对于轻量级通信的格式而言,我们熟悉的XML或者JSON,它们的解析和使用基本与 语言无关、平台无关。对于轻量级通信的协议而言,通常基于HTTP,能让服务间的通信 变得标准化并且无状态化。目前,大家所熟悉的REST (Representational State Transfer), 是实现服务之间互相协作的轻量级通信机制之一。
对于我们所熟悉的Java RMI或者.NET Remoting等,虽然这类机制能够使用RPC (Remote Procedure Call)的方式简化消费者端的调用,使消费者一端像调用本地接口 -样 调用远端的接口,但其最大的劣势在于:其对语言或者平台有较强的耦合性,同时灵活性 和扩展性较差。譬如,如果使用Java的RMI作为通信协议,则通信的两端必须采用运行 在JVM之上的开发语言实现。
对于微服务而言,通过使用语言无关、平台无关的轻量级通信机制,使服务与服务之 间的协作变得更加标准化,也就意味着在保持服务外部通信机制轻量级的情况下,团队可 以选择更适合的语言、工具或者平台来开发服务本身。
独立性指在应用的交付过程中,开发、测试以及部署的独立。
在传统的单块架构应用中,所有功能都存在于同一个代码库中。当修改了代码库中的 某个功能,很容易出现功能之间相互影响的情况。尤其是随着代码量、功能的不断增加, 风险也会逐渐增加。换句话说,功能的开发不具有独立性。
除此之外,当多个特性被不同小组实现完毕,需要经过集成,经过回归测试,团队才 有足够的信心,保证功能相互配合、正常工作并且互不影响。因此,测试过程不是一个独 立的过程。
当所有测试验证完毕,嘍麓高并欢、高可慮部|性能标料分享群:攏69512 部署过程中,单块架构部署包禅被誌生M生冒噸,如棣格性存 在缺陷,则有可能导致整个部署过程的失败或者回滚。
因此,在单块架构中,功能的开发、测试、构建以及部署耦合度较高,相互影响。
在微服务架构中,每个服务都是一个独立的业务单元,当对某个服务进行改变时,对 其他的服务不会产生影响。换句话说,服务和服务之间是独立的。
对于每个服务,都有独立的代码库。当对当前服务的代码进行修改后,并不会影响其 他服务。从代码库的层面而言,服务与服务是隔离的。
对于每个服务,都有独立的测试机制,并不必担心破坏其他功能而需要建立大范围的 回归测试。也就是说,从测试的角度而言,服务和服务之间是松耦合的。