RabbitMQ實戰二(消峯限流)

Hello,我是一名在互聯網撿破爛的程序員,最近破爛還挺好撿的,天天都是東逛逛西逛逛,收了不少的破爛呢。。。java

收廢鐵了,十塊一斤。快拿來賣哦,什麼爛電冰箱,爛電視機,無論什麼破爛我都要。。。mysql

天天騎着個人爛三輪車,天天都是活的苟且偷生的,我好可憐。。。程序員

嗚嗚嗚嗚web

無論有錢木錢,都進來看一看瞧一瞧哦。。spring

好了~~~~sql

廢話咱們就很少說了,我最近在收廢鐵的時候收到一本武功祕籍,發現了新大陸,今天我就來和大家分享一下。數據庫

咱們一塊兒成爲武林霸主,來吧。氛圍搞起來。。。。。apache

首先呢,咱們在上一期中對 RabbitMQ作了一個小小的實踐,主要是對 RabbitMQ的異步特性進行了分析。windows

若是小夥伴尚未修煉的,趕忙去修煉去吧,咱們要慢慢的成長起來,要慢慢的修煉武功祕籍,不是一天兩天就能夠練成的,讓咱們一塊兒成爲世界的主宰吧,嗯哈哈哈哈哈api

RabbitMQ實踐一(異步解耦)

若是你仍是一個一個剛剛入門的小夥伴呢,那你的加緊你的步伐,趕快去從武功祕籍的第一頁開始吧,你也不要慌張,咱們都是從小白開始的,只不過你要多花一點時間來完成前面的修煉,終有一天你會超過的,哈哈哈
RabbitMQ基本使用一(簡單介紹)/#more)

RabbitMQ基本使用二(簡單隊列)

RabbitMQ基本使用三(工做隊列)

RabbitMQ基本使用四(發佈/訂閱模式)

RabbitMQ基本使用五(路由模式)

RabbitMQ基本使用六(主題模式)

RabbitMQ入門呢?就是上面的這麼多啦。本身根據官網來理解的知識點,寫的不是那麼很好,不過很通俗易懂。代碼有詳細介紹。若有問題,請留言,多多包涵。

好吧,咱們就廢話很少說了,那就開始咱們的表演吧!!!!

1、開篇前提

本文篇幅比較長,請耐心閱讀,你會收穫不少的。

今天給你們帶來的是 RabbitMQ的消峯限流,咱們知道如今互聯網愈來愈強大,咱們的系統也是愈來愈完善,在高峯期呢,系統將要承受巨大的壓力,那麼也是咱們程序員的壓力。像淘寶、京東大型網站購物系統每逢雙十一,那就是程序員最忙的時候,當天要承受千萬級別的流量衝擊,不得不抵住壓力啊。

想必你們都知道哦我要說什麼了吧,沒錯,就是你想的那樣,咱們就是要對這千萬級別的流量打交道。咱們要抵住流量的衝擊。讓它可以緩解咱們的系統的壓力,系統壓力小了,咱們自身的壓力就小了。OK

1. 場景介紹

  • 場景一

    咱們知道在咱們雙十一中都會有秒殺的商品,咱們所秒殺的商品價格都是很是低的,而且商品也是很是好,好比華爲Mate30只要999,、蘋果12只要99等等這些秒殺商品,不論是誰,看了都會心動。可是呢,商品的數量是有限的,不是每一個用戶都會搶到,全國14億用戶,就拿一半的人來搶,那個流量衝擊是咱們沒法想像的。那要是所有流量打在咱們的數據庫中,那就只有說再見。。涼涼,最後仍是咱們程序員扛下了全部,那麼咱們應該怎麼辦呢?

  • 場景二

相信大多數人都搶過火車票吧,確定有沒有搶到票的吧。好比在咱們的12306搶票網站,每次到必定的時間段都會有大量用戶涌入搶票,可能我會遇到過服務器忙、或者加載失敗等狀況。那麼在這麼大的流量下,咱們是怎麼抗住的呢?

