来源:程序员走向架构师
一,架构演进之路
1.1 单体架构单体架构是指一个系统中所涉及的所有组件都打包在一起进行部署和运行。比如:打包成 war 包,部署在 tomcat 容器。单体架构是出现时间最早的架构,单体架构是使用人数最多,应用范围最广的架构。之所以它受到青睐,是因为有着其他架构无法比拟的优势。比如:易于开发,易于测试,易于部署,综合建设成本低等特性。是许多企业,尤其是初创企业的首选的架构。下面我们通过一张简图,了解一个单体架构的一般组成。
随着业务的增长,单机性能有可能到达极限,那么只需通过负载均衡器就可以满足一定伸缩性。
优点●起始成本低,开发,测试,部署都很快●代码都运行在一个进程空间中,无需考虑跨进程通信,运行效率高缺点●扩展性差,任何一处修改都需要重新部署整套系统●随着业务的发展,单体不可避免的变‘大’,大型单体由于参与人数多,业务变化,代码腐化,很容易“牵一发而动全身”导致不可预估的风险●异构困难,初始技术的选型限制后面引入新的技术,在互联网行业尤为明显,这将影响优秀技术人才的加入●伸缩性存在不足,CPU密集型和IO密集型必须同时满足,导致硬件资源的浪费●代码运行在同一个进程空间,任何一部分代码过度消耗资源都将影响服务整体稳定1.2 分布式架构分布式架构对单体架构做了一定改进,它将单体中不同的业务模块进行了纵向和横向的拆分,各个模块单独部署专人维护,并可以通过网络进行通信。纵向拆分:一个大应用分成几个小应用,每个小应用单独部署横向拆分:可以复用的业务拆分出来独立部署,其他应用通过消息传递与其交互优点:●流量分散,提高并发量●伸缩性变强,比如针对A业务采用CPU密集型服务器,而针对B业务采用IO密集型的服务器●可以针对某个应用单独优化,减少因为一个改动影响整个系统的可能●不同的应用运行在各自的进程中,A应用内存耗尽不会导致B应用内存不足挂掉缺点:●网络跨进程通信,随着业务的发展,应用间网络调用会愈发频繁,而网络本身是不可靠的●如果存在异构应用,则可能存在协议兼容问题●服务之间访问,需要将地址硬编码在代码中,如果地址改变则调用方也需要手动修改●应用拆分后数据是分散的,存在数据一致性问题●每个应用都是集群部署,负载均衡管理较难1.3 SOA架构SOA 即面向服务的架构,他是将业务中可重复利用的部分拆分出来下沉到服务层,并提供对外接口,供其他业务方调用,调用方可以通过服务组合来完成业务的处理。SOA 架构有两种实现模式:一种是以企业服务总线(ESB)为代表的中心化模式。一种是以RPC实现为主的去中心化模式。企业服务总线模式(ESB)这种模式下ESB总线处于中心位置,ESB总线用来连接各个拆分的服务,服务间的相互通信(消息传输,服务接入,协议转换,数据格式转换,基于内容的路由)要通过ESB总线来完成,这就让服务间实现了松散耦合。优点●服务间松散耦合,屏蔽服务调用细节●服务可重用性提高●拥有服务治理能力缺点●过于偏重技术,技术要求高●费用高昂,主要以IBM等大型企业推动,●不具普适性,更适合传统大型企业●重量级通信协议SOAP远程过程调用模式(RPC)去掉ESB总线,基于RPC调用实现各个服务的交互,本质是去中心化的,各个服务之间一般使用共同的协议和传输格式,但是像服务的路由需要借助第三方来实现,比如 Zookeeper 等。dubbo + zookeeper 就是这方面的经典组合。这种模式类似于我们后面要讲的微服务,只不过微服务更落地,粒度更小,而且是面向业务的。优点●服务松散耦合,屏蔽服务调用细节●服务的可重用性高●轻量级通信协议 REST/RPC●高可靠性,高可用性,高可扩展性●拥有服务治理能力缺点●服务粒度不好掌握,易导致服务数量膨胀●易产生分布式事务问题●服务调用链路变长,易产生连锁反应,造成服务不稳定1.4 微服务架构英文:https://martinfowler.com/articles/microservices.html中文:http://blog.cuicc.com/blog/2015/07/22/microservices微服务是一种通过多个小型服务的组合,来构建单个应用的架构风格,这些服务会围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言、不同的数据存储技术、运行在不同的进程之中。服务会采取轻量级的通讯机制和自动化的部署机制,来实现通讯与运维。

