第十周作业

用户头像
Geek_a327d3
关注
发布于: 2020 年 08 月 12 日

1. 画出Dubbo进行一次微服务调用的时序图

2. 关于微服务架构,你有什么样的思考和认知?



中台 = 适应各种场景的通用服务;

例如:每个平台的开发都需要用户,鉴权等服务,这些特别通用的服务,可以经过完善,让所有系统都依赖该服务。有了中台服务,可以快速搭建起其它系统,基础的服务都有了,调用就行。



微服务与中台最重要的就是服务如何拆分。以下为微服务拆分方法以及DDD。



微服务拆分方法/维度

基于性能拆分

对性能要求过高的功能可以组成一个服务,单独拆分出来,多部署几份,可以解决以web容器为性能瓶颈的问题。

基于业务变化频率

对经常变化的业务组成一个服务,单独拆分出来,与原有稳定的功能不互相影响。



基于业务拆分

将不同的业务放在不同的模块,每个模块作为单独的微服务,可以使用DDD设计微服务。



微服务与DDD的关系



DDD是一种架构设计方法,微服务则是一种架构风格。

DDD关注从业务视角,划分领域边界,使用通用语言高效沟通。

微服务关注服务进程通信、容错、故障隔离、构建/部署、开发、测试、中心化服务治理。

使用DDD可以更科学的划分领域边界,从而实现微服务的拆分。

DDD能解决那些问题

使用DDD设计方法可以合理的将不同的功能划分到不同的上下文边界内。

一个边界上下文也可以被认为一个组件或者一个模块,在DDD中被叫做一个子领域。

可以让系统模块化、组件化的进行开发。

DDD战略设计

战略设计从业务视角出发,建立业务领域模型,划分领域边界,上下文边界内建立通用语言,上下文边界可以作为微服务拆分参考(抄过来的说法)。

个人理解

划分:

将业务分成不同的块,将不同的业务块放到不同的模块中。

将完整的业务分成了很多个模块,模块也可以说是上下文边界。

按照学习面向对象的方法来说就是将不同的功能/方法放到不同的类中去,也就是说将关联性较强,内聚的功能放到一个类中去,这样才可以达类高内聚低耦合的目的。

面向对象与业务拆分的关系也只是粒度不同,上下文边界的拆分粒度要稍大些,可以将相同概念,多个符合单一职责的块放在一起形成一个上下文边界。



通用语言:

将业务人员与开发人员描述事物的语言进行统一,每个人都使用这个通用语言沟通。

沟通时,需要在一个上下文边界内进行沟通。



边界上下文:

就是一个环境,例如 一件事情你做的很好,别人说你厉害;一件事你做的很烂,别人还是说你厉害;

事情做的好不好就是一个环境,在不同的环境同样的词语,意义是不一样的。

所以需要在一个环境/上下文边界内,说通用语言,才能确定这个通用语言所表达的具体含义。

DDD战术设计

将划分好的多个上下文边界进行代码设计实现,DDD提倡使用充血模型,有一些常见的架构,例如六边形啊等等。



DDD与微服务总结

DDD可以用于微服务的拆分,也可以用于单体应用的模块拆分;

微服务只是一种部署形式,如果本身项目就没有经过战略设计进行模块化的划分,拆成微服务只会让项目的问题进一步被放大。

战略设计非常重要,可以不使用战术设计进行贫血模型进行开发,至少要使用战略设计让你的系统看上去是一块一块的,像积木一样,而不应该是一坨一坨的。

DDD思想很贴近面向对象,像是我们自己给自己提出一个简单的需求,例如将文件的数据读取出来解析为实体对象。

我们从面向对象角度来分析,以上需求有以下操作 ,读取数据、将数据解析为实体对象。

读取数据和解析对象就是两个操作,放到两个不同的类中。

从DDD角度分析也是这样,不过DDD提供了更加完善的整套解决方案。

像是DDD领域事件,本质上就是观察者模式。

个人认为DDD就是面向对象是互补关系,面向对象提供一些设计原则,模式等等,偏重设计。

而DDD偏重与业务人员沟通,它们有一个共同点就是将不同的东西放分到不同的类/上下文边界中。



微服务可以解决的问题

1. 编译部署困难

巨无霸应用通常编译就需要消耗一定的时间,可能修改了一行代码或者一个模块就需要重新编译打包并且部署,编译成本十分高。微服务只需要编译修改的服务,独立部署即可,一个服务的代码并不会特别多,比起整个应用肯定会少很多。



2. 新增业务困难

想要在一个乱如麻的系统中新增业务,维护旧功能,难度很大。

个人感想:

我没有经历过特别大的系统,但是经历过那些业务逻辑复杂,经过很多人改过的一个系统,一个类就有几千行代码,简直不敢直视。

要说添加一个新功能,我可以了解数据库表后,就可以开干,能不复用原有代码就不复用原有代码,全不自己写,开发效率还能高点,这样做之后代码只会越来越多,工程越来越庞大,重复的功能也越来越多。包也会越来越多,当时为了不跟他们的类搅在一块自己新建包后进行开发。

