中国重汽微服务管理_海量订单系统微服务开发:订单接口管理后台微服务开发、集成测试...

订单接口微服务开发

在对数据库进行单元测试之后,我们就可以开始微服务接口的开发了。完成数据库的开发之后,接口的开发就很简单了。下面的代码展示了订单数据查询和订单生成的设计实例:

@RestControllerCRequestMapping("/order")@slf4jpublicclass OrderRestController {@Autowiredprivate orderService orderService;@GetMapping (value="/{id] ")public Mono<0rder> fnidById(@Pathvariable string id){return orderService.findById(id);)GetMapping()public Flux findAll(Integer index,Integer size,Long userid, Longmerchantid,Integer status, String start, String end){try{OrderQo orderQ0 = new OrderQo();if(CommonUtils.isNotNul1(index)){orderQo.setPage (index);if(CommonUtils.isNotNull(size)){orderQo .setsize(size);if(CommonUtils.isNotNull (userid)){orderQo.setUserid(userid);}if(CommonUtils.isNotNull (merchantid)){orderQo.setMerchantid (merchantid);}if(CommonUtils.isNotNull(status)){orderQo.setStatus(status);if(CommonUtils.isNotNull (start)){SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-ddHH:mm: ss");orderQo .setstart(sdf.parse(start));}if(CommonUtils.isNotNull (end)){SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-ddHH:mm:sS");orderQo .setEnd(sdf.parse(end));}return orderService.findAll (orderQo);}catch  (Exception e){e.printStackTrace();)return null;CPostMapping()public Mono save (CRequestBody Ordergo orderQo) throws Exception{Order order = CopyUtil.copy(orderQo,Order.class);ListdetailList=CopyUtil.copyList(orderQo.get0rderDetails(),OrderDetail.class);order.setOrderDetails(detailList);Mono response = orderService.save (order).then();log.info("新增="+ response);return response;}}

由于使用反应式的编程方法,所以不管是哪一种接口的设计,最后都必须返回一个异步序列,例如 Mono或者Flux。

对于分页查询,则必须检测每个查询参数,只有非空,才提供相关的查询条件。注意日期类型的参数,因为在传输过程中只能使用文本方式,所以最后在提交查询条件时必须将其转换为日期类型的数据。

在订单生成的设计中,因为接收的参数是查询对象OrderQo,所以最终必须将其转换成文档Order。每一个订单明细都必须进行转换。

订单的分布式事务管理

集中式的数据管理可以在一个事务中完成,所以能保证数据的高一致性。微服务的多服务架构,使得数据可以由不同的微服务进行分散管理,所以想要保证数据的一致性,就必须有合理的设计。

对于订单来说,订单的状态变化与库存、物流、评价等各个服务息息相关,所以订单的状态变化,会涉及分布式事务管理的问题。比如当买家撤销订单时,库存服务的商品存量必须改变。

对于分布式事务管理,我们可以依据CAP原理的BASE理论实现数据最终一致性设计。

CAP (Consistency,Availability,Partition Tolerance)即一致性、可用性和分区容错性,三者不可兼得。

BASE(Basically Available,Soft State,Eventually Consistent)即基本可用、软状态和最终一致性。BASE是对CAP中一致性和可用性进行权衡的结果。

在微服务设计中,数据最终一致性设计主要使用两种方法实现,一种是通过接口调用实现实时同步操作,另一种是使用消息通道以事件响应的方式进行异步处理。

这里,我们以订单取消为例,使用异步消息传输,实现分布式事务管理的数据最终一致性设计。

订单取消的消息生成

首先,在order-restapi模块的项目对象模型配置中引入AMQP的消息组件依赖,代码如下所示:

org.springframework.cloudspring-cloud-starter-bus-amap

其次,在配置文件中,设置连接RabbitMQ服务器的配置,代码如下所示:

spring:rabbitmq:addresses: amap://localhost:5672username: developpassword: develop

服务地址、端口、用户名和密码等参数需根据RabbitMQ的安装和配置进行设定。

下面,我们创建一个消息发送器MessageSender,用来发送 MQ (Message Queue),代码如下所示:

@servicepublic class MessageSender {private static Logger logger =LoggerFactory.getLogger (MessageSender.class);@Autowiredprivate AmqpTemplate amqpTemplate;public void OrderUpdateMsg (0rderQo orderQo){MessageProperties messageProperties = new MessageProperties();messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);messageProperties.setContentType("UTF-8");String json = new Gson().toJson(orderQo);Message message = new Message (json.getBytes(),messageProperties);amapTemplate.send ("ordermsg.update", message);//接收端必须使用相同队列logger.info("发送订单变更消息:{}", json) ;}}

这里使用查询对象 OrderQo发送了一个完整的订单信息。

注意,这里的消息队列的名称设定为“ordermsg.update”,所以,消息接收方必须使用这个队列名称才能接收订单状态变化的相关信息。

最后,我们在订单修改这个接口中,使用消息发送器生成一条异步消息,代码如下所示:

@RestController@RequestMapping ("/order")@Slf4jpublic class OrderRestController {@Autowiredprivate OrderService orderService;@Autowiredprivate MessageSender messageSender;CPutMapping()public Mono update (@RequestBody OrderQo orderQo) throws Exception{Order order = CopyUtil.copy(orderQo, Order.class);order.setModify(new Date());List<0rderDetail> detailList =CopyUtil.copyList(orderQo.get0rderDetails(),OrderDetail.class);order.setorderDetails(detailList);Mono response = orderService.save(order).then();//发送 MQ消息,通知订单修改messageSender.OrderUpdateMsg(orderQo);log.info("修改="+ response);return response;}}

上面就是在分布式事务设计中消息生产方的一个设计实例。接下来,我们看看消息消费者是如何接收消息的,以完成一次分布式事务的过程。

订单取消的库存变化处理

接收订单取消的消息处理是在库存管理项目goods-microservice的goods-restapi模块中实现的。其中,MQ的组件依赖和服务器连接的配置与前面的基本相同。

在库存接口微服务应用中,我们只需设计一个订单消息接收器MessageOrderReceiver,就可以完成消息监听、消息接收、消息处理的相关操作,代码如下所示:

@componentpublic class MessageOrderReceiver {private static Logger logger=LoggerFactory.getLogger (MessageOrderReceiver.class);@Autowiredprivate GoodsService goodsService;@RabbitListener(queuesToDeclare = CQueue (value = "ordermsg.update"))public void orderUpdate (byte[] body) {logger.info("----------订单变更消息处理----------");try {OrderQo orderQo =new Gson ().fromJson (new String (body,"UTF-8"),OrderQo.class);if(orderQo != null) {logger.info("接收订单更新消息,订单编号="+orderQo.getorderNo());if (orderQo.getStatus() !=null && orderQo.getStatus() <0){List<0rderDetailQ0> list = orderQo.getorderDetails();for (orderDetailQo orderDetailQ0 : list) {Goods goods =goodsService.getById(orderDetailQo.getGoodsid());if(goods != null){Integer num= goods.getBuynum() != null &&goods.getBuynum() >0?goods.getBuynum() - 1 :0;goods.setBuynum (num) ;goodsservice.update(goods);logger.info("更新了商品购买数量,商品名称="+goods.getName ());}}}}}catch (Exception e) {e.printStackTrace();}}}

这里我们监听了消息队列“ordermsg.update”,将接收的消息转换成查询对象OrderQo,这样,即可根据订单状态和订单明细中的商品数据,决定是否执行商品库存存量减少的操作,完成一次分布式事务管理的整个流程。

订单管理后台微服务开发

订单管理后台微服务是为商家提供的一个PC端的Web微服务应用,它的设计在订单微服务项目的order-web模块中。在这个模块中,含有微服务接口调用和页面设计等内容,与前面章节中类目管理和库存管理项目的设计大同小异。这里只针对一些不同点进行详细介绍,其他方面可以参照前面章节。

订单查询主页设计

订单后台主页控制器 OrderController的设计代码如下所示:

@RestController@RequestMapping(" /order")public class OrderController {private static Logger logger =LoggerFactory.getLogger(0rderController.class);@Autowiredprivate OrderRestService orderRestservice;CRequestMapping(value="/index")public ModelAndView index (ModelMap model) throws Exceptionl//订单状态枚举集合StatusEnum[] statuses = StatusEnum.values();model .addAttribute( "statuses", statuses);return new ModelAndview( "order/index");@RequestMapping (value="/{id}")public ModelAndview findById (@PathVariable String id,ModelMap model)String json = orderRestService.findById(id);OrderQo orderQo =new Gson().fromJson(json,OrderQo.class);model .addAttribute ("status",StatusEnum.valueOf(orderQo. getStatus()).getName ());model.addAttribute( " order",orderQo);return new ModelAndView("order/show");}@RequestMapping(value = "/list")public Page> findAll(Ordergo orderQo) throws Exception {String json = orderRestService.findPage (orderQo);Pageable pageable = PageRequest.of (orderQo.getPage(),orderQo.getSize (),null);List<0rderQo> list = new Gson() .fromJson(json,newTypeToken>(){}.getType());for(0rderQo order : list){order.setStatusStr (StatusEnum. valueOf (order.getStatus()) .getName ());}String count =orderRestService.getCount();return new PageImpl(list, pageable, new Long (count));}}

这几个方法中都用到了订单状态的枚举类型集合StatusEnum,使用这个集合,为我们在订单查询和参数转换中提供很多方便。

其中在分页查询中,调用了两次订单接口,一次用来取得订单列表,另一次用来取得订单总数。通过这个总数,才能计算出总的页数。另外,对于列表中订单状态的显示,在这里提前进行了转换处理,这样在后面的页面设计中,就可以直接使用。

基于订单状态的枚举集合,主页页面设计中的查询条件设计代码如下所示:

订单状态全部

通过引用订单状态的枚举集合变量statuses,使用一个th:each循环语句,即可生成一个订单状态的下拉列表框。

订单状态修改设计

下面再来看看订单状态的修改设计。在控制器OrderController 的设计中,使用如下所示的实现方法:

@RestController@RequestMapping ("/order")public class OrderController {private static Logger logger =LoggerFactory.getLogger (0rderController.class);@Autowiredprivate OrderRestService orderRestService;@GetMapping (" /edit/{id}")public ModelAndView update (@PathVariable String id, HttpServletRequestregquest, Mode lMap model){String json = orderRestService.findById(id);OrderQo orderQo = new Gson().fromJson(json, OrderQo.class);//订单状态枚举集合StatusEnum[I] statuses = StatusEnum.values();model.addAttribute( "statuses", statuses);model .addAttribute ( "order", orderQo);return new ModelAndView("order/edit");}CPostMapping(value="/update")public String update (0rderQo orderQo,HttpServletRequest request)[String json = orderRestService.findById(orderQo.getId());OrderQo newOrder = new Gson().fromJson(json, OrderQo.class);newOrder.setStatus (orderQo.getStatus());neworder.setModify (new Date());orderRestService. update (newOrder);logger.info("修改=" +orderQo.getId());return "1";}}

这里,先从OrderRestService 中取得一个订单数据,然后在页面edit.html 中展示出来。当用户在页面上选择一个订单状态并提交之后,就会调用OrderRestService的update方法,请求数据库更新数据。

在页面edit.html的设计中,使用了一个弹出窗口,其完整代码如下所示:

子类信息

订单号状态/option>
确定返回

其中,订单编号设定为只读状态,即不能被修改,而订单的状态使用一个下拉列表框来显示。刚打开页面时,原有的订单状态会处于已经选定的状态。这样当用户在页面上选择另一个状态进行提交时,就可以对订单状态进行修改操作了。

集成测试

在开发完成之后,需要进行一个集成测试。在这个集成测试中,会用到消息队列,所以必须保证 RabbitMQ服务器已经启动,并且程序与服务器的连接配置都正确无误。

按下列顺序启动各个微服务模块:

(1)库存管理微服务API应用:goods-restapi。(2)订单微服务应用接口设计: order-restapi。(3)订单后台管理应用: order-web。

上面各个模块启动成功之后,通过浏览器打开如下链接地址,即订单Web应用的后台管理首页:

http: //Localhost:8095

如果打开成功,并且已有订单数据,则可以看到如图8-3所示页面。

3cb1371c75421439025d9c2e31f2238e.png

现在我们编辑一下订单,选择图8-3中第一个已付款的订单,单击“编辑”选项,在弹出的编辑窗口中,将状态修改为已撤销,如图8-4 所示。

2ce903b539479580d419021cd7befa7d.png

单击“确定”按钮,如果页面上返回了编辑成功的提示,则说明修改操作已经完成。查看订单接口和库存接口的控制台输出日志,看看分布式事务的消息是否已经处理完成。如果一切正常,则可以在订单接口的控制台中看到如下所示的输出日志:

MessageSender -发送订单变更消息:

("id":"5d6b777971d7ad663e4ac3dd" , "orderNo": "1567324025207", "userid":11111235,"merchantid":123456,"amount":11.2,"status":-1, "created" :"Sep 1, 2019 3:47:05PM","modify" : "Sep 1,20193:50:27PM", "orderDetails":[{ "goodsid":1, "goodsname":"测试商品1", "photo": " /images/demol.png" , "nums " : 1 , "price":11.2,"money":11.21], "page":0,"size" :10}

如果能看到上面所示的日志,则说明订单状态变更的消息已经发送到了消息队列上。

再切换到库存接口的控制台,查看库存接口的输出日志。如果能看到如下所示的输出日志,则说明消息已经处理成功,同时也说明分布式事务已经处理完毕。

接收到订单更新消息,订单编号=1567324025207...更新了商品购买数量,商品名称=测试商品1...

这时,在订单管理后台的首页上,可以看到订单的状态已修改成功,如图8-5所示。在图8-5中,我们还可以按各种查询条件和不同的参数,实现各种不同需求的分页数据的列表查询操作。

670125dbfc8509557853c6c351cd68cd.png

小结

本章我们使用MongoDB开发了一个可以支持海量数据的订单系统,并且使用Spring 5的反应式编程设计,实现了支持非阻塞异步调用的高并发微服务订单接口,所以这是一个高性能的订单微服务应用系统。有关反应式编程设计,由于其异步调用的特性,使得其只能支持无事务管理的数据库设计。而对于微服务设计来说,其本身就是一种分布式的应用,所以有关事务管理的设计,只能使用分布式的事务管理来实现。在本章订单状态变更所引起的事务管理实例中,我们使用消息队列实现了分布式事务管理中数据最终一致性的设计。

本文给大家讲解的内容

SpringCloud微服务架构实战:海量订单系统微服务开发,订单接口微服务开发、订单的分布式事务管理、 订单管理后台微服务开发、集成测试

  1. 下篇文章给大家讲解的是SpringCloud微服务架构实战:移动商城的设计和开发;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值