最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot?+rabbitmq+redis實現(xiàn)秒殺示例

 更新時間:2022年07月01日 09:03:57   作者:yy1209357299  
本文主要介紹了springboot?+rabbitmq+redis實現(xiàn)秒殺示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

實現(xiàn)說明

這里的核心在于如何在大并發(fā)的情況下保證數據庫能扛得住壓力,因為大并發(fā)的瓶頸在于數據庫。如果用戶的請求直接從前端傳到數據庫,顯然,數據庫是無法承受幾十萬上百萬甚至上千萬的并發(fā)量的。因此,我們能做的只能是減少對數據庫的訪問。例如,前端發(fā)出了100萬個請求,通過我們的處理,最終只有10個會訪問數據庫,這樣就會大大提升系統(tǒng)性能。再針對秒殺這種場景,因為秒殺商品的數量是有限的,因此采用上述實現(xiàn)方案。

假如,某個商品可秒殺的數量是10,那么在秒殺活動開始之前,把商品的ID和數量加載到Redis緩存。當服務端收到請求時,首先預減Redis中的數量,如果數量減到小于0時,那么隨后的訪問直接返回秒殺失敗的信息。也就是說,最終只有10個請求會去訪問數據庫。

如果商品數量比較多,比如1萬件商品參與秒殺,那么就有1萬*10=10萬個請求并發(fā)去訪問數據庫,數據庫的壓力還是會很大。這里就用到了另外一個非常重要的組件:消息隊列。我們不是把請求直接去訪問數據庫,而是先把請求寫到消息隊列中,做一個緩存,然后再去慢慢的更新數據庫。這樣做之后,前端用戶的請求可能不會立即得到響應是成功還是失敗,很可能得到的是一個排隊中的返回值,這個時候,需要客戶端去服務端輪詢,因為我們不能保證一定就秒殺成功了。當服務端出隊,生成訂單以后,把用戶ID和商品ID寫到緩存中,來應對客戶端的輪詢就可以了。這樣處理以后,我們的應用是可以很簡單的進行分布式橫向擴展的,以應對更大的并發(fā)。當然,秒殺系統(tǒng)還有很多要處理的事情,比如限流防刷、分布式Session等等。

1、工具準備

rabbitmq安裝:http://m.fzitv.net/article/253706.htm

界面地址:http://localhost:15672/#/
用戶名 guest
密碼 guest

redis安裝:http://m.fzitv.net/article/145704.htm
jmeter安裝:http://m.fzitv.net/article/232152.htm

2、數據表

商品表

-- ----------------------------
-- Table structure for stock
-- ----------------------------
DROP TABLE IF EXISTS `stock`;
CREATE TABLE `stock` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名稱',
  `count` int(11) NOT NULL COMMENT '庫存',
  `sale` int(11) NOT NULL COMMENT '已售',
  `version` int(11) NOT NULL COMMENT '樂觀鎖,版本號',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

訂單表

-- ----------------------------
-- Table structure for stock_order
-- ----------------------------
DROP TABLE IF EXISTS `stock_order`;
CREATE TABLE `stock_order` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `sid` int(11) NOT NULL COMMENT '庫存ID',
  `name` varchar(30) NOT NULL DEFAULT '' COMMENT '商品名稱',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3、pom

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency>

 <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

4、代碼結構

5、配置config

mq配置

package com.yy.msserver.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author code
 * @Date 2022/6/27 14:03
 * Description rabbitmq config
 * Version 1.0
 */
@Configuration
public class MyRabbitMQConfig {

    //庫存交換機
    public static final String STORY_EXCHANGE = "STORY_EXCHANGE";

    //訂單交換機
    public static final String ORDER_EXCHANGE = "ORDER_EXCHANGE";

    //庫存隊列
    public static final String STORY_QUEUE = "STORY_QUEUE";

    //訂單隊列
    public static final String ORDER_QUEUE = "ORDER_QUEUE";

    //庫存路由鍵
    public static final String STORY_ROUTING_KEY = "STORY_ROUTING_KEY";

