微服务其实就是服务化的一种概念,由过去单体架构演变成分布式系统的一个产物。

微服务架构演变由来

微服务

微服务是一种软件架构思想,它将一个大且聚合的业务项目拆解为多个小且独立的业务模块,模块即服务,各服务间使用高效的协议(protobuf、JSON 等)相互调用即是 RPC。这种拆分代码库的方式有以下特点:

  • 每个服务应作为小规模的、独立的业务模块在运行,类似 Unix 的 Do one thing and do it well,这就是微服务参考了unix的设计哲学,包括独立开发,自动化测试(细致的错误检查和处理)和(分布式)部署,不影响其他服务。同时也加快了开发交付周期,降低代码耦合度导致的沟通成本。
  • 从依赖库到依赖服务,增加了开发者选择的自由(语言,框架,库),提高了复用效率,只要符合服务 API 契约,开发人员可以自由选择开发技术。这就意味着开发人员可以采用新技术编写或重构服务,由于服务相对较小,所以这并不会对整体应用造成太大影响。

可见微服务也是分布式系统,但是微服务注重的注册中心,和服务发现,可以实现简单的扩缩容的方式,这也是微服务的一大重大特点。也就是服务治理,其实后面出现的各大框架也是着重解决这个问题。

为什么要服务化?

  • 随着模块越来越多,相互调用会产生很多的冗余,所以需要服务化架构(SOA)。
  • 解耦,解决冲突与臃肿。比如sql在专门的sql服务运行,统一检测修改。

第一代微服务—服务化

微服务落地目前存在的主要困难有如下几方面:

  • 服务间通信:单体应用拆分为分布式系统后,进程间的通讯机制和故障处理措施变的更加复杂。
  • 分布式事务:系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并操作多个数据库实现,服务调用的分布式事务问题变的非常突出。
  • 大规模部署:微服务数量众多,其测试、部署、监控等都变的更加困难。

随着RPC框架的成熟,第一个问题已经逐渐得到解决。例如springcloud可以非常好的支持restful调用,dubbo可以支持多种通讯协议,包括现在k8s上istio都志在解决服务间通信。

对于第三个问题,随着docker、devops技术的发展以及各公有云paas平台自动化运维工具的推出,微服务的测试、部署与运维会变得越来越容易。

而对于第二个问题,现在还没有通用方案很好的解决微服务产生的事务问题。分布式事务已经成为微服务落地最大的阻碍,也是最具挑战性的一个技术难题,目前主要解决方案

当然微服务架构的还有很多缺点:

  • 微服务强调了服务大小,但实际上这并没有一个统一的标准:业务逻辑应该按照什么规则划分为微服务,这本身就是一个经验工程。有些开发者主张 10-100 行代码就应该建立一个微服务。虽然建立小型服务是微服务架构崇尚的,但要记住,微服务是达到目的的手段,而不是目标。微服务的目标是充分分解应用程序,以促进敏捷开发和持续集成部署。
  • 微服务的分布式特点带来的复杂性:开发人员需要基于 RPC 或者消息实现微服务之间的调用和通信,而这就使得服务之间的发现、服务调用链的跟踪和质量问题变得的相当棘手。微服务的精髓之一就是服务之间传递的是可序列化消息,而不是对象和引用,这个思想是和 DCOM 及 EJB 完全相反的。只有数据,不包含逻辑;这个设计的好处不用我多说也很好理解,参考 CSP
  • 分区的数据库体系和分布式事务:更新多个业务实体的业务交易相当普遍,不同服务可能拥有不同的数据库。CAP 原理的约束,使得我们不得不放弃传统的强一致性,而转而追求最终一致性,这个对开发人员来说是一个挑战。
  • 测试挑战:传统的单体WEB应用只需测试单一的 REST API 即可,而对微服务进行测试,需要启动它依赖的所有其他服务。这种复杂性不可低估。
  • 跨多个服务的更改:比如在传统单体应用中,若有 A、B、C 三个服务需要更改,A 依赖 B,B 依赖 C。我们只需更改相应的模块,然后一次性部署即可。但是在微服务架构中,我们需要仔细规划和协调每个服务的变更部署。我们需要先更新 C,然后更新 B,最后更新 A。
  • 部署复杂:微服务由不同的大量服务构成。每种服务可能拥有自己的配置、应用实例数量以及基础服务地址。这里就需要不同的配置、部署、扩展和监控组件。此外,我们还需要服务发现机制,以便服务可以发现与其通信的其他服务的地址。因此,成功部署微服务应用需要开发人员有更好地部署策略和高度自动化的水平。

