Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • ☁️ Spring Microservices
    • Chapter 1 - 欢迎迈入云世界,Spring
    • Chapter 2 - 使用 Spring Boot 构建微服务
    • Chapter 3 - 使用 Spring Cloud 配置服务器控制配置
    • Chapter 4 - 服务发现
    • Chapter 5 - 使用 Spring Cloud 和 Netflix Hystrix 的客户端弹性模式
    • Chapter 6 - 使用 Spring Cloud 和 Zuul 进行服务路由
    • Chapter 7 - 保护微服务
    • Chapter 8 - 使用 Spring Cloud Stream 的事件驱动架构

Chapter 2 - 使用 Spring Boot 构建微服务

Created by : Mr Dk.

2020 / 08 / 12 11:25

@Nanjing, Jiangsu, China


传统的单体架构具有的特点:

  • 紧耦合 - 业务逻辑的调用发生在 编程语言 层面,而不是通过实现中立的协议
  • 有漏洞 - 数据几种存储,组件内部的数据结构实现细节泄漏到整个应用程序中
  • 单体的 - 组件存放在单一代码库中,任何对代码的修改都需要重新编译、运行、测试、部署

而采用微服务架构后具有的特点:

  • 有约束 - 微服务具有范围有限的单一职责,每个服务只做好一件事
  • 松耦合 - 服务之间使用非专属调用协议 (比如 HTTP 和 REST) 进行交互,只要接口不变,可以对服务进行任意修改
  • 抽象的 - 微服务完全拥有自己的数据结构和数据源,并锁定微服务数据库的访问限制,只允许微服务访问它
  • 独立的 - 每个微服务可以独立于其它服务进行编译、部署 - 这样对变化进行隔离和测试更容易

微服务的出现满足了以下需求:

  • 微服务能够快速交付,客户不必等待漫长的程序发布周期
  • 基于微服务的应用可以将故障隔离在应用程序的特定功能中
  • 不均匀的容量需求,微服务应用更容易在云服务器上水平伸缩

下面从三个视角来看微服务的设计。

2.1 架构师的故事:设计微服务架构

2.1.1 分解业务问题

将处理的问题分解为可管理的块,这样就不必把所有细节都考虑进来。

2.1.2 建立服务粒度

将代码打包到单独的项目中,梳理出服务访问的数据库表,只允许单独的服务访问特定域中的表。糟糕的微服务一般有以下特点。如果粒度过粗:

  • 服务承担了过多的职责 (业务逻辑很复杂)
  • 服务跨大量表管理数据
  • 服务测试用例太多

如果粒度过细:

  • 微服务数量上升快,每个服务只与一个数据库表交互
  • 微服务之间严重相互依赖
  • 微服务除了 CRUD 相关逻辑以外什么都不做

2.1.3 互相交流:定义服务接口

应用程序应当使用下面的设计方式交流:

  1. 拥抱 REST 理念,使用 HTTP 动词表示行为
  2. 使用 URI 来传达意图
  3. 请求和相应使用 JSON (轻量级数据序列化)
  4. 使用 HTTP 状态码传达结果

2.2 何时不应该使用微服务

  1. 微服务需要高度的运维成熟度 (因为是分布式的)
  2. 服务器散乱,管理和监控服务器的操作复杂性巨大
  3. 微服务对高度弹性、伸缩性的应用程序非常有用
  4. 微服务间执行事务没有标准

2.3 开发人员的故事:用 Spring Boot 和 Java 构建微服务

2.3.2 引导 Spring Boot 应用程序:编写引导类

微服务中极其重要的两个类:

  • Spring 引导类,可被 Spring Boot 用于启动和初始化应用程序
  • Spring 控制器类,用于公开可被其它服务调用的 HTTP end point

使用 @SpringBootApplication 注解表示这是一个引导类,然后在改类的 main() 中调用 run() 启动微服务。服务的核心初始化逻辑应该在这个类中。

2.3.3 构建微服务的入口:Spring Boot 控制器

将数据从传入的 HTTP 请求映射到处理该请求的 Java 函数。

在基于 HTTP 的微服务间发送数据时,有多种可选协议。与其它协议相比,JSON 非常轻量级,可以在没有太多文本开销的情况下传输数据。但是如果要使用比 JSON 更有效率的通信协议,最小化在链路上发送数据的大小,那么就要使用一些二进制协议。

端点命名问题:

  • 使用明确的 URL 确立服务所代表的资源
  • 使用 URL 来确立资源之间的关系 (比如父子关系)
  • 尽早建立 URL 的版本控制方案 (使用版本号作为前缀添加到所有端点上)

