微服务基础

微服务架构背景

从软件开发之初,业界一直致力于寻找大型分布式应用系统开发的“银弹”从结构化编程、面向对象,到COBRA、EJB、ESB、SOA等。Fread Brooks在《没有银弹:软件工程的本质性与附属性工作》一文中提出:大型分布式系统具有复杂性、隐匿性、配合性和易变性四大难题,不会存在任何单一软件工程上的突破,能够让开发生产力得到一个数量上的提升。而微服务的出现虽然不是一颗“银弹”,但它给出了四大难题的对策,解决了以往单体架构系统构建的困境

单体架构的困境

Java开发中一个单体架构应用就是将一个应用中的所有功能都打包到一个war文件中,并部署到服务器(Tomcat)中运行

对于单体架构来说,随着业务的扩张,其开发、部署和运维都会越来越慢,越来越复杂,甚至在单体架构应用开发中敏捷模式都无法施展开。

在单体架构中,一个应用承载的职责太多,其开发、部署运维复杂度都会呈几何形增长,应用的每次编译和启动都需要更长的时间,每次需要修改、增加新功能时都需要更多的协调和测试。而对于新功能,每次修正bug都会使系统的代码更加复杂,使开发——编译——启动——测试进入了一种恶性循环,大大降低了开发效率

单体架构应用逐渐变得不稳定,一方面是因为系统不断增长的复杂性造成的,另一方面是由于系统本身牵一发而动全身的特性造成的,可能一个不常用的模块因为存在内存泄漏而造成整个服务器无法正常提供服务,甚至引起服务崩溃

在数据管理上单体架构应用容易产生漏洞,最常见的就是数据管理。单体架构往往要管理的数据类型/表都非常多,而且分布在不同团队之间,如果沟通不好可能某些团队的开发会直接操纵数据库表,最常见的就是在数据层通过写SQL语句操作数据表。这种做法相当于埋下了定时炸弹,当数据相关团队修改了数据库结构,由于之前团队之间使用SQL语句进行处理,程序在系统编译、打包和测试时都有可能通过,但当在真实的生产环境使用时就有可能造成服务的崩溃

单体架构的应用开发时要求我们必须使用同一个技术栈,使得单体架构应用很难接受或切换到其他框架、语言。因为重写风险太大,即使有更合适的开发框架或语言,甚至应用使用的框架有了新版本,也很难进行升级

单体架构应用开发对于开发者来说,需要了解更多的东西,如系统架构、统一的开发模式、与之交互的相关模块等,造成了一个新加入的开发者需要更长的时间才能够进入开发状态

最后,单体架构应用还有一个显著的问题:多名开发者同时修改一个代码库。一方面需要开发者自己快速提交修改的代码,另一方面开发者每次提交修改都要祈求代码不要发生冲突这也造成了代码库很难快速进入稳定可发布状态

在部署上,单体架构应用还会造成系统难以进行水平扩展,因为每一个应用实例对服务器来说都需要相同的硬件配置,这让服务器无法充分发挥其能力,造成浪费,并且部署的服务速度会随着代码积累逐渐变慢,性能降低

微服务架构

微服务架构中的服务和模块化开发很相似,单服务是有明确服务边界的,所以更易于开发和管控,同时也更易于单独部署和扩展
模块化开发是在大型开发、复杂应用中常用的开发方式,大型应用的开发往往是一个人无法掌控全局的,通过模块化的方式将应用分解成多个具有关联的模块,并交由不同开发团队来完成。模块之间的调用直接使用接口,依赖关系则是可以使用Maven等工具进行管理

微服务架构的定义

微服务架构可以说是一种将功能分解成一系列服务的一种架构模式。对于一个应用系统包含两部分的需求:第一部分是功能需求,用于定义一个应用是用来做什么的,该应用系统用来达到什么目的。第二部分是非功能需求,包括了对应用系统的扩展性、灵活性,还有性能、运维、安全、测试、监控等需求,这种非功能性需求是用来保障业务系统能够正确、顺畅的运行。而对于微服务来说,更侧重于第二种需求

微服务从结构上来看就是将一个应用拆分成多个松耦合的服务,这些服务之间通过某种协议(REST、RPC等)进行互相协作,完成单元体架构下的业务功能,但提供更灵活的部署模式,更容易扩展,降低了开发、运维上的复杂度。

微服务的核心思路就是分而治之

服务是一个可以独立运行、提供范围有限的功能(可以是业务功能,也可以是非业务功能)的组件。功能具体实现隐藏在组件内部,而对外提供访问接口,外部其他服务可以通过这些接口进行访问交互

微服务架构的优点

松耦合:基于微服务架构的应用是一系列小服务的集合,这些服务之间通过非具体实现的接口及非专有通信协议进行通信(比如REST),这样只要原接口没有变,就不会对服务消费者造成任何影响

