一、思路:减少数据库访问
1.系统初始化,把商品库存数量加载到Redis
2.收到请求,Redis预减库存,库存不足,直接返回,否则进入3
3.请求入队,立即返回排队中
4.请求出队,生成订单,减少库存
5.客户端轮询,是否秒杀成功
二、安装RabbitMQ及其相关依赖
下载erlang
https://www.erlang.org/downloads
下载rabbitMQ
http://www.rabbitmq.com/download.html
安装相关依赖
yum install ncurses-devel
tar xf otp_src_21.0.tar.gz
cd otp_src_21.0
./configure --prefix=/usr/local/erlang21 --without-javac
make -j 4
make install
验证安装是否成功
yum install python -y
yum install xmlto -y
yum install python-simplejson -y
xz -d rabbitmq-server-generic-unix-3.7.7.tar.xz
tar xf rabbitmq-server-generic-unix-3.7.7.tar
mv rabbitmq_server-3.7.7 /usr/local/rabbitmq
vim /etc/profile
在最后一行添加 export PATH=$PATH:/usr/local/erlang21/bin:/usr/local/rabbitmq/sbin
source /etc/profile
为了使guest用户让远程也可以访问,需要加入以下配置文件及内容
vim /usr/local/rabbitmq/etc/rabbitmq/rabbitmq.config
[{rabbit, [{loopback_users, []}]}].
重启rabbitmq使其生效。
三、SpringBoot集成RabbitMQ
pom文件引入依赖
org.springframework.boot
spring-boot-starter-amqp
application.properties添加相关配置
#rabbitmq
spring.rabbitmq.host=120.78.235.152
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
#\u6D88\u8D39\u8005\u6570\u91CF
spring.rabbitmq.listener.simple.concurrency= 10
spring.rabbitmq.listener.simple.max-concurrency= 10
#\u6D88\u8D39\u8005\u6BCF\u6B21\u4ECE\u961F\u5217\u83B7\u53D6\u7684\u6D88\u606F\u6570\u91CF
spring.rabbitmq.listener.simple.prefetch= 1
#\u6D88\u8D39\u8005\u81EA\u52A8\u542F\u52A8
spring.rabbitmq.listener.simple.auto-startup=true
#\u6D88\u8D39\u5931\u8D25\uFF0C\u81EA\u52A8\u91CD\u65B0\u5165\u961F
spring.rabbitmq.listener.simple.default-requeue-rejected= true
#\u542F\u7528\u53D1\u9001\u91CD\u8BD5
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=1000
spring.rabbitmq.template.retry.max-attempts=3
spring.rabbitmq.template.retry.max-interval=10000
spring.rabbitmq.template.retry.multiplier=1.0
添加配置类
package com.wings.seckill.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MQConfig {
public static final String QUEUE = "queue";
@Bean
public Queue queue(){
return new Queue(QUEUE, true);
}
}
发送者
package com.wings.seckill.rabbitmq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wings.seckill.config.MQConfig;
import com.wings.seckill.redis.RedisService;
@Service
public class MQSender {
private Logger logger = LoggerFactory.getLogger(MQSender.class);
@Autowired
private RedisService redisService;
@Autowired
private AmqpTemplate amqpTemplate;
/**
* Direct 模式交换机
* @param obj
*/
public void send(Object obj){
String msg = redisService.beanToString(obj);
logger.info("sender send:" + msg);
amqpTemplate.convertAndSend(MQConfig.QUEUE, msg);
}
}
接收者
package com.wings.seckill.rabbitmq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import com.wings.seckill.config.MQConfig;
@Service
public class MQReceiver {
private Logger logger = LoggerFactory.getLogger(MQReceiver.class);
@RabbitListener(queues = MQConfig.QUEUE)
public void receive(String msg){
logger.info("receive:" + msg);
}
}
DemoController添加以下测试方法
@RequestMapping("/mq")
@ResponseBody
public Result mq() {
mqSender.send("Wings you're the hero,路飞是成为海贼王的男人!");
return Result.success(true);
}
结果如下
2018-07-22 07:46:58.224 INFO 8624 --- [nio-8080-exec-6] com.wings.seckill.rabbitmq.MQSender : sender send:Wings you're the hero,路飞是成为海贼王的男人!
2018-07-22 07:46:58.235 INFO 8624 --- [cTaskExecutor-1] com.wings.seckill.rabbitmq.MQReceiver : receive:Wings you're the hero,路飞是成为海贼王的男人!
topic模式
配置类添加以下配置方法
public static final String TOPIC_QUEUE1 = "topic.queue1";
public static final String TOPIC_QUEUE2 = "topic.queue2";
public static final String TOPIC_EXCHANGE = "topicExchage";;
@Bean
public Queue topQueue1(){
return new Queue(TOPIC_QUEUE1, true);
}
@Bean
public Queue topQueue2(){
return new Queue(TOPIC_QUEUE2, true);
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange(TOPIC_EXCHANGE);
}
@Bean
public Binding topicBind1(){
return BindingBuilder.bind(topQueue1()).to(topicExchange()).with("topic.key1");
}
@Bean
public Binding topicBind2(){
return BindingBuilder.bind(topQueue2()).to(topicExchange()).with("topic.#");
}
发送者添加以下方法
public void sendTopic(Object message) {
String msg = redisService.beanToString(message);
logger.info("send topic message:" + msg);
amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key1", msg + "1");
amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key2", msg + "2");
}
接受者添加以下方法
@RabbitListener(queues = MQConfig.TOPIC_QUEUE1)
public void receiveTopic1(String msg){
logger.info("receiveTopic1:" + msg);
}
@RabbitListener(queues = MQConfig.TOPIC_QUEUE2)
public void receiveTopic2(String msg){
logger.info("receiveTopic2:" + msg);
}
结果如下:
fanout模式(广播模式)
配置类添加以下配置方法
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange(FANOUT_EXCHANGE);
}
@Bean
public Binding fanoutBind1(){
return BindingBuilder.bind(topQueue1()).to(fanoutExchange());
}
@Bean
public Binding fanoutBind2(){
return BindingBuilder.bind(topQueue2()).to(fanoutExchange());
}
发送者添加以下方法
public void sendFanout(Object message) {
String msg = redisService.beanToString(message);
logger.info("send fanout message:" + msg);
amqpTemplate.convertAndSend(MQConfig.FANOUT_EXCHANGE, "", msg);
}
结果如下:
配置类添加以下配置方法
@Bean
public Queue headersQueue(){
return new Queue(HEADERS_QUEUE, true);
}
@Bean
public HeadersExchange headersExchange(){
return new HeadersExchange(HEADERS_EXCHANGE);
}
@Bean
public Binding headerBind(){
Map map = new HashMap();
map.put("header1", "value1");
map.put("header2", "value2");
return BindingBuilder.bind(headersQueue()).to(headersExchange()).whereAll(map).match();
}
headers模式
发送者添加以下方法
public void sendHeaders(Object message) {
String msg = redisService.beanToString(message);
logger.info("send sendHeaders message:" + msg);
MessageProperties props = new MessageProperties();
props.setHeader("header1", "value1");
props.setHeader("header2", "value2");
Message obj = new Message(msg.getBytes(), props);
amqpTemplate.convertAndSend(MQConfig.HEADERS_EXCHANGE, "", obj);
}
接收者添加以下方法
@RabbitListener(queues = MQConfig.HEADERS_QUEUE)
public void receiveHeaders(byte[] msg){
logger.info("receiveHeaders:" + new String(msg));
}
结果如下:
package com.wings.seckill.controller;
import java.util.HashMap;
import java.util.List;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.wings.seckill.domain.SeckillOrder;
import com.wings.seckill.domain.SeckillUser;
import com.wings.seckill.rabbitmq.MQSender;
import com.wings.seckill.rabbitmq.SeckillMessage;
import com.wings.seckill.redis.GoodsKey;
import com.wings.seckill.redis.OrderKey;
import com.wings.seckill.redis.RedisService;
import com.wings.seckill.redis.SeckillKey;
import com.wings.seckill.result.CodeMsg;
import com.wings.seckill.result.Result;
import com.wings.seckill.service.GoodsService;
import com.wings.seckill.service.OrderService;
import com.wings.seckill.service.SeckillService;
import com.wings.seckill.service.SeckillUserService;
import com.wings.seckill.vo.GoodsVo;
@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean{
@Autowired
SeckillUserService userService;
@Autowired
RedisService redisService;
@Autowired
GoodsService goodsService;
@Autowired
OrderService orderService;
@Autowired
SeckillService seckillService;
@Autowired
MQSender sender;
private HashMap localOverMap = new HashMap();
@Override
public void afterPropertiesSet() throws Exception {
List goodsList = goodsService.listGoodsVo();
if(goodsList == null) {
return;
}
for(GoodsVo goods : goodsList) {
redisService.set(GoodsKey.getSeckillGoodsStock, ""+goods.getId(), goods.getStockCount());
localOverMap.put(goods.getId(), false);
}
}
@RequestMapping(value = "/do_seckill", method = RequestMethod.POST)
@ResponseBody
public Result list(Model model, SeckillUser user, @RequestParam("goodsId") long goodsId) {
model.addAttribute("user", user);
if(user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
//内存标记,减少redis访问
boolean over = localOverMap.get(goodsId);
if(over) {
return Result.error(CodeMsg.SECKill_OVER);
}
//预减库存
long stock = redisService.decr(GoodsKey.getSeckillGoodsStock, ""+goodsId);
if(stock < 0) {
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.SECKill_OVER);
}
//判断是否已经秒杀到了
SeckillOrder order = orderService.getSeckillOrderByUserIdGoodsId(user.getId(), goodsId);
if(order != null) {
return Result.error(CodeMsg.REPEATE_SECKILL);
}
//入队
SeckillMessage mm = new SeckillMessage();
mm.setUser(user);
mm.setGoodsId(goodsId);
sender.sendSeckillMessage(mm);
return Result.success(0);//排队中
}
@RequestMapping(value="/reset", method=RequestMethod.GET)
@ResponseBody
public Result reset(Model model) {
List goodsList = goodsService.listGoodsVo();
for(GoodsVo goods : goodsList) {
goods.setStockCount(10);
redisService.set(GoodsKey.getSeckillGoodsStock, ""+goods.getId(), 10);
localOverMap.put(goods.getId(), false);
}
redisService.delete(OrderKey.getSeckillOrderByUidGid);
redisService.delete(SeckillKey.isGoodsOver);
seckillService.reset(goodsList);
return Result.success(true);
}
/**
* orderId:成功
* -1:秒杀失败
* 0: 排队中
* */
@RequestMapping(value="/result", method=RequestMethod.GET)
@ResponseBody
public Result seckillResult(Model model,SeckillUser user,
@RequestParam("goodsId")long goodsId) {
model.addAttribute("user", user);
if(user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
long result =seckillService.getSeckillResult(user.getId(), goodsId);
return Result.success(result);
}
}
package com.wings.seckill.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.wings.seckill.domain.OrderInfo;
import com.wings.seckill.domain.SeckillOrder;
import com.wings.seckill.domain.SeckillUser;
import com.wings.seckill.redis.RedisService;
import com.wings.seckill.redis.SeckillKey;
import com.wings.seckill.vo.GoodsVo;
@Service
public class SeckillService {
@Autowired
GoodsService goodsService;
@Autowired
OrderService orderService;
@Autowired
RedisService redisService;
@Transactional
public OrderInfo seckill(SeckillUser user, GoodsVo goods) {
//减库存 下订单 写入秒杀订单
boolean success = goodsService.reduceStock(goods);
if(success) {
//order_info maiosha_order
return orderService.createOrder(user, goods);
}else {
setGoodsOver(goods.getId());
return null;
}
}
public long getSeckillResult(Long userId, long goodsId) {
SeckillOrder order = orderService.getSeckillOrderByUserIdGoodsId(userId, goodsId);
if(order != null) {//秒杀成功
return order.getOrderId();
}else {
boolean isOver = getGoodsOver(goodsId);
if(isOver) {
return -1;
}else {
return 0;
}
}
}
private void setGoodsOver(Long goodsId) {
redisService.set(SeckillKey.isGoodsOver, ""+goodsId, true);
}
private boolean getGoodsOver(long goodsId) {
return redisService.exists(SeckillKey.isGoodsOver, ""+goodsId);
}
public void reset(List goodsList) {
goodsService.resetStock(goodsList);
orderService.deleteOrders();
}
}
package com.wings.seckill.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wings.seckill.dao.GoodsDao;
import com.wings.seckill.domain.SeckillGoods;
import com.wings.seckill.vo.GoodsVo;
@Service
public class GoodsService {
@Autowired
GoodsDao goodsDao;
public List listGoodsVo(){
return goodsDao.listGoodsVo();
}
public GoodsVo getGoodsVoByGoodsId(long goodsId) {
return goodsDao.getGoodsVoByGoodsId(goodsId);
}
public boolean reduceStock(GoodsVo goods) {
SeckillGoods g = new SeckillGoods();
g.setGoodsId(goods.getId());
int ret = goodsDao.reduceStock(g);
return ret > 0;
}
public void resetStock(List goodsList) {
for(GoodsVo goods : goodsList ) {
SeckillGoods g = new SeckillGoods();
g.setGoodsId(goods.getId());
g.setStockCount(goods.getStockCount());
goodsDao.resetStock(g);
}
}
}
package com.wings.seckill.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 com.wings.seckill.domain.SeckillGoods;
import com.wings.seckill.vo.GoodsVo;
@Mapper
public interface GoodsDao {
@Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.seckill_price from seckill_goods mg left join goods g on mg.goods_id = g.id")
public List listGoodsVo();
@Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.seckill_price from seckill_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 seckill_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0")
public int reduceStock(SeckillGoods g);
@Update("update seckill_goods set stock_count = #{stockCount} where goods_id = #{goodsId}")
public int resetStock(SeckillGoods g);
}
package com.wings.seckill.service;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.wings.seckill.dao.OrderDao;
import com.wings.seckill.domain.OrderInfo;
import com.wings.seckill.domain.SeckillOrder;
import com.wings.seckill.domain.SeckillUser;
import com.wings.seckill.redis.OrderKey;
import com.wings.seckill.redis.RedisService;
import com.wings.seckill.vo.GoodsVo;
@Service
public class OrderService {
@Autowired
OrderDao orderDao;
@Autowired
RedisService redisService;
public SeckillOrder getSeckillOrderByUserIdGoodsId(long userId, long goodsId) {
return redisService.get(OrderKey.getSeckillOrderByUidGid, ""+userId+"_"+goodsId, SeckillOrder.class);
}
public OrderInfo getOrderById(long orderId) {
return orderDao.getOrderById(orderId);
}
@Transactional
public OrderInfo createOrder(SeckillUser 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.getSeckillPrice());
orderInfo.setOrderChannel(1);
orderInfo.setStatus(0);
orderInfo.setUserId(user.getId());
orderDao.insert(orderInfo);
SeckillOrder seckillOrder = new SeckillOrder();
seckillOrder.setGoodsId(goods.getId());
seckillOrder.setOrderId(orderInfo.getId());
seckillOrder.setUserId(user.getId());
orderDao.insertSeckillOrder(seckillOrder);
redisService.set(OrderKey.getSeckillOrderByUidGid, ""+user.getId()+"_"+goods.getId(), seckillOrder);
return orderInfo;
}
public void deleteOrders() {
orderDao.deleteOrders();
orderDao.deleteSeckillOrders();
}
}
package com.wings.seckill.dao;
import org.apache.ibatis.annotations.Delete;
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 com.wings.seckill.domain.OrderInfo;
import com.wings.seckill.domain.SeckillOrder;
@Mapper
public interface OrderDao {
@Select("select * from seckill_order where user_id=#{userId} and goods_id=#{goodsId}")
public SeckillOrder getSeckillOrderByUserIdGoodsId(@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 seckill_order (user_id, goods_id, order_id)values(#{userId}, #{goodsId}, #{orderId})")
public int insertSeckillOrder(SeckillOrder seckillOrder);
@Select("select * from order_info where id = #{orderId}")
public OrderInfo getOrderById(@Param("orderId") long orderId);
@Delete("delete from order_info")
public void deleteOrders();
@Delete("delete from seckill_order")
public void deleteSeckillOrders();
}
package com.wings.seckill.redis;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
@Service
public class RedisService {
@Autowired
JedisPool jedisPool;
/**
* 获取当个对象
* */
public T get(KeyPrefix prefix, String key, Class clazz) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
String str = jedis.get(realKey);
T t = stringToBean(str, clazz);
return t;
}finally {
returnToPool(jedis);
}
}
/**
* 设置对象
* */
public boolean set(KeyPrefix prefix, String key, T value) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String str = beanToString(value);
if(str == null || str.length() <= 0) {
return false;
}
//生成真正的key
String realKey = prefix.getPrefix() + key;
int seconds = prefix.expireSeconds();
if(seconds <= 0) {
jedis.set(realKey, str);
}else {
jedis.setex(realKey, seconds, str);
}
return true;
}finally {
returnToPool(jedis);
}
}
/**
* 判断key是否存在
* */
public boolean exists(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.exists(realKey);
}finally {
returnToPool(jedis);
}
}
/**
* 删除
* */
public boolean delete(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
long ret = jedis.del(realKey);
return ret > 0;
}finally {
returnToPool(jedis);
}
}
/**
* 增加值
* */
public Long incr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.incr(realKey);
}finally {
returnToPool(jedis);
}
}
/**
* 减少值
* */
public Long decr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.decr(realKey);
}finally {
returnToPool(jedis);
}
}
public boolean delete(KeyPrefix prefix) {
if(prefix == null) {
return false;
}
List keys = scanKeys(prefix.getPrefix());
if(keys==null || keys.size() <= 0) {
return true;
}
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
jedis.del(keys.toArray(new String[0]));
return true;
} catch (final Exception e) {
e.printStackTrace();
return false;
} finally {
if(jedis != null) {
jedis.close();
}
}
}
public List scanKeys(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
List keys = new ArrayList();
String cursor = "0";
ScanParams sp = new ScanParams();
sp.match("*"+key+"*");
sp.count(100);
do{
ScanResult ret = jedis.scan(cursor, sp);
List result = ret.getResult();
if(result!=null && result.size() > 0){
keys.addAll(result);
}
//再处理cursor
cursor = ret.getStringCursor();
}while(!cursor.equals("0"));
return keys;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public static String beanToString(T value) {
if(value == null) {
return null;
}
Class> clazz = value.getClass();
if(clazz == int.class || clazz == Integer.class) {
return ""+value;
}else if(clazz == String.class) {
return (String)value;
}else if(clazz == long.class || clazz == Long.class) {
return ""+value;
}else {
return JSON.toJSONString(value);
}
}
@SuppressWarnings("unchecked")
public static T stringToBean(String str, Class clazz) {
if(str == null || str.length() <= 0 || clazz == null) {
return null;
}
if(clazz == int.class || clazz == Integer.class) {
return (T)Integer.valueOf(str);
}else if(clazz == String.class) {
return (T)str;
}else if(clazz == long.class || clazz == Long.class) {
return (T)Long.valueOf(str);
}else {
return JSON.toJavaObject(JSON.parseObject(str), clazz);
}
}
private void returnToPool(Jedis jedis) {
if(jedis != null) {
jedis.close();
}
}
}
package com.wings.seckill.rabbitmq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wings.seckill.config.MQConfig;
import com.wings.seckill.domain.SeckillOrder;
import com.wings.seckill.domain.SeckillUser;
import com.wings.seckill.redis.RedisService;
import com.wings.seckill.service.GoodsService;
import com.wings.seckill.service.OrderService;
import com.wings.seckill.service.SeckillService;
import com.wings.seckill.vo.GoodsVo;
@Service
public class MQReceiver {
private Logger logger = LoggerFactory.getLogger(MQReceiver.class);
@Autowired
RedisService redisService;
@Autowired
GoodsService goodsService;
@Autowired
OrderService orderService;
@Autowired
SeckillService seckillService;
@RabbitListener(queues=MQConfig.SECKILL_QUEUE)
public void receiveSeckill(String message) {
logger.info("receive message:"+message);
SeckillMessage mm = redisService.stringToBean(message, SeckillMessage.class);
SeckillUser user = mm.getUser();
long goodsId = mm.getGoodsId();
GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
int stock = goods.getStockCount();
if(stock <= 0) {
return;
}
//判断是否已经秒杀到了
SeckillOrder order = orderService.getSeckillOrderByUserIdGoodsId(user.getId(), goodsId);
if(order != null) {
return;
}
//减库存 下订单 写入秒杀订单
seckillService.seckill(user, goods);
}
@RabbitListener(queues = MQConfig.QUEUE)
public void receive(String msg){
logger.info("receive:" + msg);
}
@RabbitListener(queues = MQConfig.TOPIC_QUEUE1)
public void receiveTopic1(String msg){
logger.info("receiveTopic1:" + msg);
}
@RabbitListener(queues = MQConfig.TOPIC_QUEUE2)
public void receiveTopic2(String msg){
logger.info("receiveTopic2:" + msg);
}
@RabbitListener(queues = MQConfig.HEADERS_QUEUE)
public void receiveHeaders(byte[] msg){
logger.info("receiveHeaders:" + new String(msg));
}
}
package com.wings.seckill.rabbitmq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wings.seckill.config.MQConfig;
import com.wings.seckill.redis.RedisService;
@Service
public class MQSender {
private Logger logger = LoggerFactory.getLogger(MQSender.class);
@Autowired
private RedisService redisService;
@Autowired
private AmqpTemplate amqpTemplate;
public void sendSeckillMessage(SeckillMessage mm) {
String msg = redisService.beanToString(mm);
logger.info("send message:"+msg);
amqpTemplate.convertAndSend(MQConfig.SECKILL_QUEUE, msg);
}
/**
* Direct 模式交换机
*
* @param obj
*/
public void send(Object obj) {
String msg = redisService.beanToString(obj);
logger.info("sender send:" + msg);
amqpTemplate.convertAndSend(MQConfig.QUEUE, msg);
}
public void sendTopic(Object message) {
String msg = redisService.beanToString(message);
logger.info("send topic message:" + msg);
amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key1", msg + "1");
amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key2", msg + "2");
}
public void sendFanout(Object message) {
String msg = redisService.beanToString(message);
logger.info("send fanout message:" + msg);
amqpTemplate.convertAndSend(MQConfig.FANOUT_EXCHANGE, "", msg);
}
public void sendHeaders(Object message) {
String msg = redisService.beanToString(message);
logger.info("send sendHeaders message:" + msg);
MessageProperties props = new MessageProperties();
props.setHeader("header1", "value1");
props.setHeader("header2", "value2");
Message obj = new Message(msg.getBytes(), props);
amqpTemplate.convertAndSend(MQConfig.HEADERS_EXCHANGE, "", obj);
}
}
商品详情
秒杀商品详情
您还没有登录,请登陆后再操作
没有收货地址的提示。。。
商品名称
商品图片
秒杀开始时间
商品原价
秒杀价
库存数量
最终优化效果如下:
比原来提高了3倍QPS!