Rumah >pangkalan data >Redis >Bagaimana untuk melaksanakan sistem jualan kilat dalam redis

Bagaimana untuk melaksanakan sistem jualan kilat dalam redis

PHPz
PHPzke hadapan
2023-05-31 15:11:132555semak imbas

1. Idea Reka Bentuk

Sistem pembunuh kilat dicirikan oleh sejumlah besar permintaan boleh masuk dalam satu saat , sistem akan Ia runtuh dalam beberapa minit. Mari kita bincangkan cara mereka bentuk sistem bunuh kilat yang boleh dimainkan.

1. Had semasa:

Pertama sekali, tanpa mengira logik perniagaan, jika terdapat antara muka paling mudah berikut:

@GetMapping("/test")
public String test() {
 return "success";
}

Walaupun antara muka ini sangat mudah dan tidak mempunyai sebarang logik, Pelayan juga boleh ranap jika terdapat beribu-ribu permintaan mengaksesnya secara serentak. Oleh itu, perkara pertama yang perlu dilakukan oleh sistem konkurensi tinggi ialah mengehadkan aliran semasa. Projek Springcloud boleh menggunakan hystrix untuk mengehadkan semasa, dan springcloud alibaba boleh menggunakan sentinel untuk mengehadkan semasa. Bagaimana pula dengan projek bukan springcloud? Jambu batu memberikan kami kelas alat RateLimiter yang boleh mengehadkan arus. Ia terutamanya termasuk algoritma baldi bocor dan algoritma baldi token.

  • Algoritma Baldi Bocor: Baldi berlubang diisi dengan air di bawah paip Ia akan bocor sedikit apabila ia diisi sangat besar, baldi akan bocor Lambat laun, air akan melimpah, dan aliran akan terhad apabila ia melimpah. Ini sesuai untuk mengehadkan kadar muat naik dan muat turun.

  • Algoritma baldi token: masukkan token ke dalam baldi pada kadar tetap Setiap kali permintaan masuk, token mesti diambil dari baldi terlebih dahulu Jika token tidak diperoleh, permintaan akan disekat. Ini sesuai untuk pengehadan semasa, iaitu mengehadkan QPS.

Algoritma baldi token harus digunakan di sini untuk mengehadkan aliran Jika token tidak diperoleh, gesaan "terlalu ramai orang tidak boleh masuk". kembali secara langsung.

2. Semak sama ada pengguna telah log masuk:

Selepas langkah pertama pengehadan semasa, permintaan masuk harus menyemak sama ada pengguna telah log masuk. Projek ini menggunakan JWT, iaitu, pertama meminta antara muka log masuk dan kembali selepas log masuk. Token, minta semua antara muka lain untuk membawa token dalam pengepala permintaan, dan kemudian anda boleh mendapatkan maklumat pengguna melalui token. Apabila maklumat pengguna tidak diperoleh, pengguna digesa untuk log masuk semula: token tidak sah.

3. Semak sama ada produk habis dijual:

Jika dua langkah pengesahan pertama diluluskan, anda perlu menyemak sama ada produk itu habis dijual, a mesej segera "Maaf" akan dikembalikan , produk telah habis dijual dengan serta-merta. Ambil perhatian bahawa anda tidak boleh menyemak pangkalan data untuk menyemak sama ada produk itu habis dijual, jika tidak, ia akan menjadi sangat perlahan. Anda boleh menggunakan kamus untuk menyimpan ID produk, menggunakan ID produk sebagai kunci. Jika item itu habis dijual, tetapkan nilainya kepada Benar, jika tidak kepada Palsu.

4. Tambahkan produk yang menyertai jualan kilat ke redis:

