文献地址:https://www.nginx.com/blog/introduction-to-microservices/
01 Introduce to Microservices
Monolithic Application vs Microservices
微服务可以从三个维度看:X维度-横向复制扩展成多实例,Y维度-按照业务功能切分成不同的微服务,Z维度-数据存储分库分表扩展(这个表述可能不大准确)
微服务的优势:主要从开发、部署、扩展、运维的角度分析
1)把一个很大的应用拆分成多个微服务,微服务之间通过RPC或者消息队列相互通信。这样微服务更容易开发和维护
2)一个大的应用基本只能使用一套技术框架,拆分成微服务后,不同的微服务之间有了可以自由选择技术框架的可能性
3)微服务之间相互独立部署
4)单个微服务可以独立的扩展
微服务的缺点:
1)微服务,那一个应用到底拆分成几个微服务合适呢,这是值得深思的一个问题
2)微服务之间通过RPC或者消息队列通信,通信的可靠性和消耗是一个值得思考的问题
3)微服务将数据库拆分,那么跨微服务跨库操作时就很难保证事务的强一致性,只能追求最终一致性了
4)测试会更加复杂一点,当测试涉及微服务之间的调用时,相互依赖的微服务都必须启动起来
5)当迭代需求涉及到多个微服务时,每个微服务都要更新,重新部署启动
6)部署微服务也变得更加复杂,包括监控、部署、扩展、拆分等等
总结:当业务很简单的时候,微服务的必要性没有那么大;当业务复杂的时候,微服务就显得很有必要了;所以随着业务的发展系统越来越复杂,需要提前考量引入微服务
02 Building Microservices:Using an API Gateway
当外部http请求微服务时,如何访问到对应的微服务系统?有以下两种方式:
1)每个微服务都暴露一个域名访问地址给外部,调用时采用:http://serviceName.api.company.name
这种方式显然不大合理。第一,每次新增一个微服务都要新增一个域名访问地址;第二,当将一个微服务拆分或者多个微服务合并的时候,都需要更改或者新增域名,相应的客户端调用的代码也需要修改
2)采用api网关,统一一个入口,由api根据前端访问地址决定访问哪个微服务,以及如何实现同一个微服务之间的负载均衡,还有根据规则如何路由到同一个微服务的不同环境中。
采用api gateway的优缺点:
优点:统一入口,方便管理,客户端只直接跟api网关通信
缺点:引入api网关,就多了一个服务,api网关服务的流量将是巨大的,对于api网关服务自身的性能和稳定性要求是很高的,一旦网关出问题,会导致同一个大系统下面的微服务都不可访问。
实现一个api网关的要素:
1)性能和扩展性
2)采用reactor设计模式,比如java nio的selector(reactor模式还要好好理解理解)
service invocation(服务调用)
微服务之间通信:异步:消息队列,如kafka;同步:rpc/http
service discovery(服务发现)
查询服务注册表,得到每个微服务-版本号-对应部署ip机器和端口号列表
handling partial failures(能处理部分失败)
降级等处理
03 Building Microservices: Inter-Process Communication in a Microservices Architecture
主要讲微服务之间是如何通信的,通信协议采用什么方式?
同步采用http/rest请求,异步通过消息队列通信;如果是一对多则采用消息发布订阅模式
04 Service Discovery in a Microservices Architecture
微服务中的服务发现-实际项目中主要用到的是zookeeper
为什么微服务要使用服务发现?
同一个微服务的不同实例是动态注册的,注册之后可能因为故障等原因而不可用,这就要求服务发现及时去发现并更新最新可用的实例,然后重新去做负载均衡等操作
服务发现的两种模式:Client-Side Discovery vs Server-Side Discovery
Client-Side Discovery
每个微服务都有一个服务发现客户端,如下图的Registry Client.
以单个微服务为例,微服务会注册到服务注册中心,服务注册中心会记录该微服务对应实例的ip和端口,然后通过心跳的机制定期检测服务器是否存活并定期更新微服务对应的实例ip列表。
对应的Registry Client只需要去注册中心读取实例ip列表即可,然后做负载均衡。
相当于每个微服务都需要一个前置的Registry Client.
Server-Side Discovery
请求统一通过一个入口,由一个load balancer 服务去统一从注册中心获取每个微服务的实例ip列表,然后决定如何负载均衡,nginx+就可以作为Server-Side Discovery(这个就是网关可以做的事)
The Service Registry-可以理解为一个存储微服务实例列表的数据库
服务注册中心:微服务注册到服务注册中心,由服务注册中心保存微服务的实例ip列表,通过心跳实时更新微服务实例ip列表
哪些可以作为服务注册中心:
- Netflix Eureka
- etcd:Kubernetes and Cloud Foundry
- consul
- zookeeper
Service Registry Options
-
The Self‑Registration Pattern: 微服务自己注册到注册中心,自己取消注册到注册中心;当然如果有必要可以定期发送心跳给注册中心,防止过期。有点Client-Side Discovery的意思
-
The Third‑Party Registration Pattern:采用第三方统一注册、取消注册服务,当某个微服务有新实例时,这个第三方服务会自动发现,当某个微服务实例不可用时通过healthcheck检测到,然后可以取消该注册服务。
zookeeper是通过心跳来取消注册的,当心跳停止一段时间后会从列表中自动删除对应的微服务实例
05 Event-Driven Data Management for Microservices
- 分布式数据管理问题:当一个请求跨微服务访问多个数据库时,由于一个微服务无法直接访问另外一个微服务的数据库,只能通过api接口访问,由于跨数据库操作就不能保证ACID了。
解决办法:事件驱动架构方式
大致的思想是将存在跨数据库的访问拆分成一系列事件流,一步一步往下执行(一个一个数据库操作往下执行),当某个步骤失败时,通过补偿的方式回滚之前提交成功的操作(回滚也是可能存在失败的),这种方式是以最终一致性为目标的,所谓的BASE而不是ACID
- 事件驱动如何保证原子性:插入一条数据到数据库+发布一个事件,可能存在插入数据库成功但是发布事件失败,导致不一致。
解决办法1:新增一张本地事件表,插入一条数据+插入一条事件记录在同一个本地事务中,然后开启另外一个线程从事件表中读取事件记录并发布,记得标记为已发布。当读取事件发布失败可以重试,比之前要改进很多
解决办法2:通过读取数据库的transaction log, 根据插入的数据记录异步发布对应的事件
解决办法3:采用一个事件保存源,每次都将事件追加,客户端去订阅事件源消费即可。
06 Choosing a Microservices Deployment Strategy
微服务部署方式
方式1:Multiple Service Instances per Host Pattern
不同微服务部署在同一台物理机或者虚拟机上,通过不同的端口号区分;不同微服务之间不隔离,易相互影响产生问题-
方式2:Service Instance per Host Pattern
细分为两种方式:- Service Instance per Virtual Machine Pattern:每个虚拟机部署一个微服务
- Service Instance per Container Pattern:每个容器部署一个微服务
这样不同微服务之间资源是隔离的,可以指定每个微服务可用的CPU、内存等资源
方式3:Serverless Deployment
无服务部署,如AWS Lambda, 不是太了解。。。
07 Refactoring a Monolith into Microservices
如何将一个大的应用拆分成微服务?
- 第一步:停止继续将应用代码变得更多更复杂
- 第二步:前后端分离
- 第三步:抽取module成独立的微服务,根据业务功能、资源消耗等方面考虑