天天看点

【微服务理论】微服务概览

为什么要学习架构?

因为架构思路是编码的基石,如果脑海中没有架构思路,编码就是一叶障目,看不到全局也就不可能写出好的代码。先别急着撸代码,先看看理论,有个大致的概念。

原理可能看看就能懂,但是只有经历了实践,体会了痛点,才能理解和吸收。

当下互联网行业的火爆,用户量的激增,让各大公司开始考虑扩容和重构,微服务是首先引入眼帘的。然而据我所知,国内不少中小规模的技术团队对微服务的概念都不甚了解,对该不该引入微服务也不置可否。还有一些技术团队,没有考虑实际业务场景,只是为了追求技术热点,盲目引入微服务,但又缺乏相应的技术掌控能力,最后影响了业务的稳定性。

一、架构选型

组织沟通方式决定系统设计。

一定要根据自己的公司体量、业务形态来选择架构,好的架构都是一步步演进过来的。不要过于崇尚新技术、新架构。

架构的演进一定是源于痛点。

如果你不知道当前架构有什么问题,有什么痛点,那么一定无法演进。有很多公司或个人看到微服务很火爆,就开始盲目追求。这样只能是缘木求鱼。

对于微服务,我只能说,单挑死无全尸。

微服务的首要问题:

  • 该不该引入微服务
  • 微服务体系需要哪些技术
  • 团队怎么演进,怎么落地

架构选型

根据康威定律:康威定律

“设计系统的架构受制于产生这些设计的组织的沟通结构。”

每个公司的组织架构不同,则会演进出不同的架构。这个很关键,后面会反复提到。

比如你的公司是垂直架构,那就是递进关系,只有一条线:后端--前端--测试--运维,这样开发过来,最后找问题肯定是反向来的。

如果你的公司是水平架构,那就是分布式关系,比如:

游戏部+视频部+直播部----大后端(中台),那么你的架构一定是分布式的,并且做了服务拆分,将公共逻辑做成了服务。

那么,有银弹么?没有!

就像阿里为什么要建立中台又拆?只有不断的业务改进和架构演进,一切都是为了更好地服务业务,减少开发成本。

所以:

那么,我们来讲讲痛点。

二、单体架构,巨石架构

在开聊微服务之前,我先要你和介绍下单体应用。如果你不知道单体应用的痛,那也不会深刻理解微服务的价值。

当公司团队规模很小的时候,单体应用是最好的选择。主要模式有:LANMP(Linux + Apache或Nginx + MySQL + PHP)和MVC(Spring + iBatis/Hibernate + Tomcat)两种。

其优点是学习成本低,开发上手快,测试、部署、运维也比较方便,甚至一个人就可以完成一个网站的开发与部署。

1-4人小团队首选,放开膀子就是撸。

别上来就微服务,几个开发人员的小公司,去追求微服务、去追求中台架构,这是追求完美吗?不是,是找死。

单体架构的优点

  • 开发简单:

    • 只有一个git,下载部署调试都很方便。
    • 很容易看到全貌,所有的代码都在这里了。
  • 易对应用程序进行大规模修改:

  • 测试相对简单直观:

    没有服务间的联调,没有多级调用。
  • 部署简单明了:

    只有一个应用,扔上去执行就OK了。
  • 横向扩展不费吹灰之力:

    直接运行多个实例,开负载均衡即可。
【微服务理论】微服务概览

单体架构的缺点

  • 应用复杂,

    耦合性高

    • 导致了重构困难、难以敏捷性开发,一崩则全崩。牵一发而动全身。
    • 难以扩展,可能久了就谁都不敢动了。新同事一来需要阅读大量代码,学习成本极高。如果相关负责人离职,那将是雪崩效应。
  • 开发困难
    • IDE卡

      ,代码都在一起,当然卡。
    • 编译慢、启动慢

      。当单体应用的代码越来越多,依赖的资源越来越多时,应用编译打包、部署测试一次,甚至需要10分钟以上。
  • 框架升级困难

    ,只能长期依赖某个可能已过时的技术栈。
    • 升级就只能全升,不能有针对性地利用语言优势。
  • 团队协作开发成本高

    • 早期在团队开发人员只有两三个人的时候,协作修改代码,最后合并到同一个master分支,然后打包部署,尚且可控。
    • 但是一旦团队人员扩张,超过5人修改代码,然后一起打包部署,测试阶段只要有一块功能有问题,就得重新编译打包部署,然后重新预览测试,所有相关的开发人员又都得参与其中,效率低下,开发成本极高。
    • 如果分了团队开发模块,团队之间的时间安排会不一致,A团队的代码很可能对B团队的代码产生影响。
  • 无法隔离:

    如果有一个大bug,内存泄漏、DB打爆,都是噩梦。
