设为首页 - 加入收藏 邢台站长网 (http://www.0319zz.com)- 国内知名站长资讯网站,提供最新最全的站长资讯,创业经验,网站建设等!
热搜: 2018 视频 中国 运营商
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

微服务架构:利用事件驱动实现最终一致性

发布时间:2019-10-14 19:44 所属栏目:[优化] 来源:老男孩的成长之路
导读:事务一致性 首先,我们来回顾一下ACID原则: Atomicity:原子性,改变数据状态要么是一起完成,要么一起失败 Consistency:一致性,数据的状态是完整一致的 Isolation:隔离线,即使有并发事务,互相之间也不影响 Durability:持久性, 一旦事务提交,不可

?事务一致性

首先,我们来回顾一下ACID原则:

  • Atomicity:原子性,改变数据状态要么是一起完成,要么一起失败
  • Consistency:一致性,数据的状态是完整一致的
  • Isolation:隔离线,即使有并发事务,互相之间也不影响
  • Durability:持久性, 一旦事务提交,不可撤销

在单体应用中,我们可以利用关系型数据库的特性去完成事务一致性,但是一旦应用往微服务发展,根据业务拆分成不用的模块,而且每个模块的数据库已经分离开了,这时候,我们要面对的就是分布式事务了,需要自己在代码里头完成ACID了。比较流行的解决方案有:两阶段提交、补偿机制、本地消息表(利用本地事务和MQ)、MQ的事务消息(RocketMQ)。

微服务架构:利用事件驱动实现最终一致性

CAP定理

1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标。

  • Consistency:一致性
  • Availability:可用性
  • Partition tolerance:分区容错

Eric Brewer 说,这三个指标不可能同时做到。这个结论就叫做 CAP 定理。

微服务中,不同模块之间使用的数据库是不同的,不同模块之间部署的服务去也有可能是不用的,那么分区容错是无法避免的,因为服务之间的调用不能保证百分百的没问题,所以系统设计必须考虑这种情况。因此,我们可以认为CAP的P总是成立的,剩下的C和A无法同时做到。

实际上根据分布式系统中CAP原则,当P(分区容忍)发生的时候,强行追求C(一致性),会导致(A)可用性、吞吐量下降,此时我们一般用最终一致性来保证我们系统的AP能力。当然不是放弃C,而是放弃强一致性,而且在一般情况下CAP都能保证,只是在发生分区容错的情况下,我们可以通过最终一致性来保证数据一致。

事件驱动实现最终一致性

事件驱动架构在领域对象之间通过异步的消息来同步状态,有些消息也可以同时发布给多个服务,在消息引起了一个服务的同步后可能会引起另外消息,事件会扩散开。严格意义上的事件驱动是没有同步调用的。

例子:

在电商里面,用户下单必须根据库存来确定订单是否成交。

项目架构:SpringBoot2+Mybatis+tk-Mybatis+ActiveMQ【因为小例子,不做成Spring Cloud架构】

首先,我们来看看正常的服务之间调用:

微服务架构:利用事件驱动实现最终一致性

代码:

  1. @Override?
  2. @Transactional(rollbackFor?=?Exception.class)?
  3. public?Result?placeOrder(OrderQuery?query)?{?
  4. ?Result?result?=?new?Result();?
  5. ?//?先远程调用Stock-Service去减少库存?
  6. ?RestTemplate?restTemplate?=?new?RestTemplate();?
  7. ?//请求头?
  8. ?HttpHeaders?headers?=?new?HttpHeaders();?
  9. ?headers.setContentType(MediaType.APPLICATION_JSON);?
  10. ?//封装成一个请求对象?
  11. ?HttpEntity?entity?=?new?HttpEntity(query,?headers);?
  12. ?//?同步调用库存服务的接口?
  13. ?Result?stockResult?=?restTemplate.postForObject("http://127.0.0.1:8081/stock/reduceStock",entity,Result.class);?
  14. ?if?(stockResult.getCode()?==?Result.ResultConstants.SUCCESS){?
  15. ?Order?order?=?new?Order();?
  16. ?BeanUtils.copyProperties(query,order);?
  17. ?order.setOrderStatus(1);?
  18. ?Integer?insertCount?=?orderMapper.insertSelective(order);?
  19. ?if?(insertCount?==?1){?
  20. ?result.setMsg("下单成功");?
  21. ?}else?{?
  22. ?result.setMsg("下单失败");?
  23. ?}?
  24. ?}else?{?
  25. ?result.setCode(Result.ResultConstants.FAIL);?
  26. ?result.setMsg("下单失败:"+stockResult.getMsg());?
  27. ?}?
  28. ?return?result;?
  29. }?

我们可以看到,这样的服务调用的弊端多多:

1、订单服务需同步等待库存服务的返回结果,接口结果返回延误。2、订单服务直接依赖于库存服务,只要库存服务崩了,订单服务不能再正常运行。3、订单服务需考虑并发问题,库存最后可能为负。

下面开始利用事件驱动实现最终一致性

1、在订单服务新增订单后,订单的状态是“已开启”,然后发布一个Order Created事件到消息队列上

微服务架构:利用事件驱动实现最终一致性

代码:

  1. @Transactional(rollbackFor?=?Exception.class)?
  2. public?Result?placeOrderByMQ(OrderQuery?query)?{?
  3. ?Result?result?=?new?Result();?
  4. ?//?先创建订单,状态为下单0?
  5. ?Order?order?=?new?Order();?
  6. ?BeanUtils.copyProperties(query,order);?
  7. ?order.setOrderStatus(0);?
  8. ?Integer?insertCount?=?orderMapper.insertSelective(order);?
  9. ?if?(insertCount?==?1){?
  10. ?//?发送?订单消息?
  11. ?MqOrderMsg?mqOrderMsg?=?new?MqOrderMsg();?
  12. ?mqOrderMsg.setId(order.getId());?
  13. ?mqOrderMsg.setGoodCount(query.getGoodCount());?
  14. ?mqOrderMsg.setGoodName(query.getGoodName());?
  15. ?mqOrderMsg.setStockId(query.getStockId());?
  16. ?jmsProducer.sendOrderCreatedMsg(mqOrderMsg);?
  17. ?//?此时的订单只是开启状态?
  18. ?result.setMsg("下单成功");?
  19. ?}?
  20. ?return?result;?
  21. }?

【免责声明】本站内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

网友评论
推荐文章