优点●去中心化,无集中式管理●服务足够小,足够内聚,服务只关注每个业务点●学习成本低,方便了解某一项业务●进程间通过轻量级通信协议交互●服务松散耦合,伸缩性强●独立部署●技术多样性缺点●接口及服务版本难以维护●运维难度加大●测试难度加大●服务间通信,通信成本高●数据一致性难,分布式事务问题●性能监控,问题定位难●服务划分过细,服务间关系复杂●服务数量太多,团队效率急剧下降●调用链太长,性能下降,问题定位困难●没有自动化支撑,无法快速交付●没有服务治理,微服务数量多了后管理混乱二,微服务原理2.1 服务注册发现微服务架构中,服务的数量非常多,并且采用集群部署,如果由每个服务自己维护服务之间的调用关系,那将非常的复杂,甚至是不可行的。在微服务架构中如果能实现服务之间注册与发现的自动化,那么这个问题,便得到很好的解决。实践中我们一般会使用注册中心来实现服务的注册与发现。核心功能注册中心的核心功能就是实现了服务的注册与发现。服务注册是指服务提供者将服务的元数据信息(IP地址,端口号,集群信息等)发布到注册中心。服务发现是指服务的消费者通过注册中心获取某个服务的元数据信息。同时注册中心需要能感知到服务元数据的变化,并将变化后的元数据信息通知到服务的订阅者。一般情况下,服务的消费者会缓存从注册中心获取的的生产者的元数据信息,通过注册中心的变更通知或周期性刷新缓存。这样做即提高了服务路由的效率,同时具有一定的容错性,也就是说当注册中心暂时不可用时不会影响服务间的正常消费。高可用性因为注册中心的核心位置,保证注册中心高可用就显得十分重要。注册中心的高可用即包含服务本身要搭建集群,也意味着所有服务器要提供对等的数据服务。即同一时刻消费者和生产者只要连接一台注册中心服务器就可以完成服务的注册与发布。这里就使用到分布式一致性协议,Raft,ZAB等注册中心选型
| Nacos | Zookeeper | Consul | Etcd |
维护者 | Alibaba | Apache | HashiCorp | CoreOS(RedHat) |
实现语言 | Java | Java | Go | Go |
客户端 | Java/Go/Node | Java | Java/Go | Java/Go |
CAP模型 | AP/CP | CP | CP | CP |
一致性算法 | Raft | ZAB | Raft | Raft |
SpringCloud Alibaba | 支持 | 支持 | 支持 | 不支持 |
Dubbo | 支持 | 支持 | 支持 | 支持 |
Kubernetes | 支持 | 不支持 | 支持 | 支持 |
2.2 服务路由服务路由是指服务请求具体到哪个服务进行处理和响应,一般要求满足一定的条件。具体的过程就是服务消费者获取到生产者的地址列表,然后通过负载均衡策略,选择一台服务器进行访问。静态路由比如将生产者的服务地址写在配置中心,服务消费者通过获取生产者的真实地址进行调用。这种方案下,如果生产者有所变动,生产者将无法及时感知,进而造成消费异常。动态路由动态路由依赖注册中心的发布与订阅机制,如果生产者服务列表有变动,注册中心可以及时将变动信息通知给消费者。这类方案,消费者与生产者充分解耦。负载均衡负载均衡有多种实现方式:1,服务端负载均衡。2,客户端负载均衡。服务端负载均衡客户端将请求发送到负载均衡器,由负载均衡器根据负载均衡策略转发到一台服务器,并接收服务的响应。比如Nginx 是我们常见的服务端负载均衡器。客户端负载均衡:这种方式下,客户端自身具有负载均衡能力。比如dubbo自己就实现了负载均衡,请求生产者时可以在消费端根据负载均衡策略,选择一台服务器转发。再比如 Spring Cloud 中的 Ribbon 组件。
2.3 服务容错微服务架构下单个服务故障的概率变小,但是因为服务数量很多,从整体来看服务发生故障的可能性增大。如果不能及时控制某个服务的故障范围,很容易导致故障扩散。所以在微服务架构中,服务容错设计是必须要考虑的。服务容错措施超时重试请求超过设置的最大超时时间,此时可以选择直接返回给调用者,或者选择集群中的其他服务器重试,后者需要设置重试次数,避免资源浪费。服务限流限量的目的是在流量高峰期或者流量突增时,把流量速率限制在系统能够承受的范围之内,保护系统不被冲垮。常见的限流算法有:漏桶算法和令牌桶算法漏桶算法无论流量多大,漏桶都会按照固定的速率进行输出。如果请求流量超过了桶的大小,则流量将被舍弃。令牌桶算法系统以一个恒定的速度向桶内放入令牌,如果有请求过来,则先尝试从桶内获取令牌,成功则允许请求,失败则拒绝。该算法允许某种程度的突发流量。服务降级在服务器压力剧增的情况下,对一些非核心服务进行降级处理,用来保证核心业务的正常。服务熔断当某个条件触发时,直接熔断整个服务。熔断器一般有三个状态:开,关,半开。开状态对下游的调用不会真正执行,而是直接返回错误。关状态不对服务调用进行限制,但是会记录调用失败次数,达到阈值则进入开状态。半开状态当熔断器打开状态时,通常经过某个时间会进入半打开状态。该状态允许少部分容量访问下游服务。如果能够访问成功或者一定比例成功,则管理熔断器。否则恢复到打开转态。服务隔离服务隔离本质上是对资源进行分隔,从而实现当系统发生故障时能保证只有故障服务不可用,其他服务正常。线程隔离线程隔离主要通过线程池进行隔离,该机制将每个依赖服务分配到独立的线程池进行资源隔离,从而避免服务雪崩。断路器选型
| Sentinel | Resilience4J | Hystrix |
维护者 | Alibaba | 开源社区 | Netflix |
实现语言 | Java/Go | Java | Java |
客户端 | Java/Go | Java | Java |
服务隔离 | 线程隔离 | 线程隔离 | 线程隔离 |
控制台 | 支持完善 | 无 | 有限支持 |
feign | 支持 | 支持 | 支持 |
dubbo | 支持 | 不支持 | 不支持 |
grpc | 支持 | 不支持 | 不支持 |
2.4 服务监控在微服务架构下,应用被拆分成很多的小服务,一个请求往往需要经过多个服务节点。在这种情况下,我们必须实时掌握各个节点的运行情况,以便服务异常时能及时采取措施。因此服务监控便显得十分重要。微服务的服务监控可以分为:指标型数据监控,日志监控,全链路调用追踪等技术。指标型数据监控(Metrics)这类监控主要用来收集监控事件发生的时间及当时的数值,并存储在时序性数据库中。通过聚合计算来查看指标数据和指标趋势。日志监控(Log)这种监控主要收集的是打印的日志数据,通常会把这类数据存储在全文检索引擎中,便于在需要的时候查询。方案一:Elasticsearch + FileBeat + Logstash + Kibaba●FileBeat:采集日志并传输到指定位置,性能高于 Logstash●Logstash:提取日志数据,转换日志格式并输出到 Elasticsearch●Elasticsearch:存储日志数据,并提供查询检索功能●Kibana:面向用户的 Web 页面,用户搜索,分析Elasticsearch中的日志数据方案二:logback + MQ + Elasticsearch + Kibaba●logback:SpringBoot 默认的日志工具,通过Appender机制将日志数据发送到消息队列●MQ:接收Appender发来的日志,并对日志数据进行加工,转发到Elasticsearch中存储●Elasticsearch:存储日志数据,并提供查询检索功能●Kibana:面向用户的 Web 页面,用户搜索,分析Elasticsearch中的日志数据备注:相对于方案一,方案二性能更高。全链路调用追踪(Tracing)这种方式主要是为了记录一个请求的完整过程,这样我们可以很方便的知道请求在哪个环节出的问题,知道请求经过了哪些服务和每个服务的耗时是多少。这类实现通常要对代码进行埋点或者基于字节码技术实现。Java 领域常用的链路追踪技术有:CAT,SkyWalking 等CAT背靠大众点评,通过手动埋点实现,对代码有侵入,报表功能丰富SkyWalking背靠Apache,基于字节码技术,对代码无侵入,UI 功能强大2.5 配置中心传统应用开发中,配置信息往往写在静态文件中。当需要修改配置信息时,非常不便。就算修改后没有问题,也需要重新测试,部署,浪费大量的时间和精力。而且没有版本和权限管理机制,容易造成线上问题并且难以追溯。在微服务架构中,由于服务数量众多,使用传统配置方式显然不合适,于是配置中心便应运而生。所谓配置中心,是微服务中心化管理配置的地方,通常是一个独立的服务集群。使用配置中心具有如下优势:●环境隔离,不同环境的配置信息互不干扰●配置修改后,即时生效,无需重启服务,可用于开关驱动开发●保存所有修改记录,可回滚,可追溯配置中心选型
| Nacos | Apollo |
背景 | Alibaba | 携程 |
版本管理 | 支持 | 支持 |
配置回滚 | 支持 | 支持 |
权限管理 | 支持 | 支持 |
灰度发布 | 支持 | 支持 |
2.6 API网关API 网关是微服务的统一入口,即所有客户端都要通过网关接入微服务。API 网关通常会包含以下功能。路由转发路由转发是网关的核心功能,API 网关根据路由策略将请求通过负载均衡转发到后端服务业务聚合微服务提供的是细粒度API,在API 网关可以将请求聚合处理,统一客户端的输入与服务的响应输出数据格式转换如果微服务中存在异构系统,可以在API网关处理传输协议与数据格式转换非核心业务统一处理API 网关可以统一处理安全认证,验证,路由转发,请求过滤,限流熔断,缓存等,让微服务将重点放在业务处理上。API 网关选型
| SpringCloud Gateway | Kong | APISIX |
维护者 | Cloud 官方 | Kong | Apache |
实践定位 | 业务网关 | 流量网关 | 流量网关 |
技术栈 | WebFlux/Reactor | Nginx/Lua | OpenResty/Etcd |
成熟度 | 成熟 | 成熟 | 成熟 |
控制台 | 无 | 支持 | 支持 |
Kubernetes | 支持 | 支持 | 支持 |
三,微服务实践方案Apache DubboApache Dubbo 是由阿里巴巴开源的,早先的定位是一款高性能,轻量级的 RPC 框架,如今已经发展成一款拥有诸多特性且面向云原生的微服务框架(Dubbo3.0)。
| Apache Dubbo |
注册中心 | Nacos/Zookeeper |
配置中心 | Nacos/Apollo/Zookeeper |
微服务网关 | Apache Shenyu |
Apache APISIX |
Apache Dubbo-pixiu |
负载均衡 | 客户端负载均衡 |
熔断降级 | Sentinel |
服务调用 | RPC/REST |
Spring Cloud AlibabaSpringCloudAlibaba 是阿里巴巴开源的一站式微服务解决方案,他是在SpringCloud的基础上,将部分组件替换成阿里自己开源的组件而成的。如表所示:
| SpringCloud | SpringCloudAlibaba | SpringCloudNetflix |
注册中心 | Consul/Zookeeper | Nacos | Eureka |
配置中心 | Config | Nacos | SpringCloudConfig |
网关 | Gateway | Gateway | Zuul |
负载均衡 | Ribbon/LoadBalancer | Ribbon/LoadBalancer | Ribbon |
熔断降级 | Circuit breaker | Sentinel | Hystrix |
服务调用 | OpenFeign | OpenFeign | Feign |
消息中间件 | RabbitMQ/Kafka | RocketMQ | RabbitMQ/Kafka |
如果之前有过 SpringCloud(Netflix)的使用经验,那么使用 SpringCloudAlibaba 会非常容易上手。SpringCloudNetflix 因为很多组件(如Zuul,Eureka,Hystrix)不再开源,故一般生产中不再将其视为备选方案。Kubernetes不同于Apache Dubbo 和 Spring Cloud Alibaba 是在应用层面实现微服务,Kubernetes 则是在平台层面实现微服务。
| Kubernetes |
背景 | Google |
服务发现 | KubeDNS/CoreDNS |
负载均衡 | Load Balancer |
API网关 | Ingress Controller |
配置中心 | ConfigMaps/Secret |
熔断降级 | ServiceMesh |
日志监控 | EFK |
链路追踪 | Metrics API/Dashboard |
服务间调用 | 框架无关 |
微服务方案选型
| Kubernetes | dubbo | SpringCloud Alibaba |
优点 | 背靠谷歌语言无关全面覆盖微服务关注点平台级方案 | 背靠阿里RPC高性能流量治理多语言SDK | 背靠阿里组件丰富容易上手 |
缺点 | 技术门槛高偏DevOps和运维 | 门槛较高需要一定自研能力 | 针对Java运行时资源消耗较大 |
备注:Spring Cloud Alibaba + gRPC四,历史的进程云原生(Cloud Native)云原生技术有利于各组织在云环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器,服务网格,微服务,不可变基础设施和声明式API。容器开发人员使用容器将微服务与其依赖打包在一起。通过容器化微服务,云原生应用程序独立于底层操作系统和硬件运行。服务网格Service Mesh是一种新型的用于处理服务与服务之间通信的技术,尤其适用以云原生应用形式部署的服务,能够保证服务与服务之间调用的可靠性。在实际部署时,Service Mesh 通常以轻量级的网络代理的方式跟应用的代码部署在一起,从而以应用无感知的方式实现服务治理。微服务微服务是松散耦合的,开发人员通过处理单个微服务来更改应用程序。这样,即使一个微服务出现故障,应用程序仍能继续运行。不可变基础设施服务器在部署云原生应用程序后保持不变,如果应用程序需要更多的计算资源,则会丢弃旧服务器,并将应用程序移至新的高性能服务器。声明式API云原生系统使用API将松散耦合的微服务整合在一起。API会告诉您微服务想要什么数据以及它能给您带来什么结果,而不是指定实现结果的步骤。为什么会有云原生我们在开发微服务项目时,会面临一些必须要考虑的问题,比如服务注册与发现,服务追踪,服务路由,负载均衡等等。之前受限于硬件资源的限制我们不得不在应用层面上想法解决,但是近些年得益于容器技术的发展,尤其Kubernetes这类容器管理系统的出现,使得上述问题可以从软件层面剥离出来,转变成虚拟的基础设施。让软件开发只专注于业务。真正实现了,弹性伸缩扩容。无服务(Severlesss)Serverless 将提供计算资源的服务器抽象成服务,以API的方式提供用户按需调用。Serverless 包含 FaaS 和 BaaS 两方面。FaaS 解决了应用本身的“无服务器”化,BaaS解决了应用依赖的第三方服务的“无服务器”化。当两者都满足时,应用才算完整的 Serverless 应用。服务器的演进过程无服务器化
容器化
云主机
虚拟化
物理机
传统服务器方式与Serverless对比
从对比图中可以看出,传统服务器很难做到真正的按需伸缩扩容,几乎不可避免的存在资源浪费和资源不足两种看似矛盾却又辩证统一的问题。因为传统服务器资源靠的是人工评估和预置资源的方案,这种方案不可能及时响应资源的需求变动。当资源需求大的时候,服务器资源不足。当资源需求小的时候又面临资源浪费。Serverless 的出现就是要解决这个痛点,服务器虚拟成服务,按需调用,并且可以看做是无限性能。