【微服务理论】微服务概览

三、什么是服务化

最开始的启发来源于一本书《The Art of Scalability》,即《可扩展性的艺术》,探讨了现代企业的可扩展 Web 架构、流程和组织。

里面提到了三种扩展方式:

【微服务理论】微服务概览
【微服务理论】微服务概览

分别是:

  • 多个实例的负载均衡

    :单体+负载均衡(随机算法)
    【微服务理论】微服务概览
  • 根据请求的属性路由请求

    :单机+负载均衡(哈希算法)
    【微服务理论】微服务概览
  • 根据功能拆分业务

    :即服务。每个服务都是由一组专注的、内聚的功能职责组成。
    【微服务理论】微服务概览
围绕业务功能构建的,服务关注单一业务,服务间采用轻量级的通信机制,可以全自动独立部署,可以使用不同的编程语言和数据存储技术。

我们将单体应用中的逻辑分离,单独成立一个项目,独立开发、测试、上线、运维,然后将进程间调用变成远程RPC调用,各个模块之间不耦合,可以交给不同的团队管理,还可以根据各个模块的特点选择不同的语言。这样就可以解决单体应用膨胀、团队开发耦合度高、协作效率低下的问题。

【微服务理论】微服务概览

微服务1.0(SOA,面向服务的架构模式)

什么东西过大,解决的最好办法就是把它变小。

分而治之,化繁为简。

在我看来,微服务也是分级别的。也就是微的程度。

比如可以先按大的逻辑拆分,用电商举例,商品展示、购买、物流这样分。

也可以商品服务、订单服务、支付服务等这样分。

  • 纵向拆分:从业务维度进行拆分。比如可以先按大的逻辑拆分,用电商举例,商品展示、购买、物流信息。
  • 横向拆分:从公共且独立功能维度拆分。标准是按照是否有公共的被多个其他服务调用,且依赖的资源独立不与其他业务耦合。比如商品服务、订单服务、支付服务、评价服务等。
    【微服务理论】微服务概览

差别是什么?

前一种只是按接口分,我把展示的接口都放到一个单体写,那样这个单体挂了,购买的人不受影响。

问题就是公共模块(鉴权、底层方法)我要写多套。

适用于中型团队,因为公共模块也不是天天改。

好处

最大程度将业务耦合性降低,A模块挂了不影响B模块,而且模块可以分配给不同的人,单独治理。
线下麻烦点,但是线上稳定点。

举例:账号模块,此时可能是每个单体里面都有一套账号数据生成代码,然后用同一个git维护,一旦更新了就所有单体都要更新。

尽管也是模块化逻辑,但是最终它还是会打包并部署为单体式应用。

问题

  • 代码冗余严重。
  • 单机扛不住只能上负载均衡,查日志可能要查多台机器。更新也是,要批量上代码。
  • 数据库难以隔离,尤其是用户这种量最多的数据,可能一个bug导致全盘慢。
  • 故障无法转移,A机器挂了, B机器的代码是一样的,基本也会挂。
  • 影响的是一个整体业务,无法降级,无法熔断。挂就是一个业务挂。

微服务架构与SOA的异同

相似点:

  • 都是特定的风格架构;
  • 都以一系列服务方式组织系统;

不同点:

  • 完全不同的技术栈(微服务架构采用轻量级、开源技术以及哑管道通信);
  • 处理数据方式不同(微服务架构有自己的数据库);
  • 服务尺寸、规模不同(微服务架构中的服务相对较小);
  • 通信方式不同,SOA采用了ESB企业总线之类的重量级通信方式。
【微服务理论】微服务概览

微服务2.0

另一种就是按模块分,或者叫按数据分,这才是真的微服务。

什么是微服务

:围绕业务功能构建的,服务关注单一业务,服务间采用轻量级的通信机制,可以全自动独立部署,可以使用不同的编程语言和数据存储技术。

重点:每个服务都有自己数据模型和数据库。

只有独占DB,才能谈隔离,才能有针对性地扩缩容、上集群。

适用于大型公司,业务量很大,每个模块都是一个小团队负责。

最大程度减少沟通成本。

当人员数量上来后,最大的消耗并不是编码时间,而是沟通时间。

举例:我将账号集中到一个服务,所有人需要账号信息都找我来拿,然后我这边用负载均衡、集群保证我的高可用,如果有人要更新用户数据,要我走我的接口,要么走我的消息队列,我来维护账号数据的一致性。

