微服务介绍

PART1

什么是微服务?什么时候适合微服务改造?微服务架构到底是什么样的?

wiki定义介绍:微服务的概念最早是在 2014 年由 Martin Fowler 和 James Lewis 共同提出,他们定义了微服务是由单一应用程序构成的小服务,拥有自己的进程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用 HTTP API 通讯。同时,服务会使用最小规模的集中管理 (例如 Docker)技术,服务可以用不同的编程语言与数据库等。

什么是微服务

单体应用

​ 早些年,各大互联网公司的应用技术栈大致可分为 LAMP(Linux + Apache + MySQL + PHP)和 MVC(Spring +
iBatis/Hibernate + Tomcat)两大流派。无论是 LAMP 还是 MVC,都是为单体应用架构设计的,其优点是学习成本低,开发上手快,测试、部署、运维也比较方便,甚至一个人就可以完成一个网站的开发与部署。

​ 以 MVC 架构为例,业务通常是通过部署一个 WAR 包到 Tomcat 中,然后启动 Tomcat,监听某个端口即可对外提供服务。早期在业务规模不大、开发团队人员规模较小的时候,采用单体应用架构,团队的开发和运维成本都可控。然而随着业务规模的不断扩大,团队开发人员的不断扩张,单体应用架构就会开始出现问题。

部署效率低下。以我实际参与的项目为例,当单体应用的代码越来越多,依赖的资源越来越多时,应用编译打包、部署测试一次,甚至需要 10 分钟以上。

团队协作开发成本高。以我的经验,早期在团队开发人员只有两三个人的时候,协作修改代码,最后合并到同一个 master 分支,然后打包部署,尚且可控。但是一旦团队人员扩张,超过 5 人修改代码,然后一起打包部署,测试阶段只要有一块功能有问题,就得重新编译打包部署,然后重新预览测试,所有相关的开发人员又都得参与其中,效率低下,开发成本极高。

系统高可用性差。因为所有的功能开发最后都部署到同一个 WAR 包里,运行在同一个 Tomcat 进程之中,一旦某一功能涉及的代码或者资源有问题,那就会影响整个 WAR 包中部署的功能。比如我经常遇到的一个问题,某段代码不断在内存中创建大对象,并且没有回收,部署到线上运行一段时间后,就会造成 JVM 内存泄露,异常退出,那么部署在同一个 JVM 进程中的所有服务都不可用,后果十分严重。

线上发布变慢。特别是对于 Java 应用来说,一旦代码膨胀,服务启动的时间就会变长,有些甚至超过 10 分钟以上,如果机器规模超过 100 台以上,假设每次发布的步长为 10%,单次发布需要就需要 100 分钟之久。因此,急需一种方法能够将应用的不同模块的解耦,降低开发和部署成本。

以微博系统为例,微博既包含了内容模块,也包含了消息模块和用户模块等。其中消息模块依赖内容模块,消息模块和内容模块又都依赖用户模块。当这三个模块的代码耦合在一起,应用启动时,需要同时去加载每个模块的代码并连接对应的资源。一旦任何模块的代码出现bug,或者依赖的资源出现问题,整个单体应用都会受到影响。首先可以把用户模块从单体应用中拆分出来,独立成一个服务部署,以 RPC 接口的形式对外提供服务。微博和消息模块调用用户接口,就从进程内的调用变成远程 RPC 调用。这样,用户模块就可以独立开发、测试、上线和运维,可以交由专门的团队来做,与主模块不耦合。进一步的可以再把消息模块也拆分出来作为独立的模块,交由专门的团队来开发和维护。

微服务特点:

  • 服务拆分粒度更细。微服务可以说是更细维度的服务化,小到一个子模块,只要该模块依赖的资源与其他模块都没有关系,那么就可以拆分为一个微服务。
  • 服务独立部署。每个微服务都严格遵循独立打包部署的准则,互不影响。比如一台物理机上可以部署多个 Docker 实例,每个 Docker 实例可以部署一个微服务的代码。
  • 服务独立维护。每个微服务都可以交由一个小团队甚至个人来开发、测试、发布和运维,并对整个生命周期负责。
  • 服务治理能力要求高。因为拆分为微服务之后,服务的数量变多,因此需要有统一的服务治理平台,来对各个服务进行管理。

继续以前面举的微博系统为例,可以进一步对内容模块的功能进行拆分,比如内容模块又包含了 feed 模块、评论模块和个人页模块。通过微服务化,将这三个模块变成三个独立的服务,每个服务依赖各自的资源,并独立部署在不同的服务池中,可以由不同的开发人员进行维护。当评论服务需求变更时,只需要修改评论业务相关的代码,并独立上线发布;而feed 服务和个人页服务不需要变更,也不会受到发布可能带来的变更影响。

存在的问题:

服务化最难的是在数据库层,因为数据库中很多数据都是相互关联的,比如用户用户跟订单,订单和商品等等这些数据之间都是有关联的,服务拆分之后会面临以下问题:1当需要读取关联数据时,如果采用表连接的方式查询数据会出现跨数据库查询的可能,2如果是通过RPC的方式多次调用(比如要查订单,就需要查询商品及订单详细信息),也会出现多次调用导致的频繁多次的数据库连接,而如果使用缓存,也会面临数据库与缓存之间的数据同步问题,特别是数据库与缓存服务器都存在集群的情况下,会更难处理,这也是在做微服务之前必须要考虑的问题。