抽象:一个微服务对其数据结构和数据源有绝对的控制权,只有该服务才可以对数据进行修改,其他微服务只有通过该服务才能够访问数据。因此该服务可以很方便的对所能提供的数据进行有效控制

独立:每个微服务都可以在不影响其他微服务的情况下进行编译、打包和部署,这是单体架构应用无法做到的

多样性:微服务架构可以让我们轻松应对不同客户的特殊需求,通过定义良好的接口,可以让不同的微服务承担不同的职责,同时快速部署上线能力可以让用户需求尽早实现

更高可用性和弹性:微服务架构可以认为是一个去中心化的应用,每一个微服务都可以随时上线下线。这样当某个微服务出现问题时只需要将其下线即可,其他同类型的微服务将承担其功能,对外仍旧可以提供服务,不会造成整个服务器无法正常工作。

微服务项目工程代码少,不会造成IDE速度变慢,开发和调试速度也会非常有效率。微服务架构不会要求我们在一个应用中选用同一个技术栈,每个服务可以根据应用特性、开发人员特长选择合适的开发语言和框架。因为微服务足够小,非常容易重构或重写,同时在重构或重写时可以选择合适的开发语言和框架,而且一旦有更适合的技术也可以在低风险的情况下对应用进行升级或改造,而不至于影响整个应该。通过微服务架构可以为我们带来开发、运维、升级上的灵活性

因为每个服务都可以独立进行部署,开发人员可以很快对自己所开发的服务做出变更,而不会影响其他服务,也不会受其他服务影响,持续集成和开发都很灵活

每个微服务都可以快速的实施X(运行多个应用实例)、Z(数据分区的扩展)轴扩展(提升应用的容量和可用性),并为每个服务定义合适的硬件环境(I/O密集型、计算密集型),而不像单体架构采用大锅饭形式,可以为组织节约硬件成本

错误隔离:比如一个服务内存泄漏,它只会影响自身微服务实例,不会影响到其他微服务

微服务的缺点

可用性降低:微服务之间都是通过远程调用进行协作的,而远程调用的代名词就是不稳定,如果没有有效的方案,微服务架构可能会大大降低应用的可用性。当一个服务不可用时,有可能会引起级联反应,最终造成应用的“雪崩效应”而拖垮整个应用

处理分布式事务较棘手:当一个用户请求的业务涉及多个微服务时,如何保障数据的一致性就成为一个棘手的问题。传统开发通常会使用两阶段提交的解决方案来解决这个问题。但对于微服务架构来说,这个解决方案并不是一个很好的选择,甚至在某些情况下很难实现。

全能对象阻止业务拆分:在进行微服务拆分时最让人头疼的一个问题就是全能对象,几乎对于任何一个业务的应用来说都可能存在一个或多个这样的全能对象。比如电子商城中的订单,订单几乎涉及到电商应用中的每一个业务,它会阻止你进行业务拆分

学习难度曲线加大:微服务架构虽然可以将业务可以分解为更小、更容易开发的模式,但也需要开发人员学习掌握一门微服务开发技术,加大了进入门槛。

组织结构变更:虽然对于单独一个微服务的部署简化了,但整个应用部署复杂度却提升了,需要涉及服务编排和服务治理等一系列处理,即不需要制定微服务之间的部署编排、关联关系、回滚计划等,还需要协调不同的团队,以及在人事组织上进行调整来适应这种变化

微服务架构设计的步骤

1.把应用中的关键需求定义出来

2.识别出采用微服务架构时应用中所包含的所有服务

3.将第一步所定义出的关键需求作为架构需求的场景来描述服务之间如何进行协作

微服务拆分原则

单一职责原则(SRP)

单一职责原则(Single Responsibility Principle):一个类应该有且只有一个变化的原因

每一个职责都是一个变化引起类变化的中心。当功能变化时,通常需要通过更改相关的类来实现。如果一个类拥有多个职责,那么就会有多于一个原因来导致这个类的变化。另外,一个类承担多个职责后,往往这些职责就会耦合在一起,某一职责的改变可能会影响到其他的职责。这样的类设计是非常脆弱的,从而会导致应用的稳定性。因此,我们在进行类设计时要遵守单一职责原则。

同样,对于微服务设计来说,如果一个微服务承担太多职责的话,也会导致微服务业务之间的耦合,为业务进行改变时埋下了不稳定因素。所以,单一职责原则同样也适用于微服务设计,我们可以将微服务保持足够小,仅拥有一个业务职责,保持微服务的业务单一性,从而提升应用的稳定性

共同封闭原则(CCP)

