说明:
(1) 本篇博客介绍微服务的基本内容;包括,什么是微服务,微服务的特点,微服务的优缺点,微服务的两大门派,微服务的拆分,服务的扩展,微服务的模块等;
零:【Spring Cloud和微服务入门】内容概述;
(1) 介绍微服务的基本概念、设计理念、拆分原则;
(2) 微服务和Spring Cloud的关系;
(3) 微服务中常见的组件和功能;
(4) 以【课程查询项目】为例,实际演示如何使用微服务;
● 包括课程查询项目基本介绍、系统架构设计、接口设计;
● 分模块去构建Spring Cloud项目;
● 主要是开发【课程列表】、【课程价格】两个模块(服务)的开发;
● 对服务进行整合,会利用到服务注册与发现;
● 利用Feign去完成服务间的调用;
● 然后,集成网关,并接入服务;
● 也会引入服务的熔断与降级;那么在某个服务不可用的时候,这个手段可以提高可靠性和稳定性;
一:什么是微服务;
1.原先单体应用的痛点;
(1) 项目庞大,不仅仅体现在代码中,也会体现在表中;
● 其实,如果我们的项目很庞大,有很多表;;;;;但是,只要我们的技术文档完整、注释明了、表的描述清晰、表的字段名命名规范,一个新人过来的时候,想要理解这些表也不是很难;;;;;但是,在实际中,我们多多少少不会完全按照规范来,有的时候图省事会偷懒,那么一个新人过来的时候,想要理解这些表(表之间的关联关系,表的含义,表数据量等)就会比较困难;
● 即,单体应用中,所有的表都放在了一起,项目越来越大,表也会越来越多;;;;;而且,到后来,也有可能有些表已经废弃了,但却没有人去删除;
(2) 容易出现尾大不掉的情景;
● 单体应用,当项目很大时,会很难调整、很难重构、架构难优化,而且也没有人敢承担其中的风险;
(3) 代码风格、组件,不统一;
● 一个团队中,会对代码风格作一定规范的,但是规定往往也不会过于死板,也是允许团队成员在风格上有些微差异的;;;;;但是,当所有人都维护一个项目时,就会出现不同的代码风格;;;;;这就会出现,不同开发者之间不熟悉甚至看不懂对方写的代码,代码可读性就会很差;
● 比如A喜欢使用这个日志组件,而B喜欢另一个日志组件;即,在同一项目中,我们使用了多个【实现同样功能的组件】,这就不咋好了;
(4) 技术难以升级;
● 技术往往是需要升级的,项目在技术上升级迭代,也是很正常的;
● 如果项目过于庞大,我们在升级某个组件的时候,就可能会出现很多冲突和不兼容的地方;(出现冲突后,代码报红还好;;;就怕那种,代码不报红,但项目上线后出错的情况)
(5) 正是因为单体应用的这些痛点,所以出现了服务化;
2.什么是服务化;
(1) 拆分:把一个大项目拆成多个小项目,把原先聚集在一起的功能拆分成多个不同的功能;(一般我们会从业务功能角度,去拆分)
(2) 独立:虽然每个模块不足以支撑所有的功能(比如单靠【商品模块】是无法支撑完整的电商功能的),但是对于每个模块,我们至少可以做到独立开发、独立部署、分工和 责任 明确;
(3) 通信:通信一定不能是全耦合的,必须要通过调用(RPC调用或者Http调用)来实现通信;即,A模块想要和B模块通信,A模块不能把B模块具体的代码实现给引过来,即B模块的代码不能是在A模块这儿跑的;;;;;也就是说,A模块要想和B模块通信,A模块是不应该、不需要关心B模块具体怎么实现的;
3.什么是微服务;
维基百科的解释:
(1) 微服务以【专注于,单一职责与功能的小型功能区块】为基础;每一个微服务,其自身都是独立的;其职责也比较单一,功能比较小型;;;;;然后,利用模块化的方式,把多个微服务进行组合,从而组合出一个大型的的复杂的应用程序;
(2) 不同的微服务区块,使用与语言无关(Language-Independent/Language agnostic)的API(API集)进行相互通信;
二:微服务的特点;
(1)技术栈自由;
●不同的区块之间,无论是在代码语言层面还是存储系统层面都是可以不一样的;其相互之间,仅仅是通过接口相互调用的,不用关心其他模块的具体实现;
(2)按需弹性扩容;
● 比如【订单服务】和【商品服务】的流量很大,那么对于【订单服务】和【商品服务】这两个微服务,我们就可以多部署几个;
● 比如【注销用户服务】的流量很小,我们就没必要部署多个;
(3)能力复用率高;
● 比如,A团队已经开发了一个【商品服务】;;;;然后,当B团队开发比如【订单服务】的时候,需要使用【商品服务】,而且想要【商品服务】提供某个能力;;;;那么,B团队就可以告知A团队,如果【商品服务】中已经有这个能力了,那么A团队就可以直接通知B团队怎么调用;如果【商品服务】中还没有这个能力,那么A团队就可以在【商品服务】中增加那个能力;
● 而如果是单体应用的话,当我们开发【订单模块】的时候,如果想要使用【商品模块】的某个功能;由于我们不熟悉【商品模块】的代码,与其梳理清楚【商品模块】的代码,然后去调用【商品模块】已经编写好的那个接口;我们往往会自己直接开发一个可以实现这个功能的SQL;;;;而这是很不好的,会导致【相同的、类似的功能,不同的人开发了多份】,导致重复代码很多;
● 即,微服务的一个特点就是,能力(或者说是功能)的复用率高,重复代码少;
(4)部署和回滚成本低;
● 某个服务部署有问题了,我们直接回滚就行了;不会牵一发而动全身了;
(5)康威定律:组织结构会跟着变动;
● 康威定律的意思是,组织方式决定了整个系统的设计;
● 比如,原先是一个大团队,大家是一起工作的;排工作的时候,谁有空闲就让谁开发;
● 如果,我们进行微服务的改造后,比如【订单模块】由3个人负责;;那么以后在排与【订单模块】相关任务的时候,就是固定的给到这3个人了;
三:微服务的优缺点;
微服务的优点;
(1) 技术栈不受限制,新技术有发展空间;
(2) 可用性强,一个服务宕机了不会整体宕机;
(3) 代码和机器的使用率都提高了;
微服务的缺点;
(1) 运维成本过高;
● 虽然单体应用部署时间比较长,但是我们只要部署一次就够了;
● 但是,如果把单体应用拆成几十个(甚至上千个)服务后,我们就需要部署几十次;;;;同时,也要考虑到谁先谁后、谁依赖谁(很多时候,其是有依赖关系的;被依赖的那个服务,需要先部署)、谁影响谁等;;;;而且,我们知道,部署上线后,机器是要有人监控和维护的;单体应用时,我们只需要监控几台,,微服务的时候,就需要监控成百上千台;
(2) 接口可能不匹配;
● 使用微服务后,不同服务之间的通信都是通过一纸契约;;但,可能A服务,悄悄的调整了(比如修改了某个接口的返回值类型),而需要使用A服务那个接口的B服务,可能无法第一时间获知到这个(擅自的)修改,B服务还傻乎乎的按照契约行事;直到B服务调用A服务的那个接口时,才报了错;
(3) 代码可能重复;
● A服务写了一些方法后,还需要定义一遍对外暴露的接口(API)是哪些,这儿会有一定量的重复;
● B服务调用A服务的时候,然后A服务可能会返回给B服务一个用户;;;B服务调用C服务的时候,C服务也可能会返回给B服务一个用户;;;或者B服务调用A服务其他方法的时候,A服务的另一个方法可能也会返回给B服务一个用户;;;;;;这个用户对象被重复定义和引入,也造成了一定的重复;
(4) 架构复杂度提高;
● 服务拆分后,模块多了,事情也就多了;就需要一个人站在全局的角度去统筹;即使,架构师特别清楚架构的组织结构,其也很难掌握每个服务的具体实现;
所以,究竟适不适合微服务,需要充分的评估;
四:微服务的两大门派:【Spring Cloud】和【Dubbo体系】;
【Spring Cloud】和【Dubbo体系】情况简述:
● 一个公司的不同团队,其使用的技术可能完全不一样,甚至是对立的;
● 比如,A团队使用的是Spring Cloud,B团队使用的是Dubbo体系,而C团队使用的是自研工具(但在一个团队中,其技术栈最好是要统一的);
● 大团队(三五十人规模的)与大团队之间,本身就是相互独立的,其各自内部的技术栈和技术决策都是各自独立做的;由此,就逐渐演化出【Spring Cloud】和【Dubbo体系】这两个主流的技术选型;
● 【Spring Cloud】和【Dubbo体系】这两个,都还是不错的,都是可以应用在成熟的项目中的;其也有各自的拥趸和使用群体;
● 【Spring Cloud】和【Dubbo体系】也没有孰优孰劣,我们能做的就是尽量了解这两个门派,了解其各自的有优缺点;以在开发自己项目的时候,做好技术选型;
1.【Spring Cloud】和【Dubbo体系】:定位不同;
● Spring Cloud由多个子项目(比如断路器,服务发现与注册,配置中心等等)组成;
● Dubbo,在官网上的定义:高性能、轻量级的开源服务框架;我们可以访问Dubbo的官网【https://dubbo.apache.org/zh/】;
● 【Dubbo提供的能力】只是Spring Cloud的一个子集;即,比如Spring Cloud可以提供的任务调度、配置中心等,是Dubbo所不具有的;
● 所以,Dubbo体系和Spring Cloud不是一个维度的东西;只是说有一套流行的技术栈是Spring Cloud,另外一套技术栈是以Dubbo为核心的;;;;;整体而言,Dubbo体系和Spring Cloud的目标和定位,是不一样的;
2.【Spring Cloud】和【Dubbo体系】:使用的通信协议不同;
(1) Spring Cloud,更多的使用的是HTTP协议来通信;即,使用的是REST的API;;;Dubbo体系,更多的使用RPC的方式来通信;
(2) 在通信效率方面,RPC比HTTP更高;(如果我们对系统的性能要求特别高,那么就可以考虑选择Dubbo体系);
(3) 但是HTTP更加灵活;
● 在使用RPC时,如果想要调用其他方法时,我们需要把对方的包给引进来的,我们需要知道对方的方法和接口是怎么定义的;
● 如果使用HTTP时,我们不需要这么做;甚至,只给我们一个接口文档,就能直接去调用;更加灵活;
● 比如,不同的公司或者不同的项目之间,进行接口联调的话,更倾向使用HTTP的方式;如果使用HTTP,双方只需要约定好参数和返回,不需要引入什么包之类的;
3.【Spring Cloud】和【Dubbo体系】:生态和社区活跃度;
(1) Spring Cloud的生态是比较好的,迭代和维护也都不错;
(2) Dubbo以前停止维护过好几年,那么对于小公司来说,这个带来的影响是很难承受的;;现在,Dubbo已经交给Apache维护了;
(3) 但,整体而言,Spring Cloud的生态和活跃度更好些;
4.【Spring Cloud】和【Dubbo体系】:如何选型;
(1) 考拉在几年前,给出的选型经验;
● 对于国内使用来说,Dubbo提供了完备的中文文档,方便学习;而且,国内也有很多公司和团队选用Dubbo;
● 但是,相比于Dubbo,Spring Cloud是一个更完备的微服务解决方案;其在功能上是Dubbo的超集;;;即,在选型上,对于中小型企业来说,Spring Cloud可能更好;
● 微服务选型需要考虑以下几点:内部是否存在异构系统集成的问题,备选框架功能特性是否满足需求,HTTP通信协议是否真正的会成为瓶颈,社区活跃度,团队技术储备;
● 如果使用那些没有团队维护的开源项目(比如,Dubbo以前,就出现过没有团队维护的情况),公司就需要组件一个专门维护的团队;(当然,现在Dubbo已经交给Apache维护了,这一点还好)
(2) 在实际开发项目的时候,对于我们的业务来说,不一定(或者说很少)有业务因为对性能有很高的要求而不得不使用某个框架;;;;所以,在实际选型的时候,更多的可以考虑团队技术储备角度来考虑;
五:微服务的拆分;
1.需要拆分的信号;
(1) 事故频发,而且很多是由于耦合导致的;(比如依赖的版本冲突等)
(2) 服务的启动和部署越来越慢,甚至不可忍受;
(3) 系统的访问流量增加,为了应对,需要扩容;(自然,可以把一个单体应用部署多份;但这种,资源利用率不高,并不能仅针对某些模块,进行针对性的扩容;)
2.不适合拆分的情况;
(1) 为了拆分而拆分;
(2) 系统要求尽量不要变动,能在这个系统上投入的维护成本也比较低,即系统要尽量保持稳定;(比如,一个公司内部的系统,用的人不多,对性能没有很严苛的要求,但要求系统必须稳定;那么,这种情况,就完全没必要拆分)
(3) 团队规模比较小(比如<5人),那么小团队还是比较容易协作的,此时就没有必要拆分;
3.如何拆分;
(1) 通过领域,来拆分;;;;比如,商品归商品,用户归用户;然后基础服务也可以进行拆分(比如,文件系统,可以把它独立出来);
(2) 按照热点,来拆分;;;;比如,秒杀系统中,普通商品和秒杀商品是不一样的(下单流程,用户量,流量压力都是不同的),我们可以把其拆开,这样一来普通商品和秒杀商品不会相互影响(假如,秒杀系统有问题,不能提供服务类;而我们的普通商品还能继续提供服务);
(3) 业务优先、逐步迭代;;;;我们没有必要追求一步到位;比如一个单体电商系统,这个服务正在为公司提供者服务;如果,我们想要把其拆成微服务,最好不要整个团队全部停下手上的工作,就全部投入到把这个系统一步到位的拆成微服务系统上去;;因为从公司利益角度出发,我们的电商系统是需要一直对外提供服务的;;所以,一个可行的策略是逐步的去拆,比如可以先把【优惠券】这种相对独立的模块给拆出来,然后后面在慢慢地、逐步的、步步为营的、根据以后的当时需求,去拆其他模块;
(4) 统筹安排,全盘考虑;;;;拆分毕竟是一个大项目,涉及到稳定性,一旦拆不好,就会出问题;
六:服务扩展;
这儿介绍的是几种,当系统访问流量增加时候,几种应对的策略(其实也就是扩展系统的几种方式);
1.单体集群:水平复制;
● 当访问流量增加时,我们不拆分,而是把这个单体应用,多部署几个,形成集群;
● 这个方案成本比较低,如果能够支撑我们的业务的话,这个方案也不是不可以选择;只是,这个方案,会存在一些浪费而已;
2.微服务:垂直功能拆分与解耦;
● 引入微服务技术,功能拆分;当流量压力来的时候,我们可以针对那些核心的、需要扩展的服务,去扩展;
3.数据拆分:分库分表;
● 当用户量大的时候,会发现数据很快就满了;;;那么,对于这种数据量大,但功能却不是很复杂的应用,我们可以直接对数据进行拆分(比如分库分表);;然后,原先可能只能存储几百万的,那么拆分之后就可以存储千万、亿级的;
4.按需扩展;
● 我们可以监控CPU的程度,内存使用率,网络负载等;;;;当我们发现这些核心指标不足时,就可以针对性的扩展;
● 特定时间扩展;;;;比如双11,618的时候,就可以进行扩展;
● 按需扩展,资源使用率较高,能有效的解决成本,可以尽量避免无效的扩展;
七:微服务的重要模块;
1.服务注册与发现;
● 微服务,需要有一个服务注册中心;
● 比如,我们开发了一个【用户服务】,我们需要在注册中心去注册这个服务;;;然后,其他服务想要调用【用户服务】时,就可以取注册中心,去看下【用户服务】当前的IP,然后再去调用;
2.辅助框架和模块;
● 微服务提供了很多模块,比如诸Feign模块,其可以帮助我们去实现微服务之间的调用;
3.负载均衡;
● 比如微服务的【用户服务】,我们也会部署多台实例;;;那么,就需要负载均衡;
4.网关;
● 微服务中有很多服务,这些服务对外需要有一个统一的出入口;这个就是网关;(而且,网关也算是一层安全保护;网关也可以做解码等功能)
5.熔断和降级;
● 如果某个(某些,甚至全部)服务扛不住了、不可用了,我们是否有一些后备的兜底策略;从而就需要,熔断和降级;(比如,【优惠券服务】不能提供服务的时候,就可以提供一个空的优惠券,这样一来不会影响整个的下单流程)