围绕业务或者数据构建,服务关注单一业务。服务间采用轻量级的通信机制,可以全自动独立部署,可以使用不同的编程语言和数据存储技术。微服务架构通过业务拆分实现服务组件化,通过组件组合快速开发系统,业务单一的服务组件又可以独立部署,使得整个系统变得清晰灵活。

  • 小即是美:小的服务代码少,bug 也少,易测试,易维护,也更容易不断迭代完善的精致进而美妙。
  • 单一职责:一个服务也只需要做好一件事,专注才能做好。
    • 一个服务只做一件事
    • 一个包只做一件事
    • 一个函数只做一件事
      • 错误例子:Common  utils ,最后就只是垃圾桶。
  • 尽可能早地创建原型:尽可能早的提供服务 API,建立服务契约,达成服务间沟通的一致性约定,至于实现和完善可以慢慢再做。
  • 可移植性比效率更重要:服务间的轻量级交互协议在效率和可移植性二者间,首要依然考虑兼容性和移植性。
【微服务理论】微服务概览

微服务的问题是什么?

Fred Brooks 在30年前写道,

“there are no silver bullets”。

但凡事有利就有弊,微服务也不是万能的。

  • 服务的拆分与定义是一项挑战;
    • 没有具体的、良好的算法可以完成拆分工作,这让服务的拆分更像一门艺术。
    • 如果拆分的不好,会成分布式单体,会包含单体和微服务两者的弊端。
  • 分布式系统带来各种复杂性,使开发、测试和部署变得困难;
    • 跨服务的事务和查询。
    • 数据一致性。
    • 需要高度自动化。
    • 需要统一的日志采集。
  • 当部署跨越多个服务的功能时需要谨慎地协调更多开发团队;
    • 需要根据服务依赖来制定开发计划。
    • 需要制定统一的通讯协议。
  • 开发者需要思考到底应该在应用的什么阶段使用微服务架构;
    • 初创公司应该从单体开始。
  • 测试困难
    • 不知道是代码的bug还是环境的bug。
  • 连锁反应
    • 上游(调用者)写了个for循环,给下游(提供者),然后下游再给他的下游,可能就指数级放大了。
    • 需要添加限流、熔断等策略。
  • 依赖关系复杂
    • 服务都成了模块化,彼此之间的依赖关系是极其混乱的,如果不用工具,很难维护调用关系。

常见问题案例

这样带来什么问题

问题1:for循环点查,qps被成倍放大。一个请求到了下游可能就成了100个。

问题2:串行请求,慢,而且不做降级就会直接超时导致500。

问题3:测试困难,不是代码的问题,而是部署的问题。

问题4:维护困难

• 日志查看,这么多台机器(或目录)。

• 联调:不知道是哪个环节慢。

• 报警:哪一环被打挂了也不知道。

解决qps放大:

  • 粗粒度API,批量返回多个用户的元数据。
  • 并行请求,多协程请求多个服务节点,再组装结果。

微服务的优势

既然这么多人选择微服务,肯定是利大于弊的。

  • 使大型的复杂应用程序可以持续交付和持续部署
    • 自动化测试—-持续交付和持续部署
    • 独立部署
    • 开发团队自主且松散耦合
  • 每个服务都相对较小并容易维护;
    • IDE快,启动快。
    • 提高调试、部署等环节效率。
    • 拆分后,代码量少了, bug也更好发现。
    • 减少了沟通成本,每个人维护好自己的服务即可。
  • 服务可以独立部署;
  • 服务可以独立扩展;
    • 有针对地选择服务器。
  • 微服务架构可以实现团队的自治;
    • 每个团队可以有自己的技术和管理方法。
  • 更容易实验和采纳新技术;
    • 方便试错和迭代。
  • 更好的容错性;
    • 故障隔离了,进程和数据库都隔离了。
  • 耦合性极低。(去中心化)
    • 数据去中心化: 独占DB,减少数据干扰。以前一个慢SQL打挂全服的情况不会有了。
    • 技术去中心化:想用什么语言都可以,只要实现协议即可。便于重构和迭代。
    • 治理去中心化:
      • 原子服务

        :一个服务只做一件事情。
      • 独立进程

        :独立进程、独立机器、独立发布。
      • 隔离部署

        :每个服务之间独立部署,可以避免相互影响,并且和按需进行分配资源,节省成本 。
【微服务理论】微服务概览

四、实现微服务

微服务这么好,我们该怎么实现它呢?

组件服务化

传统实现组件的方式是通过库(library),库是和应用一起运行在进程中,库的局部变化意味着整个应用的重新部署。

通过服务来实现组件,意味着将应用拆散为一系列的服务运行在不同的进程中,那么单一服务的局部变化只需重新部署对应的服务进程。

我们用 Go 实施一个微服务:

  • kit:一个微服务的基础库(框架)。
  • service:业务代码 + kit 依赖 + 第三方依赖组成的业务微服务
  • rpc + message queue:轻量级通讯

