SpringBoot 集成 rabbitmq 簡單實現建立訂單、減小庫存、建立物流即應用解耦javascript
隊列實現方式:
訂單系統:用戶下單後,訂單系統完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功。
庫存系統:訂閱下單的消息,採用拉/推的方式,獲取下單信息,庫存系統根據下單信息,進行庫存操做
物流系統:用戶下單後,建立用戶的物流信息
假如:在下單時庫存系統不能正常使用。也不影響正常下單,由於下單後,訂單系統寫入消息隊列就再也不關心其餘的後續操做了。實現訂單系統與庫存系統的應用解耦html
詳細代碼訪問個人github:https://github.com/ningcs/reservemqjava
項目結構:mysql
項目結構描述:jquery
eureka :項目註冊與發現。git
全部項目都註冊到這個地址,方便三個系統之間經過feignClient進行數據交互。(eureka詳細註冊與發現這裏不過多描述)github
eureka 配置文件:web
spring.application.name=eureka-server server.port=1001 eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false ##禁用自我保護模式 #eureka.server.enable-self-preservation=false #eureka.instance.prefer-ip-address=true eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
reserve-provider-service:訂單系統,消費者從訂單系統發起訂單,同時告知物流和庫存系統。ajax
reserve-receiver-service:庫存系統。獲取庫存量,減小庫存等。spring
reserve-wuliu-receiver-service: 物流系統。訂單完成,告知物流系統建立該用戶的物流信息。
項目所需jar包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 添加springboot對amqp的支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-commons</artifactId> <version>1.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
訂單系統:
經過一個簡單的購買頁面,模擬用戶購買時的情況。
package com.example.demo.controller; import com.example.demo.dto.OrderInfo; import com.example.demo.dto.ReserveInfo; import com.example.demo.entity.Product; import com.example.demo.service.OrderService; import com.example.demo.service.ReserveService; import com.example.demo.util.ResponseResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; /** * Created by ningcs on 2017/10/30. */ @Controller @RequestMapping("rabbit") public class RabbitController { @Autowired private ReserveService reserveService; @Autowired private OrderService orderService; // // @RequestMapping(value = "/hello",method = {RequestMethod.GET, RequestMethod.POST}) // public String helloSender(){ // helloSender.send("hello,rabbit~"); // return "發送成功"; // } @RequestMapping(value = "/createOrderPage",method = {RequestMethod.GET, RequestMethod.POST}) public ModelAndView createOrderPage(Integer productId){ productId=1; ModelAndView modelAndView =new ModelAndView("/index"); //獲取庫存餘量 ReserveInfo reserve =reserveService.getReserveCount(productId); Product product =orderService.getProductById(productId); if (reserve!=null && product!=null){ modelAndView.addObject("total",reserve.getTotalCount()-reserve.getCurrentCount()); modelAndView.addObject("product",product); modelAndView.addObject("userId",2); } return modelAndView; } @RequestMapping(value = "/createOrder",method = {RequestMethod.GET, RequestMethod.POST}) @ResponseBody public ResponseResult createOrder(Integer productId, Integer count,Integer userId){ if (count==null || count<=0){ return ResponseResult.errorResult("購買數量不能爲空或爲0"); } DecimalFormat df = new DecimalFormat("#.00"); Product product =orderService.getProductById(productId); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm"); OrderInfo orderInfo =new OrderInfo(); orderInfo.setProductId(productId); orderInfo.setBuyCount(count); orderInfo.setUserId(userId); //保留兩位小數 orderInfo.setMonetary(""+df.format(Double.parseDouble(product.getProductPrice())*count)); orderInfo.setOrderId(getTenRandomLetter()+sdf.format(new Date())+"_"+userId+"_"+productId); orderInfo.setProductName(product.getProductName()); //建立訂單 向庫存系統 和物流系統發送消息 orderService.addOrder(orderInfo); return ResponseResult.successResult("生成訂單成功"); } /** * 從26爲字母中得到一個6位隨機數(純字母) * ok */ public static String getTenRandomLetter() { String word = "abcdefghijklmnopqrstuvwxyz"; String tmp = ""; for (int i = 0; i < 6; i++) { Random random = new Random(); Integer index = random.nextInt(word.length()); char c = word.charAt(index); tmp = tmp + c; } return tmp; } }
service: 將訂單,和mq放在一個事物裏面,建立訂單成功後,會經過隊列告知庫存系統減小庫存,物流系統建立該用戶的物流信息。
package com.example.demo.service.impl; import com.example.demo.dao.OrderDao; import com.example.demo.dto.OrderInfo; import com.example.demo.entity.Product; import com.example.demo.sender.HelloSender; import com.example.demo.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * Created by ningcs on 2017/11/20. */ @Service public class OrderServiceImpl implements OrderService{ @Autowired private OrderDao orderDao; @Autowired private HelloSender helloSender; @Override public Product getProductById(Integer productId) { return orderDao.getProductById(productId); } @Override @Transactional public void addOrder(OrderInfo orderInfo) { orderDao.addOrder(orderInfo); helloSender.send(orderInfo); } }
隊列配置:
package com.example.demo.conf; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Created by ningcs on 2017/10/30. */ @Configuration public class SenderConf { @Bean public Queue queueOrder() { return new Queue("queueOrder1"); } @Bean public Queue queueOrder2() { return new Queue("queueOrder2"); } }
訂單系統的配置文件:頁面框架使用的freemark
server.port=17073 eureka.client.service-url.defaultZone=http://127.0.0.1:1001/eureka/ spring.application.name=spirng-boot-rabbitmq-sender spring.rabbitmq.host=127.0.0.1 spring.rabbitmq.port=5672 spring.rabbitmq.username=ncs spring.rabbitmq.password=12345678 spring.rabbitmq.publisher-confirms=true spring.rabbitmq.virtual-host=/ spring.rabbitmq.listener.concurrency=1 spring.rabbitmq.listener.max-concurrency=5 spring.resources.static-locations=classpath:/static/ spring.freemarker.template-loader-path=classpath:/views/templates/ spring.freemarker.cache=false spring.freemarker.charset=UTF-8 spring.freemarker.check-template-location=true spring.freemarker.content-type=text/html spring.freemarker.expose-request-attributes=true spring.freemarker.expose-session-attributes=true spring.freemarker.request-context-attribute=request spring.freemarker.suffix=.ftl spring.datasource.url=jdbc:mysql://**.**.206.***:3306/order?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8 spring.datasource.username=*** spring.datasource.password=pning***** spring.datasource.driver-class-name=com.mysql.jdbc.Driver feign.hystrix.enabled=false
庫存系統:至關於消費者 ,接收建立訂單成功後,減小庫存。
package com.example.demo.receiver; import com.example.demo.dto.OrderInfo; import com.example.demo.service.ReserveService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Created by ningcs on 2017/10/30. */ @Component public class HelloReceive { Log log = LogFactory.getLog(getClass()); @Autowired ReserveService reserveService; @RabbitListener(queues="queueOrder1") //監聽器監聽指定的Queue public void queueOrder1(OrderInfo orderInfo) { Integer count=0; if (orderInfo!=null){ count= reserveService.updateReserveCount(orderInfo.getBuyCount(),orderInfo.getProductId()); log.info("Receive:隊列:queueOrder1 商品名字:"+orderInfo.getProductName()+",商品購買數量:"+orderInfo.getBuyCount()); } } @RabbitListener(queues="queueOrder2") //監聽器監聽指定的Queue public void queueOrder2(OrderInfo orderInfo) { Integer count=0; if (orderInfo!=null){ count= reserveService.updateReserveCount(orderInfo.getBuyCount(),orderInfo.getProductId()); log.info("Receive:隊列:queueOrder2 商品名字:"+orderInfo.getProductName()+",商品購買數量:"+orderInfo.getBuyCount()); } } }
物流系統:至關於消費者 ,接收建立訂單成功後,建立該用戶的物流信息。
package com.example.demo.receiver; import com.example.demo.dto.OrderInfo; import com.example.demo.service.WuliuService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Created by ningcs on 2017/10/30. */ @Component public class HelloReceive { Log log = LogFactory.getLog(getClass()); @Autowired private WuliuService wuliuService; @RabbitListener(queues="queueWuLiu1") //監聽器監聽指定的Queue public void queueOrder1(OrderInfo orderInfo) { if (orderInfo!=null){ wuliuService.addWuliu(orderInfo); log.info("Receive:隊列:queueWuLiu1 商品名字:"+orderInfo.getProductName()+",商品購買數量:"+orderInfo.getBuyCount()); } } @RabbitListener(queues="queueWuLiu2") //監聽器監聽指定的Queue public void queueOrder2(OrderInfo orderInfo) { Integer count=0; if (orderInfo!=null){ wuliuService.addWuliu(orderInfo); log.info("Receive:隊列:queueWuLiu2 商品名字:"+orderInfo.getProductName()+",商品購買數量:"+orderInfo.getBuyCount()); } } }
簡單購買頁面:
<!DOCTYPE html> <html lang="en"> <head> <script src="/js/jquery.js"></script> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form id="form"> <div class="form-group"> <label for="name">名稱:</label> <input type="text" id="productName" disabled value="${product.productName}"> </div> <div class="form-group"> <label for="count">購買數量:</label> <input type="number" id="count" value=""> </div> <div class="form-group"> <label for="count">單價:</label> <p class="form-control-static" id="price">${product.productPrice}</p> </div> <div class="form-group"> <label for="count">庫存:</label> <p class="form-control-static" id="total">${total}</p> </div> <div class="form-group"> <a href="javascript:void(0);" id="sub">購買</a> </div> </form> <script> $('#sub').click(function () { $.ajax({ url: '/rabbit/createOrder', type: 'post', data: { productId: ${product.id}, count: $('#count').val(), userId: ${userId} }, dataType: 'json', cache: false, success: function (json) { if(json.code==0){ window.location.reload(); }else { alert(json.msg); } }, error: function () { alert('出錯'); } }); return false; }); </script> </body> </html>
本文只是本身通過簡單的構思,對Springboot和rabbitmq的集成設計的訂單、物流、庫存寫的一個簡單的例子,只是構思了大抵的思路,並經過簡單的代碼實現,並無用到什麼先進的技術和嚴密的構思。甚至還有不少缺陷,望你們多多指導,也但願對初學者有所幫助。
詳細代碼地址訪問個人github:https://github.com/ningcs/reservemq