设计微服务架构
构建代码的脚手架
分解业务问题
- 描述业务问题,安装名词来描述问题
- 注意动词
- 寻找数据内聚性
确定服务粒度
使用数据模型作为将单体应用分解到微服务的基础。
可以通过下面的概念来确定正确的服务粒度:
- 广泛的使用微服务和重构为更小的服务
- 关注你的服务如何与其他服务交互
- 随着你对问题领域的理解不断增长,服务职责将不断变化
感受不好的微服务设计
服务粒度太粗:
- 服务具有太多的职责
- 服务通过大量的表管理数据
- 测试用例过多
服务粒度太细:
- 在部分问题领域有太多微服务
- 微服务彼此严重依赖
- 微服务组成一个简单的CRUD服务集
微服务应该负责一种业务逻辑
定义服务接口
/**
* @RequestMapping 暴露Controller的根URL
* {organizationId}是一个占位符,表示URL在每次调用会传递一个organizationId参数
*/
@RestController
@RequestMapping(value = "/v1/organizations/{organizationId}/licenses")
public class LicenseServiceController {
/**
* @RequestMapping(
* method = {RequestMethod.GET}
* )
*
*
* @param organizationId 通过@PathVariable来访问
* @param licenseId
* @return
*/
@GetMapping(value="/{licenseId}")
public License getLicenses(
@PathVariable("organizationId") String organizationId,
@PathVariable("licenseId") String licenseId) {
return License.builder()
.id(licenseId)
.productName("Teleco")
.licenseType("Seat")
.organizationId(organizationId)
.build();
}
}
端点的名称问题
微服务的URL应该能清楚的表达服务的目的,尽量使用下面的准则:
- 使用明确的URL名称建立服务所代表的资源
- 为URL建立早起版本控制方案
URL 及其相应端点表示服务所有者和服务消费者之间的契约。一个常见的模式是在所有端点前使用一个版本号。及早建立你的版本控制计划并坚持下去。在已经有好几个用户使用它们之后,为 URL 升级版本是非常困难的。
微服务开发的四项原则
- 微服务应该是独立和单独可部署的,多个服务实例作为单独的软件构件启动和停止。
- 微服务是可配置的,当服务实例启动时,它应该从配置中心读取所需的数据来配置自己,或将其配置信息传递为环境变量。配置服务不需要认为干预
- 微服务实例需要对客户端透明
- 微服务应该显示其健康状况
示例图
构建微服务应用的十二要素
基准代码
所有应用程序代码和服务器配置信息都应处于版本控制中。每个微服务在源码控制系统内有自己独立的代码库。
依赖
通过使用构建工具如 Maven(java)显式声明你的应用程序的依赖。第三方 JAR 依
赖性应该使用其特定版本号声明。这允许你的微服务总是使用相同版本的 lib 库进行构建
配置
独立于代码存储应用程序配置。你的应用程序配置不应该与源代码在同一个存储库中
后端服务
你的微服务往往会通过网络与一个数据库或消息系统迚行通信。当它这样做时,
你应该确保在任何时候,你都可以将数据库的实现从内部管理的服务转换为第三方服务。
构建,发布,运行
将你的构建,发布,和运行部署的应用程序各部分完全分离。一旦代码
被构建,开发人员就永远不应该在运行时对代码进行更改。 任何修改都需要重新执行构建
过程和重新部署。构建的服务是不可变的,不能更改。
进程
你的微服务应该是无状态的。它们在任何时候都可以被杀死和取代,而不必害怕一个受损的服务实例将导致数据丢失。
端口绑定
一个微服务对服务的运行时引擎(打包在服务的可执行文件)是完全独立的。
你将能运行该服务而不需要分离的 Web 或应用服务器。服务应该在命令行中独立启动,并通过暴露的 HTTP 端口立即被访问。
并发
当需要进行伸缩时,不要依赖于单个服务中的线程模型。相反,加载更多的微服务
实例和在水平方向扩展。这并不排除在你的微服务使用线程,但别指望把它作为你伸缩的唯一途径。规模扩大,而不是上升。
通用性
微服务是一次性的,一经要求就可以启动和停止。启动时间应尽量减少,当接收到
操作系统的一个终止信号时,进程应该优雅地关闭
开发环境不线上环境等价
最小化服务运行的所有环境之间的差距(包括开发人员的桌面)。
开发人员应该在本地使用与实际服务将运行的相同的基础设施来进行服务开发。它还意味着服务在环境之间部署的时间应该是几个小时,而不是几个星期。一旦代码被提交,它应该已经被测试和从开发一直到生产尽快升级。
日志
日志是一个事件流。一旦日志被输出,它们对工具将是可流化的,如 Splunk或Fluentd ,它们将聚合日志并将其写入到中心存储设备。微服务不应该关心这项技术是如何出现的,当日志正在被输出的时候,开发人员应该在视觉上通过 STDOUT看日志。
管理进程
开发人员常常不得不针对他们的服务(数据迁移或转换)执行管理任务。 这些任
务不应该是临时的,而应该通过源代码存储库管理和维护的脚本来完成。这些脚本应该是可重复的,并且在它们运行的每个环境中都不改变(在每个环境中都不修改脚本代码)。