本质上等同于,多个微服务组合(compose)完成了一个完整的用户场景(usecase)。

基础设施自动化

无自动化不微服务,自动化包括测试和部署。单一进程的传统应用被拆分为一系列的多进程服务后,意味着开发、调试、测试、监控和部署的复杂度都会相应增大,必须要有合适的自动化基础设施来支持微服务架构模式,否则开发、运维成本将大大增加

  • 基建搭建好:消息队列、日志采集、容器编排等。

    容器化是微服务的基石

  • 文档写好,gRPC代码即文档。
  • 自动部署,CICD:Gitlab + Gitlab Hooks + k8s
  • 染色发布,方便开发和压测。
  • 监控体系:k8s,以及一系列 Prometheus、ELK、Conrtol Panle
  • Testing:测试环境、单元测试、API自动化测试
【微服务理论】微服务概览

可用性

著名的 Design For Failure 思想(所有依赖的东西都会崩)

微服务架构采用粗粒度的进程间通信,引入了额外的复杂性和需要处理的新问题,如网络延迟、消息格式、负载均衡和容错,忽略其中任何一点都属于对“分布式计算的误解”。

  • 隔离
  • 超时控制
  • 负载保护
  • 限流
  • 降级
  • 重试
  • 负载均衡

鲁棒是Robust的音译,也就是健壮和强壮的意思。 它也是在异常和危险情况下系统生存的能力。

比如说,计算机软件在输入错误、磁盘故障、网络过载或有意攻击情况下,能否不死机、不崩溃,就是该软件的鲁棒性。

所谓“鲁棒性”,也是指控制系统在一定(结构,大小)的参数摄动下,维持其它某些性能的特性。

兼容性

一旦采用了微服务架构模式,那么在服务需要变更时我们要特别小心,服务提供者的变更可能引发服务消费者的兼容性破坏,时刻谨记保持服务契约(接口)的兼容性。

伯斯塔尔法则伯斯塔尔法则:

Be conservative in what you send, be liberal in what you accept.

发送时要保守,接收时要开放。

按照伯斯塔尔法则的思想来设计和实现服务时,发送的数据要更保守,意味着最小化的传送必要的信息,接收时更开放意味着要最大限度的容忍冗余数据,保证兼容性。

既然分了部门。语言、习惯等都会有很多不一样,所以要多做兼容,多写文档(gRPC代码即文档)。

所有依赖的东西都会崩。

五、微服务的政治

想要推行一种新的架构,都是困难的,毕竟谁也不想做改变。

银弹现象:

过热期(迷恋和崇拜)— 低谷期(失望)— 成熟期(生产力的高地,理性地使用)

理性地看待新技术。

骑大象的人:大象代表思维中情绪化的部分,它完成了绝大部分决策。骑大象的人代表理性的部分,用来对大象的决策进行判断。

而架构的改变也必然会带来组织架构的改变。

比如以前,后端开发只是按功能大致分一分,谁写的就一直维护那一块,然后写完了就发布测试,然后打包给运维上线。

但是一旦线上有问题的时候,只能运维先看服务器有没有问题,没有问题就开始摇后端负责人,然后负责人大致看一下,看不出来就喊对应功能的负责人。

有的甚至连日志在哪里看都不知道,毕竟不知道运维怎么部署的。

现在转为了微服务,大家自己负责这个模块的开发、单元测试、运维部署、监控,自动化部署,日志采集也是统一的。

You build it , you run it

运维只需要提供好用的工具,测试只用黑盒测试,测用户场景和UI。

这样就形成了一个闭环团队,而且可扩展性大大提升。

总结

没有最好的架构,只有最适合自己的架构。根据自己的业务、团队来定制架构,能快速交付、快速迭代、持续重构的架构,就是好架构。

第一,要记住微服务不是解决所有问题的万能"银弹"。

第二,编写整洁的代码和使用自动化测试至关重要,因为这是现代软件开发的基础。

第三,关注微服务的本质,即服务的分解和定义,而不是技术,如容器和其他工具。

第四,确保你的服务松耦合,并且可以独立开发、测试和部署,不要搞成分布式单体(Distributed Monolith),那将会是巨大的灾难。

第五,也是最重要的,不能只是在技术上采用微服务架构。拥抱 DevOps 的原则和实践,在组织结构上实现跨职能的自治团队,这必不可少。还必须记住∶ 实现微服务架构并不是你的目标。你的目标是加速大型复杂应用程序的开发。

当问题足够大、有足够多的不确定性因素时,人们习惯把大的问题拆分成小的问题,通过分割、抽象和重用小而可靠的功能模块来构建整体的方案。

但是当这些小的、可重用的部分越来越多时,又会出现新的问题。

参考:

  • 《微服务架构设计模式》

继续阅读