Pertama dapatkan kunci ISINREDIS untuk menunjukkan sama ada produk telah ditambahkan ke redis untuk mengelakkan pertindihan bagi setiap permintaan. Operasi ini. Jika nilai ISINREDIS palsu, ini bermakna tiada produk jualan kilat dalam redis. Kemudian tanya semua produk yang mengambil bahagian dalam jualan kilat, gunakan id produk sebagai kunci, dan inventori produk sebagai nilai, dan simpannya dalam redis Pada masa yang sama, gunakan id produk sebagai kunci dan palsu sebagai nilai dan letakkannya dalam peta langkah ketiga, menunjukkan bahawa produk itu belum dijual. Akhir sekali, tetapkan nilai ISINREDIS kepada benar, yang bermaksud bahawa semua produk yang menyertai jualan kilat telah ditambahkan pada redis.

5. Menahan inventori:

Gunakan fungsi decr redis untuk mengurangkan kuantiti barang dan menilai nilai yang dikurangkan. Jika keputusan selepas pengurangan diri kurang daripada 0, ini bermakna produk telah habis dijual, maka nilai ID produk yang sepadan dalam peta ditetapkan kepada benar dan gesaan "Sudah lewat, produk telah habis dijual" dikembalikan.

6. Tentukan sama ada jualan kilat diulang:

Jika jualan kilat pengguna berjaya, selepas pesanan jualan kilat disimpan dalam pangkalan data, id pengguna dan id produk akan digunakan sebagai kunci, dan benar akan disimpan dalam redis sebagai nilai, menunjukkan Pengguna ini telah menjual produk ini serta-merta. Jadi di sini kita pergi ke redis berdasarkan ID pengguna dan ID produk untuk menentukan sama ada jualan kilat diulang. Jika ya, gesaan "Jangan ulangi jualan kilat" dikembalikan.

7. Pemprosesan tak segerak:

Jika pengesahan di atas diluluskan, maka jualan kilat boleh diproses. Jika anda memotong inventori dan membuat pesanan untuk setiap permintaan jualan kilat, bukan sahaja kelajuannya akan menjadi sangat perlahan, tetapi ia juga boleh menyebabkan pangkalan data ranap. Jadi kami boleh memprosesnya secara tidak segerak, iaitu, selepas lulus pengesahan di atas, ID pengguna dan ID produk akan dihantar ke MQ sebagai mesej, dan kemudian gesaan "beratur" akan dikembalikan kepada pengguna dengan serta-merta. Kemudian gunakan mesej di bahagian pengguna MQ, dapatkan ID pengguna dan ID produk, dan tanya inventori berdasarkan ID produk untuk memastikan inventori yang mencukupi sekali lagi, kemudian anda juga boleh menentukan sama ada untuk mengulangi jualan kilat. Selepas meluluskan penghakiman, kendalikan pangkalan data, tolak inventori dan buat pesanan jualan kilat. Ambil perhatian bahawa menolak inventori dan membuat pesanan jualan kilat perlu dalam transaksi yang sama.

8. Masalah terlebih jual:

Masalah terlebih jual ialah inventori negatif barang. Sebagai contoh, jika inventori adalah 1, maka 10 pengguna menjual serta-merta pada masa yang sama Apabila menilai inventori, mereka semua adalah 1, jadi 10 orang boleh membuat pesanan dengan jayanya, dan inventori akhir ialah -9. Bagaimana untuk menyelesaikannya? Malah, masalah sedemikian tidak akan berlaku dalam sistem ini sama sekali, kerana redis digunakan untuk pra-pengurangan inventori pada mulanya, dan modul teras arahan redis adalah satu-benang, jadi ia boleh dijamin tidak terlebih jual. Jika redis tidak digunakan, anda juga boleh menambah medan versi pada produk Semak versi sebelum menolak inventori setiap kali Tambah syarat pada sql untuk menolak inventori, iaitu versi mestilah sama dengan versi yang baru ditemui.

 

二、核心代码

@RestController
@RequestMapping("/seckill")
public class SeckillController {
 
 @Autowired
 private UserService userService;
 @Autowired
 private SeckillService seckillService;
 @Autowired
 private RabbitMqSender mqSender;
 
 // 用来标记商品是否已经加入到redis中的key
 private static final String ISINREDIS = "isInRedis";
 
