通过上节我们已经可以将用户的信息存入redis中,然后通过cookie传给客户端,客户端再进行访问的时候携带这个sessionid然后我们通过HandlerMethodArgumentResolver来获取到用户的信息,并且将用户信息如同Model一样传递给Controller方法.
这节我们将建立一个秒杀的简单应用,然后我们来模拟高并发.
安装Jmeter
https://jmeter.apache.org/
按照提示安装即可
编写秒杀应用
继续接着我们 spring-boot系列的工程进行编写,代码将会在我的github上面可以看到
我们需要构建如下几个功能:
商品列表,商品详情,秒杀
因此需要建模
商品信息表,秒杀商品信息表,订单表,秒杀订单表
建模
CREATE TABLE `goods` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`goods_name` varchar(45) DEFAULT NULL COMMENT '商品名称',
`goods_title` varchar(45) DEFAULT NULL COMMENT '商品标题',
`goods_img` varchar(64) DEFAULT NULL COMMENT '商品的图片',
`goods_detail` longtext COMMENT '商品详情介绍',
`goods_price` decimal(10,2) DEFAULT '0.00' COMMENT '商品单价',
`goods_stock` int(11) DEFAULT '0' COMMENT '商品库存,-1表示没有限制',
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='商品表\n'
CREATE TABLE `miaosha_goods` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '秒杀商品表主键',
`goods_id` bigint(20) DEFAULT NULL COMMENT '商品id',
`miaosha_price` decimal(10,2) DEFAULT '0.00' COMMENT '秒杀价',
`stock_count` int(11) DEFAULT NULL COMMENT '库存数量',
`start_date` datetime DEFAULT NULL COMMENT '活动开始时间',
`end_date` datetime DEFAULT NULL COMMENT '结束时间',
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='秒杀商品表'
CREATE TABLE `order_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`goods_id` bigint(20) DEFAULT NULL COMMENT '商品id',
`delivery_addr_id` bigint(20) DEFAULT NULL COMMENT '收货地址id',
`goods_name` varchar(45) DEFAULT NULL COMMENT '冗余过来的商品名称',
`goods_count` int(11) DEFAULT '0' COMMENT '商品数量',
`goods_price` decimal(10,2) DEFAULT '0.00' COMMENT '商品单价',
`order_channel` tinyint(4) DEFAULT '0' COMMENT '购买商品渠道 1 :pc ,2:android,3:ios',
`status` tinyint(4) DEFAULT '0' COMMENT '订单状态 ,0:新建未支付,1:已支付,2:已发货,3:已收货,4:已退款,5:已完成',
`create_date` datetime DEFAULT NULL COMMENT '订单创建时间',
`pay_date` datetime DEFAULT NULL COMMENT '订单支付时间',
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='订单信息表'
CREATE TABLE `miaosha_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`order_id` bigint(20) DEFAULT NULL COMMENT '订单id',
`goods_id` bigint(20) DEFAULT NULL COMMENT '商品id',
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='秒杀订单'
建立DAO
GoodsDao.java
package miaosha.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import miaosha.dao.domain.MiaoshaGoods;
import miaosha.vo.goods.GoodsVo;
@Mapper
public interface GoodsDao {
/**
* 查询2张表的信息使用 GoodsVo来封装2张表的数据
* @return
*/
@Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id = g.id")
public List<GoodsVo> listGoodsVo();
/**
*
* @param goodsId
* @return
*/
@Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id = g.id where g.id = #{goodsId}")
public GoodsVo getGoodsVoByGoodsId(@Param("goodsId")long goodsId);
@Update("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId}")
public int reduceStock(MiaoshaGoods g);
}
OrderDao.java
package miaosha.dao;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectKey;
import miaosha.dao.domain.MiaoshaOrder;
import miaosha.dao.domain.OrderInfo;
@Mapper
public interface OrderDao {
@Select("select * from miaosha_order where user_id=#{userId} and goods_id=#{goodsId}")
public MiaoshaOrder getMiaoshaOrderByUserIdGoodsId(@Param("userId")long userId, @Param("goodsId")long goodsId);
@Insert("insert into order_info(user_id, goods_id, goods_name, goods_count, goods_price, order_channel, status, create_date)values("
+ "#{userId}, #{goodsId}, #{goodsName}, #{goodsCount}, #{goodsPrice}, #{orderChannel},#{status},#{createDate} )")
@SelectKey(keyColumn="id", keyProperty="id", resultType=long.class, before=false, statement="select last_insert_id()")
public long insert(OrderInfo orderInfo);
@Insert("insert into miaosha_order (user_id, goods_id, order_id)values(#{userId}, #{goodsId}, #{orderId})")
public int insertMiaoshaOrder(MiaoshaOrder miaoshaOrder);
}
建立Service
GoodsServiceImpl.java
package miaosha.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import miaosha.dao.GoodsDao;
import miaosha.dao.domain.MiaoshaGoods;
import miaosha.service.GoodsService;
import miaosha.vo.goods.GoodsVo;
@Service
public class GoodsServiceImpl implements GoodsService {
@Autowired
GoodsDao goodsDao ;
public List<GoodsVo> listGoods() {
return goodsDao.listGoodsVo();
}
public GoodsVo getGoodsVoByGoodsId(Long goodsId) {
return goodsDao.getGoodsVoByGoodsId(goodsId);
}
public void reduceStock(GoodsVo goods) {
MiaoshaGoods g = new MiaoshaGoods();
g.setGoodsId(goods.getId());
goodsDao.reduceStock(g);
}
}
OrderServiceImpl.java
package miaosha.service.impl;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import miaosha.dao.OrderDao;
import miaosha.dao.domain.MiaoshaOrder;
import miaosha.dao.domain.OrderInfo;
import miaosha.dao.domain.User;
import miaosha.service.OrderService;
import miaosha.vo.goods.GoodsVo;
/**
* 订单服务
* @author kaifeng1
*
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
OrderDao orderDao;
public MiaoshaOrder getMiaoshaOrderByUserIdGoodsId(Long userId, Long goodsId) {
return orderDao.getMiaoshaOrderByUserIdGoodsId(userId, goodsId);
}
@Transactional
public OrderInfo createOrder(User user, GoodsVo goods) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setCreateDate(new Date());
orderInfo.setDeliveryAddrId(0L);
orderInfo.setGoodsCount(1);
orderInfo.setGoodsId(goods.getId());
orderInfo.setGoodsName(goods.getGoodsName());
orderInfo.setGoodsPrice(goods.getMiaoshaPrice());
orderInfo.setOrderChannel(1);
orderInfo.setStatus(0);
orderInfo.setUserId(user.getId());
Long orderId = orderDao.insert(orderInfo);
MiaoshaOrder miaoshaOrder = new MiaoshaOrder();
miaoshaOrder.setGoodsId(goods.getId());
miaoshaOrder.setOrderId(orderId);
miaoshaOrder.setUserId(user.getId());
orderDao.insertMiaoshaOrder(miaoshaOrder);
return orderInfo;
}
}
MiaoshaServiceImpl.java
package miaosha.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import miaosha.dao.domain.OrderInfo;
import miaosha.dao.domain.User;
import miaosha.service.GoodsService;
import miaosha.service.MiaoshaService;
import miaosha.service.OrderService;
import miaosha.vo.goods.GoodsVo;
/**
* 秒杀服务
* @author kaifeng1
*
*/
@Service
public class MiaoshaServiceImpl implements MiaoshaService {
@Autowired
GoodsService goodsService;
@Autowired
OrderService orderService;
public OrderInfo miaosha(User user, GoodsVo goods) {
// 减库存 下订单 写入秒杀订单
goodsService.reduceStock(goods);
// order_info maiosha_order
return orderService.createOrder(user, goods);
}
}
Controller编写
GoodsController.java
package miaosha.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import miaosha.dao.domain.User;
import miaosha.service.GoodsService;
import miaosha.service.UserService;
import miaosha.util.ConstValue;
import miaosha.vo.goods.GoodsVo;
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private UserService userService ;
@Autowired
private GoodsService goodsService ;
/**
* 不用WebConfig 进行的参数的获取 User
* @param response
* @param model
* @param cookieUUid
* @param paramUuid
* @return
* @throws Exception
*/
@RequestMapping("/toListOldSample")
public String toListOldSample(HttpServletResponse response,Model model,@CookieValue(value=ConstValue.COOKI_NAME_TOKEN,required=false) String cookieUUid,@RequestParam(value=ConstValue.COOKI_NAME_TOKEN , required=false) String paramUuid) throws Exception {
String uuid = null ;
if(cookieUUid != null ) {
uuid = cookieUUid ;
} else if(paramUuid != null) {
uuid = paramUuid ;
} else {
//uuid丢失了重新到登录页
return "login";
}
User user = this.userService.getByToken(response, uuid);
model.addAttribute("user", user);
return "goods_list";
}
/**
* 使用WebConfig,我们做了这个UserArgumentResolver之后的简洁处理
* @param model
* @param user
* @return
*/
@RequestMapping("/toList")
public String toList(Model model , User user) {
if(user == null) {
return "login";
}
model.addAttribute("user", user);
//查询商品信息
List<GoodsVo> goodsList = goodsService.listGoods();
model.addAttribute("goodsList", goodsList);
return "goods_list";
}
@RequestMapping("/to_detail/{goodsId}")
public String detail(Model model,User user,
@PathVariable("goodsId")Long goodsId) {
model.addAttribute("user", user);
GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
model.addAttribute("goods", goods);
long startAt = goods.getStartDate().getTime();
long endAt = goods.getEndDate().getTime();
long now = System.currentTimeMillis();
int miaoshaStatus = 0;
int remainSeconds = 0;
if(now < startAt ) {//秒杀还没开始,倒计时
miaoshaStatus = 0;
//单位是毫秒
remainSeconds = (int)((startAt - now )/1000);
}else if(now > endAt){//秒杀已经结束
miaoshaStatus = 2;
remainSeconds = -1;
}else {//秒杀进行中
miaoshaStatus = 1;
remainSeconds = 0;
}
model.addAttribute("miaoshaStatus", miaoshaStatus);
model.addAttribute("remainSeconds", remainSeconds);
return "goods_detail";
}
}
这里我们主要先压测这个
toList方法