从经典架构项目中透析微服务架构的核心概念和充血模型

微服务架构和SOA区别

微服务如今辣么火,业界流行的对比的却都是所谓的Monolithic单体应用,而大量的系统在十几年前都是已是分布式系统了,那么微服务做为新的理念和原来的分布式系统,或者说SOA(面向服务架构)是什么区别呢?前端

咱们先看相同点程序员

  • 须要Registry,实现动态的服务注册发现机制;数据库

  • 须要考虑分布式下面的事务一致性,CAP原则下,两段式提交不能保证性能,事务补偿机制须要考虑;编程

  • 同步调用仍是异步消息传递,如何保证消息可靠性?SOA由ESB来集成全部的消息;后端

  • 都须要统一的Gateway来汇聚、编排接口,实现统一认证机制,对外提供APP使用的RESTful接口;设计模式

  • 一样的要关注如何再分布式下定位系统问题,如何作日志跟踪,就像咱们电信领域作了十几年的信令跟踪的功能;架构

那么差异在哪?并发

  • 是持续集成、持续部署?对于CI、CD(持续集成、持续部署),这自己和敏捷、DevOps是交织在一块儿的,我认为这更倾向于软件工程的领域而不是微服务技术自己;框架

  • 使用不一样的通信协议是否是区别?微服务的标杆通信协议是RESTful,而传统的SOA通常是SOAP,不过目前来讲采用轻量级的RPC框架Dubbo、Thrift、gRPC很是多,在Spring Cloud中也有Feign框架将标准RESTful转为代码的API这种仿RPC的行为,这些通信协议不该该是区分微服务架构和SOA的核心差异;异步

  • 是流行的基于容器框架仍是虚拟机为主?Docker和虚拟机仍是物理机都是架构实现的一种方式,不是核心区别;

微服务架构的精髓在切分

  • 服务的切分上有比较大的区别,SOA本来是以一种“集成”技术出现的,不少技术方案是将原有企业内部服务封装为一个独立进程,这样新的业务开发就可重用这些服务,这些服务极可能是相似供应链、CRM这样的很是大的颗粒;而微服务这个“微”,就说明了他在切分上有讲究,不妥协。无数的案例证实,若是你的切分是错误的,那么你得不到微服务承诺的“低耦合、升级不影响、可靠性高”之类的优点,而会比使用Monolithic有更多的麻烦。

  • 不拆分存储的微服务是伪服务:在实践中,咱们经常见到一种架构,后端存储是所有和在一个数据库中,仅仅把前端的业务逻辑拆分到不一样的服务进程中,本质上和一个Monolithic同样,只是把模块之间的进程内调用改成进程间调用,这种切分不可取,违反了分布式第一原则,模块耦合没有解决,性能却受到了影响。

分布式设计第一原则 — “不要分布你的对象”

  • 微服务的“Micro”这个词并非越小越好,而是相对SOA那种粗粒度的服务,咱们须要更小更合适的粒度,这种Micro不是无限制的小。

若是咱们将两路(同步)通讯与小/微服务结合使用,并根据好比“1个类=1个服务”的原则,那么咱们实际上回到了使用Corba、J2EE和分布式对象的20世纪90年代。遗憾的是,新生代的开发人员没有使用分布式对象的经验,所以也就没有认识到这个主意多么糟糕,他们正试图重复历史,只是此次使用了新技术,好比用HTTP取代了RMI或IIOP。

微服务和Domain Driven Design

一个简单的图书管理系统确定无需微服务架构。既然采用了微服务架构,那么面对的问题空间必然是比较宏大,好比整个电商、CRM。

如何拆解服务呢?

使用什么样的方法拆解服务?业界流行1个类=1个服务、1个方法=1个服务、2 Pizza团队、2周能重写完成等方法,可是这些都缺少实施基础。咱们必须从一些软件设计方法中寻找,面向对象和设计模式适用的问题空间是一个模块,而函数式编程的理念更多的是在代码层面的微观上起做用。

