「札记」基业长青、轻量级微服务架构

Learning Jan 31, 2024

《基业长青》

这些高瞻远瞩公司虽然杰出,纪录却绝非完美无缺。(审视一下你本人列出的高瞻远瞩公司,我们怀疑在其历史中,这些公司中的大多数,甚至所有公司至少经历过一次严重的挫折,有的可能经历过好几次。)迪斯尼在1939年遭遇严重的周转困难,被迫公开上市;后来,在8O年代初期,股市做手看上它低迷的股价,使它几乎无法以独立的实体继续生存。波音公司在3O年代中期、4O年代末期都面临过严重的困难,到7O年代初又遇到严重困境,裁员6万多人。3M诞生之初,是一家失败的矿场,在2O世纪初期几乎倒闭。惠普公司在 1945年面临严重的挫折,1990年眼睁睁地看着股价跌破票面值。索尼创业前5年(1945年一1950年)推出的产品一再失败,到7O年代,在录像机市场争夺战中,自己的贝它小带系统败给VHS大带系统。福特汽车在8O年代初期出现美国企业史上最大的年度亏损,3年内共亏损33亿美元,之后才开始令人称羡地反败为胜和长久所需的企业再造。花旗银行早在1812年,也就是拿破仑进攻莫斯科时创立,但是在19世纪下半叶以及 1930年代大萧条期间,都曾营运不振,到8O年代末期,因为应付全球不良贷款组合的问题,又苦苦挣扎,IBM在1914年几乎破产,1921年时再度濒临破产边缘,到9O年代初期又遭遇困境。 的确,本研究中所有高瞻远瞩公司在某些时候都曾遇到过挫折、犯过错误,有些公司在我们撰写本书时正经历困难;但是——这一点是关键——高瞻远瞩公司展现出可观的弹性,展现出从逆境中恢复的能力。

《轻量级微服务架构》

把大的问题分解为容易解决的小问题,找到小问题的解决办法,再来解决大问题,这就是分而治之的哲学。正如万事万物由分子、原子组成一样,软件也可以分解为基本单元,以这样的基本单元进行开发、测试、维护,是解决大规模系统建设的思路。分而治之首先要解决如何分的问题,企业软件的分法应该是以业务驱动的,而不是以技术驱动的,也就是分解为独立的业务逻辑,而这样的不可再分的业务逻辑就是微服务。

微服务是一种分布式系统架构,它建议我们将业务切分为更加细粒度的服务,并使每个服务的责任单一且可独立部署,服务内部高内聚,隐含内部细节,服务之间低耦合,彼此相互隔离。此外,我们根据面向服务的业务领域来建模,对外提供统一的API接口。微服务的思想不只是停留在开发阶段,它贯穿于设计、开发、测试、部署、运维等软件生命周期阶段。

请求会先发送到LB上,通过LB上的路由算法(例如轮询或哈希),将请求转发到后面具体的Web Server上,这类请求转发技术被称为Reverse Proxy(反向代理)。 由于进入LB的请求(流量)被均衡到下方各台Web Server中了,流量得到了分摊,负载得到了均衡,因此该技术也称为Load Balance(负载均衡)。

使用微服务架构开发应用程序,我们实际上是针对一个个微服务进行设计、开发、测试、部署,因为每个服务之间是没有彼此依赖的,大概的交付流程如图1-3所示。 图1-3 微服务交付流程 在设计阶段,架构师将产品功能拆分为若干服务,为每个服务设计API接口(例如REST API),需要给出API文档,包括API的名称、版本、请求参数、响应结果、错误代码等信息。在开发阶段,开发工程师去实现API接口,也包括完成API的单元测试工作。在此期间,前端工程师会并行开发Web UI部分,可根据API文档造出一些假数据(我们称为“mock数据”)。这样一来,前端工程师就不必等待后端API全部开发完毕,才能开始自己的工作。在测试阶段,前后端工程师分别将自己的代码部署到测试环境上,测试工程师将针对测试用例进行手工或自动化测试,随后产品经理将从产品功能上进行验收。在部署阶段,运维工程师将代码部署到预发环境中,测试工程师再次进行一些冒烟测试,当不再发现任何问题时,经技术经理确认,运维工程师将代码部署到生产环境中,这一系列的部署过程都需要做到自动化,才能提高工作效率。

