转自:https://blog.csdn.net/qq_41305266/article/details/81146716

一、思路:减少数据库访问

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文件引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
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<Boolean> 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<String, Object> map = new HashMap<String, Object>();
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<Long, Boolean> localOverMap = new HashMap<Long, Boolean>();

@Override
public void afterPropertiesSet() throws Exception {
List<GoodsVo> 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<Integer> 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<Boolean> reset(Model model) {
List<GoodsVo> 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<Long> 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<GoodsVo> 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<GoodsVo> 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<GoodsVo> 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<GoodsVo> 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> T get(KeyPrefix prefix, String key, Class<T> 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 <T> 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 <T> 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 <T> 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 <T> 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<String> 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<String> scanKeys(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
List<String> keys = new ArrayList<String>();
String cursor = "0";
ScanParams sp = new ScanParams();
sp.match("*"+key+"*");
sp.count(100);
do{
ScanResult<String> ret = jedis.scan(cursor, sp);
List<String> 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 <T> 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> T stringToBean(String str, Class<T> 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);
}
}
<!DOCTYPE HTML>
<html >
<head>
<title>商品详情</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- jquery -->
<script type="text/javascript" src="/js/jquery.min.js"></script>
<!-- bootstrap -->
<link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css" />
<script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
<!-- jquery-validator -->
<script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script>
<script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script>
<!-- layer -->
<script type="text/javascript" src="/layer/layer.js"></script>
<!-- md5.js -->
<script type="text/javascript" src="/js/md5.min.js"></script>
<!-- common.js -->
<script type="text/javascript" src="/js/common.js"></script>
</head>
<body>

<div class="panel panel-default" >
<div class="panel-heading">秒杀商品详情</div>
<div class="panel-body">
<span id="userTip"> 您还没有登录,请登陆后再操作<br/></span>
<span>没有收货地址的提示。。。</span>
</div>
<table class="table" id="goodslist">
<tr>
<td>商品名称</td>
<td colspan="3" id="goodsName"></td>
</tr>
<tr>
<td>商品图片</td>
<td colspan="3"><img id="goodsImg" width="200" height="200" /></td>
</tr>
<tr>
<td>秒杀开始时间</td>
<td id="startTime"></td>
<td >
<input type="hidden" id="remainSeconds" />
<span id="seckillTip"></span>
</td>
<td>
<!--
<form id="seckillForm" method="post" action="/seckill/do_seckill">
<button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
<input type="hidden" name="goodsId" id="goodsId" />
</form>-->
<button class="btn btn-primary btn-block" type="button" id="buyButton"onclick="doSeckill()">立即秒杀</button>
<input type="hidden" name="goodsId" id="goodsId" />
</td>
</tr>
<tr>
<td>商品原价</td>
<td colspan="3" id="goodsPrice"></td>
</tr>
<tr>
<td>秒杀价</td>
<td colspan="3" id="seckillPrice"></td>
</tr>
<tr>
<td>库存数量</td>
<td colspan="3" id="stockCount"></td>
</tr>
</table>
</div>
</body>
<script>

function getSeckillResult(goodsId){
g_showLoading();
$.ajax({
url:"/seckill/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(){
getSeckillResult(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("客户端请求有误");
}
});
}

function doSeckill(){
$.ajax({
url:"/seckill/do_seckill",
type:"POST",
data:{
goodsId:$("#goodsId").val(),
},
success:function(data){
if(data.code == 0){
//window.location.href="/order_detail.htm?orderId="+data.data.id;
getSeckillResult($("#goodsId").val());
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.msg("客户端请求有误");
}
});

}

function render(detail){
var seckillStatus = detail.seckillStatus;
var remainSeconds = detail.remainSeconds;
var goods = detail.goods;
var user = detail.user;
if(user){
$("#userTip").hide();
}
$("#goodsName").text(goods.goodsName);
$("#goodsImg").attr("src", goods.goodsImg);
$("#startTime").text(new Date(goods.startDate).format("yyyy-MM-dd hh:mm:ss"));
$("#remainSeconds").val(remainSeconds);
$("#goodsId").val(goods.id);
$("#goodsPrice").text(goods.goodsPrice);
$("#seckillPrice").text(goods.seckillPrice);
$("#stockCount").text(goods.stockCount);
countDown();
}

$(function(){
//countDown();
getDetail();
});

function getDetail(){
var goodsId = g_getQueryString("goodsId");
$.ajax({
url:"/goods/detail/"+goodsId,
type:"GET",
success:function(data){
if(data.code == 0){
render(data.data);
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.msg("客户端请求有误");
}
});
}

function countDown(){
var remainSeconds = $("#remainSeconds").val();
var timeout;
if(remainSeconds > 0){//秒杀还没开始,倒计时
$("#buyButton").attr("disabled", true);
$("#seckillTip").html("秒杀倒计时:"+remainSeconds+"秒");
timeout = setTimeout(function(){
$("#countDown").text(remainSeconds - 1);
$("#remainSeconds").val(remainSeconds - 1);
countDown();
},1000);
}else if(remainSeconds == 0){//秒杀进行中
$("#buyButton").attr("disabled", false);
if(timeout){
clearTimeout(timeout);
}
$("#seckillTip").html("秒杀进行中");
}else{//秒杀已经结束
$("#buyButton").attr("disabled", true);
$("#seckillTip").html("秒杀已经结束");
}
}

</script>
</html>
最终优化效果如下:

比原来提高了3倍QPS!

Java秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化)的更多相关文章

  1. 【高并发】Redis如何助力高并发秒杀系统,看完这篇我彻底懂了!!

    写在前面 之前,我们在<[高并发]高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!>一文中,详细讲解了高并发秒杀系统的架构设计,其中,我们介绍了可以使用Redis存储秒杀商品的库存数量.很 ...

  2. Java生鲜电商平台-SpringCloud微服务架构高并发参数优化实战

    Java生鲜电商平台-SpringCloud微服务架构高并发参数优化实战 一.写在前面 在Java生鲜电商平台平台中相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这 ...

  3. Java高并发秒杀系统【观后总结】

    项目简介 在慕课网上发现了一个JavaWeb项目,内容讲的是高并发秒杀,觉得挺有意思的,就进去学习了一番. 记录在该项目中学到了什么玩意.. 该项目源码对应的gitHub地址(由观看其视频的人编写,并 ...

  4. Java高并发秒杀API之业务分析与DAO层

    根据慕课网上关于java高并发秒杀API的课程讲解用maven+ssm+redis实现的一个秒杀系统 参考了codingXiaxw's blog,很详细:http://codingxiaxw.cn/2 ...

  5. 2017.4.26 慕课网--Java 高并发秒杀API(一)

    Java高并发秒杀API系列(一) -----------------业务分析及Dao层 第一章 课程介绍 1.1 内容介绍及业务分析 (1)课程内容 SSM框架的整合使用 秒杀类系统需求理解和实现 ...

  6. Java高并发秒杀系统API之SSM框架集成swagger与AdminLTE

    初衷与整理描述 Java高并发秒杀系统API是来源于网上教程的一个Java项目,也是我接触Java的第一个项目.本来是一枚c#码农,公司计划部分业务转java,于是我利用业务时间自学Java才有了本文 ...

  7. Java高并发秒杀API之Service层

    Java高并发秒杀API之Service层 第1章 秒杀业务接口设计与实现 1.1service层开发之前的说明 开始Service层的编码之前,我们首先需要进行Dao层编码之后的思考:在Dao层我们 ...

  8. imooc课程:Java高并发秒杀API 记录

    Java高并发秒杀API之业务分析与DAO层 Java高并发秒杀API之Service层 Java高并发秒杀API之web层 Java高并发秒杀API之高并发优化 除了并发部分外的这个web开发的总结 ...

  9. Java进阶知识点:服务端高并发的基石 - NIO与Reactor AIO与Proactor

    一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...

随机推荐

  1. 人人都可以写的一个Python可视化小程序,带你走进编程的世界

    当年的PHP号称是最好的编程语言,今天的Python就是最简单的编程语言,一个小小的程序,寥寥几行代码,带你体验一下编程的乐趣. 最简单的编程语言 今天要介绍的小工具是Python环境安装好之后,自带 ...

  2. Flutter之ExpansionTile组件

    ExpansionTile组件 ExpansionTile Widget就是一个可以展开闭合的组件,常用的属性有如下几个. title:闭合时显示的标题,这个部分经常使用Text Widget. le ...

  3. C#开发者必须知道的13件事情

    1.开发流程 程序的Bug与瑕疵往往出现于开发流程当中.只要对工具善加利用,就有助于在你发布程序之前便将问题发现,或避开这些问题. 标准化代码书写 标准化代码书写可以使代码更加易于维护,尤其是在代码由 ...

  4. js延迟2秒执行事件

    有时候,我们在做修改回显数据时,就需要默认触发一些事件,但是由于数据没有很快从服务器中取回,所以就有延迟执行js事件 setTimeout(function () { // 这里就是处理的事件 }, ...

  5. Reactor系列(三)创建Flux,Mono(续)

    创建Mono 视频讲解:https://www.bilibili.com/video/av78944069/ FluxMonoTestCase.java package com.example.rea ...

  6. storm drpc分布式本地和远程调用模式讲解

    一.drpc 的介绍 1.rpc RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. 2.drpc drp ...

  7. [github] 关于华为鸿蒙OS

    English Docs | 中文文档 | Türkçe Dökümanlar HarmonyOS Ⅰ. 鸿蒙系统简介 鸿蒙系统(HarmonyOS),是第一款基于微内核的全场景分布式OS,是华为自主 ...

  8. 1、Ubuntu linux下同步windows火狐foxfire 浏览器收藏夹问题

    最近在ubuntu系统中使用自带的firefox浏览器,发现有一些问题,比如登陆后,书签,历史记录等,原本在windows下同步的数据无法同步,添加书签的功能也无法使用. 经过查询资料后得知,unbu ...

  9. CodeBlocks 配置

    CodeBlocks 配置 Code::Blocks 17.12 时间:2019.6 下载网址 http://www.codeblocks.org/downloads/26 ,这里选择的是 mingw ...

  10. Comet OJ Contest #15 D. 双十一特惠(困难版)

    以 $d(x)$ 表示正整数 $x$ 的十进制表示的数位之和.熟知下列关于 $d(x)$ 的结论: $d(x) \equiv x \pmod{9}$.从而对于任意正整数列 $a_1, a_2, \do ...