《微服务设计》读书笔记
2020-04-01 09:56:16 2 举报
AI智能生成
微服务设计的读书笔记
作者其他创作
大纲/内容
第2章-演化式架构师
不同之处
软件架构师面对的需求不固定,并且经常变更,必须要适应这种变更
其他架构师(例如,建筑师)需求固定,设计好之后不会再变更
架构师的演化视角
必须改变那种从一开始就要设计出完美产品的想法
我们应该设计一个合理的框架,在这个框架下慢慢的演化出正确的系统
软件架构师的一个职责是:保证该框架是适合开发人员在上面工作的
划分模块
作为软件架构师,不应该过多的关注每个模块内细节,而是要关注模块之间如何交互
一个原则性的方法
领导者确定公司战略目标
架构师根据战略目标确定具体的系统架构原则
原则不能太多(不超过10个),否则没人能记住
架构师确保程序员在实践时遵从原则
要求的标准
监控
接口
架构安全性
代码治理
架构师提供范例和服务代码模板
让程序员看文档的难度远远大于看代码
程序员都是懒惰的,能copy就不会自己写
例外管理与技术债务
在一些情况下,会偏离指导原则,这是无法避免的
指导原则不适应变化,此时要参考现实情况,做优化升级
指导原则没有问题,此时就带来了技术债务
架构师维护一个债务列表,并提供温和的指导,然后让程序员自行决定如何偿还这些技术债务
集中治理和领导
架构师的部分职责是治理
确保大方向正确,小方向由各个程序员自己把控
建设团队
架构师需要帮助程序员提升技术,利人利己
第4章-集成
寻找理想的集成技术
避免破坏性修改
对某个服务做的一些修改不应该会导致该服务的消费方也随之发生改变
保证APl的技术无关性
技术栈不应该成为限制原因
使你的服务易于消费方使用
使用客户端库对于消费方来说很方便,但是会造成耦合的增加
隐藏内部实现细节
共享数据库
最快最流行的集成方式,但不是一个好的方式
这使得外部系统能够查看内部实现细节,并与其绑定在一起
最终导致:低内聚,高耦合
通信模式:同步与异步
具体场景决定采用哪种方式
同步方式
RPC(远程过程调用)
RPC的核心想法是隐藏远程调用的复杂性
REST
常用方案是:HTTP+JSON
异步方式
实现基于事件的异步协作方式
主要有两个部分需要考虑:微服务发布事件机制和消费者接收事件机制
尽量让中间件保持简单,而把业务逻辑放在自己的服务中
异步架构的复杂性
编排(orchestration)与协同(choreography)
使用编排(orchestration)的话,我们会依赖于某个中心大脑来指导并驱动整个流程,就像管弦乐队中的指挥一样
使用协同(choreography)的话,我们仅仅会告知系统中各个部分各自的职责,而把具体怎么做的细节留给它们自己,就像芭蕾舞中每个舞者都有自己的方式,同时也会响应周围其他人
子主题
服务即状态机
响应式扩展
微服务世界中的DRY和代码重用的危险
在微服务内部不要违反DRY,但在跨服务的情况下可以适当违反DRY
客户端库
对于微服务架构来说,客户端库必须有类似服务发现、故障模式、日志等方面的工作,来提升服务开发的效率。否则,采用微服务架构首先会带来极大的开发量,明显降低开发效率。
按引用访问
讨论的是:数据实体的有效性问题(是否过期)
两种方式
客户端定时更新
通过事件订阅来触发更新
版本管理:如何做微服务的版本升级
尽可能推迟
现实中不太容易实现
及早发现破坏性修改
现实中不太容易实现
使用语义化的版本管理
语义化版本管理的每一个版本号都遵循这样的格式:MAJOR.MINOR.PATCH
MAJOR的改变意味着其中包含向后不兼容的修改
MINOR的改变意味着有新功能的增加,但应该是向后兼容的
PATCH的改变代表对已有功能的缺陷修复
不同的接口共存
新版本在新接口上,老版本在老接口上,让调用方慢慢从老接口迁移到新接口上,待迁移完,即可下掉老接口,完成版本升级
同时维护两个接口期间,可以让老接口调用新接口,来提升开发效率
同时使用多个版本的服务
新版本在新服务上,老版本在老服务上,让调用方满门从老服务迁移到新服务上,待迁移完成,即可下掉老服务,完成版本升级
同时维护两个服务期间,可以让老服务调用新服务,来提升开发效率
用户界面
APl组合
倾向于把API设计得比较细粒度化
API入口(gateway):将多个底层的调用会被聚合成为一个调用
界面调用API入口(gateway)
Ul片段的组合
界面由多个块组成,每个块调用不同的API入口(gateway)
一个块由一个团队负责,从前端到后端,减少沟通的代价
为前端服务的后端BFF(Backends For Frontends)
使用服务端的聚合接口或API入口
可以对多个后端调用进行编排,并为不同的设备提供定制化的内容
与第三方软件集成
应该自己做,还是买?
对于一般规模的组织来说,如果某个软件非常特殊,并且它是你的战略性资产的话,那就自己构建;如果不是这么特别的话,那就购买
CMS
Content Management System:内容管理系统
CRM
Customer RelationshipManagement:客户关系管理
小结:最大程度地保证微服务之间的低耦合
无论如何避免数据库集成
第6章-部署
CI(Continuous Integration,持续集成)
测试是否真正理解CI的三个问题
是否每天签入代码到主线
是否有一组测试来验证修改
当构建失败后,团队是否把修复CI当作第一优先级的事情来做
把持续集成映射到微服务:三种模式
把所有微服务放在同一个代码库中,并且只有一个Cl构建
将一个代码库的子目录映射到不同的构建中
每个微服务有一个源代码库和Cl构建
CD(Continuous Delivery,持续交付)
一个使用构建流水线建模的标准发布流程
与微保的工作环境对应关系
快速测试:DEV环境
耗时测试:SIT环境
用户验收测试:UAT环境
性能测试:PRE环境
生产环境:PRD环境
平台特定的构建物
Ruby中有gem, Java中有JAR包和WAR包,Python中有egg
通过自动化工具可以对不同构建物的底层部署机制进行屏蔽
使用OS特定构建物的好处是,在做部署时不需要考虑底层使用的是什么技术
从来没有遇到过这种部署方式
定制化镜像/容器部署方案
不可变服务
不用考虑OS的环境配置问题
需要注意:机器上的持久化数据也被保存到了其他地方
不同环境的管理是一个问题
服务配置
推荐的方案是:有一个专门的系统来提供配置管理=>配置中心
保证在CD流水线的构建物是同一个,而不是分TEST构建物和PRD构建物
服务与主机之间的映射
单主机多服务
运维团队的工作量通常与所要管理的主机量成正比
成本降低
主机的监控复杂
主机故障所造成的影响很严重
服务的部署也会变得更复杂
不利于团队的自治性
应用程序容器:java和.net的方式
每个主机一个服务:推荐这种方式
带来了很多的主机管理问题,人工管理是不可取的,必须自动化管理
第8章-监控
监控的内容,不管是单体架构,还是微服务架构,就只有那么三种
1. 物理机器资源
2. 业务信息
3. 服务的性能
1. 物理机器资源
3. 服务的性能
2. 业务信息
开源实时监控套装:ELK
Logstash:日志收集
ElasticSearch:分布式检索系统,用于数据的分析
Kibana:数据聚合后得到的指标的页面展示
多个服务的指标跟踪
有一个汇总的概要指标信息,方便快速定位问题
有一个详细指标信息,方便分析具体问题
服务指标
尽可能多的记录服务基本指标
我们永远无法知道什么数据是有用的!所以尽可能多的记录下来
这里要考虑的时序DB存储数据
语义监控
伪造case去请求系统服务,服务的响应结果与期望的结果作比较,从而达到监控的目的
关联标识(traceId)
使用关联标识(traceId)来重建调用链
需要标准化,强制在系统中执行该标准
传递关联标识(traceId)时需要保持一致性,这是使用共享的薄客户端库
开源工具库
Zipkin:twitter
Dapper:Google
级联故障监控
每个服务的实例都应该追踪和显示其下游服务的健康状态
应该将这些信息汇总,以得到一个整合的画面
标准化
通过公共库来标准化日志格式,上报的监控指标,指标名称等等信息
考虑受众
我们为不同的人收集这些数据,帮助他们完成工作
老板想知道的数据,和运维人员想知道的数据,肯定是不一样的
对于查看这些数据的不同类型的人来说,需考虑以下因素
他们现在需要知道什么
他们之后想要什么
他们如何消费数据
第10章-康威定律和系统设计
组织结构和他们创建的系统之间的关系
松耦合组织
典型代表是:分布式开源社区
Amazon和Netflix
“两个比萨团队”
没有一个团队应该大到两个比萨不够吃
紧耦合组织
典型代表是:商业产品公司
服务所有权
意味着拥有服务的团队负责对该服务进行更改、测试、部署、运维这一整套的权限
所有权程度的增加会提高自治和交付速度
避免不同团队共享服务
孤儿服务
第12章-总结
微服务的原则
围绕业务概念建模
接受自动化文化
隐藏内部实现细节
让一切都去中心化
至可以让团队自己决定什么时候让那
可独立部署
服务上线发布不受其他服务影响限制
隔离失败
避免级联故障
高度可观察
图形化监控
什么时候你不应该使用微服务
服务架构演化过程:从单体到微服务
为服务找到合适的限界上下文后,才开始服务拆分
当你已经有帮助管理微服务的工具后,才进行微服务改造
第1章-微服务
什么是微服务
1. 很小,专注于做好一件小事
通常通过创建一个抽象层或者模块来保证代码的内聚性
对外提供一个简单的API接口,逻辑的复杂性在内部解决
单一职责原则
微服务将单一职责原则应用到服务上,根据业务边界来确定服务边界
多小是合适的?
服务越小,微服务架构的优先和缺点越明显
优点:开发灵活,速度快等等
缺点:管理维护复杂等等
2. 自治性
对外提供一个简单的API接口,逻辑的复杂性在内部解决
服务的发布更新不会影响到调用方
主要好处
1. 技术异构性
支持不同的技术栈,更好的适应不同的场景
2. 弹性
处理服务不可用和功能降级的问题
3. 扩展
可以对需要扩展的微服务进行扩展
4. 简化部署
各个微服务部署独立,可以快速部署
5. 与组织结构相匹配
与一个个小开发团队匹配
6. 可组合性
每个微服务都是一个小功能
多个微服务组合成一个大功能
7. 对可替代性的优化
微服务足够小,重构or新技术重写速度快,影响面小
面向服务的架构SOA
作者认为微服务是SOA的一个具体实现例子
其他分解技术
共享库SDK
模块
没有银弹
微服务架构并不是通用解决方案,不能解决任何技术问题
第3章-如何建模服务
什么样的服务是好服务
松耦合
高内聚
限界上下文(bounded context)
任何一个给定的领域都包含多个限界上下文
每个限界上下文中的模型分为两种
不需要与外部通信
需要与外部通信
每个限界上下文都有明确的接口,该接口决定了它会暴露哪些模型给其他的限界上下文
避免过早服务拆分
如果服务之间的边界搞错了,后面修复的代价会很大
最好能够等到系统稳定下来之后,再确定把哪些东西作为一个服务划分出去
业务功能
从业务功能角度来确定限界上下文、划分服务是一个不错的选择
逐步划分上下文
先从单体系统,划分成粗粒度的上下文,然后在粗粒度的上下文里划分细粒度的上下文
不一定要一步到位,而且这么做大概率是失败的
组织架构和软件架构是相互影响的
不同团队之间的沟通协调是一个大问题
关于业务概念的沟通
对于某个功能所要做的修改,就更倾向于局限在一个单独的微服务边界之内
减小了修改的范围,并能够更快地进行部署
第5章-分解单块系统
关键是接缝
从接缝处可以抽取出相对独立的一部分代码,对这部分代码进行修改不会影响系统的其他部分
识别出接缝不仅仅能够清理代码库,更重要的是,这些被识别出的接缝可以成为服务的边界
杂乱的依赖
数据库是所有杂乱依赖的源头
通过API来访问,而不是直接访问数据库
对性能表示担忧问题
思考一个问题:你的系统需要多快?系统现在是多快?
一部分变慢会带来更大的好处,尤其是当这个“慢”事实上还在可接受的范围内时
共享静态数据的处理方式
每个服务复制一份静态数据的内容
代码
配置文件
放入一个单独的服务中
共享数据:特点是数据可变
抽象出一个单独的服务处理共享数据
事务边界
若某个API出现失败了,事务如何处理??
再试一次
保证最终一致性
终止整个操作
分布式事务
避免自己去创建这套API
尽量使用现有的实现
所有这些方案都会增加复杂性
如果你遇到的场景确实需要保持一致性,那么尽量避免把它们放在不同的地方,一定要尽量这样做
否则,尝试转变为:最终一致性
创建一个概念来表示这个事务
举个例子,你可以创建一个叫作“处理中的订单”的概念,围绕这个概念可以把所有与订单相关的端到端操作(及相应的异常)管理起来
将数据库里的事务转变到代码中来保证
报表数据库的问题
通过服务调用来获取数据
只适合一个非常简单的报表系统
数据导出
一个cron任务,定时从库中导出数据,再导入到同步库中
报表系统表结构的耦合的问题
可以创建一个大视图来做数据聚合:大宽表,导入和导出只对大宽表做
事件数据导出
在事件发生时就给报表系统发送数据,而不是靠原有的周期性数据导出,时效性高
数据导出的备份
第7章-测试
测试范围
单元测试
单元测试是我们开发人员的,是面向技术而非面向业务的
通过TDD(Test-Driven Design,测试驱动开发)写的测试
在单元测试中,我们不会启动服务,并且对外部文件和网络连接的使用也很有限。通常情况下你需要大量的单元测试。
服务测试
直接针对服务某一个功能接口的测试
对于包含多个服务的系统,一个服务测试只测试其中一个单独服务的功能
为了达到这种隔离性,我们需要给所有的外部合作者打桩,以便只把服务本身保留在测试范围内
端到端测试:用户界面测试
端到端测试会覆盖整个系统
通常需要打开一个浏览器来操作图形用户界面
测试金字塔
比例:测试case的数量问题
单元测试>服务测试>端到端测试
实现服务测试
关键问题是如何打桩,需要一个打桩服务,最好是已有的工具
微妙的端到端测试
覆盖多个服务的端到端测试的一种标准方式
任意一个服务在任何时候只要发生变化,都会触发端到端测试
脆弱的测试
端到端测试失败有时是因为资源竞争、超时等,有时是功能真的被破坏了
当发现脆弱的测试时,我们应该竭尽全力去解决这个问题,并且是尽快
看看能否用不易出现问题的小范围测试取代脆弱的端到端测试。
谁来写这些测试
开发+测试
测试多长时间
运行缓慢和脆弱性是很大的问题
大量的堆积
保障频繁发布软件的关键是基于这样的一个想法:尽可能频繁地发布小范围的改变
部署后再测试
冒烟测试
使用蓝/绿部署区分部署和上线
金丝雀发布(canary releasing)
金丝雀发布是指通过将部分生产流量引流到新部署的系统,来验证系统是否按预期执行
金丝雀发布与蓝/绿发布的不同之处在于,新旧版本共存的时间更长,而且经常会调整流量
非功能的测试:性能测试
第9章-安全
身份验证和授权
身份验证
在安全领域中,身份验证是确认他是谁的过程
授权
通过授权机制,可以把主体映射到他可以进行的操作中
当一个主体通过身份验证后,我们将获得关于他的信息,这些信息可以帮助我们决定其可以进行的操作
对于单一的单块系统来说,应用程序本身会处理身份验证和授权
对于微服务架构,我们的目标是要有一个单一的标识且只需进行一次验证
常用的SSO(Single Sign-On,单点登录)实现
企业级领域常用的两种方式
SAML
OpenID Connect
身份提供者
身份验证
目录服务
授权
服务提供者
真正的服务
用户->服务提供者->身份提供者->目录服务->身份提供者->服务提供者->用户
单点登录网关
集中处理重定向用户的行为,只在网关做单点登录
下游服务如何接受主体信息的问题??
如果你使用HTTP,可以把这些信息放到HTTP头上
一种虚假的安全感
内网与外网之间由单点登录网关做身份验证
内网之内,服务之间的调用是没有身份验证的
单点登录网关一定要简单,不能太复杂
细粒度的授权
单点登录网关可以提供相当有效的粗粒度的身份验证
例如:用户是否登录
细粒度的授权由每个服务提供者自己决定
单点登录网关获取主体属性,透传给下游服务提供者,服务提供者根据这些信息,做细粒度的授权
服务间的身份验证和授权
在边界内允许一切
内网环境,各个微服务之间的调用都允许
如果一个攻击者入侵了你的网络,攻击者就可以为所欲为
中间人攻击
边界内信任这种形式被大多数组织采用
发展初期,没有那么多精力做内网的身份验证和权限控制
需要意识到它的风险
HTTP(S)基本身份验证
在HTTP协议头上透传用户名和密码,服务端做身份验证
通过HTTPS加密
SSL证书管理问题
若经过代理服务转发,则不能在代理服务上缓存数据
使用这种方法,服务器只知道客户端有用户名和密码。用户名和密码泄露,则毫无办法了。
使用SAML或OpenlD Connect
内部服务也通过单点网关来做身份验证和授权
单点网关压力会很大
客户端证书TLS
服务端要管理TLS证书,比较复杂
当你特别关注所发数据的敏感性,或无法控制发送数据所使用的网络时,才考虑使用这种技术
HTTP之上的HMAC(Hash-based MessageAuthentication Code,基于哈希的消息码)
使用HMAC,请求主体和私有密钥一起被哈希处理,生成的哈希值随请求一起发送。然后,服务器使用请求主体和自己的私钥副本重建哈希值。如果匹配,它便接受请求
比HTTPS简单方便、系统开销小的方案
APl密钥
服务方给客户端分配API密钥做签名,然后在签名值的比较
API密钥重点关注的是对程序来说的易用性。相对于处理SAML握手,基于API密钥的身份验证更简单直接
可以采用网关模型
集中管理API密钥
结合目录服务,获得授权能力
由开发者自行决定使用方式,不受通信协议限制
代理问题
有一种安全漏洞叫作混淆代理人问题
已经登录的用户,查询其他账户的订单信息
用户登录成功了,通过了网关的身份验证
静态数据的安全
使用众所周知的加密算法
一切皆与密钥相关
如何管理密钥
使用单独的安全设备来加密和解密数据
密钥和加解密服务在一起
使用单独的密钥库,当你的服务需要密钥的时候可以访问它
密钥和加解密服务分开
选择你的目标
考虑哪些数据需要加密
按需解密
第一次看到数据的时候就对它加密。只在需要时进行解密,并确保解密后的数据不会存储在任何地方
加密备份
深度防御
防火墙
日志
可以让你事后看看是否有不好的事情发生过
入侵检测(和预防)系统
IDS(Intrusion Detection Systems,入侵检测系统)
IPS(Intrusion Prevention Systems,入侵预防系统)
不同于防火墙主要是对外阻止坏事进来,IDS和IPS是在可信范围内积极寻找可疑行为
网络隔离
创建VPC(Virtual Private Cloud,虚拟私有云)进行网络隔离
操作系统
给操作系统的用户尽量少的权限
定期为你的软件打补丁
黄金法则
不要实现自己的加密算法
外部验证
由外部方实施的类似渗透测试这样的实验,模拟现实世界的意图
上面是一种评价安全效果的方法
第11章-规模化微服务
扩展
垂直扩展
性能更加强劲的单机
水平扩展
拆分负载
负载均衡
硬件负载均衡
软件负载均衡
重新设计
你的设计应该考虑10倍容量的增长,但超过100倍容量时就要重写了
扩展数据库
数据库服务是有状态的:数据持久性
扩展读取
读写分离
最终一致性
扩展写操作
使用分片
实质类似于通过哈希来分库存储
新增一个分片,是个很难的事情,因为数据需要重新哈希分配
Cassandra:开源分布式nosql数据,支持不停机增加分片节点
共享数据库基础设施
一个正在运行的数据库可以承载多个独立的模式,每个微服务一个
单点故障问题
CQRS(Command-Query Responsibility Segregation,命令查询职责分离)模式
系统的一部分负责获取修改状态的请求命令并处理它,而另一部分则负责处理查询
好复杂
缓存
按位置分为
客户端缓存
客户端会存储缓存的结果
极大的减少网络通信次数
缓存如何更新的问题
代理缓存
反向代理、CDN(Content DeliveryNetwork,内容分发网络)
服务器端缓存
Redis、Memcache
HTTP缓存
因为HTTP协议的通用性,有大量开源产品可以直接使用,实现HTTP缓存
为写使用缓存
后写式缓存是在缓冲可能的批处理写操作时,进一步优化性能的很有用的方法
为弹性使用缓存
缓存可以在出现故障时实现弹性
隐藏源服务
当发生缓存雪崩时,源服务如何应对超大流量?
源服务快速失败,并填充缓存,恢复缓存服务
调用方应该有断路器,快速失败
保持简单
自动伸缩
预测型伸缩
根据每天的流量分布,在高峰期增加运行的实例,在低谷期减少运行的实例
响应型伸缩
应对突增流量
应对故障节点
CAP定理
一致性(consistency)、可用性(availability)和分区容忍性(partition tolerance)
AP系统,牺牲一致性
要求降低为:最终一致性
CP系统,牺牲可用性
支持分布式一致性是非常困难的
CA系统,牺牲分区容忍性
在分布式系统的世界里不可能存在CA系统
真实世界更加复杂,一般:AP系统会优于CP系统
服务注册与发现
DNS
Zookeeper
Consul
Eureka
ETCD
文档服务
我们会确保文档总是和最新的微服务API同步,并当大家需要知道服务在哪里时,能够很容易地看到这个文档。
Swagger
Swagger让你描述API,产生一个很友好的Web用户界面,使你可以查看文档并通过Web浏览器与API交互
HAL和HAL浏览器
0 条评论
下一页