微服务架构的特点 1.微小度颗粒——微服务的粒度是根据业务功能来划分的,对于某些复杂的业务来说,可能粒度较大,对于相对简单的业务而言,可能粒度较小。总之,微服务的粒度可大可小,但往往我们更希望它尽可能的小,但又不希望微服务之间有直接的依赖,因此粒度的划分是一件非常考验架构师水平的事情。 2.责任单一性——我们需要确保每个微服务只做一件事情,也就是我们经常提到的“单一职责原则”,该原则对微服务的划分提供了指导方针。如果我们将一个服务提供多个API,那么就要确保每个API必须做到责任单一性。 3.运行隔离性——每个服务相互隔离,且互不影响。也就是说,每个服务运行在自己的进程中。众所周知,进程之间是隔离的,是安全的,而进程内部或线程之间的资源是共享的。换句话说,一个服务出了问题,不会影响到其他微服务。 4.管理自动化——随着业务功能不断增多,服务的数量也会逐渐增加,我们需要对服务提供自动化部署与监控预警的能力,这样才能更加高效地管理这些服务。需要注意的是,我们必须借助自动化技术,才能确保管理服务变得更加容易。

微服务技术选型 我们可使用Spring Boot作为微服务开发框架,Spring Boot拥有嵌入式Tomcat,可直接运行一个jar包来发布微服务,此外它还提供了一系列“开箱即用”的插件,可大大提高我们的开发效率,我们也可以去扩展更多的插件。 在发布微服务时,可连接ZooKeeper来注册微服务,实现“服务注册”。实际上ZooKeeper中有一个名为ZNode的内存树状模型,树上的节点用于存放微服务的配置信息。使用Node.js处理浏览器发送的请求,在Node.js中连接ZooKeeper,发现服务配置,实现“服务发现” 通过Node.js将请求转发到Tomcat上,实现“反向代理”,同样也有大量的Node.js库可供我们自由选择。Node.js的“单线程模型”且“非阻塞异步式I/O”特性,通过“事件循环”的方式来支撑大量的高并发请求,此外Node.js原生也提供了集群特性,可确保高可用性。 为了实现微服务自动化部署,我们可通过Jenkins搭建自动化部署系统,并使用Docker将服务进行容器化封装。 综上所述,微服务架构技术选型如下所示。 Spring Boot:http://projects.spring.io/spring-boot/。 ZooKeeper:http://zookeeper.apache.org/。 Node.js:https://nodejs.org/。 Jenkins:https://jenkins.io/。 Docker:https://www.docker.com/。

微服务网关类似于经典设计模式中的Facade模式(门面模式),它将底层的复杂细节进行屏蔽,对外提供简单且统一的调用方式,比如HTTP方式。此时,对于客户端而言,可以是PC端网页,也可以是移动端设备,客户端通过HTTP方式调用微服务网关。 微服务网关也称为服务网关(Service Gateway)或API网关(API Gateway)。

Service Registry(服务注册表),它是整个“微服务架构”中的核心,它不仅提供了Service Registry(服务注册)功能,同时也为Service Discovery(服务发现)功能提供了支持。服务注册很好理解,就是在服务启动后,将服务的相关配置信息(例如,IP与端口)注册到服务注册表中。当客户端调用这些服务时,将通过Service Gateway(服务网关)从服务注册表中获取这些服务配置,然后通过反向代理的方式去调用具体的服务接口,从服务注册表中获取服务配置的过程就是服务发现。 此外,服务注册表会定期检测已经注册的服务,若发现某服务无法访问了,则将其从服务注册表中移除掉,这个定期检测的过程被称为“心跳检测”。由此可见,服务注册表对“分布式数据一致性”的要求是相当高的,换句话说,服务注册表中的服务配置一旦变更了,通知机制必须做到高性能,且服务注册表本身还需要具备高可用。 那么,谁才能担当服务注册表的重任呢?我们认为ZooKeeper是服务注册表的最佳解决方案之一。