2. 問題描述

在咱們面對瞬時流量的狀況下,所有的流量都打在咱們的數據庫中,那是很難受的。

那麼咱們應該怎麼來解決這種瞬時流量下的併發狀況?

在咱們秒殺中,庫存只有一份,全部人會集中在時間讀和寫寫這些數據,多人讀取同一個數據

3. 優化方案

咱們不管在搶票或者秒殺商品中時,爲何我沒有搶到,別人卻搶到了?

下面帶你打開這扇門

  • 咱們將請求儘可能攔截在系統的上游(不要讓鎖衝突到數據上)。爲何那些傳統的秒殺系統會掛,那是由於全部的請求都壓到了數據層上,致使數據讀取鎖衝突,併發的響應數據慢,巨幾乎都是全部請求超時。流量雖大,下單成功的有效流量去不多。若是咱們秒殺商品庫存有100件,那麼有1000W人來搶,那麼那時的瞬時流量很大,可是基本沒有人秒殺成功,緣由在於全部的流浪都打在咱們的數據層上,到時響應速度慢,請求的有效率爲0。那麼這是一次很失敗的秒殺活動。

    那用戶不得吐槽啊,還不得上熱搜啊,某某什麼秒殺系統,垃圾的很,沒人秒殺成功,都是請求超時,哎,溜了溜了

    那程序員就遭殃了啊,老闆還不得直接暴扣到頭上啊,那是很難受的,薪水都要大打折扣。

  • 充分利用緩存來實現這種瞬時流量大的狀況,秒殺買票,這就是一個典型的讀多寫少的應用場景,大部分請求是車次的查詢,票查詢,下單和支付纔是寫請求。一趟火車其實只有2000張票,200W人來買,最多隻有2000人下單成功,其餘人都是查詢庫存,寫比例只有0.1%,讀的比例佔99.9%,很是適合用緩存來優化。

這下知道爲何有時候搶不到商品了吧

這樣的設計就是爲了犧牲用戶流量來換系統穩定

4. 架構

image
這個架構我是根據本身的理解來的,可能不怎麼符合理念,知道大概意思就是了哈

OK,說了這麼多,咱們就開始咱們的代碼設計吧

2、實踐操做

1. 項目開始階段

下面的全部代碼都是本人獨立完成,可能不是那麼完美,不過應該問題不大吧

若有錯誤之處,還請包涵,多多指出

這裏咱們加入咱們的 Redis緩存技術,有時間會更新的。。

首先咱們要有 RabbitMQ的客戶端,能夠在Linux上安裝,也能夠在Windows上安裝。

我使用的是Linux上安裝的,能夠去參考 RabbitMQ在Linux下安裝

1.1 建立SpringBoot項目

這個就不用多說了吧,這個都是咱們的屢見不鮮了

1.2 導入依賴
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

主要是導入咱們咱們的 MQ依賴

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.2.4.RELEASE</version>
        </dependency>
1.3 修改application.yml
server:
  port: 9000
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test_rabbitmq?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: root
  rabbitmq:
    addresses: 192.168.2.2
    virtual-host: /
    username: guest
    password: guest
    template:
      exchange: ORDER.EXCHANGE
    listener:
     simple:
       #指定最小的消費者數量
       concurrency: 1
       retry:
         enabled: false #是否支持重試
       prefetch: 100
       acknowledge-mode: manual
logging:
  level:
    com.example.rabbitmq.mapper: debug