要说维护那是真的很恶心,哪块代码出了bug,哪块功能需要微调,代码根本不敢动,动了这一块可能这个功能好了,但是其它的功能莫名其妙的出了问题。

如果代码耦合严重,系统乱如麻,微服务并没有办法解决这样的问题,只会让问题更明显,更严重。



3. 数据库连接耗尽

所有业务都在一个应用中,连接一个数据库,大量的访问直接会耗尽数据库连接,而数据库连接是有限的,就会导致大量的请求在等待导致请求超时。微服务不同的服务有不同的数据库进行支撑,不同的服务有单独的数据库连接池。



4. 技术栈受限

不同的语言会有不同语言的优势,根据当时团队的情况也可能会出现多种语言进行开发,微服务可以支持多种语言开发,只要提供服务即可。单体应用只支持单种语言。



5. 功能复用

可以单独拆出通用的模块供很多项目进行使用,可以独立部署这些通用的模块。单体应用只能整个部署,数据库也需要进行配套,导致很多不相关的功能和不想管的数据库表,就像类设计违反了接口隔离原则。



6. 按需伸缩

需要高性能标准的服务可以多部署几份,将不是特别重要的模块部署一份,当然为了高可用最少还是部署两份。

7. 故障隔离

单体应用因为OOM导致应用的所有功能都不可用,微服务将应用拆成了多个块,彼此之间并不影响,例如用户服务OOM,并不会影响到订单服务。



充血模型与贫血模型

充血模型与贫血模型本质就是面向过程与面向对象。

面向对象以类为思考对象。在进行面向对象编程时的时候,我们并不是一上来就去思考如何将复杂的流程拆解为一个一个方法。先去思考如何给业务建模,如何将需求翻译为类,如何给类之间建立交互关系,而完成这些工作完全不用考虑错综复杂的处理流程。当我们有了类的设计之后,然后再像搭积木一样,按照处理流程,将类组装起来形成整个程序。

面向对象更加能够应对大规模复杂程序的开发。

DDD本身就是用来应对高度复杂的系统,所以战术设计提倡充血模型。



贫血模型Demo

public class OrderService{
private IdGenerator idGenerator;
public void saveOrder(OrderDTO dto){
// 基本信息验证 用户是否存在等等
Coupon coupon = getCouponById(dto.getCouponId()); // 获取到可用的优惠券
// if coupon != null && couponIsValid(coupon)
// 优惠券不可以是空,优惠券是可用状态的。
// 计算价格
double price = calcPrice(dto,coupon);
// 设置价格
dto.setPrice(price);
// 生成订单号
dto.setOrderNo(idGenerator.nextId());
dto.setCreateTime(new Date());
// 将DTO 转为PO
OrderPO order = convert(dto);
this.orderDao.save(order);
}
}

充血模型Demo

public class OrderDomain{
private IdGenerator idGenerator = IdGeneratorFactory.getIdGenerator(IdGeneratorType.RANDOM);
private String orderNo;
private double price;
private int userId;
private int state;
private Date createTime;
// ...
public OrderDomain create(){
// 初始化操作
this.createTime = new Date();
this.orderNo = idGenerator.nextId();
// 一些参数校验
return this;
}
// 使用优惠券 引用优惠券值对象
public OrderDomain useCoupon(CouponValueObject couponValue){
// 判断优惠券能否使用(优惠券价格不能大于price)
// 价格计算
return this;
}
// 支付订单
public OrderDomain paymentOrder(){
// 设置订单支付状态 已支付
this.state = 1;
return this;
}
}
public class OrderService{
public void saveOrder(OrderDomain orderDomain,CouponValueObject coupon){
OrderPO order = convert(orderDomain.create()
.useCoupon(coupon));
orderDao.save(order);
}
}
public class OrderApplicationService{
private OrderService orderService;
private CouponService couponService;
public void saveOrder(OrderDTO orderDTO){
// 获取可用的优惠券
CouponDTO couponDTO = couponService.getValidCoupon(orderDTO.getCouponId());
// DTO 2 Domain
OrderDomain orderDomain = convert(orderDTO);
orderService.saveOrder(orderDomain,couponDTO);
}
}



总结

以上代码逻辑很简单,当遇到特别复杂的逻辑时,Service代码会特别多,查找某段逻辑也会略显困难,看起来也不直观。

而充血模型所有的方法都在Order中,看起来非常直观。

贫血模型的功能无法复用,一个Service方法中那么多代码,如果懂得封装,打折啊等等操作使用策略模式设计以下还好,如果全堆在Service中,就很麻烦了。

贫血模型是面向过程的,自然不符合面向对象设计原则。



面向过程也不全面向过程,例如加入打折功能也可以使用策略模式进行打折策略封装。

使用哪种方式需要考虑当前项目属性以及团队属性。



用户头像

Geek_a327d3

关注

还未添加个人签名 2020.04.14 加入

还未添加个人简介

评论

发布
暂无评论
第十周作业