Eric Evans 的《领域驱动设计》这本书对微服务架构有很大借鉴意义,这本书提出了一个能将一个大问题空间拆解分为领域和实体之间的关系和行为的技术。目前来讲,这是一个最合理的解决拆分问题的方案,透过限界上下文(Bounded Context,下文简称为BC)这个概念,咱们能将实现细节封装起来,让BC都可以实现SRP(单一职责)原则。而每一个微服务正是BC在实际世界的物理映射,符合BC思路的微服务互相独立松耦合。

微服务架构是一件好事,逼着你们关注设计软件的合理性,若是原来在Monolithic中领域分析、面向对象设计作很差,换微服务会把这个问题成倍的放大

以电商中的订单和商品两个领域举例,按照DDD拆解,他们应该是两个独立的限界上下文,可是订单中确定是包含商品的,若是贸然拆为两个BC,查询、调用关系就耦合在一块儿了,甚至有了麻烦的分布式事务的问题,这个关联如何拆解?BC理论认为在不一样的BC中,即便是一个术语,他的关注点也不同,在商品BC中,关注的是属性、规格、详情等等(实际上商品BC这个领域有价格、库存、促销等等,把他做为单独一个BC也是不合理的,这里为了简化例子,你们先认为商品BC就是商品基础信息), 而在订单BC中更关注商品的库存、价格。因此在实际编码设计中,订单服务每每将关注的商品名称、价格等等属性冗余在订单中,这个设计解脱了和商品BC的强关联,两个BC能够独立提供服务,独立数据存储

微服务架构首先要关注的不是RPC/ServiceDiscovery/Circuit Breaker这些概念,也不是Eureka/Docker/SpringCloud/Zipkin这些技术框架,而是服务的边界、职责划分,划分错误就会陷入大量的服务间的相互调用和分布式事务中,这种状况微服务带来的不是便利而是麻烦。

DDD给咱们带来了合理的划分手段,可是DDD的概念众多,晦涩难以理解,如何抓住重点,合理的运用到微服务架构中呢?

我认为以下的几个架构思想是重中之重

  • 充血模型

  • 事件驱动

实际上DDD和面向对象设计、设计模式等等理论有千丝万缕的联系,若是不熟悉OOA、OOD,DDD也是使用很差的。不过学习这些OO理论的时候,你们每每感受到无用武之地,由于大部分的Java程序员开发生涯是从学习J2EE经典的分层理论开始的(Action、Service、Dao),在这种分层理论中,咱们基本没有啥机会使用那些所谓的“行为型”的设计模式,这里的核心缘由,就是J2EE经典分层的开发方式是“贫血模型”。

Martin Fowler在他的《企业应用架构模式》这本书中提出了两种开发方式“事务脚本”和“领域模型”,这两种开发分别对应了“贫血模型”和“充血模型”。

针对上面的技术我特地整理了一下,有不少技术不是靠几句话能讲清楚,因此干脆找朋友录制了一些视频,不少问题其实答案很简单,可是背后的思考和逻辑不简单,要作到知其然还要知其因此然。若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java进阶群:582505643,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。

事务脚本开发模式

事务脚本的核心是过程,能够认为大部分的业务处理都是一条条的SQL,事务脚本把单个SQL组织成为一段业务逻辑,在逻辑执行的时候,使用事务来保证逻辑的ACID。最典型的就是存储过程。固然咱们在平时J2EE经典分层架构中,常常在Service层使用事务脚本。


使用这种开发方式,对象只用于在各层之间传输数据用,这里的对象就是“贫血模型”,只有数据字段和Get/Set方法,没有逻辑在对象中。

咱们以一个库存扣减的场景来举例:

  • 业务场景

首先谈一下业务场景,一个下订单扣减库存(锁库存),这个很简单

先判断库存是否足够,而后扣减可销售库存,增长订单占用库存,而后再记录一个库存变更记录日志(做为凭证)

  • 贫血模型的设计

首先设计一个库存表 Stock,有以下字段


设计一个Stock对象(Getter和Setter省略)

1
2
3
4
5
6
public class Stock {
private String spuId;
private String skuId;
private int stockNum;
private int orderStockNum;
}
  • Service入口

设计一个StockService,在其中的lock方法中写逻辑

入参为(spuId, skuId, num)

实现伪代码