ZooKeeper一般都以集群的方式对外提供服务,一个集群包含多个节点,每个节点都对应一台ZooKeeper服务器,所有的节点共同对外提供服务。整个集群环境对分布式数据一致性提供了全面的支持,具体包括以下五大特性。 1.顺序性——从同一个客户端发送的请求,最终将会严格按照其发送顺序进入ZooKeeper中。可见,这就像一个队列,拥有“先进先出”的特性,也就确保了请求的顺序性,一个个地来,大家都别插队。 2.原子性——所有请求的响应结果在整个分布式集群环境中具备原子性,也就是说,要么整个集群中所有机器都成功地处理了某一个请求,要么就都没有处理,绝对不会出现集群中一部分机器处理了某一个请求,而另一部分机器却没有处理的情况,这方面的要求与事务的原子性是一样的。 3.单一性——无论客户端连接到哪个ZooKeeper服务器,每个客户端所看到的服务端数据模型都是一致的,不可能出现两种不同的数据状态。实际上每台ZooKeeper服务器之间是会进行数据同步的,而这个同步过程是相当高效的。 4.可靠性——一旦服务端数据状态发生了变化,就会立即存储起来,除非此时有另一个请求对其进行了变更,否则数据一定是可靠的。 5.实时性——当某个请求被成功处理后,客户端能够立即获取服务端的最新数据状态,整个过程具备实时性。

服务发现(Service Discovery)是一种微服务架构核心模式,它一般与服务注册模式共同使用,在微服务网站上介绍过两种服务发现模式。 (1)客户端发现:http://microservices.io/patterns/client-side-discovery.html。 (2)服务端发现:http://microservices.io/patterns/server-side-discovery.html。 其中“客户端发现”指的是服务发现机制在客户端中实现,而“服务端发现”指的是服务发现机制通过一个路由中间件来实现。我们目前使用Node.js实现的正是服务端发现这种模式,因为Node.js实际上作为了一个独立的中间件来提供服务发现功能。如果我们将服务发现功能放入前端JS中,则为客户端发现模式。

1.Docker引擎(Docker Engine) Docker引擎可理解为一个运行在服务器上的后台进程,也称为Docker Daemon,还有很多人称它为Docker服务,因为它本质上就是一个服务,只要我们启动该服务,我们就能随时使用它。我们可通过Docker命令客户端发送相关Docker命令,并与Docker引擎进行通信。 2.Docker客户端(Docker Client) 实际上Docker客户端有两种,一种是我们刚提到的Docker命令客户端,只要我们打开命令终端窗口,并输入相关Docker命令,就能操作Docker引擎。另一种Docker客户端是REST API客户端,我们一般会在应用程序中通过REST API与Docker引擎发生交互。本章我们将重点放在Docker命令客户端方式上,请大家自行学习REST API客户端。 3.Docker镜像(Docker Images) Docker镜像有点类似于我们曾经使用的光盘,光盘上刻录了数据,我们只需将光盘放入光驱中,就能读取光盘中的数据。同样,我们只需获取Docker镜像(光盘),就能将其载入到Docker引擎(光驱)中,并运行Docker镜像中的程序。一般情况下,我们首先需要将程序打包到Docker镜像中,随后才能将Docker镜像交给其他人使用。 4.Docker容器(Docker Containers) 当我们获取到Docker镜像以后,可随时运行该Docker镜像,此时便会启动一个Docker容器,该容器中将运行镜像中封装的程序。如果我们将Docker镜像理解为Java类的话,那么Docker容器就相当于Java实例。在同一个Docker镜像上理论上可运行无数个Docker容器,只要机器性能足够好。 5.Docker镜像注册中心(Docker Registry) Docker官方提供了一个叫作Docker Hub的镜像注册中心(Docker Registry),用于存放公开和私有的Docker镜像仓库(Docker Repository)。也就是说,我们可随时通过Docker Hub拉取(下载)Docker镜像,也可自由将自己创建的Docker镜像推送(上传)到 Docker Hub上,只是在上传时需要先注册Docker用户,获取Docker ID,通过用户名与密码登录Docker Hub,进行身份认证。需要注意的是,一个Docker Registry可以包含多个Docker Repository。

Docker的四大特点: 1.快速运行——启动虚拟机需要几分钟,而启动Docker容器却只需几秒钟,开发效率瞬间提升了。 2.节省资源——Docker容器运行在Docker引擎之上,可 3.便于交付——传统的软件交付物是程序,而在Docker时代的交付物却是镜像,镜像不仅封装了程序,还包含了运行程序所需的相关环境。 4.容易管理——可通过Docker客户端直接操作Docker引擎,非常方便地管理Docker镜像与容器。

Tags