共同封闭原则(Common Closure Principle,CCP):包中的所有的类对于同一种性质的变化应该是共同封闭的。一个变化若对一个封闭的包产生影响,则将对该包中的所有类产生影响,而对其他包则不造成任何影响。

共同封闭原则是延伸了面向对象开发中六大原则之一的开闭原则(OCP)中的关闭概念。就是说当需要修改某项业务时,我们需要将修改的范围限制在同一个包内,而不是遍布在很多包中。共同封闭原则指导我们如何对类进行有效的组织,将那些在业务概念上联系得非常紧密、通常一起发生改变的类,封装到同一个包中。通过共同封闭原则可以提升对应用组织上的管理。

同样,通过使用共同封闭原则可以将那些在业务上联系紧密,由于同一个原因而改变的服务组织在一个微服务中。这样一方面我们可以减少微服务的数量,另外一方面当业务发生改变时我们只需要一个业务开发团队进行单独修改,只需要重新部署该服务即可,减少了不同微服务开发团队之间沟通成本

微服务自治原则

一个团队越大,那么沟通与协助成本就会越高。因此,在微服务治理中有一个重要的理念就是自治,自治范围并不只是代码和数据,还包含微服务的运行和维护管理,所以亚马逊的微服务有一个规则:你构建,你运行。

将微服务分而治之的另一个重要方面是数据管理的分而治之。传统单体架构应用的开发在很多时候多个业务之间的数据交互是直接通过操作数据库来完成,当需要更改某一业务数据库表时往往会涉及多个模块,甚至有时候根本不清楚修改这张数据库表到底会影响到多少业务代码,从而不敢动数据库表的定义,只好退而求其次,通过增加表来处理,进而加剧了系统架构的恶化。

虽然现在O/R mapping技术的出现从一定程度上解决了这个头痛的问题,但终未从根本上解决。而微服务中的分而治之理念,不但是指业务功能,也同时包含了对业务数据的管理。将业务数据管理进行私有化之后就进一步降低了业务之间的耦合度,所以实施微服务的架构师,一定要保持业务数据管理的私有化,即使你在项目中不能够分库,也要牢记这条规则,严格要求各微服务团队看好自己的数据。

服务架构中的数据自治是指每个微服务拥有其业务领域对象下的数据,只有该微服务可以对这些数据进行操作(包含读取与更改),而其他微服务只有通过该服务才能访问到这些数据,不能直接通过数据库进行沟通。因此,我们可以不用为每一个微服务创建一个独立数据库,可以将它们统一存放在一个数据库中,保障不破坏上述的数据访问原则即可。

微服务交互原则

REST协议:(http+uri)REST在微服务互相调用之间起着非常重要的角色,强烈建议使用HTTP作为服务的调用协议,并在服务处理上使用HTTP标准动词(GET、POST、PUT、DELETE)。

URI表达:服务端点的URI应该能清晰的表达出我们想要解决的问题、提供的方法、相应资源信息及资源之间的关联关系

JSON数据格式:JSON作为轻量级数据格式协议,及自带的序列化和反序列化机制,几乎已经成为通信中的数据标准协议,并且对于前端开发来说非常容易使用和整合

HTTP标准状态码:HTTP协议本身具有非常丰富的状态码,那么使用这些状态码来作为服务调用结果的状态是非常合适的

微服务架构迁移

单体架构应用迁移到微服务架构意味着一个漫长的过程,不过这和在开发时经常做的代码重构类似,只是变成了对架构的重构,因此可以从中吸取一些思路。对于代码重构,有一个很重要的指导思想就是不要大规模进行重构,而是一小步一小步来。作为开发人员,每次听到重写代码可能会很兴奋,但实际上却是充满了风险,道路也是非常崎岖坎坷,最终也有可能会失败,每一个重写过代码的开发者可能对这一点深有体会。

因此,与大规模进行重构相反,在进行微服务架构迁移时可以使用Martin Fowler提出绞杀(Strangler)模式。该策略名字来源于雨林中的绞杀藤,绞杀藤为了能够爬到森林顶端都要缠绕着某棵大树生长,最终使被缠绕的大树死掉,只留下树形一样的绞杀藤。通过这种策略,我们在迁移时应首先围绕着传统应用开发出新的微服务应用,并逐渐替代传统应用中的部分业务功能。通过这种方式逐步构建微服务应用,并替代、兼容整合旧的传统应用,直到微服务承担全部应用功能,而传统单体架构应用此时也就可以退出历史舞台了。

不应用微服务的场景

构建分布式架构非常吃力

服务器蔓延时

采用小型应用、快速产品原型时

对数据事务的一致性有一定要求时

-------------本文结束❤️感谢您的阅读-------------
ボ wechat
扫描二维码,可获得菜鸡一枚
打赏测试
0%