mybatis:
  mapper-locations: classpath:mapper/*.xml

主要配置的說明

1.3 數據庫鏈接
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test_rabbitmq?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: root

將用戶名和密碼修改成本身的

若是你的數據庫是在Linux上,那麼你須要將localhost修改成你的IP地址,並開啓3306端口

1.4 RabbitMQ基本配置
  • 基本配置
rabbitmq:
    addresses: 192.168.2.2
    virtual-host: /
    username: guest
    password: guest

注意,我這裏沒有列出格式

rabbitmq這是是在spring下的

address:這個是你的IP地址,一樣的若是你的 RabbitMQ是在Linux上,使用Linux的IP地址。如果windows版本,直接使用localhost

virtual-host:這個是你rabbitmq上的一個虛擬主機

username:用戶名,若是你沒有添加用戶:默認的是guest

password:密碼,一樣,若是你沒有添加用戶:默認是guest

具體說明請詳見 RabbitMQ在Linux下安裝

  • 默認交換機
template:
      exchange: ORDER.EXCHANGE

這裏咱們能夠定義默認的交換機名稱,那麼在代碼中咱們就須要設置這樣的名稱

  • 消費者監聽配置
listener:
     simple:
       #指定最小的消費者數量
       concurrency: 1
       retry:
         enabled: false #是否支持重試
       prefetch: 100 #每次處理的消息
       acknowledge-mode: manual #手動確認

上面的註釋已經很清楚明白了吧,具體須要配置本身能夠自行配置。

1.5 日誌打印配置(主要打印咱們的SQL語句)
logging:
  level:
    com.example.rabbitmq.mapper: debug
1.6 mybatis配置文件位置
mybatis:
  mapper-locations: classpath:mapper/*.xml

2. 數據庫(test_rabbitmq)

2.1 Order(訂單表)
DROP TABLE IF EXISTS `test_order`;

CREATE TABLE `test_order` (
  `order_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '訂單Id',
  `order_user_name` varchar(255) DEFAULT NULL COMMENT '訂單人的名稱',
  `order_user_email` varchar(255) DEFAULT NULL COMMENT '訂單人的郵箱',
  `order_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '訂單時間',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMEN
2.2 Googs(商品表)
DROP TABLE IF EXISTS `test_goods`;

CREATE TABLE `test_goods` (
  `goods_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品Id',
  `goods_name` varchar(255) DEFAULT NULL COMMENT '商品名稱',
  `goods_stock` int(100) DEFAULT NULL COMMENT '商品庫存',
  PRIMARY KEY (`goods_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
insert  into `test_goods`(`goods_id`,`goods_name`,`goods_stock`) values (1,'商品',0);

基本類的建立

3.1 數據響應類
ApiResponse
package com.example.rabbitmq.common;
import java.util.HashMap;

/**
 * 數據響應返回
 */
public class ApiResponse extends HashMap<String, Object> {
    /**
     * 狀態碼
     */
    private Integer code;
    /**
     * 信息
     */
    private String msg;

    @Override
    public Object put(String key, Object value) {

        super.put(key, value);

        return this;
    }

    public ApiResponse code(Integer code){

        this.put("code", code);

        return this;
    }

    public ApiResponse msg(String msg){

        this.put("msg",msg);

        return this;
    }
}

用戶下單響應數據

搶單成功,返回給用戶信息code=200,msg='搶單成功'

搶單失敗,返回給用戶信息code=400,msg='搶單失敗,或者是請求超時'

3.2 訂單類
Order
package com.example.rabbitmq.entity;

import lombok.Data;

import java.util.Date;

/**
 * 基本類
 */
@Data
public class TestOrder {

    /**
     * 訂單Id
     */
    private Long orderId;

    /**
     * 訂單人名稱
     */
    private String orderUserName;

    /**
     * 訂單人郵箱
     */
    private String orderUserEmail;

    /**
     * 訂單時間
     */
    private Date orderDate;
}

這裏咱們使用的是一個註解@Data,這個註解來源於依賴lombok,要使用這個依賴不只須要添加這個依賴,並且在IDEA中還要下載這個插件

我相信如今絕大多數人都是使用的IDEA編譯器吧~~~

若是還有小夥伴不知道這個編譯器或者沒有使用的,那就趕快行動起來吧!!!