 // 用goodsId作为key,标记该商品是否已经卖完
 private Map<integer> seckillOver = new HashMap<integer>();
 
 // 用RateLimiter做限流,create(10),可以理解为QPS阈值为10
 private RateLimiter rateLimiter = RateLimiter.create(10);
 
 @PostMapping("/{sgId}")
 public JsonResult> seckillGoods(@PathVariable("sgId") Integer sgId, HttpServletRequest httpServletRequest){
  
  // 1. 如果QPS阈值超过10,即1秒钟内没有拿到令牌,就返回“人太多了,挤不进去”的提示
  if (!rateLimiter.tryAcquire(1, TimeUnit.SECONDS)) {
   return new JsonResult(SeckillGoodsEnum.TRY_AGAIN.getCode(), SeckillGoodsEnum.TRY_AGAIN.getMessage());
  }
  
  // 2. 检查用户是否登录(用户登录后,访问每个接口都应该在请求头带上token,根据token再去拿user)
  String token = httpServletRequest.getHeader("token");
  String userId = JWT.decode(token).getAudience().get(0);
  User user = userService.findUserById(Integer.valueOf(userId));
  if (user == null) {
   return new JsonResult(SeckillGoodsEnum.INVALID_TOKEN.getCode(), SeckillGoodsEnum.INVALID_TOKEN.getMessage());
  }
  
  // 3. 如果商品已经秒杀完了,就不执行下面的逻辑,直接返回商品已秒杀完的提示
  if (!seckillOver.isEmpty() && seckillOver.get(sgId)) {
   return new JsonResult(SeckillGoodsEnum.SECKILL_OVER.getCode(), SeckillGoodsEnum.SECKILL_OVER.getMessage());
  }
  
  // 4. 将所有参加秒杀的商品信息加入到redis中
  if (!RedisUtil.isExist(ISINREDIS)) {
   List<seckillgoods> goods = seckillService.getAllSeckillGoods();
   for (SeckillGoods seckillGoods : goods) {
    RedisUtil.set(String.valueOf(seckillGoods.getSgId()), seckillGoods.getSgSeckillNum());
    seckillOver.put(seckillGoods.getSgId(), false);
   }
   RedisUtil.set(ISINREDIS, true);
  }
  
  // 5. 先自减,预扣库存,判断预扣后库存是否小于0,如果是,表示秒杀完了
  Long stock = RedisUtil.decr(String.valueOf(sgId));
  if (stock (SeckillGoodsEnum.SECKILL_OVER.getCode(), SeckillGoodsEnum.SECKILL_OVER.getMessage());
  }
  
  // 6. 判断是否重复秒杀(成功秒杀并创建订单后,会将userId和goodsId作为key放到redis中)
  if (RedisUtil.isExist(userId + sgId)) {
   return new JsonResult(SeckillGoodsEnum.REPEAT_SECKILL.getCode(), SeckillGoodsEnum.REPEAT_SECKILL.getMessage());
  }
  
  // 7. 以上校验都通过了,就将当前请求加入到MQ中,然后返回“排队中”的提示
  String msg = userId + "," + sgId;
  mqSender.send(msg);
  return new JsonResult(SeckillGoodsEnum.LINE_UP.getCode(), SeckillGoodsEnum.LINE_UP.getMessage());
 }

}
</seckillgoods></integer></integer>
     

三、压测

用jmeter模拟并发请求,测试高并发情况下系统能否扛得住。由于只有一个id为1的商品,所以商品id固定写死1。但是每个用户都要先请求登录接口获取到token才能进行秒杀请求,有点儿麻烦,所以可以先把jwt模块注释掉,把userId当成参数传进去。jmeter配置如下图:

Bagaimana untuk melaksanakan sistem jualan kilat dalam redis
jmeter压测配置
Bagaimana untuk melaksanakan sistem jualan kilat dalam redis
jmeter压测配置

Atas ialah kandungan terperinci Bagaimana untuk melaksanakan sistem jualan kilat dalam redis. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam