SpringBoot +Redis +RabbitMQ 实现高并发限时秒杀


提示:以下是本篇文章正文内容,下面案例可供参考

一、软件安装

1.安装RabbitMQ

docker安装:docker安装RabbitMQ_liangshitian的博客-CSDN博客

windows安装:windows安装rabbitmq安装详细步骤_青蛙与大鹅的博客-CSDN博客_window安装rabbitmq

2.安装Redis

docker安装:https://blog.csdn.net/qq_33612228/article/details/10360918

windows安装:Windows 64位下安装Redis 以及 可视化工具Redis Desktop Manager的安装和使用_零碎de記憶的博客-CSDN博客_redis可视化工具下载

springboot整合redis:SpringBoot整合Redis_liangshitian的博客-CSDN博客

3.安装 Jmeter测试工具

windows安装:Jmeter安装教程_liuyanh2006的博客-CSDN博客

4.安装工具包:我的资源; rabbitmq+Erlang工具+压力测试jmeter-Java文档类资源-CSDN下载otp_win64_24.3.3.exe+rabbitmq-server-3.9.15.exe+ap更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/jlonghou/85195659

二、详细

1.思路

  1. 在用户发起秒杀访问时,先访问本地已经初始化好的map,看当前秒杀商品id的库存是否已售罄,若已售罄,直接返回秒杀结束异常,若库存还有,在执行下面的操作。通过内存标记可以减少对后面步骤中的redis访问操作,降低redis的压力,不然每个请求都将访问一次redis
  2. 系统启动时,即将商品和库存数据初始化到redis中,所有的抢购操作都在redis中进行处理,通过Redis预减少库存来减少数据库访问
  3. 通过使用RabbitMQ用异步队列处理下单,实现系统高响应。此处响应客户端后,一般都是抢购成功了,当然不排除例外,此时客户端通过ajax请求轮询访问下单结果接口,直到响应状态成功或者失败

2.数据库设计

