Java秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化)

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

1.系统初始化,把商品库存数量加载到Redis

2.收到请求,Redis预减库存,库存不足,直接返回,否则进入3

3.请求入队,立即返回排队中

4.请求出队,生成订单,减少库存

5.客户端轮询,是否秒杀成功

二、安装RabbitMQ及其相关依赖

下载erlang

https://www.erlang.org/downloadsJava秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化)_第1张图片

下载rabbitMQ

http://www.rabbitmq.com/download.html

Java秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化)_第2张图片

安装相关依赖

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);
	}

结果如下:

Java秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化)_第3张图片

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!

 

 

 

 

你可能感兴趣的:(Java秒杀实战 (六) 服务级高并发秒杀优化(RabbitMQ+接口优化))