在腾讯课堂听了一节公开课,学习到了这么一个项目,遂记录下来。
发货系统和订单系统基于Spring-Boot项目,其中springboot整合了mybatis,log4j2等 ,项目中使用到了generator代码生成工具,生成dao/domain/mapper.xml文件
- 发货系统模拟(target-service)
- Controller层实现
/** * @description: 模拟仓库发货类 * @author: GaraYing * @create: 2018-08-14 09:53 **/ @RestController @RequestMapping("/bank") public class BankController { private Logger logger = LoggerFactory.getLogger(getClass()); /** * @Description: 远程提供发货处理接口 * @Param: [orderid] * @return: java.lang.String * @Author: GaraYing * @Date: 2018/8/15 14:05 */ @RequestMapping(value = "/handleOrder") public String handleOrder(@RequestParam(required = false) String orderid){ logger.info("收到订单号:" + orderid + ",正在出货处理中……"); try { Thread.currentThread().sleep(10000); }catch (Exception e){ logger.error("出现错误了"+e.getMessage()); e.printStackTrace(); return "-1"; } return "0"; } }
- 订单系统模拟(client-service)
- mapper.xml文件
<select id="findOrderById" resultMap="result"> SELECT * FROM t_order where orderId = #{orderid}; </select> <update id="update" parameterType="com.gara.lock_demo.domain.Order" flushCache="true"> <![CDATA[ update t_order set orderStatus = #{orderStatus,jdbcType=VARCHAR} where orderId = #{orderId,jdbcType=VARCHAR} ]]> </update> <insert id="updateByVersion" parameterType="com.gara.lock_demo.domain.Order" flushCache="true"> <![CDATA[ update t_order set orderStatus = #{orderStatus,jdbcType=VARCHAR}, version = version+1 where orderId = #{orderId,jdbcType=VARCHAR} and version = #{version} ]]> </insert>
-
- Controller实现
/** * @description: 消费端 * @author: GaraYing * @create: 2018-08-14 14:33 **/ /* 接口层:对外开放接口路径及地址 http://127.0.0.1:8080/order/sendOrder?orderid=1 */ @RestController @RequestMapping("/order") public class CustController { @Autowired private OrderService orderService; @RequestMapping("/query") @ResponseBody public Object query(@RequestParam(required = true) String orderid) { Order order = orderService.findOrderById(orderid); return order; } @RequestMapping("/sendOrder") @ResponseBody public String sendOrder(@RequestParam(required = true) String orderid) { Order order = orderService.findOrderById(orderid); return orderService.sendOrder(order); } @RequestMapping("/sendOrderByTemplate") @ResponseBody public String sendOrderByTemplate(@RequestParam(required = true) String orderid) { Order order = orderService.findOrderById(orderid); return orderService.sendOrderByTemplate(order); } @RequestMapping("/sendOrderByTemplateThread") @ResponseBody public String sendOrderByTemplateThread(@RequestParam(required = true) String orderid) { Order order = orderService.findOrderById(orderid); for (int i = 0; i < 6; i++) { Thread t = new Thread(new ExcuteThread(order)); t.start(); } return null; } private class ExcuteThread implements Runnable { private Order order; public ExcuteThread(Order order) { this.order = order; } @Override public void run() { orderService.sendOrderByTemplateThread(order); } } }
- TemplateConfig核心配置类,用于调用第三方(仓库系统)接口,使用RestTemplate.getForEntity()方法,调用远程发货接口
/** * @description: Config 类 * @author: GaraYing * @create: 2018-08-14 18:05 **/ @Configuration public class TemplateConfig { @Bean RestTemplate restTemplate() { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setConnectTimeout(6000); requestFactory.setReadTimeout(6000); RestTemplate restTemplate = new RestTemplate(requestFactory); return restTemplate; } }
-
- 使用RestTemplate.getForEntity()方法,调用远程发货接口
@Autowired private RestTemplate restTemplate; /** * @Description: 获取发货系统返回数据 * @Param: [url, orderid] * @return: java.lang.String * @Author: GaraYing * @Date: 2018/8/15 17:12 */ @Override public String invoke(String url, String orderid) { return restTemplate.getForEntity(url+orderid,String.class).getBody(); }
-
- Service实现(sendOrderByTemplateThread方法)
重点: 这里做了一个判断,对每次传入的订单实体,会进行一次数据库update操作,只有第一次进入的线程才会返回true,后续的重复请求返回false , 从而实现乐观锁 1 == orderMapper.updateByVersion(order)
/** * @Description: 基于状态机的乐观锁 * @Param: [order] * @return: java.lang.String * @Author: GaraYing * @Date: 2018/8/15 9:35 */ @Override public String sendOrderByTemplateThread(Order order) { String orderId = order.getOrderId(); // 只有第一个操作返回true,其他返回false Boolean lock = template.execute(new TransactionCallback<Boolean>() { @Override public Boolean doInTransaction(TransactionStatus transactionStatus) { Order order = new Order(); order.setOrderId(orderId); order.setOrderStatus("4");//订单处理中 order.setVersion(0); // orderMapper.update(order); return 1 == orderMapper.updateByVersion(order);//受影响的记录数 } }); if (lock) { // 只允许一个线程发货,其他全部拦截 String flag = transService.invoke(url, orderId); template.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus transactionStatus) { Order orderFin = new Order(); orderFin.setOrderId(orderId); orderFin.setOrderStatus(flag);//订单处理中 orderFin.setVersion(1); // orderMapper.update(order); orderMapper.updateByVersion(orderFin);//受影响的记录数 return null; } }); } else { logger.error("lockFail************" + order.getOrderId()); } return null; }
-
- 实体类参考
/** * @description: Order订单实体类 * @author: GaraYing * @create: 2018-08-14 10:45 **/ public class Order { private String orderId; //订单ID private String orderTime; // 订单时间 private Long orderMoney; // 订单金额 private String orderStatus; //订单状态:0未处理/1处理中/2处理失败/3处理成功/4处理完成 private Integer version; // 版本 public Order() { } public Order(String orderId, String orderTime, Long orderMoney, String orderStatus, Integer version) { this.orderId = orderId; this.orderTime = orderTime; this.orderMoney = orderMoney; this.orderStatus = orderStatus; this.version = version; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getOrderTime() { return orderTime; } public void setOrderTime(String orderTime) { this.orderTime = orderTime; } public Long getOrderMoney() { return orderMoney; } public void setOrderMoney(Long orderMoney) { this.orderMoney = orderMoney; } public String getOrderStatus() { return orderStatus; } public void setOrderStatus(String orderStatus) { this.orderStatus = orderStatus; } public Integer getVersion() { return version; } public void setVersion(Integer version) { this.version = version; } @Override public String toString() { return "Order{" + "orderId='" + orderId + '\'' + ", orderTime='" + orderTime + '\'' + ", orderMoney=" + orderMoney + ", orderStatus='" + orderStatus + '\'' + ", version='" + version + '\'' + '}'; } }
核心点总结:基于状态机的乐观锁的实现主要利用了一下核心数据库语句,当用户在前端页面,以单身狗的手速疯狂点击产生重复订单的情况下,可以保证只有第一次请求会处理并进入,即完成了只有一个线程发货,其他全部拦截
update t_order set orderStatus =#{orderStatus,jdbcType=VARCHAR},version = version+1 where orderId = #{orderId,jdbcType=VARCHAR} and version = #{version}
因为自己也是菜鸟一枚,在视频的帮助下,加上了一些自己的理解,初步完成了这样一个示例,还有很多不足和需要完善的地方,后续会更新改正,希望能帮助到需要的朋友们,有问题大家一起交流。