• 从 MVC 到微服务,技术演变的必经之路


    架构模式演进

    CGI 模式

    图 1 

    CGI 出现于 1993 年,图 1 是 CGI 模式比较简单的结构图。

    MVC 模式

    开源电商软件等都是采用 MVC 模式,MVC 模式是做软件开发必学和必经历的一个阶段。

    图 2

    1970 年提出了 MVC 的概念,当时的主机和客户端早已凸显了这个概念。如图 2 所示,这是一个 MVC 模式的标准框架,做一个电商网站时,会把订单、用户、物流信息整合起来,做一个单体服务。

    90 年代正是 Java 的时代,MVC 模式在这个时期开始变火。开发者把 MVC 的实践引入 Java  世界。这个模式大放异彩后,各种语言都在借鉴。

    SOA 模式

    图 3

    单体服务之后是 SOA 标准化(图 3 ),在 SOA 里面比较重要的是 Service bus。在企业架构里它起到很关键的作用,主要用来沟通各个服务组件,就像高速公路一样,用来连接所有的村庄。

    经过 MVC 模式后,单体模式开发速度很快,网民的速度增长也很快,服务器接收的请求越来越多,慢慢需要做拆分才出现了 SOA 概念。虽然 1983 年提出了 SOA 的概念,但国内绝大部分企业至少是在 2000 年之后才开始 SOA 化。

    Microservices 模式

    图 4

    图 4 是 Microservices 的架构图,每个 Host 里面都有一个服务,它是独立的。Microservices 于 2005 年出现,到现在仍备受关注和讨论。一个概念由提出到落地,有很深层次的原因,具体后文分析。

    何时使用微服务?

    图 5

    微服务并不能解决所有问题,所以一定要慎重考虑何时采用微服务。这张图(图 5 )很常见,单体服务的开发速度、代价都是比较低的,只有业务复杂到一定程度的时候才适合采用微服务。微服务其实是一个理念和框架,它对团队的要求比较高,首先团队所有成员都要认可这个事情,认可微服务的理念和治理框架,否则执行过程中会有很多问题。

    图 6

    单体服务是把所有东西都部署在一块,如图 6 所示,这是最简单的单体服务。在一定阶段下,简单代表高效,所有东西部署在一起时,整个过程的沟通效率会提高。

    提到微服务时首先会提到康威定律,即当开发一个新的架构时,组织架构图跟软件架构图是极其相似的。举例说明,一个最简单的 APP 开发架构包括移动端和服务端,如果把后台服务的人进行角色分层,会有运维、前端、做中间商业逻辑的后台开发者,就会发现软件架构跟组织架构很相似。对微服务进行的拆分,其实也是对人员组织架构和角色职能的拆分。这两者是很相似的。

    为什么 API 很重要?

    微服务里面最重要的是 API ,API 是服务公开化的方法,它是服务价值的精华体现。API 一定要可靠、可用、可读,前两点大部分人都能做到,但是可读性,很多团队却做得很差。许多国内开发出来的开发系统文档可读性很差,看完之后可能还需要跟对方人员沟通。然而,无论是给内部开发,还是给外部用户提供服务,都不希望产生更多的沟通。

    出现这种情况的原因在于程序员本身的特性,程序员是写程序的。很多程序员认为写文档不如写程序有快感,这对于很多人来说是一种挑战,也是一件很枯燥的事情。可是,对于公众来说,文档却很重要,就像要成为一个好的架构师就要有良好的沟通能力,要做好微服务,API 可读性很重要。

    API 的设计永远只有一次机会,特别是像七牛这类做对外服务的云服务厂商。API 设计完之后,一直都会有人调用,由于无法知道谁正在调用 API ,就无法让对方停用,从而无法升级。所以 API 只有一次机会,发布后就定了型。例如京东的部门不算多,但是 API 发布之后有很多部门在调用。团队做升级时,会通知所有部门停用 API 并且要升级的事情。但在升级完成之后,还是会有部门询问 API 为什么不通了?所以要符合可靠可用这两点,只有一次机会。

    API Gateway

    通常在企业里用的是 API Gateway ,因为它们可以直连。用这个方式有几个优点:

    • 一些通用的事情可以在 Gateway 上面做,包括所有API的请求调用量、请求响应时间、4xx 和 5xx 等错误出错数,这些东西可以通过日志或者其它方式很实时展现出来。
    • 所有的服务其实都需要负载均衡,可以在 API Gateway 统一做一些规则、限流。

    API 的设计要素

    • 版本号

    永远要清楚谁调了哪一个版本的 API 。虽然 API 发布只有一次机会,但有版本号之后能有所防备,做版本升级或版本兼容性的时候会更容易。

    • RequstID

    RequstID 可以有效地提高效率,并在后台加快 Bug 复查和定位问题。给客户端的请求都有ID ,客户端发现有问题或者返回失败时,直接把 RequstID 给服务器端,服务器端拿 RequstID 到后台日志里查,就可以定义到问题。实际上从客户端发过来的一个请求,在后台经过了十几个微服务。而这十几个微服务调用的上下文串联和跟踪的工作,都需要一个 RequstID ,能够从请求端开始,往后去传递,每个人都要记录 RequstID ,出现问题时,把微服务的日志拉出来,就可以很快定位问题的来源。

    • 安全认证和 Signature

    要知道谁调了什么东西,并且保证被调的东西不被篡改。外部调动 API 时都会做安全认证,但是内部调用时不一定会做安全认证。但往往很多时候事情的发展都是出乎意料的,有可能因为老板的决定或者业务的变化,API 需要对外开放,如果前期做了安全认证,就可以避免很多麻烦。

    • 文档可读性

    一定要写好文档,不论是对客户、领导还是外部用户,文档写得漂亮更能彰显性能的优点。

    • Error Code 和 Message 的问题

    以前有两个流派的 Error Code 和 Message 的展现方式。第一种是调用一个请求,对方把错误码写在 HTTP 的 Code  里面,可能用 400、404、409。第二种调法是客户端给服务端发了一个请求,返回 200,在 Body 里面有一个真正的 Code ,通过观察这个 Code 来判断调用是否成功,不成功就会通知 Error Message 。经过几年的实践后推荐使用第二种,因为在微服务相互调用时,它的协议不仅仅局限于 HTTP ,可能是其它方式的调用,直接用这个协议表示这种方式的调用状态并不合适,相反在里面表示一个状态会更好。

    图 7

    图 8

    做一个案例分析,如图 7 所示是一家公司的架构图,野蛮生长的时候更注意的是功能的效率。架构很简单,没有服务需要进行小拆分,都是放在一起形成一个大块整体。前期功能性开发很快,但到了一定阶段后,不管是调用效率还是开发效率都会显现出不足之处。所以进行一个拆分,把大块的拆成十几个、二十几个(如图 8 所示)。基本上每家在做架构升级时,都会把一堆大的东西拆成小的,即微服务;而把四个变六个,就是微服务的架构升级。

    然而在升级的过程中,也会存在一些「战争」,如下:

    1. 效率与规则的战争。
    2. 习惯与规范的战争。
    3. 迭代与优化的战争。
    4. 产品与开发的战争。
    5. 业务爆发与服务能力的战争。

    业务爆发有两种:

    一种是用户量、请求量上来了,服务却跟不上;

    其次是是业务模式增加,需要开发更多的功能,但是开发更多功能时,却会导致服务能力和服务质量的下降。

    微服务变火的原因

    十年前的技术到近几年才开始变得很火的原因,需要从微服务的优劣势进行分析,主要是以下几个原因:

    第一,微服务有按需伸缩的特点。按需伸缩是微服务的优点也是它的缺点。它的缺点是会带来部署与监控运维的成本,每个部署都要监控要运维,每个微服务后面都包含一个缺点。

    第二,每个微服务都可以独立部署,但它的缺点是增加机器数量与部署成本。

    第三,业务独立。微服务的业务很独立,但有服务依赖、治理、版本管理、事务处理的缺点。

    第四,技术多样性。它带来的成本是环境部署成本,因为每个语言环境不同。以及约定成本,和跨语言如何调,都是成本。

    微服务如何治理?

    当了解微服务的优缺点之后,自然会考虑到如何治理微服务,可以通过以下几点进行治理:

    第一,运行状态治理。监控、限流、SLA 、LB 、日志分析,都有一些成熟的开发组件,现在有开源版本的也有云端服务,比如做性能监控的 APM SaaS 等。限流不需要做多么复杂的东西,SLA 自己有日志分析就可以。

    第二,服务注册与发现,现在已有一些比较完善的解决方案。

    第三,部署。部署和布置上的成本大部分被解决了,不管是用容器还是虚拟机,都可以快速部署、快速复制镜像,再进行扩容。当本机上的线上环境所有服务都布好了,在本地开发是最方便的,且不依赖于其他同事提供服务软件。

    第四,调用。注重安全、容错、服务降级、调用延迟。 

    服务注册

    服务注册有两种方案:第一种是客户端发现,第二种是服务端发现。这两种方案是很主流的方案,都有各自的优缺点。下面就是对这两种方案的介绍。

    客户端发现

    首先分析客户端发现。所有的路由表信息存在客户端,客户端知道应该调用哪台机器设备的哪个微服务后,再去做相应的 Load Balance ,比如微服务有四个服务,每个服务的权重都推送到客户端里,客户端再决定调用哪个服务。客户端拥有所有微服务的服务器列表和端口,每个客户端和 Service Registry 都会保持一个长连接,常见做法是每个客户端和 Service Registry 建立一个连接,当微服务后台增加或减少一个微服务,都可以实时地通知客户端。但是这个方案也存在优缺点。下面就是对其优缺点的分析。

    优点:快,可以直接跟微服务直连。

    缺点:

    • Service Registry 很多时候会成为一个瓶颈,当客户端越多瓶颈越明显,任何一个微服务有更新,都必须通知所有客户端,对实时性的要求比较高。
    • 升级痛苦。因为微服务嵌在客户端,很难知道哪些内部用户是用哪个版本的 API ,同时也很难约定大家在同一个时间升级。
    • 维护成本高。

    服务端发现

    服务端发现,通常的服务范围是,所有对外提供服务的接口,七牛用的也是这个方式,因为有成千上万个客户端,不可能把路由表写在七牛的客户端上面。当然这种方式也存在优缺点。

    优点是:所有的服务可控。增加或删除一个服务时,不需要通知客户端,所有客户端可以脱离管控范围。

    缺点:

    • 治理效率和瓶颈是一个很难的问题。
    • 标准的 HTTP 协议问题不大,如果是自定义协议会很麻烦,需要做的工作会很多。

    微服务的部署

    平台

    微服务的部署和治理跟云平台没有必然的联系,用微服务并不意味着只能用容器,仍然可以用 IaaS 、 PaaS 、老的数据中心等平台,这个跟基础设施并没有多大的关系,但使用容器可以更好的解决某些方面的问题。

    手段

    部署方式一般分为手动部署和使用脚本来自动化部署。大公司有基础设施自动化、应用部署自动化平台,一点按钮就可以完成测试、打包、上线、监控、部署,但是小公司基本上没有这种基础设施建设。目前还没有公司把基础设施建设进行开源,因为每个公司的基础设施自动化测试不同,业务场景不同,所以不一定适用。

    应用的自动化部署。 2010 年 PaaS 很火,很多人想把这个部署做成自动化。因为当部署应用特别方便的时,大家才愿意做拆分。管理一台机器,部署一件事情、一个服务,跟部署十个是有明显的时间成本的,如果时间成本降低,大家更愿意进行拆分。

    还有一种方式是 Image 的部署。把虚拟机打包成一个镜像(它的扩展很迅速),常见做法是镜像里面不放代码而是放脚本。每次部署一个新的用户服务时,需要到上面取一个最新的包来部署新的工作,而不是把代码放上去。因为镜像的制作成本比较高,更新麻烦。

    容器和微服务

    近几年微服务突然变火,其实跟容器有很大的关系。

    第一容器够小,可以解决微服务独立部署的问题,解决微服务对机器数量的诉求。

    第二容器独立,可以解决多语言的问题。

    第三是开发环境与生产环境相同,针对非 Windows 开发者,如果使用容器技术,就比较容易使本机的开发环境与生产环境相同。Windows 开发者需要自己部署一台服务器,保持服务器跟线上相同。当一个公司里面有多语言时,有一个镜像就容易解决这个问题。

    第四是容器效率高,省钱。

    第五是代码和 image 一体化。简网以前以服务为核心,每个服务的管理体系比较难做,现在用 Docker 后可以把 image 做完,再把服务和 image 做一一对应后,管理就可以实现一体化。

    第六是容器的横向扩展。容器很容易横向扩展,但纵向扩展对公司来说更有意义。从整个公司的层面来看,机器利用率并不好,这是一个客观存在的问题,当时的技术无法解决问题,因为要多少机器是业务决定的,运维并没有决策权。此时,可以把内存和 CPU 做一个动态调整,最初,可以调低一点,当增上来之后再进行扩容。

    但是这样做仍然存在一些问题。

    第一是 Image 管理问题,有很多服务做,但是谁真正有资格做 Image 管理呢?Image 镜像越来越多,如果开放给所有人,每发布一个升级就多一个镜像,那这是需要治理的。

    第二是系统安全管理问题,在 KVM 里面套一个 Docker ,用 KVM 做隔离,这样做,不仅加强了安全性,又利用了 Docker 快速部署、快速开发微服务管理的体系,系统安全上,短期内无解,但在公司内部运用还可以。

    第三是授权管理问题,授权管理很多时候是特指数据库,如果服务是有状态的,扩容的时候需要做数据库授权,这个授权的操作和管理是有难度的,特别是发布后发现系统有漏洞,管理的难度很高。

    第四是系统成熟度问题,虽然现在 Docker 很火,但整体来说系统成熟度没有达到理想的状态,运用过程中多少会出现一些问题。

    第五是社区成熟度问题,Docker 在两三年的时间内突然变热,但社区成熟度是需要时间沉淀的。

  • 相关阅读:
    Gradle 是什么
    Spring AOP知识
    Spring IOC常用注解
    spring 依赖注入
    Java实现基数排序
    Java实现基数排序(解决负数也可以排序)
    2020/4/10安卓开发:Spinner下拉框
    Spring ioc使用
    java实现:归并排序
    centos中docker的安装
  • 原文地址:https://www.cnblogs.com/firstdream/p/6973543.html
Copyright © 2020-2023  润新知