总的来说(问题和挑战):API Gateway、服务间调用、服务发现、服务容错、服务部署、数据调用。其实大部分也是我们一开始的说的微服务落地的三大难题。

不过,现在很多微服务的框架(比如 Spring Cloud、Dubbo)已经很好的解决了上面的问题。

Spring Cloud

Spring Cloud为开发者提供了快速构建分布式系统的通用模型的工具(包括配置管理、服务发现、熔断器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等)

其实主要解决了部署的问题,他就是一个很好的部署工具,目前也开始拥抱docker了。

Dubbo

Dubbo是一个阿里巴巴开源出来的一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案,其实主要解决的服务间通信的问题。

在这个时候组织架构就发生了很大的变化,基本都是要加上一层网关或者企业总线,也就是前台-网关(ESB)-后台的结构。

SOA

SOA是什么?SOA全英文是Service-Oriented Architecture,中文意思是面向服务架构,是一种思想,一种方法论,一种分布式的服务架构。

其实就是上面实现的架构思想,抽象共享解耦。

下一代微服务–去中心化

Service Mesh

随着注册中心的出现,任何调用都走网关,网关的瓶颈有到来,于是出现了下一代的微服务架构service mesh,主要落地的项目就是istio,还有一些其他的项目,主要是去中心化的设计。

在云原生模型里,一个应用可以由数百个服务组成,每个服务可能有数千个实例,而每个实例可能会持续地发生变化。这种情况下,服务间通信不仅异常复杂,而且也是运行时行为的基础。管理好服务间通信对于保证端到端的性能和可靠性来说是无疑是非常重要的。种种复杂局面便催生了服务间通信层的出现,这个层既不会与应用程序的代码耦合,又能捕捉到底层环境高度动态的特点,让业务开发者只关注自己的业务代码,并将应用云化后带来的诸多问题以不侵入业务代码的方式提供给开发者。

这个服务间通信层就是 Service Mesh,它可以提供安全、快速、可靠的服务间通讯(service-to-service)。

Service Mesh,可以将它比作是应用程序或者说微服务间的 TCP/IP,负责服务之间的网络调用、限流、熔断和监控。对于编写应用程序来说一般无须关心 TCP/IP 这一层(比如通过 HTTP 协议的 RESTful 应用),同样使用 Service Mesh 也就无须关系服务之间的那些原来是通过应用程序或者其他框架实现的事情,比如 Spring Cloud、OSS,现在只要交给 Service Mesh 就可以了。

微服务的各种框架也有一些。但这些框架大多是编程语言层面来解决的,需要用户的业务代码中集成框架的类库,语言的选择也受限。这种方案很难作为单独的产品或者服务给用户使用,升级更新也受限于应用本身的更新与迭代。直到 Service Mesh 的概念的提出。Service Mesh 貌似也没有比较契合的翻译(有的译做服务齿合层,有的翻译做服务网格),这个概念就是试图在网络层抽象出一层,来统一接管一些微服务治理的功能。这样就可以做到跨语言,无侵入,独立升级。其中前一段时间 Google,IBM,Lyft 联合开源的istio就是这样一个工具,先看下它的功能简介:

  • 智能路由以及负载均衡
  • 跨语言以及平台
  • 全范围(Fleet-wide)策略执行
  • 深度监控和报告

主要做了什么

  • 可视化 其实本质上微服务治理的许多技术点都包含可视化要求,比如监控和链路追踪,比如服务依赖
  • 弹性(Resiliency 或者应该叫柔性,因为弹性很容易想到 scale) 就是网络层可以不那么生硬,比如超时控制,重试策略,错误注入,熔断,延迟注入都属于这个范围。
  • 效率(Efficiency) 网络层可以帮应用层多做一些事情,提升效率。比如卸载 TLS,协议转换兼容
  • 流量控制 比如根据一定规则分发流量到不同的 Service 后端,但对调用方来说是透明的。
  • 安全保护 在网络层对流量加密/解密,增加安全认证机制,而对应用透明。

