zoukankan      html  css  js  c++  java
  • 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=/
    #u6D88u8D39u8005u6570u91CF
    spring.rabbitmq.listener.simple.concurrency= 10
    spring.rabbitmq.listener.simple.max-concurrency= 10
    #u6D88u8D39u8005u6BCFu6B21u4ECEu961Fu5217u83B7u53D6u7684u6D88u606Fu6570u91CF
    spring.rabbitmq.listener.simple.prefetch= 1
    #u6D88u8D39u8005u81EAu52A8u542Fu52A8
    spring.rabbitmq.listener.simple.auto-startup=true
    #u6D88u8D39u5931u8D25uFF0Cu81EAu52A8u91CDu65B0u5165u961F
    spring.rabbitmq.listener.simple.default-requeue-rejected= true
    #u542Fu7528u53D1u9001u91CDu8BD5
    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!

  • 相关阅读:
    如何把一个一般的git库变成“裸库”?
    MacOSX下杀掉sudo进程
    nginx FastCGI错误Primary script unknown解决办法
    Lua继承userdata
    Unity图文混排
    C++轻量级跨平台文件系统API
    lua_next()
    重载方法匹配算法
    C++模板函数只能全特化不能偏特化
    xcode离线安装包下载
  • 原文地址:https://www.cnblogs.com/sharpest/p/10960411.html
Copyright © 2011-2022 走看看