IDEA這款編譯器是真的很友好

哇哦,扯到一邊去了。。。

OK,咱們迴歸正題

3.3 商品類
Goods
package com.example.rabbitmq.entity;

import lombok.Data;

/**
 * 商品
 */
@Data
public class TestGoods {

    /**
     * 商品ID
     */
    private Long goodsId;

    /**
     * 商品名稱
     */
    private String goodsName;

    /**
     * 商品庫存
     */
    private Integer goodsStock;
}

首先呢咱們要模擬一個沒有限流的場景

咱們先要將goods表中的庫存修改成100(本身自定義),這裏咱們就模擬只有100個庫存的商品

個人習慣是從Controller層到持久層,這個因人而異吧。。沒有什麼強制要求

4. 無限流操做(正常操做)

4.1訂單Controller層
OrderController
package com.example.rabbitmq.controller;

import com.example.rabbitmq.common.ApiResponse;
import com.example.rabbitmq.entity.TestOrder;
import com.example.rabbitmq.service.OrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


/**
 * 訂單Controller
 */
@RestController
@RequestMapping("order")
public class OrderController {
    
    @Autowired(required = false)
    private OrderService orderService;

    private static Integer count = 0;

    private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);
    /**
     * 無RabbitMQ建立訂單
     * @return
     */
    @GetMapping("/save/{goodsId}")
    public ApiResponse save(@PathVariable("goodsId") Long goodsId){

        ApiResponse apiResponse = this.orderService.save(goodsId);

        LOGGER.info("流量請求:" + count++);

        return apiResponse;
    }
}

上面代碼可能會報錯,不過不要慌,心不亂,手不抖,咱們跟着感受走

那是由於咱們有些類尚未建立

接下里就是咱們的Service類建立

4.2 訂單Service層
OrderService
package com.example.rabbitmq.service;

import com.example.rabbitmq.common.ApiResponse;
import com.example.rabbitmq.entity.TestOrder;

import java.util.List;

public interface OrderService {
    /**
     * 無RabbitMQ消峯限流
     * @return
     */
    ApiResponse save(Long goodsId);
}
4.3 訂單Service實現類
OrderServiceImpl
package com.example.rabbitmq.service.impl;

import com.example.rabbitmq.common.ApiResponse;
import com.example.rabbitmq.entity.TestGoods;
import com.example.rabbitmq.entity.TestOrder;
import com.example.rabbitmq.mapper.GoodsMapper;
import com.example.rabbitmq.mapper.OrderMapper;
import com.example.rabbitmq.service.OrderService;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.UUID;

/**
 * OrderService
 */
@Service
public class OrderServiceImpl implements OrderService {


    @Autowired(required = false)
    private AmqpTemplate amqpTemplate;
    
    @Autowired(required = false)
    private OrderMapper orderMapper;

    @Autowired(required = false)
    private GoodsMapper goodsMapper;


    /**
     * 無RabbitMQ消峯限流
     *
     * @return
     */
    @Override
    public ApiResponse save(Long goodsId) {

        if (goodsId == null){

            return new ApiResponse().code(400).msg("參數錯誤");
        }

        // 根據商品Id查詢商品
        TestGoods testGoods = this.goodsMapper.selectStockById(goodsId);

        // 商品不存在或者商品庫存爲0
        if (testGoods == null || testGoods.getGoodsStock() <= 0){

            return new ApiResponse().code(400).msg("商品不存在或者庫存爲0");
        }

        // 直接添加
        // 建立訂單
        TestOrder testOrder = new TestOrder();

        // 設置參數
        testOrder.setOrderUserEmail("111@qq.com");

        testOrder.setOrderUserName("FC");

        testOrder.setOrderDate(new Date());

        this.orderMapper.save(testOrder);

        // 更新庫存
        this.goodsMapper.updateGoodsStock(goodsId);

        return new ApiResponse().code(200).msg("訂單建立成功");
    }
}

