微服务笔记
微服务是一个这些年才流行起来的软件架构,这篇文章主要是基于Martin Fowler的那篇文章的学习笔记以及一些心得。然后尝试用于工作中的软件架构演进。
1. 基于服务的组件化: 模块的划分
- 首先这里定义一下组件(component)
- 定义:是一个软件单元,可以独立替换,也可以独立升级;
- 微服务和LIB的区别,
- 首先微服务也会使用LIB
- LIB库(libraries)都会被link到程序中,并且在进行内存中的函数调用;
- 而服务(services)是一个进程外的概念,通过web服务请求或者远端调用来访问;
- 我们这里把服务(services)当作一个组件(component),是因为服务是可以独立部署的,而library库则不可以。
- 如果一个应用程序是由多个LIB库组成的,那么任何一个LIB库的修改都会导致整个app重新部署;
- 如果一个应用程序是有多个服务构成的,那么任意一个服务的更新,只要重新部署这个服务就好(仅限于服务间的接口不发生变更);
- 微服务的缺陷
- 远程调用更加消耗资源;
- 远程API的颗粒度更粗,更不友好;
2. 围绕业务能力(Business Capability)进行组织: 开发团队的组织模式
举个例子,我们考虑实现一个应用程序(或者网站),通常我们会从技术层面进行拆分
- 用户界面 -> 好的,交给UI 团队了;
- 业务逻辑 -> 交给逻辑团队
-
数据库 -> 交给数据库团队
这个时候就会出现一个现象,任何一个小的改动(feature)都会涉及所有的团队;需要把这个功能再分解到各个团队,团队之间在进行沟通,讨论接口方案,而后各自实现,最终一致提交测试,最终交付。
微服务则不一样:基于业务能力(business capacity)来对服务进行划分和组织
每一个服务都可以使用不同的技术栈来实现
- UI
- 持久存储
-
以及对外协作
这种情况下对于团队来说是跨职能的,需要各种各样的技术(Feature Team)
- 用户体验
- 数据库
- 项目管理
3. 是产品(Product)不是项目(Program):项目管理的模式
传统的项目模式是,我们的目标是完成一个软件的开发,然后就提交给维护的组;项目结束,项目组解散。
微服务的目标是避免这种模式
- 每个team应该负责整个产品的生命周期
- 开发者需要接触他们软件的生产环境,增加与用户的联系
同样的方法也能用在单体应用程序上,但是服务的颗粒度更小,使得它更容易在开发者和用户之间建立个人关系。
4. 智能端点和哑管道:服务之间的交互原则
当我们在不同的进程间构建通信体系的时候,我们通常会把智能强压进通信机制本身。
微服务则主张另一种方法,智能端点(端点负责更多的处理)和哑管道(管道功能单一通用)
基于微服务而构建的应用程序会尽可能的解耦和内聚。
- Endpoint要是智能的,可以处理和过滤消息
- 管道是简单,只是负责转发消息
微服务团队使用的规则和协议正好就是设计万维网的规则(更扩展一步说,就是Unix的设计规则)。
第二种方法是基于轻量的消息总线,比如说RabbitMQ或者ZeroMQ
- 只是提供一个可靠的异步通信结构(只做消息路由,所以称为哑的)
- 只能依旧存在于Endpoint中,负责消息的生成和消费
单体应用程序,不同的组件都运行在同一个进程中;他们之间的通信都是通过函数调用的方式进行;
拆解成微服务后,最大的一个变化是通信方式的变化
- 最简单的方法是,从内存函数调用转变成RPC;但是会导致通信频繁性能不佳;
- 所以,你需要变更这种细颗粒度的通信到更粗颗粒度的通信方式
5. 去中心化治理:更高的自由度
5.1. 你可以使用各种你喜欢的技术栈来实现新功能了
集中治理的一个后果就是单一的技术平台标准化发展趋势。
你可能只会用一种语言去实现这种单一的应用程序。
单体的组件分裂成服务之后
- 你可以用node.js去实现一个简单的报告页面
- 也可以用C++去实现一个接近实时处理的组件
- 等等
5.2 去中心化的数据管理
去中心化的数据管理表现在很多不同方面。
最抽象的来说的话,就是不同的系统里面对于世界的概念模型也是完全不一致的。比如说一个大公司里面,销售眼里的客户和技术支持里面客户就是不一样的。
这个问题(差异的世界观?)也常见于应用程序之间,但是它也会出现在应用程序内,尤其是一个应用程序被分割成了多个单独的组件。
一种非常有用的思维方式是领域却动的设计(Domain-Driven Design)。DDD这种设计方式会把一个复杂的领域划分成多个有界的上下文,并且建立出他们之间的映射关系。这种流程对于单体应用以及微服务都是有效的。
和概念模型的去中心化决策一样,微服务也会使用去中心化数据存储决策。单体应用更喜欢单一的逻辑数据库做数据存储,而企业也更倾向于多个应用程序共用一个数据库(有时候也是由于license所决定的)。
微服务则允许每个服务管理自己的数据库,可以使用相同的数据库技术的不同实体,也可以使用不同的数据库技术。
这就是所谓的混合持久化(Polyglot Persistence)
对于微服务之间的数据,去中心化的责任(会有不同的服务组件影响这些数据)对于管理升级是有影响的。处理更新最常用的方法是用事物来保证一致性。
NOTE: 使用事务有助于一致性,但是会带来明显的临时耦合。而分布式事务是非常难以实现的,所以微服务架构强调服务间的无事务协作。 这里需要有个明确的认知,就是所谓的一致性只是在最终达到一致,或者是通过一些补偿的操作来进行处理.
5.3 允许试错
对于很多个开发团队来说,这样的非一致性管理的方法是一种新的挑战,然而这又是一种匹配日常业务(business)时间的处理方式。日常的业务会处理很多的不一致性以达到快速响应的目的。我们允许试错,允许返工,并以此为代价(牺牲一致性)来保证生意不会丢。
6. 基础设施的自动化:让你的生活更加简单
其实微服务的自动化是在先前的持续集成以及持续交付的基础上发展而来的。
自动化的编译,集成测试,性能测试,以及生产环境的部署。
image.png
几个关键点
- 自动化测试
- 自动化部署
自动化的引入让“做正确的事情”这件事儿变得更加容易了。
7. 为失败设计: 设计模块的前提就是要容忍各种失败
7.1 任何时候都有可能失败
使用服务作为组件的一个后果就是,应用程序需要设计成他们能容忍某些服务的失败,任何服务的调用都可能由于提供服务方的不工作而失败,调用端需要能容忍这些失败。
这个是相对于单体应用的一个劣势,你需要考虑各种可能失败的场景以及对于用户体验的影响。
由于服务会在任何时候都会失败,所以需要尽快的检测到失败,并且如果可能的话,要能自动恢复服务。
7.2. 所以我们需要实时监控
微服务应用程序增强了应用程序的实时监控,不仅检查软件架构相关的元素(比如说一秒钟多少次数据库查询请求),还要业务相关的指标(比如说每分钟收到的订单次数)
这种监控对于微服务架构尤为重要,因为微服务偏好编排和时间协作,但是这个会导致突发行为的发生。监测对于快速发现不良的突发行为至关重要
NOTE: 同步调用被认为是有害的
8. 进化式设计:让重构变得更加简单
8.1. 学会控制变化
微服务的从业者,通常有进化式设计的背景,并且把服务的分解看成是一个进一步的工具来保证开发者可以控制应用程序的变化但是却又并不会减缓应用程序的变化。
控制变化并不意味着就是减少变化。使用正确的态度和工具,你可以迅速频繁的并且可控的去修改软件。 (持续的重构,模块越小,重构的负担就越轻,就越加可控)
不管什么时候你去尝试把一个系统变成多个组件,你都会面临这样的决策,怎么切分。这个时候切分的原则是什么呢?
- 组件的关键特性是,独立的替换,独立的升级;这就意味着如果我们重写了一个组件,那么我们不会影响它的协作方。
8.2. 记住,任何一个服务都是有可能会被废弃的
很多微服务组织会明确的表明这里面的一些组件有可能被废弃,以这个为前提来进行划分组件
Guardian这个网站就是一个最初设计成单体应用,但是最终演进到微服务架构的一个很好的例子。这个网站的核心依旧是个单体应用,但是他们非常乐于通过添加微服务组件的方式来添加新的功能,而这些微服务组件使用的还是单体应用的API。这种方法对于天然临时的特性特别方便,比如说处理体育赛事的专题页面,网站的这一部分可以使用快速开发语言去构建,赛事结束后立即丢弃。 --> 模块的可丢弃性
8.3. 更一般的原则,服务是可替代的
模块设计过程中,强调可替代性是一个更加一般的原则。
- 你应该要想办法把会同一时间发生变更的内容(功能)放到同一个模块中去
- 而这个时候不怎么变化的那些内容(功能),我们应该放到别的组件中去
- 如果你发现你重复的在一起修改两个服务,那你就有可能要把他们合并到一起去了