1.商品库存表:stock表

 CREATE TABLE `stock` (`id` varchar(64) NOT NULL,`name` varchar(255) DEFAULT NULL,`stock` varchar(255) DEFAULT NULL,`remarks` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',`update_date` datetime DEFAULT NULL COMMENT '最后更新时间',`create_date` datetime DEFAULT NULL COMMENT '创建时间',`update_by` varchar(64) NOT NULL DEFAULT '',`create_by` varchar(64) NOT NULL DEFAULT '',`del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0正常,1删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='商品库存表';

2.秒杀订单表:t_order表

 CREATE TABLE `t_order` (`id` varchar(64) NOT NULL,`order_name` varchar(255) DEFAULT NULL,`order_user` varchar(255) DEFAULT NULL,`remarks` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',`update_date` datetime DEFAULT NULL COMMENT '最后更新时间',`create_date` datetime DEFAULT NULL COMMENT '创建时间',`update_by` varchar(64) NOT NULL DEFAULT '',`create_by` varchar(64) NOT NULL DEFAULT '',`del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0正常,1删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='秒杀订单表';

3.代码参考

1.pom引入

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

2.配置application.yml

server:port: 8090spring:datasource:url: jdbc:mysql://127.0.0.1:3306/rabbitmq?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=falseusername: rootpassword: root# 使用Druid数据源type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverdruid:filters: statmaxActive: 20initialSize: 1maxWait: 60000minIdle: 1timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 'x'testWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: truemaxOpenPreparedStatements: 20data:redis:repositories:enabled: falseredis:database: 0   # redis数据库索引(默认为0),我们使用索引为其他(0-15)的数据库,避免和其他数据库冲突host: 127.0.0.1port: 6379password:#  4369 -- erlang发现口
#
#  5672 --client端通信口
#
#  15672 -- 管理界面ui端口 http://localhost:15672/
#
#  25672 -- server间内部通信口rabbitmq:  #mq配置host: 127.0.0.1port: 5672username: guestpassword: guestlogging:config: classpath:logback-spring.xml
 3.实体类 Order.java+Stock 商品库存表
/*** @Description: 商品库存表*/
@Data
@TableName("t_order")
public class Order {@TableId(type = IdType.AUTO)private Integer id;@TableField("order_name")private String orderName;@TableField("order_user")private String orderUser;@TableField("create_by")private String createBy;@TableField("update_by")private String updateBy;@TableField("create_date")private Date createDate;@TableField("update_date")private Date updateDate;@TableField("del_flag")private String delFlag;
}/*** @Description: 商品库存表*/
@TableName("stock")
@Data
public class Stock {@TableId(type = IdType.AUTO )private Integer id;/*** 产品名称*/@TableField("name")private String name;/*** 存货*/@TableField("stock")private Integer stock;
}

4.服务层

StockService存货服务层+OrderService订单服务层

import com.orange.entity.Stock;import java.util.List;/*** 存货服务层*/
public interface StockService {/*** 秒杀商品后-减少库存* @param name 商品名称*/int decrByStock(String name);/*** 秒杀商品列表* @return List<Stock>*/List<Stock> selectList();}import com.orange.entity.Order;
import org.springframework.stereotype.Service;/*** 订单服务层*/
@Service
public interface OrderService {/*** 订单保存* @param order 实体*/int saveOrder(Order order);
}
  OrderServiceImpl订单实现层+StockServiceImpl存货实现层 
package com.example.rabbit.service.imp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.rabbit.entity.Stock;
import com.example.rabbit.exception.CustomException;
import com.example.rabbit.mapper.StockMapper;
import com.example.rabbit.service.StockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;/*** @Author HOUJL* @Date 2022/4/21* @Description:*/
@Service
@Slf4j
public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements StockService {@Autowiredprivate StockMapper stockMapper;@Overridepublic int decrByStock(String name) {Stock stock = stockMapper.selectOne(new QueryWrapper<Stock>().lambda().eq(Stock::getName,name));stock.setStock(stock.getStock()-1);int i = stockMapper.updateById(stock);if( i<= 0){throw new CustomException("减少库存失败");}return i;}@Overridepublic List<Stock> selectList() {return stockMapper.selectList(new QueryWrapper<>());}
}package com.example.rabbit.service.imp;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.rabbit.entity.Order;
import com.example.rabbit.exception.CustomException;
import com.example.rabbit.mapper.OrderMapper;
import com.example.rabbit.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @Author HOUJL* @Date 2022/4/21* @Description:*/
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {@AutowiredOrderMapper orderMapper;@Overridepublic int saveOrder(Order order) {int i = orderMapper.insert(order);if (i <= 0) {throw new CustomException("保存订单失败");}return i;}
}

5.配置rabbitmq的实现方式以及redis的实现方式

1)在 service包下面直接新建 MQOrderServiceImpl.java,这个类属于订单的消费队列。

package com.example.rabbit.service.imp;import com.example.rabbit.config.RabbitMqConfig;
import com.example.rabbit.entity.Order;
import com.example.rabbit.service.OrderService;
import com.example.rabbit.service.StockService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.io.IOException;
import java.util.Date;/*** @Author HOUJL* @Date 2022/4/21* @Description: 消费消息*/
@Service
@Slf4j
public class MqOrderServiceImpl {private final OrderService orderService;private final StockService stockService;public MqOrderServiceImpl(OrderService orderService, StockService stockService) {this.orderService = orderService;this.stockService = stockService;}/*** MQ监听订单消息队列,并消费* @param order*/@RabbitListener(queues = RabbitMqConfig.ORDER_QUEUE, containerFactory = "rabbitListenerContainerFactory")@Transactional(rollbackFor = Exception.class)public void saveOrder(Message message, Order order, Channel channel) throws IOException {log.info("收到订单消息,订单用户为:{},商品名称为:{}", order.getOrderUser(), order.getOrderName());/*** 调用数据库orderService创建订单信息*/order.setCreateBy(order.getOrderUser());order.setCreateDate(new Date());order.setUpdateBy(order.getOrderUser());order.setUpdateDate(new Date());order.setDelFlag("0");int i = orderService.saveOrder(order);int j = stockService.decrByStock(order.getOrderName());if (i>0 && j>0){//消费成功channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);log.info("消费订单成功,订单用户为:{},商品名称为:{}", order.getOrderUser(), order.getOrderName());}else {channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,true);log.info("消费订单失败,订单用户为:{},商品名称为:{}", order.getOrderUser(), order.getOrderName());}}
}

2)MQStockServiceImpl.java这个属于库存得消费队列。

package com.example.rabbit.service.imp;import com.example.rabbit.config.RabbitMqConfig;
import com.example.rabbit.entity.Order;
import com.example.rabbit.utils.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;import java.util.Objects;/*** @Author HOUJL* @Date 2022/4/21* @Description: 生产消息*/
@Service
@Slf4j
public class MqStockServiceImpl {private final RedisCache redisCache;private final RabbitTemplate rabbitTemplate;public MqStockServiceImpl(RedisCache redisCache, RabbitTemplate rabbitTemplate) {this.redisCache = redisCache;this.rabbitTemplate = rabbitTemplate;}/*** 使用redis+消息队列进行秒杀实现** @param userName  用户名称* @param stockName 商品名称* @return String*/public String secKill(String userName, String stockName) {log.info("参加秒杀的用户是:{},秒杀的商品是:{}", userName, stockName);String message = "";//redis中key对应的value减一Long decrByResult = redisCache.decrBy(stockName);if (Objects.nonNull(decrByResult) && decrByResult >= 0) {/*** 说明该商品的库存量有剩余,可以进行下订单操作*/log.info("用户:{}, 秒杀该商品:{},库存余量{},可以进行下订单操作", userName, stockName, decrByResult);//1.发消息给订单消息队列,创建订单 2.发消息给库存消息队列,将库存数据减一 3.将订单保存到redis 实现限购功能Order order = new Order();order.setOrderUser(userName);order.setOrderName(stockName);rabbitTemplate.convertAndSend(RabbitMqConfig.ORDER_EXCHANGE,RabbitMqConfig.ORDER_ROUTING_KEY,order);message = "用户" + userName + "秒杀" + stockName + "成功";limitNumber(userName,stockName);} else {/*** 说明该商品的库存量没有剩余,直接返回秒杀失败的消息给用户*/log.info("用户:{}秒杀时商品的库存量没有剩余,秒杀结束", userName);message = "用户:" + userName + "商品的库存量没有剩余,秒杀结束";}return message;}private void limitNumber(String userName, String stockName) {String key = userName + ":" + stockName + ":number";redisCache.incrBy(key);}
}

6.RabbitMqConfig 和redisUtil工具类

1.RabbitMqConfig.java

package com.example.rabbit.config;import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
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;
import org.springframework.stereotype.Component;/*** @Author HOUJL* @Date 2022/4/21* @Description: RabbitMQConfig插件配置*/
@Configuration
public class RabbitMqConfig {/*** 库存交换机*/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";/**** @param connectionFactory* @return SimpleRabbitListenerContainerFactory*/@Bean(name = "rabbitListenerContainerFactory")public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();//手动确认消息factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);factory.setConnectionFactory(connectionFactory);factory.setMessageConverter(new Jackson2JsonMessageConverter());//消费数量factory.setPrefetchCount(50);return factory;}@Beanpublic MessageConverter messageConverter() {return new Jackson2JsonMessageConverter();}/*** 创建库存交换机* @return*/@Beanpublic Exchange getStoryExchange() {return ExchangeBuilder.directExchange(STORY_EXCHANGE).durable(true).build();}/*** 创建库存队列* @return*/@Beanpublic Queue getStoryQueue() {return new Queue(STORY_QUEUE,true);}/*** 库存交换机和库存队列绑定* @return*/@Beanpublic Binding bindStory() {return BindingBuilder.bind(getStoryQueue()).to(getStoryExchange()).with(STORY_ROUTING_KEY).noargs();}/*** 创建订单队列* @return*/@Beanpublic Queue getOrderQueue() {return new Queue(ORDER_QUEUE);}/*** 创建订单交换机* @return*/@Beanpublic Exchange getOrderExchange() {return ExchangeBuilder.directExchange(ORDER_EXCHANGE).durable(true).build();}/*** 订单队列与订单交换机进行绑定* @return*/@Beanpublic Binding bindOrder() {return BindingBuilder.bind(getOrderQueue()).to(getOrderExchange()).with(ORDER_ROUTING_KEY).noargs();}}

2.RedisCacheConfig.java

package com.example.rabbit.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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.StringRedisSerializer;@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);ObjectMapper mapper = new ObjectMapper();mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);serializer.setObjectMapper(mapper);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}
}

