rabbitMQ

消息队列,通过一定的通信协议,生产者和消费者在应用程序内传递通信。

主要的作用,提高负载,减耦合。

场景描述:当点击秒杀按钮的那个时刻,有很高的并发量,客户端发出请求之后,会判断库存,如果库存大于0,就判断是否已经下单,如果没有下单,就执行秒杀逻辑,对于秒杀逻辑,分两个步骤,一是减库存,二是创建订单。

以上就是不使用rabbitMQ的场景描述。

利用消息队列,我们可以在执行秒杀逻辑之前,将用户和待秒杀的商品进行入队(rabbitMQ的生产者类),然后将秒杀逻辑放在出队上(rabbitMQ的消费者类)

在客户端页面上创建一个轮询函数,每隔一段时间,设置一个请求是否有秒杀结果,然后进行数据展示,或者跳转。

通过以上操作,我们可以将用户的请求与返回的结果通过rabbitMQ进行解耦合。从而降低网站的并发。

  • 内存标记库存

  假如后面的操作已经确认库存小于0,则设置一个标记器,直接返回错误信息。

//内存标记,第一次拦截
Boolean over = localOverMap.get(goodsId);
if(over){
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
  • 预减库存

  该操作相当于一个拦截器,当我们的秒杀请求进到秒杀控制器中,需要先从redis中减库存,然后判断库存是否充足。因此,需要先在redis中加载商品的库存数

  这里利用控制器继承一个接口,来写一个系统初始化的方法,将商品的库存数放到redis中,每次从秒杀点击过来的请求,先加载该初始化方法,把库存数从数据库里拿出来,(这里可以改进为从缓存中拿值),然后判断库存数,如果库存数小于0,则不需要后续操作,直接返回错误。

/**
* 系统初始化
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception { //得到商品的所有列表
List<GoodsVo> goodsList = goodsService.listGoodsVo();
if(goodsList==null){
return;
} for (GoodsVo goods : goodsList) {
redisService.set(GoodsKey.getMiaoshaGoodsStock,""+goods.getId(),goods.getStockCount());
//对内存进行标志,若没有结束库存就是false,若有库存就是true
localOverMap.put(goods.getId(),false);//false表示有库存
} } //预减库存
Long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, "" + goodsId);
if(stock<0){
localOverMap.put(goodsId,true);
return Result.error(CodeMsg.MIAO_SHA_OVER);
}

当判断,既有库存,又没有下订单,则执行入队操作,将需要传递的用户和商品名加入到队列中,传递给消费者。

//判断是否已经秒杀到了
MiaoshaOrder order=orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if(order!=null){
return Result.error(CodeMsg.REPEATE_MIAOSHA);
} //入队操作
//创建需要入队的数据类,需要用户类和商品的id。
MiaoshaMessage message=new MiaoshaMessage();
message.setGoodsId(goodsId);
message.setUser(user);
//调用rabbitMQ生产者的发送方法,发送该数据类
mqSender.sendMiaoshaMessage(message);
  • 完整MiaoshaController类

@Controller
@RequestMapping("/miaosha")
public class MiaoshaController implements InitializingBean{ @Autowired
private GoodsService goodsService; @Autowired
private OrderService orderService; @Autowired
private MiaoshaService miaoshaService; @Autowired
private RedisService redisService; @Autowired
private MQSender mqSender; private HashMap<Long,Boolean> localOverMap=new HashMap<>(); /**
* 系统初始化
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception { //得到商品的所有列表
List<GoodsVo> goodsList = goodsService.listGoodsVo();
if(goodsList==null){
return;
} for (GoodsVo goods : goodsList) {
redisService.set(GoodsKey.getMiaoshaGoodsStock,""+goods.getId(),goods.getStockCount());
//对内存进行标志,若没有结束库存就是false,若有库存就是true
localOverMap.put(goods.getId(),false);//false表示有库存
} } @RequestMapping(value = "/do_miaosha",method = RequestMethod.POST)
@ResponseBody
public Result<Integer> list(Model model, MiaoshaUser user,
@RequestParam("goodsId") long goodsId){ model.addAttribute("user",user);
if(user==null){
//如果没有获取到user值,报异常
return Result.error(CodeMsg.SESSION_ERROR);
} //内存标记,第一次拦截
Boolean over = localOverMap.get(goodsId);
if(over){
return Result.error(CodeMsg.MIAO_SHA_OVER);
} //预减库存
Long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, "" + goodsId);
if(stock<0){
localOverMap.put(goodsId,true);
return Result.error(CodeMsg.MIAO_SHA_OVER);
} //判断是否已经秒杀到了
MiaoshaOrder order=orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if(order!=null){
return Result.error(CodeMsg.REPEATE_MIAOSHA);
} //入队操作
//创建需要入队的数据类,需要用户类和商品的id。
MiaoshaMessage message=new MiaoshaMessage();
message.setGoodsId(goodsId);
message.setUser(user);
//调用rabbitMQ生产者的发送方法,发送该数据类
mqSender.sendMiaoshaMessage(message); return Result.success(0);//返回0代表排队中 // //判断库存
// GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
// Integer stock= goods.getStockCount();
//
// if(stock<=0){
// model.addAttribute("errmsg", CodeMsg.MIAO_SHA_OVER.getMsg());
// return Result.error(CodeMsg.MIAO_SHA_OVER);
// }
//
// //判断是否已经秒杀到了
// MiaoshaOrder order=orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
//
// if(order!=null){
// return Result.error(CodeMsg.REPEATE_MIAOSHA);
// }
//
// //进行秒杀逻辑
// //减库存,下订单,写入秒杀订单
// OrderInfo orderInfo=miaoshaService.miaosha(user, goods); // return Result.success(orderInfo);
} @RequestMapping(value="/result", method=RequestMethod.GET)
@ResponseBody
public Result<Long> miaoshaResult(Model model,MiaoshaUser user,
@RequestParam("goodsId")long goodsId){
model.addAttribute("user",user);
if(user==null){
return Result.error(CodeMsg.SESSION_ERROR);
}
//获取秒杀的结果
long result =miaoshaService.getMiaoshaResult(user.getId(), goodsId); return Result.success(result);
} }

将队列的消息寄出去之后,需要由消费者接收数据,并执行秒杀的具体逻辑

这里的逻辑代码就很简单,继续判断库存以及是否秒杀到,然后执行秒杀逻辑

  • MQReceiver类
@Service
public class MQReceiver { private static Logger log = LoggerFactory.getLogger(MQReceiver.class); @Autowired
private GoodsService goodsService; @Autowired
private OrderService orderService; @Autowired
private MiaoshaService miaoshaService; @RabbitListener(queues= MQConfig.MIAOSHA_QUEUE)
public void receive(String message){ //获取到生产者发送过来的数据
log.info("收到的消息:"+message); //转换数据格式
MiaoshaMessage mm = RedisService.stringToBean(message, MiaoshaMessage.class); MiaoshaUser user = mm.getUser();
long goodsId = mm.getGoodsId(); //数据传过来之后,仍然需要判断库存以及是否完成订单
GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
int stock = goods.getStockCount();
if(stock <= 0) {
return;
}
//判断是否已经秒杀到了
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if(order != null) {
return;
} //当真正秒杀成功之后,再将请求转到秒杀逻辑内
miaoshaService.miaosha(user,goods); } }

具体的秒杀逻辑是减库存,然后创建订单,这些代码在之前已经写过。如果没有减库存成功,则表示已经结束秒杀了,在之前的代码中,什么都没做,让事务进行回滚。这次在缓存中设置一个秒杀结束的标志。

@Transactional
public OrderInfo miaosha(MiaoshaUser user, GoodsVo goods) { // //减库存,下订单,写入秒杀订单
// goodsService.reduceStock(goods);
// return orderService.createOrder(user, goods); boolean success = goodsService.reduceStock(goods);
if (success){
return orderService.createOrder(user, goods);
}else {
setGoodsOver(goods.getId());
return null;
} } private void setGoodsOver(Long goodsId) {
redisService.set(MiaoshaKey.isGoodsOver, ""+goodsId, true);
} private boolean getGoodsOver(long goodsId) {
return redisService.exists(MiaoshaKey.isGoodsOver, ""+goodsId);
}

做到此处,秒杀的逻辑结束了,但并没有返回任何页面,只是对数据库做了相应的修改。

接下来应该在客户端进行循环提出请求,来判断是否秒杀成功,在商品的详情页面,当入队成功返回一个0时,就开始启动轮询函数,不停的向服务器发请求,看是否已经下单成功,若成功就跳转到订单详情页面。

function getMiaoshaResult(goodsId) {
g_showLoading();//显示处理等待
//发起ajax请求
$.ajax({
url:"/miaosha/result",
type:"GET",
data:{
goodsId:$("#goodsId").val()
},
success:function(data){
if (data.code==0){
var result=data.data;
if(result<0){
layer.msg("对不起,秒杀失败");
}else if(result==0){
//继续轮询
setTimeout(function () {
getMiaoshaResult(goodsId)
},50);
}
else {
layer.confirm("恭喜你,秒杀成功!查看订单?", {btn:["确定","取消"]},
function(){
window.location.href="/order_detail.htm?orderId="+result;
},
function(){
layer.closeAll();
});
}
}else {
layer.msg(data.msg);
} },
error:function(){
layer.msg("客户端请求有误");
}
});
}

每次轮询代码请求服务器时,都会将秒杀的结果返回

public long getMiaoshaResult(Long userId, long goodsId) {

        //查看是否下订单成功
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(userId, goodsId);
if (order!=null){
return order.getOrderId();
}
else {
boolean isover = getGoodsOver(goodsId);
if(isover){
return -1;
}
else {
return 0;
}
} }

若订单不为空,则返回订单的id,若为空,则查看有没有库存,若没有库存则返回-1,若有库存,则说明还在排队中,返回0,

@RequestMapping(value="/result", method=RequestMethod.GET)
@ResponseBody
public Result<Long> miaoshaResult(Model model,MiaoshaUser user,
@RequestParam("goodsId")long goodsId){
model.addAttribute("user",user);
if(user==null){
return Result.error(CodeMsg.SESSION_ERROR);
}
//获取秒杀的结果
long result =miaoshaService.getMiaoshaResult(user.getId(), goodsId); return Result.success(result);
}

到此,rabbitMQ实现的秒杀功能就完成了。

java初探(1)之秒杀中的rabbitMQ的更多相关文章

  1. 浅谈surging服务引擎中的rabbitmq组件和容器化部署

    1.前言 上个星期完成了surging 的0.9.0.1 更新工作,此版本通过nuget下载引擎组件,下载后,无需通过代码build集成,引擎会通过Sidecar模式自动扫描装配异构组件来构建服务引擎 ...

  2. 二、消息队列之如何在C#中使用RabbitMQ

    1.什么是RabbitMQ.详见 http://www.rabbitmq.com/. 作用就是提高系统的并发性,将一些不需要及时响应客户端且占用较多资源的操作,放入队列,再由另外一个线程,去异步处理这 ...

  3. [java初探总结篇]__java初探总结

    前言 终于,java初探系列的学习,要告一阶段了,java初探系列在我的计划中是从头学java中的第一个阶段,知识主要涉及java的基础知识,所以在笔记上实在花了不少的功夫.虽然是在第一阶段上面花费了 ...

  4. [java初探10]__关于数字处理类

    前言 在我们的日常开发过程中,我们会经常性的使用到数字类型的数据,同时,也会有众多的对数字处理的需求,针对这个方面的问题,在JAVA语言中.提供解决方法的类就是数字处理类 java中的数字处理类包括: ...

  5. 二、消息队列之如何在C#中使用RabbitMQ(转载)

    二.消息队列之如何在C#中使用RabbitMQ 1.什么是RabbitMQ.详见 http://www.rabbitmq.com/. 作用就是提高系统的并发性,将一些不需要及时响应客户端且占用较多资源 ...

  6. .NET 环境中使用RabbitMQ RabbitMQ与Redis队列对比 RabbitMQ入门与使用篇

    .NET 环境中使用RabbitMQ   在企业应用系统领域,会面对不同系统之间的通信.集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要.其次,系统中一般会有很多对实时性要求不高的 ...

  7. Spring Boot中使用RabbitMQ

    很久没有写Spring Boot的内容了,正好最近在写Spring Cloud Bus的内容,因为内容会有一些相关性,所以先补一篇关于AMQP的整合. Message Broker与AMQP简介 Me ...

  8. 在SpringBoot中使用RabbitMQ

    目录 RabbitMQ简介 RabbitMQ在CentOS上安装 配置文件 实践 概述 Demo 遇到的BUG 启动异常 无法自动创建队列 RabbitMQ简介 wikipedia RabbitMQ在 ...

  9. ubuntu中安装rabbitmq服务并成功启动

    在我们使用rabbitmq时,首先要对其进行安装,而后才能对其进行使用 安装 Erlang 由于 RabbitMQ 是采用 Erlang 编写的,所以需要安装 Erlang 语言库.就像 java 需 ...

随机推荐

  1. doc属性__module__属性__del__(垃圾回收)__call__方法

    __module__属性: 析构函数:del 是python的垃圾回收方法,当实例运行完的时候触发,回收资源 __call__

  2. 【Python 实例】面向对象 | 按逗号分割列表

    [Python 实例]面向对象 | 按逗号分割列表 题目: 按逗号分割列表 应该得到如下结果: ["xx"],["xx"],["xx"] 解 ...

  3. 020_go语言中的接口

    代码演示 package main import ( "fmt" "math" ) type geometry interface { area() float ...

  4. 阿里云ECS服务器购买流程

    先说说什么是阿里云服务器ECS?云服务器(Elastic Compute Service,即弹性计算服务,简称ECS)是阿里云提供的性能卓越.稳定可靠.弹性扩展的IaaS(Infrastructure ...

  5. 我给这个Python库打101分!

    日志在开发过程中是一种被很多程序员 不重视 ,但是却 至关重要 的一项功能. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案 ...

  6. webMvcConfigurer的详情

                  摘要 Spring的WebMvcConfigurer接口提供了很多方法让我们来定制SpringMVC的配置.而且Spring还提供了WebMvcConfigurerAdap ...

  7. ~/.ssh/目录找不到解决方法

    执行 cd ~/.ssh发现.ssh目录找不到 原因是因为没有用root用户ssh登录过,执行一下ssh操作就会自动生成了

  8. JS 与 jQery 的区别主要在于 DOM

    //目前正在学习前端阶段,把知识点整理.保存下来以便日后查看 首先引入jQery: 需要先引入css,再引入js: jQery需要在js前引入,再引入框架,最后才是js的引入:css也相同,先引入框架 ...

  9. C#LeetCode刷题之#55-跳跃游戏(Jump Game)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3674 访问. 给定一个非负整数数组,你最初位于数组的第一个位置. ...

  10. 文件上传控件bootstrap-fileinput中文设置没有效果的情况

    1.引入zh.js顺序错误 zh.js需放到fileinput.js下面 2. 组件创建语法错误 (class=“file”) 如果你使用js初始化fileinput组件,那么在html元素中应删除 ...