1
2
3
4
5
6
7
count = select stocknum from stock where spuId=xx and skuid=xx
if count>num {
update stock set stocknum=stocknum-num, orderstocknum=orderstocknum+num where skuId=xx and spuId=xx
} else {
//库存不足,扣减失败
}
insert stock_log set xx=xx, date= new Date()
  • ok,打完收工,若是作的好一些,能够把update和select count合一,这样能够利用一条语句完成自旋,解决并发问题(高手)。

小结一下:

有没有发现,在这个业务领域很是重要的核心逻辑 — 下订单扣减库存中操做过程当中,Stock对象根本不用出现,所有是数据库操做SQL,所谓的业务逻辑就是由多条SQL构成。Stock只是CRUD的数据对象而已,没逻辑可言。

  • 马丁福勒定义的“贫血模型”是反模式,面对简单的小系统用事务脚本方式开发没问题,业务逻辑复杂了,业务逻辑、各类状态散布在大量的函数中,维护扩展的成本一会儿就上来,贫血模型没有实施微服务的基础。

  • 针对上面的技术我特地整理了一下,有不少技术不是靠几句话能讲清楚,因此干脆找朋友录制了一些视频,不少问题其实答案很简单,可是背后的思考和逻辑不简单,要作到知其然还要知其因此然。若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java进阶群:582505643,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。

  • 虽然咱们用Java这样的面向对象语言来开发,可是其实和过程型语言是同样的,因此不少状况下你们用数据库的存储过程来替代Java写逻辑反而效果会更好,(ps:用了Spring boot也不是微服务),

领域模型的开发模式

  • 领域模型是将数据和行为封装在一块儿,并与现实世界的业务对象相映射。各种具有明确的职责划分,使得逻辑分散到合适对象中。这样的对象就是“充血模型” 。

  • 在具体实践中,咱们须要明确一个概念,就是领域模型是有状态的,他表明一个实际存在的事物。仍是接着上面的例子,咱们设计Stock对象须要表明一种商品的实际库存,并在这个对象上面加上业务逻辑的方法


这样作下单锁库存业务逻辑的时候,每次必须先从Rep


ository根据主键load还原Inventory这个对象,而后执行对应的lock(num)方法改变这个Inventory对象的状态(属性也是状态的一种),而后再经过Repository的save方法把这个对象持久化到存储去。

完成上述一系列操做的是Application,Application对外提供了这种集成操做的接口


领域模型开发方法最重要的是把扣减形成的状态变化的细节放到了Inventory对象执行,这就是对业务逻辑的封装。

Application对象的lock方法能够和事务脚本方法的StockService的lock来作个对比,StockService是彻底掌握全部细节,一旦有了变化(好比库存为0也能够扣减),Service方法要跟着变;而Application这种方式不须要变化,只要在Inventory对象内部计算就能够了。代码放到了合适的地方,计算在合适层次,一切都很合理。这种设计能够充分利用各类OOD、OOP的理论把业务逻辑实现的很漂亮。

  • 充血模型的缺点

从上面的例子,在Repository的load 到执行业务方法,再到save回去,这是须要耗费必定时间的,可是这个过程当中若是多个线程同时请求对Inventory库存的锁定,那就会致使状态的不一致,麻烦的是针对库存的并发不只难处理并且很常见。

贫血模型彻底依靠数据库对并发的支撑,实现能够简化不少,但充血模型就得本身实现了,无论是在内存中经过锁对象,仍是使用Redis的远程锁机制,都比贫血模型复杂并且可靠性降低,这是充血模型带来的挑战。更好的办法是能够经过事件驱动的架构来取消并发。

领域模型和微服务的关系

上面讲了领域模型的实现,可是他和微服务是什么关系呢?在实践中,这个Inventory是一个限界上下文的聚合根,咱们能够认为一个聚合根就是一个微服务进程。

不过问题又来了,一个库存的Inventory必定和商品信息是有关联的,仅仅靠Inventory中的冗余那点商品ID是不够的,商品的上下架状态等等都是业务逻辑须要的,那不是又把商品Sku这样的重型对象引入了这个微服务?两个重型的对象在一个服务中?这样的微服务拆不开啊,仍是必须依靠商品库?!

相关文章
相关标签/搜索