3.RedisUtil.java部分代码

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

7.controller提供了二个方法,一个为redis+rabbitmq实现高并发秒杀,第二个则用纯数据库模拟秒杀,出现超卖现象。

import com.orange.annotation.AccessLimit;
import com.orange.annotation.LimitNumber;
import com.orange.service.impl.MqStockServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j
@Api(value = "SecKillController",  tags = "秒杀控制层")
@RequestMapping("/seck")
public class SecKillController {@Autowiredprivate MqStockServiceImpl mqStockService;/*** 使用redis+消息队列进行秒杀实现* @param userName 用户名称* @param stockName 商品名称* @return String*/@PostMapping(value = "/secKill")@ApiOperation(value = "redis+消息队列进行秒杀实现", notes = "redis+消息队列进行秒杀实现")@LimitNumber(value = 2)@AccessLimit(seconds = 1,maxCount = 800)public String secKill(@RequestParam(value = "userName") String userName, @RequestParam(value = "stockName") String stockName) {return mqStockService.secKill(userName, stockName);}}

8.需要在springboot得启动类中进行对redis得初始化,简而言之就是调用我们上面写得方法,新建一个redis缓存,模拟商品信息。

package com.example.rabbit;import com.example.rabbit.entity.Stock;
import com.example.rabbit.service.StockService;
import com.example.rabbit.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;import java.util.List;
import java.util.concurrent.TimeUnit;@SpringBootApplication
public class RabbitApplication implements ApplicationRunner {public static void main(String[] args) {SpringApplication.run(RabbitApplication.class, args);}@Autowiredprivate RedisCache redisCache;@Autowiredprivate StockService stockService;@Overridepublic void run(ApplicationArguments args) throws Exception {List<Stock> stockList = stockService.selectList();for (Stock stockItem : stockList) {redisCache.setCacheObject(stockItem.getName(),stockItem.getStock(),3600, TimeUnit.SECONDS);}}
}