咱們來解釋一下咱們的正常邏輯

  • 判斷參數是否有效
if (goodsId == null){
  return new ApiResponse().code(400).msg("參數錯誤");
}
  • 查詢商品信息
// 根據商品Id查詢商品
 TestGoods testGoods = this.goodsMapper.selectStockById(goodsId);

 // 商品不存在或者商品庫存爲0
 if (testGoods == null || testGoods.getGoodsStock() <= 0){

  return new ApiResponse().code(400).msg("商品不存在或者庫存爲0");
 }

先從數據庫中查詢商品信息,返回查詢結果

判斷是否爲空或者是庫存是否爲0

  • 建立訂單
// 直接添加
 // 建立訂單
 TestOrder testOrder = new TestOrder();

 // 設置參數
 testOrder.setOrderUserEmail("111@qq.com");

 testOrder.setOrderUserName("FC");

 testOrder.setOrderDate(new Date());

this.orderMapper.save(testOrder);

上面條件都知足,那麼咱們能夠下訂單了

  • 更新庫存
// 更新庫存
 this.goodsMapper.updateGoodsStock(goodsId);

固然呢咱們下單成功了,固然要更新咱們的庫存吶。

那這就是咱們下單的通常邏輯啦

這裏確定是有問題的啦,這樣咱們全部的請求都打在咱們的數據庫上了,那數據庫確定是罩不住的,那要崩啊

稍後會給出解決方案

4.4 商品Service層
GoodsService
package com.example.rabbitmq.service;

import com.example.rabbitmq.entity.TestGoods;

public interface GoodsService {

    /**
     * 根據商品Id查詢商品庫存
     * @param goodsId
     * @return
     */
    TestGoods selectGoodsById(Long goodsId);

    /**
     * 更新庫存
     * @param goodsId
     */
    void updateGoodsStock(Long goodsId);
}
4.5 商品Service實現類
GoodsServiceImpl
package com.example.rabbitmq.service.impl;

import com.example.rabbitmq.entity.TestGoods;
import com.example.rabbitmq.mapper.GoodsMapper;
import com.example.rabbitmq.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 商品Service
 */
@Service
public class GoodsServiceImpl implements GoodsService {
    @Autowired(required = false)
    private GoodsMapper goodsMapper;
    /**
     * 根據商品Id查詢商品
     * @param goodsId
     * @return
     */
    @Override
    public TestGoods selectGoodsById(Long goodsId) {

        if (goodsId == null){

            return null;
        }

       TestGoods testGoods = this.goodsMapper.selectStockById(goodsId);
        
        return testGoods;
    }

    /**
     * 更新庫存
     *
     * @param goodsId
     */
    @Override
    public void updateGoodsStock(Long goodsId) {

        if (goodsId == null){

            return;
        }

        try {

            this.goodsMapper.updateGoodsStock(goodsId);

            return;

        }catch (Exception e) {

            return;
        }
    }
}
4.6 商品Mapper層
GoodsMapper
package com.example.rabbitmq.mapper;

import com.example.rabbitmq.entity.TestGoods;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface GoodsMapper {

    /**
     * 根據商品Id查詢商品
     * @param goodsId
     * @return
     */
    TestGoods selectStockById(Long goodsId);

    /**
     * 更新庫存
     * @param goodsId
     */
    void updateGoodsStock(Long goodsId);
}
4.7商品Mapper的xml文件
GoodsMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.example.rabbitmq.mapper.GoodsMapper">

    
    <resultMap id="BaseGoodsMap" type="com.example.rabbitmq.entity.TestGoods">

        <id property="goodsId" column="goods_id"/>

        <result property="goodsName" column="goods_name"/>

        <result property="goodsStock" column="goods_stock"/>

    </resultMap>


    <!-- 根據商品Id查詢商品 -->
    <select id="selectStockById" resultMap="BaseGoodsMap" parameterType="java.lang.Long">

        select goods_stock from test_goods where goods_id = #{goodsId};

    </select>

    <!-- 更新庫存 -->
    <update id="updateGoodsStock" parameterType="java.lang.Long">
        
        update test_goods set goods_stock  = goods_stock - 1  where goods_id = #{goodsId} ;
        
    </update>

