Java秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化)
转自: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+接口优化)的更多相关文章
- 【高并发】Redis如何助力高并发秒杀系统,看完这篇我彻底懂了!!
写在前面 之前,我们在<[高并发]高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!>一文中,详细讲解了高并发秒杀系统的架构设计,其中,我们介绍了可以使用Redis存储秒杀商品的库存数量.很 ...
- Java生鲜电商平台-SpringCloud微服务架构高并发参数优化实战
Java生鲜电商平台-SpringCloud微服务架构高并发参数优化实战 一.写在前面 在Java生鲜电商平台平台中相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这 ...
- Java高并发秒杀系统【观后总结】
项目简介 在慕课网上发现了一个JavaWeb项目,内容讲的是高并发秒杀,觉得挺有意思的,就进去学习了一番. 记录在该项目中学到了什么玩意.. 该项目源码对应的gitHub地址(由观看其视频的人编写,并 ...
- Java高并发秒杀API之业务分析与DAO层
根据慕课网上关于java高并发秒杀API的课程讲解用maven+ssm+redis实现的一个秒杀系统 参考了codingXiaxw's blog,很详细:http://codingxiaxw.cn/2 ...
- 2017.4.26 慕课网--Java 高并发秒杀API(一)
Java高并发秒杀API系列(一) -----------------业务分析及Dao层 第一章 课程介绍 1.1 内容介绍及业务分析 (1)课程内容 SSM框架的整合使用 秒杀类系统需求理解和实现 ...
- Java高并发秒杀系统API之SSM框架集成swagger与AdminLTE
初衷与整理描述 Java高并发秒杀系统API是来源于网上教程的一个Java项目,也是我接触Java的第一个项目.本来是一枚c#码农,公司计划部分业务转java,于是我利用业务时间自学Java才有了本文 ...
- Java高并发秒杀API之Service层
Java高并发秒杀API之Service层 第1章 秒杀业务接口设计与实现 1.1service层开发之前的说明 开始Service层的编码之前,我们首先需要进行Dao层编码之后的思考:在Dao层我们 ...
- imooc课程:Java高并发秒杀API 记录
Java高并发秒杀API之业务分析与DAO层 Java高并发秒杀API之Service层 Java高并发秒杀API之web层 Java高并发秒杀API之高并发优化 除了并发部分外的这个web开发的总结 ...
- Java进阶知识点:服务端高并发的基石 - NIO与Reactor AIO与Proactor
一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...
随机推荐
- 人人都可以写的一个Python可视化小程序,带你走进编程的世界
当年的PHP号称是最好的编程语言,今天的Python就是最简单的编程语言,一个小小的程序,寥寥几行代码,带你体验一下编程的乐趣. 最简单的编程语言 今天要介绍的小工具是Python环境安装好之后,自带 ...
- Flutter之ExpansionTile组件
ExpansionTile组件 ExpansionTile Widget就是一个可以展开闭合的组件,常用的属性有如下几个. title:闭合时显示的标题,这个部分经常使用Text Widget. le ...
- C#开发者必须知道的13件事情
1.开发流程 程序的Bug与瑕疵往往出现于开发流程当中.只要对工具善加利用,就有助于在你发布程序之前便将问题发现,或避开这些问题. 标准化代码书写 标准化代码书写可以使代码更加易于维护,尤其是在代码由 ...
- js延迟2秒执行事件
有时候,我们在做修改回显数据时,就需要默认触发一些事件,但是由于数据没有很快从服务器中取回,所以就有延迟执行js事件 setTimeout(function () { // 这里就是处理的事件 }, ...
- Reactor系列(三)创建Flux,Mono(续)
创建Mono 视频讲解:https://www.bilibili.com/video/av78944069/ FluxMonoTestCase.java package com.example.rea ...
- storm drpc分布式本地和远程调用模式讲解
一.drpc 的介绍 1.rpc RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. 2.drpc drp ...
- [github] 关于华为鸿蒙OS
English Docs | 中文文档 | Türkçe Dökümanlar HarmonyOS Ⅰ. 鸿蒙系统简介 鸿蒙系统(HarmonyOS),是第一款基于微内核的全场景分布式OS,是华为自主 ...
- 1、Ubuntu linux下同步windows火狐foxfire 浏览器收藏夹问题
最近在ubuntu系统中使用自带的firefox浏览器,发现有一些问题,比如登陆后,书签,历史记录等,原本在windows下同步的数据无法同步,添加书签的功能也无法使用. 经过查询资料后得知,unbu ...
- CodeBlocks 配置
CodeBlocks 配置 Code::Blocks 17.12 时间:2019.6 下载网址 http://www.codeblocks.org/downloads/26 ,这里选择的是 mingw ...
- Comet OJ Contest #15 D. 双十一特惠(困难版)
以 $d(x)$ 表示正整数 $x$ 的十进制表示的数位之和.熟知下列关于 $d(x)$ 的结论: $d(x) \equiv x \pmod{9}$.从而对于任意正整数列 $a_1, a_2, \do ...