6.redis+RabbitMQ测试

1.项目启动时,redis里的watch会初始化10。

2.打开我们得JMeter工具运行测试(具体使用Jmeter可自行百度)

1)选择中文

2)完成中文之后,我们在测试计划右键,添加一个线程组。

3)给这个线程组的数量为40,这个线程组的作用就是模拟40个用户发送请求,去秒杀;然后再在线程组右键,添加一个Http请求,这个就是我们用来发送请求的组件了

4)这个请求唯一要说得就是,随机参数了,因为用户名肯定不可能给40个相同得名字,这边我们利用JMeter给用户名得值为随机数
点击上方得白色小书本,选择random,1-99得随机数。

5)然后我们把这个函数字符串复制到http得参数上面去。

最后点击运行按钮运行。

5)查看控制台日志,可以看到运行结果已经打印到控制台了,用户名为我们生成的随机数。

再来看下数据库订单表t_order,就保存了10条数据(秒杀成功的),我们初始化的时候给watch库存得数量为10,而我们使用JMeter模拟了40个人发请求,所以这10条数据,也就是40个用户中抢到商品的10个人,也就是线程,谁抢到就是谁得。

6)再来查看下我们得结果树

源码:


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

SpringBoot +Redis +RabbitMQ 实现高并发限时秒杀相关推荐

  1. SpringBoot+Redis+Cookies实现高并发的购物车

    案例实战:SpringBoot+Redis+Cookies实现高并发的购物车 步骤1:代码逻辑 /*** 添加购物车*/@PostMapping(value = "/addCart" ...

  2. redis解决“高并发定时秒杀”库存误差问题

    前言:高并发的秒杀活动中,通过查询数据库判断是否还有库存,然后对库存字段进行增减,极易出现库存超出或者库存为负的情况,一般来说有3中解决办法(数据库表加锁,memche缓存,redis队列): 我们这 ...

  3. php redis下单,redis 队列简单实现高并发抢购/秒杀

    redis 队列简单实现高并发抢购/秒杀 2019-03-21 14:34 阅读数 82 前提为每人限购1件 <>开抢前 把秒杀商品库存存进 Redis 队列中 $redis = new ...

  4. 【高并发】秒杀系统架构解密,不是所有的秒杀都是秒杀(升级版)!!

    写在前面 很多小伙伴反馈说,高并发专题学了那么久,但是,在真正做项目时,仍然不知道如何下手处理高并发业务场景!甚至很多小伙伴仍然停留在只是简单的提供接口(CRUD)阶段,不知道学习的并发知识如何运用到 ...

  5. Java互联网架构-京东国美高并发核心技术“秒杀”

    一丶 秒杀业务分析 正常电子商务流程 (1)查询商品:(2)创建订单:(3)扣减库存:(4)更新订单:(5)付款:(6)卖家发货 秒杀业务的特性 (1)低廉价格:(2)大幅推广:(3)瞬时售空:(4) ...

  6. 如何解决高并发,秒杀问题

    相信不少人会被这个问题困扰,分享大家一篇这样的文章,希望能够帮到你! 一.秒杀业务为什么难做? 1)im系统,例如qq或者微博,每个人都读自己的数据(好友列表.群列表.个人信息): 2)微博系统,每个 ...

  7. PHP高并发商品秒杀问题的解决方案

    前言 秒杀会产生一个瞬间的高并发,使用数据库会增加数据库的访问压力,也会降低访问速度,所以我们应该使用缓存,来降低数据库的访问压力: 可以看出这里的操作和原来的下单是不一样的:产生的秒杀预订单不会马上 ...

  8. Redis锁解决高并发问题

    Redis锁解决高并发问题 redis真的是一个很好的技术,它可以很好的在一定程度上解决网站一瞬间的并发量,例如商品抢购秒杀等活动. redis之所以能解决高并发的原因是它可以直接访问内存,而以往我们 ...

  9. 50W-100W高并发,秒杀功能是怎么实现的?

    问:电商网站中,50W-100W高并发,秒杀功能是怎么实现的? 秒杀的套路千千万,反正物品肯定满足不了需求,抢不到东西也是正常的,所以套路可以全链路安排!下面以100w并发为例: 1 浏览器端直接随机 ...

