1. 微服务¶
一些系统工作的小而自治的服务。
专注于做好一件事 自治性,解耦,正确地剑魔服务和API
好处:技术异构性,弹性(不可用和功能降级问题),扩展,简化部属,与组织结构相匹配,可组合性,对可替代性的优化,
1.3 面相服务的架构SOA
2. 演化式架构师¶
职责:愿景,同理心,合作,适应性,自治性,治理
3. 如何建模服务¶
什么样的服务是好服务: 低耦合:修改一个服务无需修改另一个服务。限制两个服务之间的不同调用形式的数量 高内聚:相关的行为聚集一起
3.3限界上下文 共享的隐藏模型(领域驱动设计) 模块和服务:服务边界和领域的限界上下文保持一致 过早划分:
3.4 业务功能 建模服务时,应该将这些功能作为关键操作提供给其他协作者(其他服务)
3.5 逐步划分上下文
3.6 关于业务概念的沟通 修改系统的目的是为了满足业务需求 技术边界:
4.集成¶
4.1寻找理想的集成技术 避免破坏性修改 保证API的技术无关性 使你的服务易于消费方式用 隐藏内部实现细节 为用户创建接口 共享数据库
4.4同步异步 4.5编排与协同 编排: 依赖于某个中心大脑指导并且驱动整个流程 协同:告知系统中各个部分的职责,细节留给他们自己。发布订阅。降低耦合度,但是需要对业务流程做跨服务监控。RPC 和 REST
4.6远程过程调用 缺点: 技术的耦合;本地调用和远程调用并不相同;脆弱性
4.7 REST
4.8实现基于事件的异步协作方式 微服务发布事件机制和消费者接受事件机制 尽量让消息中间件保持简单,把业务逻辑放在自己的服务中 关联ID对跨进程间的请求进行追踪
4.9 服务即状态机 4.10响应式扩展
4.11微服务世界中的DRY 和代码重用的危险: 跨服务可以适度违反DRY
语义化版本控制 MAJOR.MINOR.PATCH。通过语义化版本控制可以直接看出来是否有不兼容修改,调用方是否需要修改调用点代码。
5.分解单块系统¶
通过限界上下文来确定业务拆分边界。
SchemaSpy 工具生成数据库之间的关系图
拆除外键:通过API 来提供访问。(这样做会增加请求减慢访问速度,需要做个权衡) 作者这里推荐先做数据库拆分,之后去拆分业务代码。
数据库重构: 拆分过程可以根据业务边界重新设计db
拆分之后如何处理事务不一致问题:
- 再试一次,比如一个操作成功了,一个失败了。但是要保证重试可以修复这个问题(最终一致性)
- 终止整个操作。发起一个补偿事务抵消之前的操作,但是补偿也可能失败,不好处理,可能需要一个后台
- 分布式事务:两阶段提交.尽量用现有的技术方案
把设计画在白板上,在你认为的服务边界上运行用例,看看会发生什么。
CRC(class-responsibility-collaboration): 类、职责、交互卡片
6 部署¶
CI (continuous intergration) 持续集成
- 是否每天签入代码到主线
- 是否有一组测试验证修改
- 构建失败后,是否把修复 CI 当做第一优先级事情来做
CD(continuous delivery) 持续交付
7 测试¶
stub vs mock
stub: 打桩是指为被测服务的请求创建一些有预设相应的打桩服务。 mock 还会进一步验证请求本身是否被正确调用。
《测试驱动的面向对象软件开发》
8 监控¶
监控小的服务,然后聚合起来看整体。
日志: ELK elastic-search + logstash + kibana
多个服务的指标跟踪:graphite
zipkin 跨多个系统边界跟踪调用
9 安全¶
身份验证和授权。分布式系统下,我们希望有一个单一的标识且只需要一次验证。
单点登录SSO(single signon);使用网关实现单点登录。
服务间身份验证和授权:在边界内允许一切;https 基本身份验证
9.3 静态数据的安全¶
使用众所周知的加密算法
9.4 深度防御¶
防火墙(iptables);日志(踢出敏感信息);入侵检测和防御系统;网络隔离
9.7 个人因素¶
心怀不满的雇员损害你的系统
10 康威定律和系统设计¶
任何组织在设计一套系统(广义概念的系统)时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。
组织的耦合度越低,创建的系统模块化就越好,否则越差。
11 规模化微服务¶
11.1 故障无处不在¶
从统计学看,规模化后故障成为必然事件。 假设一切都会失败,会让你从不同的角度去思考如何解决问题。
考虑是否扩展系统,首先尝试理解一下需求:
- 响应时间/延迟
- 可用性
- 数据持久性
11.3 功能降级¶
如果这个微服务宕了会发生什么?
11.4 架构性安全措施¶
一个无法控制的下游服务,可以让整个系统宕掉。
11.5 反脆弱的组织¶
混乱猴子,故意在系统中做破坏
做什么来应对系统故障呢?
- 超时。给所有的跨进程调用设置超时,并选择一个默认的超时时间。记录日志并响应调整
- 断路器。对下游资源请求发生一定量的失败后,断路器会打开。接下来所有的请求在断路器打开的状态下,会快速地失败。 一段时间后,客户端发送一些请求查看服务是否恢复,如果正常响应了,重置断路器。 Hystrix 基于 JVM 的断路器,附带监控
- 舱壁(bulkhead):关注点分离。每个下游服务使用不同的连接池
- 隔离,允许下游服务离线
- 幂等, 多次执行产生的影响和一次执行的影响相同
11.7 扩展¶
- 更强大的主机(垂直扩展);
- 拆分负载;
- 分散风险;
- 负载均衡。aws elbs(elastci load balancers)
- 基于 worker 的系统。
- 重新设计服务
11.8 扩展数据库¶
11.8.1 服务的可用性和数据的持久性¶
数据库副本
11.8.2 扩展读取¶
使用只读副本。最终一致性
11.8.3 扩展写操作¶
通过哈希写分片。
11.8.4 共享数据库基础设施¶
11.9 缓存¶
11.9.1 客户端、代理和服务器缓存¶
客户端:减少网络调用,减少下游负载。但是改变缓存方式和失效数据比较棘手
代理服务器:squid 或varnish。增加网络跳数(network hops),但是相比性能优化来说可忽略
服务器:适合多客户端。追踪和统计命中率
11.9.2 HTTP 缓存¶
cache-control: 告诉客户端是否缓存资源,缓存几秒。
Expires: 指定改日知和时间后被认为失效
ETag: 标识资源是否已改变
《Rest 实战》
11.9.3 为写使用缓存¶
使用 writebehind 方式使用缓存
11.9.4 为弹性使用缓存¶
可以使用缓存中的数据保证某些场景服务不会宕机(但是数据可能过时,总比挂了好)
11.9.5 隐藏源服务¶
让请求快速失败,确保不占用资源或增加延迟,给下游服务恢复机会
11.9.6 保持简单¶
先保持简单,使用缓存之前慎重考虑
11.11 CAP 定理¶
- 一致性(consistency):访问多个节点能得到同样的值
- 可用性(availability): 每个请求都能获得响应
- 分区容忍性(partition tolerance): 集群中某些节点失联后,集群整体还能继续进行服务的能力
考虑使用双主数据库来进行数据库同步,加入两个库之间断开了:
- 牺牲一致性。AP 系统。最终一致性,用户可能看到失效的数据
- 牺牲可用性。CP系统。为了让数据一致,拒绝用户请求。保证多个节点一致性很难。
- 牺牲分区容忍性。不存在,除非单独的进程不跨越网络
需要权衡使用 AP 还是 CP。构建 AP 比 CP 很容易一些。
11.12 服务发现¶
常见的服务发现解决方案:
- DNS
11.13 动态服务注册¶
- Zookeeper
- Consul:支持配置管理和服务发现
- Eureka(netflix):
11.14 文档服务¶
- Swagger
- HAL和 HAL 浏览器。hypertext application language
12 总结¶
12.1 微服务原则¶
微服务(自治的小服务)的原则:
- 围绕业务概念建模: 使用限界界上下文
- 自动化: 持续交付
- 隐藏内部实现细节:隐藏数据库。使用技术无关的 api,比如 rest/rpc
- 一切都去中心化: 团队保持对服务的所有权;内部开源模式;康威定律;内聚性
- 可独立部署: 你的消费者应该自己决定何时更新
- 隔离失败: 超时;断路;舱壁。CAP
- 高度可观察: 聚合日志和数据
12.2 什么时候不该使用微服务?¶
先花时间了解系统做什么的,尝试识别清晰的模块边界。
首先构建单块系统,稳定之后再考虑拆分
花时间构建工具和实践,管理微服务。