微服务划分的粒度?

1. 数据库是否拆分?从数据架构的角度看,可能有三个选择:1) 一个db实例,所有微服务在一个schema中,2) 一个db实例,每个微服务有自己的schema,3) 每个微服务有自己的db实例。我们在实际项目中应该如何取舍?
2. DAO层是否拆分?可能有两个选择: 1) DAO层保持不变或者单独部署成微服务,所有业务微服务通过引用jar包或者HTTP的方式和DAO进行交互。2) 把DAO进行拆分,每个业务微服务内部管理自己会用到的db数据。在所有微服务采用的技术栈相同的情况下,上面哪种方式更可取?

作者回复: 第一个问题:数据库拆分其实不是微服务拆分的关键,如果会引入分布式事务的话,最好不要拆分。
第二个问题:长远来看,建议方案2,这样会减少每个微服务同db的连接,在集群规模上千台的情况下,db的连接数不会成为瓶颈,服务启动也会变快,因为初始化连接也会变少。

什么时候适合微服务改造

比如做一个社交 App,初期为了快速上线,验证可行性,可以只开发首页信息流、评论等基本功能。产品上线后,经过一段时间的运营,用户开始逐步增多,可行性验证通过,下一阶段就需要进一步增加更多的新特性来吸引更多的目标用户,比如再给这个社交 App 添加个人主页显示、消息通知等功能。

一般情况下,这个时候就需要大规模地扩张开发人员,以支撑多个功能的开发。如果这个时候继续采用单体应用架构,多个功能模块混杂在一起开发、测试和部署的话,就会导致不同功能之间相互影响,一次打包部署需要所有的功能都测试 OK 才能上线。

根据我的实际项目经验,一旦单体应用同时进行开发的人员超过 10 人,就会遇到上面的问题,这个时候就该考虑进行服务化拆分了。

服务化拆分的两种姿势

以前面提到的社交 App 为例,你可以认为首页信息流是一个服务,评论是一个服务,消息通知是一个服务,个人主页也是一个服务。

这种服务化拆分方式是纵向拆分,是从业务维度进行拆分。标准是按照业务的关联程度来决定,关联比较密切的业务适合拆分为一个微服务,而功能相对比较独立的业务适合单独拆分为一个微服务。

还有一种服务化拆分方式是横向拆分,是从公共且独立功能维度拆分。标准是按照是否有公共的被多个其他服务调用,且依赖的资源独立不与其他业务耦合。

继续以前面提到的社交 App 举例,无论是首页信息流、评论、消息箱还是个人主页,都需要显示用户的昵称。假如用户的昵称功能有产品需求的变更,你需要上线几乎所有的服务,这个成本就有点高了。显而易见,如果我把用户的昵称功能单独部署成一个独立的服务,那么有什么变更我只需要上线这个服务即可

服务化拆分的前置条件

从单体应用迁移到微服务架构时必将面临也必须解决的。

  • 服务如何定义。对于单体应用来说,不同功能模块之前相互交互时,通常是以类库的方式来提供各个模块的功能。对于微服务来说,每个服务都运行在各自的进程之中,应该以何种形式向外界传达自己的信息呢?答案就是接口,无论采用哪种通讯协议,是 HTTP 还是 RPC,服务之间的调用都通过接口描述来约定,约定内容包括接口名、接口参数以及接口返回值。
  • 服务如何发布和订阅。单体应用由于部署在同一个 WAR 包里,接口之间的调用属于进程内的调用。而拆分为微服务独立部署后,服务提供者该如何对外暴露自己的地址,服务调用者该如何查询所需要调用的服务的地址呢?这个时候你就需要一个类似登记处的地方,能够记录每个服务提供者的地址以供服务调用者查询,在微服务架构里,这个地方就是注册中心。
  • 服务如何监控。通常对于一个服务,我们最关心的是 QPS(调用量)、AvgTime(平均耗时)以及 P999(99.9% 的请求性能在多少毫秒以内)这些指标。这时候你就需要一种通用的监控方案,能够覆盖业务埋点、数据收集、数据处理,最后到数据展示的全链路功能。
  • 服务如何治理。可以想象,拆分为微服务架构后,服务的数量变多了,依赖关系也变复杂了。比如一个服务的性能有问题时,依赖的服务都势必会受到影响。可以设定一个调用性能阈值,如果一段时间内一直超过这个值,那么依赖服务的调用可以直接返回,这就是熔断,也是服务治理最常用的手段之一。
  • 故障如何定位。在单体应用拆分为微服务之后,一次用户调用可能依赖多个服务,每个服务又部署在不同的节点上,如果用户调用出现问题,你需要有一种解决方案能够将一次用户请求进行标记,并在多个依赖的服务系统中继续传递,以便串联所有路径,从而进行故障定位。

针对上述问题,你必须有可行的解决方案之后,才能进一步进行服务化拆分。建议的标准是按照每个开发人员负责不超过 3 个大的服务为标准

原文地址:https://www.cnblogs.com/xubeiping0930/p/10421324.html