2.4 DevOps 工程师的故事:构建运行时的严谨性

在这里,微服务的设计关乎在投入生产后如何管理服务。微服务应当基于如下原则进行构建:

  1. 可独立部署 - 多个服务实例可以使用单个软件制品进行启动和拆卸
  2. 可配置 - 实例启动时,应当从中央位置读取自身的配置数据,无需人为干预
  3. 微服务实例对用户透明 - 客户端不应知道服务的确切位置,而是与 服务代理 通信
  4. 微服务应当传达它的健康信息 - 客户端需要绕开不良的服务实例

上述原则被映射到了微服务运行的生命周期中:

  1. 服务装配 - 打包、部署服务,保证服务的可重复性、一致性
  2. 服务引导 - 配置与代码分开,在任意环境中快速启动、部署实例,无需人为干预
  3. 服务注册 / 发现 - 如何让新启动的服务实例被其它客户端发现
  4. 服务监控 - 保证服务高可用,监控微服务实例,状态不佳的服务实例被拆卸

一些经验总结:

  • 代码库 - 每个微服务都应该有独立的版本控制
  • 依赖 - 通过构建工具 (如 Maven) 明确地生命应用的依赖项 (特定版本号)
  • 配置 - 应用程序配置与代码 分开存储
  • 后端服务 - 确保随时可以将数据库从内部管理切换为第三方管理
  • 构建、发布和运行 - 三个流程完全分开,保证构建后开发人员无法对运行时代码进行更改
  • 进程 - 微服务应该始终是无状态的,可以随时被杀死和替换
  • 端口绑定 - 服务应该在命令行上自行启动,通过公开的端口访问
  • 并发 - 启动更多的微服务实例水平伸缩
  • 可任意处置 - 微服务可根据需要启动和停止
  • 开发环境与生产环境等同 - 最小化服务运行的所有环境之间存在的差距
  • 日志 - 流式传输,写入中央位置
  • 管理进程

2.4.1 服务装配:打包和部署微服务

微服务需要作为 带有所有依赖项的单个制品 进行打包和安装,然后这个制品可以部署到任何服务器上。几乎所有微服务框架都包含可以打包和部署的运行时引擎。比如可以将工程通过 Maven 打包为 JAR,并具有嵌入式的 Tomcat 引擎内置其中。这样这个 JAR 可以在服务器上通过命令行直接启动。

将运行时引擎 (如 Tomcat) 内置在可部署制品中的做法,消除了配置漂移的可能性。

2.4.2 服务引导:管理微服务的配置

服务引导发生在微服务 首次启动并需要加载应用程序配置信息 的时候。由于微服务的部署范围广,将配置数据存储在服务器外部能够解决管理复杂的问题。

  • 配置数据的结构简单,且读取频繁不常写入,可以直接用文件系统来管理
  • 配置数据需要具有低延迟可读性
  • 数据存储必须高可用

2.4.3 服务注册和发现:客户端如何与微服务通信

从微服务消费者的角度看,微服务的位置应该是 透明 的。微服务架构可以通过运行多个服务实例来实现高度的 可伸缩性 和 可用性,因此每个实例都会被分配一个 唯一、非永久 的 IP 地址。缺点在于,随着服务实例的启动和拆卸,手动管理 IP 地址很麻烦。

微服务实例需要向第三方代理进行注册,这个过程被称为服务发现。实例需要告诉代理两方面数据:

  1. 实例的 IP 地址或域名
  2. 应用程序查找服务的逻辑名称

某些服务发现代理还要求能访问到服务实例暴露的 URL,以便执行健康检查。

2.4.4 传达微服务的健康状况

服务代理负责监视其注册的每个服务实例的健康状况,并从路由表中移除有问题的服务实例,确保客户端不会访问已经发生故障的服务实例。

发现微服务后,服务代理将会持续监视、ping 健康检查接口,以确保服务可用。如果发现实例存在问题,可以采取一些纠正措施,比如关闭实例或启动另外的实例。

Spring Actuator 提供了开箱即用的运维 end point。在访问实例的 /health end point 后,可以查看实例是否正在运行,还可以获取一些服务器状态信息,如磁盘占用空间等。这样可以获得更丰富的监控体验。

Edit this page on GitHub
Prev
Chapter 1 - 欢迎迈入云世界,Spring
Next
Chapter 3 - 使用 Spring Cloud 配置服务器控制配置