    //訂單路由鍵
    public static final String ORDER_ROUTING_KEY = "ORDER_ROUTING_KEY";
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
    //創(chuàng)建庫存交換機
    @Bean
    public Exchange getStoryExchange() {
        return ExchangeBuilder.directExchange(STORY_EXCHANGE).durable(true).build();
    }
    //創(chuàng)建庫存隊列
    @Bean
    public Queue getStoryQueue() {
        return new Queue(STORY_QUEUE);
    }
    //庫存交換機和庫存隊列綁定
    @Bean
    public Binding bindStory() {
        return BindingBuilder.bind(getStoryQueue()).to(getStoryExchange()).with(STORY_ROUTING_KEY).noargs();
    }
    //創(chuàng)建訂單隊列
    @Bean
    public Queue getOrderQueue() {
        return new Queue(ORDER_QUEUE);
    }
    //創(chuàng)建訂單交換機
    @Bean
    public Exchange getOrderExchange() {
        return ExchangeBuilder.directExchange(ORDER_EXCHANGE).durable(true).build();
    }
    //訂單隊列與訂單交換機進行綁定
    @Bean
    public Binding bindOrder() {
        return BindingBuilder.bind(getOrderQueue()).to(getOrderExchange()).with(ORDER_ROUTING_KEY).noargs();
    }
}

redis配置

package com.yy.msserver.config;

/**
 * @author code
 * @Date 2022/6/27 14:06
 * Description redis config
 * Version 1.0
 */
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
 * @author code
 *@Date 2022/6/27 14:05
 *Description redis config
 *Version 1.0
 */
@Configuration
public class RedisConfig {
    // 配置redis得配置詳解
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

6、訂單業(yè)務層

接口層

package com.yy.msserver.service;

import com.yy.msserver.model.vo.Stock;

/**
 * @author code
 * @Date 2022/6/24 9:25
 * Description 訂單接口
 * Version 1.0
 */
public interface StockOrderService {
    public Integer createOrder(Integer id);

    public void decrByStock(Integer id);
}

實現(xiàn)層

package com.yy.msserver.service.impl;

import com.yy.msserver.dao.StockMapper;
import com.yy.msserver.dao.StockOrderMapper;
import com.yy.msserver.model.vo.Stock;
import com.yy.msserver.model.vo.StockOrder;
import com.yy.msserver.service.StockOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * @author code
 * @Date 2022/6/24 9:25
 * Description 訂單實現(xiàn)
 * Version 1.0
 */
@Service
public class StockOrderServiceImpl implements StockOrderService {
    @Autowired
    private StockOrderMapper stockOrderMapper;

    @Autowired
    private StockMapper stockMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer createOrder(Integer id) {
        //校驗庫存
        Stock stock = checkStock(id);
//        if(stock.getCount()>0){
//            System.out.println("當前庫存:" + stock.getCount());
//            //扣庫存
//            if(updateSale(stock) == 1){
//
//            }else {
//                return 0;
//            }
//        }
//        return 0;
        return createOrder(stock);
    }

    @Override
    public void decrByStock(Integer id){
        //校驗庫存
        Stock stock = checkStock(id);
        if(stock.getCount()>0){
            System.out.println("當前庫存:" + stock.getCount());
            //扣庫存
            updateSale(stock);
        }
    }

    //校驗庫存
    private Stock checkStock(Integer id) {
        return stockMapper.checkStock(id);
    }

    //扣庫存
    private int updateSale(Stock stock){
        return stockMapper.updateSale(stock);
    }