</mapper>
4.8 訂單Mapper層
OrderMapper
package com.example.rabbitmq.mapper;

import com.example.rabbitmq.entity.TestOrder;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

/**
 *
 */
@Mapper
@Repository
public interface OrderMapper {
    /**
     * 無RabbitMQ消峯限流保存訂單
     */
    void save(TestOrder testOrder);
}
4.9 訂單Mapper的xml文件
OrderMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.example.rabbitmq.mapper.OrderMapper">

    
    <resultMap id="BaseOrderMap" type="com.example.rabbitmq.entity.TestOrder">

        <id property="orderId" column="order_id"/>

        <result property="orderUserName" column="order_user_name"/>

        <result property="orderUserEmail" column="order_user_email"/>
        
        <result property="orderDate" column="order_date"/>
        
    </resultMap>
    <insert id="save" parameterType="com.example.rabbitmq.entity.TestOrder">

        insert into test_order (order_user_name, order_user_email, order_date) values (

        #{orderUserName},

        #{orderUserEmail},

        #{orderDate}
        );
    </insert>
</mapper>

OK,這裏咱們基本操做就完成了,這些都是咱們的正常操做

那麼咱們啓動項目來測試一下

4.10 驗證

這裏咱們先用postman來驗證咱們的接口

咱們能夠在控制檯看見

咱們的所有請求都到了數據庫上,咱們這裏只是發起了兩次請求,數據庫確定不會啊,數據庫應該每秒可以撐起2000次請求吧,那麼要是超過了2000,那就要出現問題了呀。。。

咱們來看數據庫中的數據

庫存信息

訂單信息

這樣咱們就能夠輕鬆的建立訂單了,哇哈哈哈哈

可是事實不撩人啊,咋個這麼輕鬆哦,,那是不可能的

那麼咱們就須要壓力測試來啦,這裏咱們使用的是jmeter

這裏咱們建立了4000個線程來請求咱們接口

這樣看咱們的數據庫壓力還大不大,哼

那就來瞧一瞧咱們數據庫怎麼來解決吧

媽呀,這是什麼哦,怎麼能夠亂來哦。

這裏咱們看見,雖然有查詢,有添加,還有更新庫存的操做

可是呢,別個還在執行的時候,我還在下訂單的時候,另一個就在更新庫存,Are You Sure?

這。。。。

這我看不下去了

那麼咱們再來看看絕望的時刻

哇哦,這就很尷尬了,這樣下去不得了啊,咱們不得虧死啊,盡然還超賣了。。。。

爲何會出現這樣的狀況呢?

當咱們的請求流量瞬時就來了,並且通常仍是同一時間來的,這樣咱們的所有流量就打到咱們的數據庫上,

當咱們一個線程還在查詢這一步,查詢出來哦,還有1個庫存,能夠下訂單,那麼另一個線程也來了,

查詢出來,哦,我也還有1個庫存,能夠下訂單。

這樣兩個線程都去更新庫存信息,這樣就會出現超賣的狀況那。

在咱們秒殺活動中,若是這樣去實現,那不得虧死哦。

那麼咱們應該怎麼辦呢?左想一想,右想一想

哦,咱們不是學過消息隊列嗎?咱們能夠用這個來進行優化啊。。。

消息隊列中不是能夠消峯限流嗎?

鼠標往上一劃,哇哦,原來寫了這麼多啦,那咱們就先這樣結束嗎?

不會,咱們在下一節會繼續講到的,咱們下期再見啦

拜拜

相關文章
相關標籤/搜索