分布式架构的演进
在软件行业,一个应用服务随着功能越来越复杂,用户量越来越大,尤其是互联网行业流量爆发式的增长,导致我们需要不断的重构应用的结构来支撑庞大的用户量,最终从一个简单的系统主键演变成了一个非常复杂的可以支撑高并发的高可用的分布式架构,但是一个系统再复杂也是不断演变来的,所以从另一方面来说,其实是业务(问题)推动了技术的发展。
传统的单体应用
在早期,我们开发的都是单体应用,也就是一个系统所有的模块都在一个服务上:
这种传统的应用开发和运维都非常简单,随着用户量的增加,我们发现应用程序的压力越来越大,于是我们会选择对应用进行集群部署:
当然因为选择了集群,我们就需要考虑服务分发的问题,所以需要有负载均衡服务器,比如我们最常用的 nginx,还有 lvs,HaProxy 等,硬件层面也可以选择 F5 来实现负载均衡等等。
当然,在使用了集群之后,我们还需要考虑 session 共享的问题,所以相比较单机架构会稍微复杂一点点,那么到这里我们应用进行了扩展了,这时候发现数据库又到瓶颈了,所以数据库又需要进行扩展。
数据库的扩展可以有两种主流方式:
读写分离
通过读写分离以及在某些场景用分布式存储系统替换关系型数据库的方式,能够降低主库的压力,解决数据存储方面的问题,不过随着业务的发展,主库依然会遇到瓶颈。
分库分表
当采用读写分离之后,如果再次遇到瓶颈,那么就可以采用垂直拆分的方式来实现,垂直拆分的意思是把数据库中不同的业务数据拆分到不同的数据库中。但是这样有些热门模块依然迟早会遇到瓶颈,于是可以更进一步采用水平拆分,水平拆分就是把同一个表的数据拆分到不同的数据库中。
垂直拆分还比较容易处理,毕竟同一个模块的数据还是在一起,水平拆分就会比较复杂了,比如说用户表拆成了两张,存在不同的数据库中,那么存的时候到底该存的哪个库,取的时候又该到哪个库去查询,所以水平拆分需要考虑以下问题:
插入和查询的路由问题,需要根据某一个条件来决定当前数据应该分到哪个库。
主键的处理,主键不能采用自增主键的形式,因为不同的库采用自增主键会有冲突。
如果某些查询需要到两个库去查询,会比较难处理。
数据库的拆分可以使用当前比较流行的 Sharding JDBC 或者 MyCat 来实现。当然,为了进一步优化,可以视情况加入缓存层,或者使用消息队列等技术来削峰等优化措施。
分布式架构
分布式架构是指位于网络计算机上的各个组件(系统)仅通过传递消息来通信和协调目标系统,分布式系统其实也可以认为是一种去中心化的实现思路,对于用户来说是无感知的。
分布式架构的意义
从单机单用户到单机多用户,再到现在的网络时代,应用系统发生了很多的变化,为什么单体架构会逐渐满足不了需求转而要采用分布式架构呢?原因主要有以下几点:
升级单机处理能力的性价比越来越低。
单机处理能力存在瓶颈,一台服务器的处理能力始终是会有上限的。
对于稳定性和可用性的要求,单机环境下无法提供,一旦单机应用挂了,整个系统就全部挂了,而分布式架构则不会存在这个问题,某一个模块不可用并不会导致整个系统的不可用。
SOA 架构
SOA 全称为 Service Oriented Architecture,即面向服务架构。SOA 是一种架构理念。它的提出主要是解决服务之间的耦合问题。
SOA 对服务之间的解耦是一种比较粗粒度的划分,比如我们的电商网站按服务可以拆分为:用户模块,订单模块,商品模块等。SOA 其本质上是服务的集合,然后服务间一般会通过 ESB 总线来进行通信。比如之前比较常用的 webservice 就是一种 SOA 架构的实现。
微服务架构
微服务架构在 SOA 架构的基础上做了进一步的细化,微服务架构和 SOA 架构并没有本质上的区别,都是为了服务的解耦,只不过微服务架构更加关注服务的粒度,比如上面提到的用户模块我们还可以进一步拆分成更细粒度的服务。
随着微服务架构的普及,原本一个单体应用可能会被拆分成几十个甚至更多的服务,从应用的压力上来说,我们把压力进行了分流,但是原本一个服务变成了多个服务对开发者和运维者来说也带来了极大的挑战,这也就随之衍生了一些技术组件,比如服务与与服务之间如何通信?单个服务如果是集群如何实现负载均衡?配置如何进行统一管理?适合实现分流?如何实现监控等。
注册中心
各个微服务相互之间需要进行调用,那么服务与服务之间又是如何知道对方的调用信息(如 ip,端口,路由等),最简单最直接的办法就是每个服务都维护一个其他需要调用的服务地址信息,但是这样会给开发和运维带来相当大的工作量,当我们有某一个服务 A 的地址信息发生变更,那么只要调用了 A 服务的其他所有服务都要随之修改。而且假如 A 服务宕机了,其他服务也无法发现,当然,也可以做大发现,但是这会相当麻烦,而且每个服务都要重复实现这个功能,这会导致非常繁琐和重复的工作,所以微服务常用组件中就有了注册中心。
注册中心是微服务架构中一个核心的基础服务,主要用来管理所有的微服务,并且注册中心需要实现服务上线和下线的感知。
也就是说我们所有的微服务都将自己的地址信息注册到注册中心,然后其他调用者只需要维护注册中心的地址即可,当一个服务下线的时候,注册中心也会及时将该服务剔除。
常用的注册中心有:Eureka,consul,Nacos,其他的还有 Zookeeper,Redis 等也可以实现注册中心。
远程通信协议
微服务之间各个服务可能会非常频繁的调用,所以我们一定需要一款高效便捷的通讯协议来完成远程通信。
为什么使用 rpc 而不直接使用 http
回答这个问题之前我们先来回答另一个问题,微服务之间能不能直接使用 http 来进行通信?答案是肯定的,但是直接使用 http 来作为远程通信会有以下问题:
请求和返回参数需要自己封装,过程比较繁琐。
http 协议是基于 tcp 协议实现的,每次连接和断开需要三次握手和四次挥手,这过程会带来一定的网络开销。
基于上面两个问题,我们需要另一种更加高效便捷的通信方式来完成微服务之间的通信,这就是 rpc 通信。
RPC(Remote Procedure Call)远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议,达到调用远程服务就像调用本地方法一样,也就是调用者并不知道这个方法会具体去调用哪个服务。
不过需要强调的是 RPC 并不是一种协议,这一点和 http 是有本质区别的,rpc 只是一种技术名词,其底层实现也可以使用 http 协议,也可以基于 tcp 协议自己去进行改造。
RPC 主要是用来解决两个问题:
处理分布式架构中各个微服务之间的通讯问题。
远程调用时,调用者就像调用本地方法一样方便。
常用的分布式服务之间远程通讯组件有:feign,openfeign,dubbo 等。
负载均衡
提到负载均衡大家的第一反应就是 nginx,一般我们使用 http 通讯时大部分都会使用 nginx 作为负载均衡来处理,那么我们的微服务能否直接使用 nginx 来进行负载呢?
答案是可以的,但是我们为什么不直接使用 nginx 作为服务转发呢?我个人觉得主要有以下三个考虑:
nginx 主要是一款基于 http 来进行的 七层负载(当然其也能实现四层负载),而我们的微服务通信之间不一定会基于 http 协议。
如果使用了 nginx,等于是微服务之间又多引入了一个单点,我们还需要考虑 nginx 转发的问题,还需要对其进行配置调优等。
微服务使用了注册中心来进行统一管理服务的上线和下线,而如果使用 nginx 那么就需要使用 openresty 结合 lua 脚本才能实现从注册中心获取服务。
也就是说直接使用 nginx 来进行负载的话,技术上是可行的,但是却可能会引入一些新的问题,所以微服务之间的负载均衡并没有直接选择使用 nginx,而是重新开发了负载均衡组件。
常用的分布式服务之间负载均衡组件有:ribbon 等。
配置中心
假如我们某一个模块部署了几十甚至上百个集群部署,那么如果每个服务都单独使用自己的配置文件的话,一旦修改某一个配置,那么我们需要同时修改即使甚至上百个服务的配置,这是一个苦力活,所以我们就需要考虑让这些服务共用同一套配置,这样只要修改这一套配置,所有服务都能能生效。
配置中心主要就是用来解决这个问题,为了解决这个问题,配置中心需要具备以下能力:
提供配置文件的管理界面(dashboard),这样使用者可以直接通过访问 dashboard 来实现可视化配置。
配置中心配置修改之后,需要能及时通知到对应服务,让对应服务修改最新配置。
常用的分布式服务之间负载均衡组件有:apollo,nacos,Spring Cloud config,disconf,diamond,Zookeeper 等。
服务降级/熔断
引入微服务我们的目的就是为了让每一个微服务都成为一个独立的单元,我们可以对每一个服务进行独立扩展,实现高可用,假如现在有一个服务 A 因为一下子并发量过高导致请求堆积,那么就会造成越来越多的请求阻塞,最终造成雪崩效应导致服务 A 宕机,最终可能会导致整个微服务架构不可用,所以为了保证高可用用,微服务需要提供一种降级和熔断措施。
降级也可以分为主动降级和被动降级,主动降级就是在高峰期比如我关闭一些非核心功能,如:评论,留言等功能。
而熔断一般指的是某一个方法或者接口负载过高,或者说因为网络都动等原因造成响应超时或者失败等,那么这时候应该主动触发熔断,也就是对后续请求不再处理而是直接返回,当然这也要视具体业务来决定采用何种熔断措施。
常用的分布式服务之间降级/熔断组件有:Hystrix 和 Sentinel 等。
服务网关
微服务架构是由单体服务架构发展而来,一般我们一个一个微服务架构其实是一个大的应用系统,那么必然这一个大的系统有公共部分,比如:统一授权,统一路由,统一记录日志,也可以进行全局的限流措施等。
不过微服务网关并不是必须的,这些工作也可以放到每个服务中进行处理,常用的微服务网关组件有:Zuul,Spring Cloud GatWay。
这么多分布式组件该如何选择
分布式架构中主要有六大组件,而每个组件又有不同的实现,看起来技术五花八门,感觉需要学的东西非常多,但是上面介绍了这么多分布式组件,其实其主要就是三大类型:Spring Cloud Netflix,Spring Cloud 官方,Spring Cloud Alibaba,下面我们对这些分布式组件进行归纳分类,这样大家在学习的时候就可以进行有目的的针对性学习:
Spring Cloud Netflix 是由 Netflix(美国奈飞)公司开源的一套分布式组件,这套组件应该也是大家比较熟悉的一套分布式组件,不过其只有 1.0 版本开源,2.0 之后就不再开源了,Spring Cloud 官方自己也提供了部分组件,而且基于 Feign 的基础上改造成了 Open Feign。
另外一套比较完整的分布式组件就是 Spring Cloud Alibaba,这是由阿里巴巴开源的的一套分布式组件,这套组件中的 dubbo 大家应该也是比较熟悉的,除了这两套组件外,其他的也有一些可以用来作为分布式组件,比如 Zookeeper,Consul 等,配置中心像 apollo 是携程开源的,用的也比较多,所以大家学习的时候可以对同类组件进行了解,并对比其特性,然后选择一套适合自己系统的组件使用。
除了上面的六大分布式组件外,分布式架构中还会涉及到另外两个比较大的问题:
分布式消息
分布式消息一般就使用消息队列,比如 Rabbit MQ,Rocket MQ(阿里巴巴体系),kafka 等。
分布式事务
分布式事务的话,Spring Cloud Alibaba 也提供了一个组件 seata 来实现。
另外分布式系统当中还涉及到链路监控相关问题,这方面可以选择 sleuth + zipkin,pinponit,skywalking等等。所以说分布式架构解决了单体架构一些问题的同时,也带来了一些问题,但是技术总是在向前发展的,比如现在号称为了微服务而生的 Kubernetes(k8s),又有号称是下一代微服务架构的 Service Mesh等。
一门技术的诞生总是为了解决一些问题,所以还是那句话:业务才是推动技术发展的根本原因。只有随着业务的发展出现了问题,才会去解决问题,才有更好的促进新技术的诞生。比如现在流行的 docker,也是为了解决微服务过多导致部署困难问题,任何一门技术能得到发展,它一定是解决了当前的痛点,否则我们为什么要使用它?假如互联网没有兴起,并发量始终很低,那么微服务也不会兴起,直接使用传统的单点应用反而更简单直接。
总结
本文主要讲述了从单点应用到分布式架构的发展历程,并且描述了微服务当中为什么会诞生出一批组件,其根本原因就是为了解决微服务所带来的的挑战和问题,在文中最后对当前流行的分布式架构组件进行了分类整理,帮助大家梳理思路,这样就可以做到有目的的进行针对性的学习。