    //下訂單
    private Integer createOrder(Stock stock){
        StockOrder order = new StockOrder();
        order.setSid(stock.getId());
        order.setCreateTime(new Date());
        order.setName(stock.getName());
        stockOrderMapper.createOrder(order);
        return order.getId();
    }
}

7、redis實現(xiàn)層

package com.yy.msserver.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author code
 * @Date 2022/6/27 17:25
 * Description redis
 * Version 1.0
 */
@Service
public class RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 設置String鍵值對
     * @param key
     * @param value
     * @param millis
     */
    public void put(String key, Object value, long millis) {
        redisTemplate.opsForValue().set(key, value, millis, TimeUnit.MINUTES);
    }
    public void putForHash(String objectKey, String hkey, String value) {
        redisTemplate.opsForHash().put(objectKey, hkey, value);
    }
    public <T> T get(String key, Class<T> type) {
        return (T) redisTemplate.boundValueOps(key).get();
    }
    public void remove(String key) {
        redisTemplate.delete(key);
    }
    public boolean expire(String key, long millis) {
        return redisTemplate.expire(key, millis, TimeUnit.MILLISECONDS);
    }
    public boolean persist(String key) {
        return redisTemplate.hasKey(key);
    }
    public String getString(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }
    public Integer getInteger(String key) {
        return (Integer) redisTemplate.opsForValue().get(key);
    }
    public Long getLong(String key) {
        return (Long) redisTemplate.opsForValue().get(key);
    }
    public Date getDate(String key) {
        return (Date) redisTemplate.opsForValue().get(key);
    }

    /**
     * 對指定key的鍵值減一
     * @param key
     * @return
     */
    public Long decrBy(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }
}

8、mq實現(xiàn)層

減庫存

package com.yy.msserver.service;

import com.yy.msserver.config.MyRabbitMQConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author code
 * @Date 2022/6/27 15:22
 * Description mq商品信息
 * Version 1.0
 */
@Slf4j
@Service
public class MQStockService {
    @Autowired
    private StockOrderService stockService;
    /**
     * 監(jiān)聽庫存消息隊列,并消費
     * @param id
     */
    @RabbitListener(queues = MyRabbitMQConfig.STORY_QUEUE)
    public void decrByStock(Integer id) {
        /**
         * 調用數據庫service給數據庫對應商品庫存減一
         */
        log.info("減庫存");
        stockService.decrByStock(id);
    }
}

下訂單

package com.yy.msserver.service;

import com.yy.msserver.config.MyRabbitMQConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author code
 * @Date 2022/6/27 15:19
 * Description mq 訂單隊列
 * Version 1.0
 */
@Service
@Slf4j
public class MQOrderService {
    @Autowired
    private StockOrderService orderService;
    /**
     * 監(jiān)聽訂單消息隊列,并消費
     *
     * @param id
     */
    @RabbitListener(queues = MyRabbitMQConfig.ORDER_QUEUE)
    public void createOrder(Integer id) {
        log.info("收到訂單消息");
        orderService.createOrder(id);
    }
}

9、redis模擬初始化庫存量

package com.yy.msserver.config;

import com.yy.msserver.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * @author code
 * @Date 2022/6/29 14:08
 * Description 初始化
 * Version 1.0
 */
@Component
public class InitConfig {
    @Autowired
    private RedisService redisService;
    /**
     * redis初始化商品的庫存量和信息
     * @param
     * @throws Exception
     */
    @PostConstruct
    public void init() {
        redisService.put("1", 10, 20);
    }
}

10、controller控制層

 /**
     * 使用redis+消息隊列進行秒殺實現(xiàn)
     *
     * @param id 商品id
     * @return
     */
    @GetMapping( value = "/sec",produces = "application/json;charset=utf-8")
    @ResponseBody
    public String sec(@RequestParam(value = "id") int id) {

        String message = null;
        //調用redis給相應商品庫存量減一
        Long decrByResult = redisService.decrBy(id+"");
        if (decrByResult >= 0) {
            /**
             * 說明該商品的庫存量有剩余,可以進行下訂單操作
             */
            //發(fā)消息給庫存消息隊列,將庫存數據減一
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.STORY_EXCHANGE, MyRabbitMQConfig.STORY_ROUTING_KEY, id);

            //發(fā)消息給訂單消息隊列,創(chuàng)建訂單
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EXCHANGE, MyRabbitMQConfig.ORDER_ROUTING_KEY, id);
            message = "商品" + id + "秒殺成功";
        } else {
            /**
             * 說明該商品的庫存量沒有剩余,直接返回秒殺失敗的消息給用戶
             */
            message ="商品" + id + "秒殺商品的庫存量沒有剩余,秒殺結束";
        }
        return message;
    }

