[TOC]
架构演进
单体架构
初期流量比较小、功能比较简单,APP、web端、管理后台都在一个应用里面,提高开发效率,减少部署成本。垂直架构
流量增加,访问量变大,单体应用增加机器带来的效果越来越小。而且随着功能增加代码也难维护,开发成本越来越高。
把单一应用垂直拆分成互不相干的应用,提高效率。SOA架构
随着垂直拆分的服务越来越多,不可避免的服务之间会产生交互,核心业务被抽取出来单独构建服务部署,统一提供给前端应用。微服务架构
SOA的一种特定的实现方案。
微服务架构
微服务定义
关于微服务没有一个特别清晰的定义。但是随着时间推移,业内普遍形成一种共识,主要包括如下几点:
- 轻量级通讯,服务之间基于轻量级的协议通过网络进行通信(一般http)。
- 独立部署,不需要特别去协调。
- 围绕业务能力构建
- 可以使用不同技术栈开发
微服务优点
1. 技术异构性
可以针对特定业务场景选择最合适的技术,比如采取不同的开发语言,技术框架,数据库,从而来提高开发效率,系统性能。
因此开发人员有更多机会采用新技术,这种快速学习和采用新技术的能力对个人和公司都非常有价值。此外由于微服务中服务之间耦合低,即使在采用新技术时出现了问题影响面也比较小风险也比较低。
2. 扩展性
可以针对特定有性能问题的服务进行横向扩展,相对单体应用而言横向扩展的性价比更高。
3. 简化部署
单体应用,修改一个小功能就必须整个重新发布。而微服务下则可以针对特定服务进行部署,相对也容易实施回滚。
4. 弹性
如果系统中一个组件出现故障,但没有导致级联故障,那么系统中其他部分就可以正常运行。
在单体架构系统中如果服务不可用,那么所以功能都会不可用。虽然可以通过将相同实例部署到不同节点上来降低整个系统完全不可用的概率。而微服务本身由于服务之间的领域边界天然形成一个很好的舱壁,可以更好处理服务功能不可用和功能降级问题。
5. 与组织结构相匹配
微服务架构可以更好的跟组织架构相匹配,从而获得理想的团队大小和生产力。
6. 可复用,可组合性
由于服务的拆分,底层核心服务可以得到很好的复用和组合。再来由于web、app、移动端web等的发展,往往一个功能需要针对各个平台提供应用,这种情况下微服务的这个优势就更加明显。
7. 对可替代性的优化
单体架构的系统由于代码庞大,业务复杂很难进行整体甚至单个功能的重构替换。
微服务架构则由于代码量小,功能相对单一,可以轻易的重写甚至直接删除该服务,也不会有额外的心理上的负担。
可能遇到的问题与方案
一. 可靠性
分布式系统中有总多的服务,每个服务都不可避免的会出故障。如果依赖这些服务的上层服务没有隔离这些可能的故障服务,那他则可能被这些故障服务拖垮。
假设一个上层应用依赖30个服务,每个服务99.99%的时间都正常运行,那么可以得到大致如下的结论。
99.9930 = 99.7% 正常运行时间
0.3% of 10亿 requests = 3,000,000 失败
2h+的故障时间每个月,及时所有依赖都有的99.99的可靠性
如果不解决分布式系统中可靠性的问题则可能会出现如下情况,导致整个服务不可用,甚至导致更大的级联故障。
如下是示意图:
解决方案
采用Hystrix或者Sentinel组件来提供熔断限流降级以及资源隔离的功能,提高可靠性。在发生网络分区或者服务故障时不至于产生级联故障影响其他正常的服务。
Hystrix简介
1. 服务熔断
如下是hsytrix的熔断机制:
主要通过统计时间窗口内的失败率来控制熔断器的打开,以及通过一个sleep时间来关闭熔断,恢复服务。
在熔断器打开的情况下,所有针对受该Hystrix保护的服务的访问都会直接失败,如果有配置fallback则执行fallback逻辑。
2. 功能降级
通过配置fallback方法,在服务被熔断或者出现执行出现异常时提供一个功能降级的机制。
示意图如下:
通过配置fallback方法,可以做到在目标服务故障时返回默认值、返回空值、通过网络请求返回缓存数据(放弃一致性,返回老版本的数据)。
3. 资源隔离
Hystrix通过基于线程池和信号量两种模式,对依赖服务进行彼此隔离,避免出现因为某个依赖服务故障而耗尽了所有的资源的问题。基于线程隔离的示意图如下:
实际中到底采用线程池还是信号量隔离的主要考虑之一是你是否需要线程池的异步执行能力。虽然基于线程池隔离相对信号量更加彻底,但是线程数量一旦分配过多会对性能产生影响,所以需要综合考虑。(这也是阿里Sentinel只提供信号量机制的原因)
ps. Hystrix在1.4.0版本后也支持了基于信号量的超时配置。
4. 限流
Hystrix通过配置线程池数量大小或者信号量的数值来进行流量控制。
Sentinel还额外支持QPS限流以及集群限流。
二. 一致性
在了解一致性问题之前需要先了解下CAP,BASE理论。
CAP理论主要讲的是分布式环境下无法同时做到一致性,可用性,分区容错性,只能3选2。
BASE理论主要讲的是基于CAP理论的前提下,怎么处理一致性的问题。其中引入了基本可用(Basically Available)、软状态(Soft state)、最终一致性(Eventually consistent)的概念。
综上解决在分布式环境下一致性问题的方案就是分布式事务。
参考:http://note.youdao.com/noteshare?id=2983ce2f08776797263a7f184c6fd122
分布式事务简介
在分布式环境下一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用。那么在网络分区故障的情况下很可能出现数据不一致的问题。而分布式事务作用就是保证这些小操作要么全部成功,要么全部失败。
本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
是否需要分布式事务
分布式事务解决方案引入会增加系统复杂度,能不用分布式事务就不用,不要因为追求某些设计,而引入不必要的成本和复杂度。
比如避免不必要的服务拆分。
分布式事务常用解决方案
目前阿里的SEATA组件在社区比较活跃,可以考虑使用。其他解决方案一般如下:
1. 本地消息表
2. RocketMq
3. 2PC
没有做过实践,不做介绍
4. TCC
没有做过实践,不做介绍
5. Saga
没有做过实践,不做介绍
其他
在处理分布式事务的同时也需要注意接口幂等性的处理,避免一些重试机制或者消息的重复消费导致脏数据之类问题。
三. 性能问题
微服务架构相比于单体架构,需要通过网络进行彼此通信,而网络的延迟不可避免,因此也不可避免的会对性能有所影响。
在解决这类问题的时候一般可以从如下方面着手。
微服务调用链路监控,查找性能瓶颈
可能是代码问题,或者确实是因为网络延迟而导致响应返回比较慢。发现影响来源后则可以针对性的进行优化处理。
针对网络延时优化
网络延时不可避免,但是相对于例如杭州机房调用美国机房的服务而言,毫无疑问是调用离得近的机房服务延迟会小的多。
eureka的解决方案是提供了一个参数可以配置服务的所属区域,以及是否偏向调用同一区域服务的配置来优化网络延迟的问题。
eureka:
instance:
lease-expiration-duration-in-seconds: 6 #配置在服务提供端, 失效时间 6s内没有发renew就认为该服务失效
lease-renewal-interval-in-seconds: 2 #配置在服务提供端,renew的频率 这里是2s发一次
metadata-map:
zone: hangzhou #指定zone
client:
serviceUrl:
#peer对端的eureka服务,高可用;
#不同机器上建议使用EUREKA_PEER环境变量来修改
defaultZone: http://${EUREKA_PEER:sc-eureka1}:8761/eureka/
prefer-same-zone-eureka: true #更加倾向调用同一个zone的服务
如果没有使用eureka也可以使用类似的思想来处理。
针对调用链路过长优化
服务的调用链路一旦过长,就会放大网络延迟带来的影响。针对业务场景可以采用同步转异步、本地缓存、串行改并行方案来进行优化。
不过需要考虑这样做可能引入的其他问题,比如缓存一致性,异步回调,并行执行消耗线程资源等等问题。
- 针对通讯协议以及序列化方案进行优化
在服务对性能要求很高的时候,可以考虑私有化协议,采用更好的序列化方案,以及数据压缩传输等方案来优化。 - 私有化协议相对通用的HTTP协议,有效信息占比会更高。
- 二进制序列化协议相对于基于文本的比如json序列化协议,序列化后的码流更小,能让传输更快完成。
- 大数据量传输时可以考虑数据压缩之后传输。(参考http的Gzip压缩)
四. 运维复杂度
在服务器与应用数量比较小的情况下我们可以直接登录服务器进行管理维护,但是数量一旦上来这就是一件麻烦事。而且直接登录服务器进行操作也有一定的安全隐患问题。一般为了降低运维的压力和复杂度可以从以下几个方面考虑。
日志聚合管理
如上所说的,服务数量比较少我们可以直接在服务器上查看日志,但是数量上来后必须得有一个统一查看管理的日志平台。一般现在大家采用的是基于ELK+Beats栈来做日志聚合。
简介如下:
Elasticsearch:
分布式搜索和分析引擎,具有高可伸缩、高可靠和易管理等特点。
Logstash:
数据收集引擎。它支持动态的从各种数据源搜集数据,并对数据进行过滤、分析、丰富、统一格式等操作,然后存储到指定的位置;
Kibana:数据分析和可视化平台。通常与 Elasticsearch 配合使用,对其中数据进行搜索、分析和以统计图表的方式展示;
Filebeat:
一个轻量级开源日志文件数据搜集器,基于 Logstash-Forwarder 源代码开发,是对它的替代。在需要采集日志数据的 server 上安装 Filebeat,并指定日志目录或日志文件后,Filebeat 就能读取数据,迅速发送到 Logstash 进行解析,亦或直接发送到 Elasticsearch 进行集中式存储和分析。
常用架构如下:
服务监控
一般采用Zabbix或者Prometheus进行服务监控和报警。
Prometheus的架构图如下:
Prometheus支持对各种服务组件进行监控,比如数据库,短生命周期的job,各种mq开源组件等等,也支持基于k8s和其他服务注册发现中心进行自动化的服务发现并监控。此外Prometheus还支持联邦集群的方式来进行横向扩展。
全链路监控
微服务架构下,一个请求可能会经过总多的服务,横跨多个数据中心。而且这些服务可能是由不同的开发团队维护的,在发生异常时候为了快速定位错误源就需要对整个调用链路进行监控。此外进行全链路监控还可以帮助我们了解性能瓶颈从而进行优化。
目前比较活跃的全链路监控组件有skywalking、zipkin、jeager、pinpoint、cat这几种。
在选型时主要需要从如下几个方面考虑:
- 侵入性
各个组件在agent的实现上都各异,有的基于filter,有的需要手动埋点,有的则采用javaagent探针技术进行采集。 - 性能影响
性能主要从传输协议,以及agent-sdk的实现上来考虑 - 监控粒度
选型时也需要考虑监控粒度是否符合你的要求,而且得考虑他的扩展性。 - 时效性
更高的时效性数据则更有利于及时排查问题。
持续集成
在服务数量越来越多的情况下,为了避免人工操作导致的问题需要引入持续集成的方案。
一般采用jenkins加代码库或者镜像库的方案来处理。
一方面可以在代码提交时自动进行自动化的测试生成分析报告,也可以集成sonar等进行代码分析提高代码质量,此外也可以利用jenkins集成Publish over SSH等组件进行自动发布并可以集成审计等功能。如果有镜像仓库也可以基于镜像仓库进行发布。
版本管理
主要分为代码管理和镜像管理
代码管理一般采用gitlab、bitbucket进行管理,镜像管理可以采用云服务商提供的或者基于harbor自建。
版本管理主要可以方便项目维护,以及可能的服务回滚操作。