最新文章

  1. 四、双向链表的操作分析和代码实现
  2. Why to do,What to do,Where to do 与 Lambda表达式!
  3. oracle 时间转化函数及常见函数 .
  4. Magicodes.IE 在100万数据量下导入导出性能测试
  5. asp.net 导出word excel 当前上下文中不存在名称“Encoding”报错问题
  6. 开发人员在编写 HTML 和 CSS 时最常犯的六大错误
  7. 在程序中表示什么_程序开发中:什么是前后端分离?你搞清楚了吗?
  8. Linux下mysql数据库的自动备份与还原 远程备份和本地备份
  9. Linux系统终于可以安装正版的QQ了
  10. 一行代码查看电脑Wifi密码
  11. 2005年度世界500强公司名单[转]
  12. 武汉_金山wps Java 一面 二面
  13. linux spec cpu,SPEC CPU2006的安装和使用
  14. ansible-playbook 远程启动程序ansible结束后程序也被关闭
  15. 删除node_modules慢【rimraf】
  16. 微信小程序怎么免费做
  17. tensorflow进阶(更新中...)
  18. ASEMI快恢复模块MUR20060CT的发展前景
  19. 晶闸管有很多种,最开始发明的是可控硅整流管
  20. Linux 修改 IP , DNS, NETMASK, GETEWAY

热门文章

  1. css+引入手机端插件,jQuery手机端mobiscroll时间选择插件
  2. C#_Word详细解析
  3. Sentinel使用教程
  4. 制作静态页面的悬浮框
  5. 微信小程序图片上传以及身份证识别
  6. Java Swing用户登陆界面
  7. 熊乃瑾自制蛋糕探望孤儿 岁末温情分享关爱
  8. java jsch 调用shell_使用Jsch执行Shell脚本
  9. 工业废水回用 实现工业绿色、和谐发展
  10. 立体视觉(一 概述)