11、測試

新建線程組——新建取樣器(http請求)——新建查看結果樹

12、測試結果

到此這篇關于springboot +rabbitmq+redis實現(xiàn)秒殺的文章就介紹到這了,更多相關springboot rabbitmq redis秒殺內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java實現(xiàn)手機號碼歸屬地查詢

    Java實現(xiàn)手機號碼歸屬地查詢

    這篇文章主要為大家詳細介紹了如何利用Java實現(xiàn)手機號碼歸屬地查詢功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-12-12
  • Java數據庫連接池之c3p0簡介_動力節(jié)點Java學院整理

    Java數據庫連接池之c3p0簡介_動力節(jié)點Java學院整理

    這篇文章主要為大家詳細介紹了Java數據庫連接池之c3p0簡介的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Mybatis plus 配置多數據源的實現(xiàn)示例

    Mybatis plus 配置多數據源的實現(xiàn)示例

    這篇文章主要介紹了Mybatis plus 配置多數據源的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • RabbitMQ的核心原理場景解析及具體應用

    RabbitMQ的核心原理場景解析及具體應用

    RabbitMQ是基于AMQP的開源消息中間件,通過靈活路由、可靠性保障和高可用特性,實現(xiàn)服務解耦、流量緩沖及異步通信,本文梳理RabbitMQ 的核心知識,并結合實戰(zhàn)場景解析其在項目中的具體應用,感興趣的朋友一起看看吧
    2025-08-08
  • Java中的FileInputStream 和 FileOutputStream 介紹_動力節(jié)點Java學院整理

    Java中的FileInputStream 和 FileOutputStream 介紹_動力節(jié)點Java學院整理

    FileInputStream 是文件輸入流,它繼承于InputStream。FileOutputStream 是文件輸出流,它繼承于OutputStream。接下來通過本文給大家介紹Java中的FileInputStream 和 FileOutputStream,需要的朋友可以參考下
    2017-05-05
  • SpringBoot配置默認HikariCP數據源

    SpringBoot配置默認HikariCP數據源

    咱們開發(fā)項目的過程中用到很多的開源數據庫鏈接池,比如druid、c3p0、BoneCP等等,本文主要介紹了SpringBoot配置默認HikariCP數據源,具有一定的參考價值,感興趣的可以了解一下
    2023-11-11
  • 解決Maven依賴沖突的方法

    解決Maven依賴沖突的方法

    本文主要介紹了解決Maven依賴沖突的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-05-05
  • SpringAOP如何獲取方法參數上的注解

    SpringAOP如何獲取方法參數上的注解

    這篇文章主要介紹了SpringAOP如何獲取方法參數上的注解操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Spring中事務幾個常見的問題解決

    Spring中事務幾個常見的問題解決

    這篇文章主要介紹了Spring中事務幾個常見的問題解決,事務這個概念是數據庫層面的,Spring只是基于數據庫中的事務進行擴展,以及提供了一些能讓程序員更新方便操作事務的方式
    2022-08-08
  • springboot響應json?null值過濾方式

    springboot響應json?null值過濾方式

    這篇文章主要介紹了springboot響應json?null值過濾方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評論

油尖旺区| 上思县| 桐城市| 庆城县| 色达县| 仙游县| 灵川县| 达州市| 长汀县| 寿阳县| 克什克腾旗| 冕宁县| 常德市| 杭锦后旗| 水富县| 浦城县| 诸城市| 通化市| 临潭县| 沈阳市| 叙永县| 蛟河市| 东源县| 金乡县| 楚雄市| 咸阳市| 抚宁县| 马鞍山市| 邵阳市| 谢通门县| 渝中区| 名山县| 保定市| 故城县| 闻喜县| 常熟市| 庆阳市| 潍坊市| 武定县| 普定县| 湖北省|