1.单体架构简介
在软件设计中,常常说起和使用经典的3曾模型,即表示层、业务逻辑层和数据访问层。java
虽然在软件设计中划分了经典的3层模型,可是对业务场景没有划分。一个典型的单体应用就是将全部的业务场景的表示层、业务逻辑层和数据访问层放在一个工程中,最终经过编译、打包,部署在一台服务器上。例如典型的J2EE工程,它是将表示层的JSP、业务逻辑层的Service、Controller和数据访问层的Dao打成war包,部署在Tomcat、Jetty或者其余Servlet容器中运行。经典的单体应用以下图所示:
spring
在一个小型应用的初始阶段,访问量较小,应用只须要一台服务器就可以部署全部的资源,例如将应用程序、数据库、文件资源等部署在同一台服务器上。最典型的就是LAMP系统,即服务器采用Linux系统,开发应用程序的语言为PHP,部署在Apache服务器上,采用MySQL数据库。在应用程序的初始阶段,采用这种架构的性价比是很是高的,开发速度快,开发成本低,只须要一台廉价的服务器。此时的服务器架构以下图所示:
数据库
2.单体架构存在的不足
在应用的初始阶段,单体架构不管是在开发速度、运维难度上,仍是服务器的成本上都有着显著的优点。在一个产品的前景不明确的初始阶段,用单体架构是很是明智的选择。随着应用业务的发展和业务复杂度的提升,这种架构明显存在不少的不足,主要体如今如下3个方面:编程
3.单体架构使用服务器集群及存在的不足
随着业务的发展,大多数公司会将单体应用进行集群部署,并增长负载均衡服务器(例如Nginx等)。另外,还须要增长集群部署的缓存服务器和文件服务器,并将数据库读写分离,以应对用户量的增长而带来的高并发访问量。此时的系统架构以下如所示:
用负责均衡服务器分发高并发的网络请求,用户的访问被分派到不一样的应用服务器,应用服务器的负载再也不成为瓶颈,用户量增长时,添加应用服务器便可。经过添加缓存服务器来缓解数据库的数据以及数据库读取数据的压力。大多数的读取操做是由缓存完成的,可是仍然有少数操做是从数据库读取的,例如缓存失效、实时数据等。当有大量的读写操做时,将数据库进行读写分离是一个不错的选择,例如MySQL的主从热备份,经过相关配置能够将主数据流服务器的数据同步到从数据库服务器,实现数据库的读写分离,读写分离可以改善数据库的负载能力。
这种架构有必定的处理高并发的能力,也能应对必定复杂的业务需求,改善了系统的性能。可是依然没有改变系统为单体架构的事实,此时系统存在的不足之处以下:缓存
因而可知,在应用初期,单体应用从成本、开发时间和运维等方面都有明显的优点。可是随着业务量和用户量的增长,它所暴露出来的缺点也显而易见。单体架构已经不能知足复杂的业务和海量的用户系统,改变单体架构势在必行。服务器
一.什么是微服务
"微服务"最初是由Martin Fowler在2014年写的一篇文章《MicroServices》中提出来的。关于Martin Fowler的介绍,维基百科上是这样描述的:网络
Martin Fowler,软件工程师,也是一个软件开发方面的著做者和国际知名演说家,专一于面向对象分析>与设计、统一建模语言、领域建模,以及敏捷软件开发方法,包括极限编程。主要著做有《可重用对象>模型》《重构-改善既有代码的设计》《企业应用架构模式》等;架构
对于微服务,业界没有一个严格统一的定义,可是做为"微服务"这一名词的发明人,Martin Fowler对于微服务的定义彷佛更具备权威性和指导意义。他的理解以下:并发
简而言之,微服务架构的风格,就是将单一程序开发成一个微服务,每一个微服务运行在本身的进程>中,并使用轻量级机制通讯,一般是HTTP RESTFUL API。这些服务围绕业务能力来划分构建的,>并经过彻底自动化部署机制来独立部署。这些服务可使用不一样的编程语言,以及不一样数据存储技>术,以保证最低限度的集中式管理;负载均衡
以我我的对这段话的理解,总计微服务具备以下特色:
1.微服务单元按业务来划分
微服务的"微"到底须要定义到什么样的程度,这是一个很是难以界定的概念,能够从如下3个方面来界定:一是根据代码量来定义,根据代码的多少来判断程序的大小;二是根据开发时间的长短来判断;三是根据业务的大小来划分。
根据Martin Fowler的定义,微服务的"微"是按照业务来划分的。一个大的业务能够拆分红若干小的业务,一个小的业务又能够拆分红若干更小的业务,业务到底怎么拆分才算合适,这须要开发人员本身去决定。例如微博最多见的功能是微博内容、关注和粉丝,而其中微博内容又有点赞、评论等,如何将微博这个复杂的程序划分为单个的服务,须要开发团队去决定。
按业务划分的微服务单元独立不是,运行在独立的进程中。这些微服务单元是高度组件化的模块,并提供了稳定的模块边界,服务与服务之间没有任何的耦合,有很是好的扩展性和复用性。
传统的软件开发模式一般由UI团队、服务端团队、数据库和运维团队构成,相应地将软件按照职能划分为UI、服务端、数据库和运维等模块。一般这些开发人员各司其职,不多有人跨职能去工做。若是按照业务来划分服务,每一个服务都须要独立的UI、服务端、数据库和运维。也就是说,一个小的业务的微服务须要动用一个团队的人去协做,这显然增长了团队与团队之间交流协做的成本。因此产生了跨职能团队,这个团队负责一个服务的全部工做,包括UI、服务端和数据库。当这个团队只有1-2我的的时候,就对开发人员提出了更高的要求。
2.微服务经过HTTP来互相通讯
按照业务划分的微服务单元独立部署,并运行在各自的进程中。微服务单元之间的通讯方式通常倾向于使用HTTP这种简单的通讯机制,更多的时候是使用RESTFUL API。这种接受请求、处理业务逻辑、返回数据的HTTP模式很是高效,而且这种通讯机制与平台和语言无关。例如用Java写的服务能够消费用Go语言写的服务,用Go语言写的服务又能够小费用Ruby写的服务。不一样的服务采用不一样的语言去实现,不一样的平台去部署,它们之间使用HTTP进行通讯,以下图所示:
服务与服务之间也能够经过轻量级的消息总线来通讯,例如RabbitMQ、Kafaka等。经过发送消息或者订阅消息来达到服务与服务之间通讯的目的。
服务与服务通讯的数据格式通常为JSON、XML,这两种数据格式与语言、平台、通讯协议无关。通常来讲,JSON格式的数据比XML轻量,而且可读性也比XML要好。另一种就是用Protobuf进行数据序列化,通过序列化的数据为二进制数据,它比JSON更轻量。用Protobuf序列化的数据为二进制数据,可读性很是差,须要反序列化才能读懂。因为用Protobuf序列化的数据更为轻量,因此Protobuf在通讯协议和数据存储上十分受欢迎。
服务与服务之间经过HTTP或消息总线的方式进行通讯,这种方式存在弊端,其通讯机制是不可靠的,虽然成功率很高,可是仍是会有失败的时候。
3.微服务的数据库独立
在单体架构中,全部的业务都共用一个数据库。随着业务量的增长,数据库表的数量愈来愈多,难以管理和维护,而且数据量的增长会致使查询速度愈来愈慢。例如一个应用有这样几个业务:用户信息、用户帐户、用户购物车、数据报表服务等。典型的单体架构以下图所示:
微服务的一个特色就是按照业务划分服务,服务与服务之间无耦合,就连数据库也是独立的。一个典型的微服务的架构就是每一个微服务都有本身独立的数据库,数据库之间没有任何联系。这样作的好处在于,随着业务的无论扩张,服务与服务不须要提供数据库集成,而是提供API接口相互调用;还有一个好处是数据库独立,单业务的数据量少,易于维护,数据库性能有着明显的优点,数据库的迁移也很方便。
另外,随着存储技术的发展,数据库的存储方式数据库的存储方式再也不仅仅是关系型数据库,非关系型数据库的应用也很是普遍。例如Redis、MongoDB,它们有着良好的读写性能,所以愈来愈受欢迎。一个典型的微服务系统,可能每个服务的数据库都不相同,每一个服务所使用的数据存储技术须要根据业务需求来选择,以下图所示:
4.微服务的自动化部署
在微服务架构中,系统会被拆分为若干个微服务,每一个微服务又是一个独立的应用程序。单体架构的应用程序只须要部署一次,而微服务架构有多少个服务就须要部署多少次。随着服务数量的增长,若是微服务按照单体架构的部署方式,部署的难度会呈指数增长。业务的粒度划分的越细,微服务的数量就越多,这时须要更稳定的部署机制。随着技术的发展,尤为是Docker容器技术的推动,以及自动化部署工具(开源组件Jenkins)的出现,自动化部署变得愈来愈简单。
自动化部署能够提升部署的效率,减小人为的控制,部署过程当中出现错误的几率下降,部署过程的每一步自动化,提升软件的质量。构建一个自动化部署的系统,虽然在前期须要开发人员或运维人员的学习,可是对于整个软件系统来讲是一个全系的概念。在软件系统的整个生命周期中,每一步是由程序控制的,而不是人为控制,软件的质量提升到了一个新的高度。随着DevOps这种全新概念的推动,自动化部署必然会成为微服务部署的一种方式。
5.服务集中化管理
微服务系统是按业务单元来划分服务的,服务数量越多,管理起来就越复杂,所以微服务必须使用集中化管理。目前流行的微服务框架中,例如Spring Cloud采用Eureka来注册服务和发现,另外Zookeeper、Consul等都是优秀的服务集中化管理框架。
6.分布式架构
分布式系统是集群部署的,由不少计算机相互协做共同构成,它可以处理海量的用户请求。当分布式系统对外提供服务时,用户是绝不知情的,还觉得是一台服务器在提供服务。分布式系统的复杂任务经过计算机之间的相互协做来完成,固然简单的任务也能够在一台计算机上完成。
分布式系统经过网络协议来通讯,因此分布式系统在空间上没有任何限制,即分布式服务器能够部署不一样的机房和不一样的地区。
微服务架构是分布式架构,分布式系统比单体系统更加复杂,主要体如今服务的独立性和服务相互调用的可靠性,以及分布式事务、全局锁、全局ID等,而单体系统不须要考虑这些复杂性。
另外,分布式系统的应用都是集群化部署,会给数据一致性带来困难。分布式系统中的服务通讯依赖于网络,网络很差,必然会对分布式系统带来很大的影响。在分布式系统中,服务之间相互依赖,若是一个服务出现了故障或网络延迟,在高并发的状况下,会致使线程阻塞,在很短的时间内该服务的线程资源会消耗殆尽,最终使得该服务不可用。因为服务的相互依赖,可能会致使整个系统的不可用,这就是"雪崩效应"。为了防止此类事件的发生,分布式系统必然要采起相应的措施,例如"熔断机制"。
7.熔断机制
为了防止"雪崩效应"事件的发生,发呢不是系统采用了熔断机制。在用Spring Cloud构建的微服务系统中,采用了熔断器(即Hytrix组建的Circuit Breaker)去作熔断。
例如在微服务系统中,有a、b、c、d、e、f、g、h等多个服务,用户的请求经过网关后再到具体的服务,服务之间相互依赖,例如服务b依赖于服务f,一个对外暴露的API接口须要服务b和服务f相互协做才能完成。服务之间相互以来的架构以下图所示:
若是此时服务b出现故障或网络延迟,在高并发的状况下,服务b会出现大量的线程阻塞,有可能在很短的时间内线程资源就被消耗完了,致使服务b的不可用。若是服务b为较底层的服务,会影响到其余服务,致使其余服务会一直等待服务b的处理。若是服务b迟迟不处理,大量的网络请求不只仅堆积在服务b,并且会堆积到依赖于服务b的其余服务。于是服务b出现故障影响的服务,也会影响到依赖于服务b出现故障影响的服务的其余服务,从而由b开始,影响到整个系统,致使整个系统的不可用。这是一件很是可怕的事,由于服务器运营商的不可靠,必然会致使服务的不可靠,而网络服务商的不可靠,也会致使服务的不可靠。在高并发的场景下,稍微有点不可靠,因为故障的传播性,会致使大量的服务不可用,甚至致使整个系统崩溃。
为了解决这一难题,微服务架构引入了熔断机制。当服务b出现故障,请求失败次数超过设定的阈值以后,服务b就会开启熔断器,以后服务b不进行任何的业务逻辑操做,执行快速失败,直接返回请求失败的信息。其余依赖于b的服务就不会由于得不到响应而线程阻塞,这时除了服务b和依赖于服务b的部分功能不可用外,其余功能正常。熔断服务以下图所示:
熔断器还有另一个机制,即自我修复的机制。当服务b熔断后,通过一段时间,半打开熔断器。半打开的熔断器会检查一部分请求是否正常,其余请求执行快速失败,检查的请求若是响应成功,则能够断定服务b正常了,就会关闭服务b的熔断器;若是服务b还不正常,则继续打开熔断器。这种自我熔断机制和自我修复机制在微服务架构中有着重要的意义,一方面它使程序更加健壮,另外一方面为开发和运维减小不少没必要要的工做。
最后熔断组件每每会提供一系列的监控,例如服务可用与否、熔断器是否打开、目前的吞吐量、网络延迟状态的监控等,从而很容易让开发人员和运维人员实时了解服务的状态。
二.微服务的优点
相对于单体服务来讲,微服务具备不少优点,主要体如今如下方面:
1)将一个复杂的业务分解成若干小的业务,每一个业务拆分红一个服务,服务的边界明确,将复杂的问题简单化。服务按照业务拆分,编码也是按照业务来拆分,代码的可读性和可扩展性增长。新人加入团队,不须要了解全部的业务代码,只须要了解他所接管的服务的代码,新人学习时间成本减小。
2)因为微服务系统是分布式系统,服务与服务之间没有任何的耦合。随着业务的增长,能够根据业务再拆分服务,具备极强的横向扩展能力。随着应用的用户量的增长,并发量增长,能够将微服务集群化部署,从而增长系统的负载能力。简而言之,微服务系统的微服务单元具备很强的横向扩展能力。
3)服务与服务之间经过HTTP网络通讯协议来通讯,单个微服务内部高度耦合,服务与服务之间彻底独立,无耦合。这使得微服务能够采用任何开发语言和技术来实现。开发人员再也不被强迫使用公司之前的技术或已通过时的技术,而是能够自由选择最合适业务场景的或适合本身的开发语言和技术,提供开发效率、减低开发成本。
4)若是是一个单体的应用,因为业务的复杂性、代码的耦合性,以及可能存在的历史问题。在重写一个单体应用时,要求重写应用的人员了解全部的业务,因此重写单体应用是很是困难的,而且重写风险也很高。若是是微服务系统,因为为服务是按照业务的进行拆分的,而且有坚实的服务边界,因此重写某个服务就至关于重写某一个业务的代码,很是简单。
5)微服务的每一个服务单元都是独立部署的,即独立运行在某个进程里。微服务的修改和部署对其它服务没有影响。试想,假设一个应用只有一个简单的修改,若是是单体架构,须要测试和部署整个应用;而若是采用微服务架构,只须要测试并部署被修改的那个服务,这就大大减小了测试和部署的时间。
6)微服务在CAP理论中采用的是AP架构,即具备高可用和分区容错的特色。高可用主要体如今系统7*24小时不间断的服务,它要求系统有大量的服务器集群,从而提升了系统的负载能力。另外,分区容错也使得系统更加健壮。
三.微服务的不足
凡事都有两面性,微服务也不例外,微服务相对于单体应用来讲具备不少的优点,固然也有其不足之处,主要体如今以下方面:
1.微服务的复杂度
构建一个微服务系统并非一件容易的事,微服务系统是分布式系统,构建的复杂度远远超过单体系统,开发人员须要付出必定的学习成本去掌握跟过的架构知识和框架知识。服务于服务之间经过HTTP协议或消息传递机制通讯,开发者须要选出最佳的通讯机制,并解决网络服务较差时带来的风险。
另外服务与服务之间相互依赖,若是修改某一个服务,会对另一个服务产生影响,若是掌控很差,会产生没必要要的麻烦。因为服务的依赖性,测试也会变得复杂,好比修改一个比较基础的服务,可能须要重启全部的服务才能完成测试。
2.分布式事务
微服务架构所设计的系统是分布式系统。分布式系统有一个著名的CAP理论,即同时知足"一致性"、"可用性"和"分区容错"是一件不可能的事。CAP理论是有Eric Brewer在2000年PODC会议上提出的,该理论在两年后被证实成类。CAP理论告诉架构师不要妄想设计出同时知足三者的系统,应该有所取舍,设计出适合业务的系统。CAP理论以下如所示:
在分布式系统中,P是基本要求,而单体服务是CA系统。微服务系统一般是一个AP系统,即同时知足可用性和分区容错。这就有了一个难题:在分布式系统中如何确保数据的一致性?这就是你们常常讨论的分布式事务。
在微服务系统中,每一个服务都是独立的进程单元,每一个服务都有本身的数据库。一般状况下,只有关系型数据库在特定的数据引擎下才支持事务,而大多数非关系型数据库是不支持事务的,例如MongoDB是不支持事物的,而Redis是支持事务的。在微服务架构中,分布式事务一直都是一个难以解决的问题,业界给出的解决方案一般是两阶段提交。
网上购物在平常生活中是一个很是普通的场景,假设咱们在淘宝上买了一部手机,须要从个人帐户中扣除1000元钱,同时手机的库存数量须要减1.固然须要在卖方的帐户中加1000元钱,为了使案例简单化,暂时不用考虑其余。
若是这是一个单体应用,而且使用支持事物的MySQL数据库(InnoDB数据库引擎才支持事务),咱们能够这样写代码:
@Transacntional public void update() throws RuntimeException{ updateAccountTable(); // 更新帐户表 updateGoodsTable(); // 更新商品表 }
若是是微服务架构,帐户是一个服务,而商品是一个服务,这时不能用数据库自带的事务,由于这两个数据表不在一个数据库中。所以经常用到两个阶段提交,两个阶段提交的过程以下如所示:
第一阶段,service-account发起一个分布式事务,交给事务协调器TC处理,事务协调器TC向全部参与的事务的节点发送处理事务操做的准备操做。全部的参与节点执行准备操做,将Undo和Redo信息写入日志,并向事务管理器返回准备操做是否成功;
第二阶段,事务管理器收集全部节点的准备操做是否成功,若是都成功,则通知全部的节点执行提交操做;若是有一个失败,则执行回滚操做;
两阶段提交,将事务分红两部分可以大大提升分布式事务成功的几率。若是在第一阶段都成功了,而执行第二阶段的某一个节点失败,仍然致使数据的不许确,这时通常须要人工去处理,这就是当初在第一步记录日志的缘由。另外,若是分布式事务涉及的节点不少,某一个节点的网络出现异常会致使整个事务处于阻塞状态,大大下降数据库的性能。因此通常状况下,尽可能少用分布式事务。
3.服务的划分
将一个完整的系统拆分红不少个服务,是一件很是困难的事,由于这涉及到了具体的业务长江,比明明一个类更加困难。对于微服务的拆分原则,Martin Fowler给出的建议是:服务是能够被替换和更新的。也就是服务和服务之间无耦合,任何一个服务均可以被替换,服务有本身严格的边界。固然这个原则很抽象,根据具体的业务场景来拆分服务,须要依靠团队人员对业务的熟悉程度和理解程度,并考虑与已有架构的冲入、业务的扩展性、开发的风险和将来业务的发展等诸多因素。
领域驱动设计是一个全新的概念,也是一个比较理想的微服务拆分的理念。领域驱动设计经过代码和数据分析找到合理的切分点,并经过数据分析来判断服务的划分边界和划分粒度。目前来讲,在中国几乎没有公司去落地领域驱动设计这个理念,随着微服务的发展,这一理念在之后有可能会落地。
4.服务的部署
一个简单的单体系统可能只须要将程序集群部署并配置负载均衡服务器便可,而部署一个复杂的微服务架构的系统就复杂得多。由于每个微服务可能还涉及比较低层的组件,例如数据库、消息中间件等。微服务系统每每由数量众多的服务构成,例如Netflix公司大约有600个服务,而每一个服务又有大量的实例。微服务系统须要对每一个服务进行治理、监控和管理等,而每一个服务有大量的配置,还须要考虑服务的启动顺序和启动时机等。
部署微服务系统,须要开发人员或者运维人员对微服务系统有足够强的控制力。随着云计算和云服务器的发展,部署微服务系统并非一件难事,例如使用PaaS服务、使用Docker编排等。这就是人们每每提到微服务,就会想到Docker、DevOps的缘由。其中,微服务是核心;Docker为容器技术,是微服务最佳部署的容器;DevOps是一种部署手段或理念,他们的关系如图所示:
四.微服务和SOA的关系
SOA即面向服务的架构,这种架构在20年前就已经被提出了。SOA每每与企业服务总线(ESB)联系在一块儿,主要缘由在于SOA的实施思路是根据ESB模式来整合集成大量单一庞大的系统,这是SOA主要的落地方式。然而,SOA在过去20年并无取得成功。在谈到微服务时,人们很容易联想到它是一个面向服务的架构,的确,微服务的概念提出者Martin Fowler并无否定这一层关系。
微服务相对于和ESB联系在一块儿的SOA显然轻便敏捷得多,微服务将复杂的业务组件化,实际也是一种面向服务思想的体现。对于微服务来讲,它是SOA的一种实现,可是它比ESB实现的SOA更加轻便、敏捷和简单。
五.微服务的设计原则 软件设计就比如建筑设计。Architect这个词在建筑学中是"建筑师"的意思,而在软件领域则是"架构师"的意思,可见它们确实有类似之处。不管是建筑师仍是架构师,他们都但愿把做品设计出本身的特点,而且更愿意把创造出来的东西称之为艺术品。然而现实倒是,建筑设计和软件设计有很是大的区别。建筑师设计并建造出来的建筑每每很难有变化,除非拆了重建。而架构师设计出来的软件系统,为了知足产品的业务发展,在它的整个生命周期中,每个版本都有不少的变化。 软件设计每个版本都在变化,因此软件设计应该是渐进式发展。软件从一开始就不该该被设计成微服务架构,微服务架构当然有优点,可是它须要更多的资源,包括服务器资源、技术人员等。追去大公司所带来的技术解决方案,刻意地追求某个新技术,企图使用新技术解决全部的问题,这些都是软件设计的误区。 技术应该是随着业务的发展而发展的,任何脱离业务的技术是不能产生价值的。在初创公司,业务很单一时,若是在LAMP单体架构够用的状况下,就应该用LAMP,由于它开发速度快,性价比高。随着业务的发展,用户量的增长,能够考虑将数据库读写分离、加缓存、加负载均衡器、将应用程序集群化部署等。若是业务还在不断发展,这时能够考虑使用分布式系统,例如微服务架构的系统。无论使用什么样的架构,驱动架构发展的必定是业务的发展,只有当前架构不适合当前业务的发展,才考虑更换架构。 在微服务架构中,有三大难题,那就是服务故障的传播性、服务的划分和分布式事务。在微服务设计时,必定要考虑清楚这三个难题,从而选择合适的框架。目前比较流行的微服务框架有Spring社区的Spring Cloud、Google公司的Kubernetes等。无论使用哪种框架或者工具,都须要考虑这三大难题。为了解决服务故障的传播性,通常的微服务框架都有熔断机制组件。另外,服务的划分没有具体的划分方法,通常来讲根据业务来划分服务,领域驱动设计具备指导做用。最后,分布式事务通常的解决办法就是两阶段提交或者三阶段提交,无论使用哪种都存在事务失败,致使数据不一致的状况,关键时刻还得人工去恢复数据。总之,微服务的设计必定是渐进式的,而且是随着业务的发展而发展的。