目前流行的 Service Mesh 开源软件还有 Linkerd、Envoy,而最近 Buoyant(开源 Linkerd 的公司)又发布了基于 Kubernetes 的 Service Mesh 开源项目 Conduit。

  • Linkerd(https://github.com/linkerd/linkerd):第一代 Service Mesh,2016 年 1 月 15 日首发布,业界第一个 Service Mesh 项目,由 Buoyant 创业小公司开发(前 Twitter 工程师),2017 年 7 月 11 日,宣布和 Istio 集成,成为 Istio 的数据面板。
  • Envoy(https://github.com/envoyproxy/envoy):第一代 Service Mesh,2016 年 9 月 13 日首发布,由 Matt Klein 个人开发(Lyft 工程师),之后默默发展,版本较稳定。
  • Conduit(https://github.com/runconduit/conduit):第二代 Service Mesh,2017 年 12 月 5 日首发布,由 Buoyant 公司开发(借鉴 Istio 整体架构,部分进行了优化),对抗 Istio 压力山大,也期待 Buoyant 公司的毅力。
  • nginMesh(https://github.com/nginmesh/nginmesh):2017 年 9 月首发布,由 Nginx 开发,定位是作为 Istio 的服务代理,也就是替代 Envoy,思路跟 Linkerd 之前和 Istio 集成很相似,极度低调,GitHub 上的 star 也只有不到 100。
  • Kong(https://github.com/Kong/kong):比 nginMesh 更加低调,默默发展中。
  • go-micro是基于Go语言实现的插件化RPC微服务框架,与go-kit,kite等微服务框架相比,它具有易上手、部署简单、工具插件化等优点。go-micro框架提供了服务发现、负载均衡、同步传输、异步通信以及事件驱动等机制,它尝试去简化分布式系统间的通信,让我们可以专注于自身业务逻辑的开发。所以对于新手而言,go-micro是个不错的微服务实践的开始。

目前,市面上有许多 Service Mesh 的实现。我们这里挑选 4 种当前最主流的 Service Mesh,对其诸多方面( 包括功能特性、支持平台、是否付费等 )进行横向对比,用以说明 Istio 所存在的优势和不足。

关于微服务和服务网格的区别,我的一些理解:

  • service mesh主要是解决了微服务框架中三大难题之一的服务间通信的问题,类似于上一代服务架构dubbo的作用,是在容器docker和k8s的原生基础上实现的服务间通信的方案,所以比java系的dubbo方案更加友好。
  • service mesh在上一代的SOA架构上做了升级,设计的是去中性化的通信架构,解决的网关或者总线的瓶颈问题。

k8s和微服务

k8s在docker的基础上实现了管理,促使了下一代的微服务得以落地,主要解决了微服务三大难题的部署问题。类似于springcloud,springcloud目前也开始拥抱k8s了,可见k8s是一种趋势。

随着docker、容器的日渐成熟,容器编排的问题就凸显出来,大量的容器怎么去管理,怎么调度,怎么启停都成了迫切需要解决的问题。有需求就有人去解决,ApacheMesos、kubernetes、docker swarm陆续登场,大有三足鼎立之势,而随着各自的发展,到了2017年下半年,google的亲儿子kubernetes的呼声越来越高,社区也更加活跃、成熟。2017年底,docker swarm和ApacheMesos陆续宣称支持kubernetes,预示着容器编排大战的结束,kubernetes已然成为容器编排领域的事实标准。

服务编排框架的成熟,使得容器的管理越来越方便、高效,容器带来的好处也随之凸显:提升资源利用率节省成本、更高效的持续集成,持续交付、解放运维、快速扩缩容,应对突发流量…

服务编排框架的成熟也让微服务的概念得以落地,同时也催生了java界微服务化的方案,像SpringBoot,SpringCloud。然而服务编排一定是对微服务的编排吗?也就是我们容器里运行的一定是微服务吗?不是的,我们可以运行任何服务,我们现有的业务可以不做任何改造就运行到容器中,让kubernetes去管理、调度。至于微服务呢,只是有了kubernetes,让微服务变得容易管理了。让我们有条件把服务拆分的足够小,足够简单。再也不用担心运维管理的复杂了。了解了docker,服务编排,微服务的关系,我们在看看他们在企业的落地情况。

k8s实现了容器编排工具,能够满足微服务架构拆分落地的需求,包括了管理和运维。k8s算是一个工具,而微服务更多的是个一个概念,架构,可以使用k8s实现,Istio就是微服务使用k8s实现的一个实现。

Serverless

Serverless被翻译为“无服务器架构”,重应用,轻服务器,k8s其实就是这么一个概念。

互联网架构的服务化

为什么服务化

在服务化之前,互联网的高可用架构大致是这样一个架构:

  • 用户端是浏览器browser,APP客户端
  • 后端入口是高可用的nginx集群,用于做反向代理
  • 中间核心是高可用的web-server集群,研发工程师主要编码工作就是在这一层
  • 后端存储是高可用的db集群,数据存储在这一层

痛点:代码冗余

比如各个业务线都是自己通过DAO写SQL访问user库来存取用户数据,这无形中就导致了代码的拷贝。

痛点:复杂性扩散

随着并发量的越来越高,用户数据的访问数据库成了瓶颈,需要加入缓存来降低数据库的读压力,于是架构中引入了缓存,由于没有统一的服务层,各个业务线都需要关注缓存的引入导致的复杂性,这个复杂性是典型的“业务无关”的复杂性,业务方需要被迫升级。

随着数据量的越来越大,数据库需要进行水平拆分,于是架构中又引入了分库分表,由于没有统一的服务层,各个业务线都需要关注分库分表的引入导致的复杂性,这个复杂性也是典型的“业务无关”的复杂性,业务方需要被迫升级。

包括bug的修改,发现一个bug,多个地方都需要修改。

痛点:库的复用与耦合

服务化并不是唯一的解决上述两痛点的方法,抽象出统一的“库”是最先容易想到的解决,的方法。抽象出一个user.so,负责整个用户数据的存取,从而避免代码的拷贝。至于复杂性,也只有user.so这一个地方需要关注了。

但是解决了旧的问题,会引入新的问题,库的版本维护与业务线之间代码的耦合,服务化在这个时候各用各自的实例可以实现解耦。

SQL质量得不到保障,业务相互影响,本质上SQL语句还是各个业务线拼装的,资深的工程师写出高质量的SQL没啥问题,经验没有这么丰富的工程师可能会写出一些低效的SQL,假如业务线A写了一个全表扫描的SQL,导致数据库的CPU100%,影响的不只是一个业务线,而是所有的业务线都会受影响。

服务化

为了解决上面的诸多问题,互联网高可用分层架构演进的过程中,引入了“服务层”,其实就是对db的服务封装。

好处:调用直接

有服务层之前:业务方访问用户数据,需要通过DAO拼装SQL访问

有服务层之后:业务方通过RPC访问用户数据,就像调用一个本地函数一样,非常之爽

好处:复用性,防止代码拷贝,屏蔽底层,统一优化

升级一处升级,bug修改一处修改。

在有了服务层之后,只有服务层需要专注关注底层的复杂性了,向上游屏蔽了细节。

有了服务层之后,所有的SQL都是服务层提供的,业务线不能再为所欲为了。底层服务对于稳定性的要求更好的话,可以由更资深的工程师维护,而不是像原来SQL难以收口,难以控制。

总结

这里只是一个简单的数据库的封装,给数据库新增的一个服务层,通过本地调用函数的方式来获取数据,具体的数据库优化交给数据库专业的人来处理。其实实际业务中还可以服务化的组件有很多,比如会员订单等子业务和序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等非业务,最后其实就是我们常说的SOA的架构。

微服务的粒度

大家也都认可,随着数据量、流量、业务复杂度的提升,服务化架构是架构演进中的必由之路,但是微服务架构多“微”才合适?

不同粒度的服务化各有优缺点总的来说,细粒度拆分的优点有:

  • 服务都能够独立部署
  • 扩容和缩容方便,有利于提高资源利用率
  • 拆得越细,耦合相对会减小
  • 拆得越细,容错相对会更好,一个服务出问题不影响其他服务
  • 扩展性更好

细粒度拆分的不足也很明显:

  • 拆得越细,系统越复杂
  • 系统之间的依赖关系也更复杂
  • 运维复杂度提升
  • 监控更加复杂
  • 出问题时定位问题更难

个人觉得,以“子业务系统”粒度作为微服务的单位是比较合适的。