0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

通过秒杀商品来模拟高并发的场景

jf_ro2CN3Fa 来源:CSDN 2023-02-07 10:47 次阅读

1.引言

高并发场景在现场的日常工作中很常见,特别是在互联网公司中,这篇文章就来通过秒杀商品模拟高并发的场景。文章末尾会附上文章的所有代码、脚本和测试用例。

本文环境: SpringBoot 2.5.7 + MySQL 8.0 X + MybatisPlus + Swagger2.9.2

模拟工具: Jmeter

模拟场景: 减库存->创建订单->模拟支付

2.商品秒杀-超卖

在开发中,对于下面的代码,可能很熟悉:在Service里面加上@Transactional事务注解和Lock锁

控制层:Controller

@ApiOperation(value="秒杀实现方式——Lock加锁")
@PostMapping("/start/lock")
publicResultstartLock(longskgId){
try{
log.info("开始秒杀方式一...");
finallonguserId=(int)(newRandom().nextDouble()*(99999-10000+1))+10000;
Resultresult=secondKillService.startSecondKillByLock(skgId,userId);
if(result!=null){
log.info("用户:{}--{}",userId,result.get("msg"));
}else{
log.info("用户:{}--{}",userId,"哎呦喂,人也太多了,请稍后!");
}
}catch(Exceptione){
e.printStackTrace();
}finally{

}
returnResult.ok();
}

业务层:Service

@Override
@Transactional(rollbackFor=Exception.class)
publicResultstartSecondKillByLock(longskgId,longuserId){
lock.lock();
try{
//校验库存
SecondKillsecondKill=secondKillMapper.selectById(skgId);
Integernumber=secondKill.getNumber();
if(number>0){
//扣库存
secondKill.setNumber(number-1);
secondKillMapper.updateById(secondKill);
//创建订单
SuccessKilledkilled=newSuccessKilled();
killed.setSeckillId(skgId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(newTimestamp(System.currentTimeMillis()));
successKilledMapper.insert(killed);

//模拟支付
Paymentpayment=newPayment();
payment.setSeckillId(skgId);
payment.setSeckillId(skgId);
payment.setUserId(userId);
payment.setMoney(40);
payment.setState((short)1);
payment.setCreateTime(newTimestamp(System.currentTimeMillis()));
paymentMapper.insert(payment);
}else{
returnResult.error(SecondKillStateEnum.END);
}
}catch(Exceptione){
thrownewScorpiosException("异常了个乖乖");
}finally{
lock.unlock();
}
returnResult.ok(SecondKillStateEnum.SUCCESS);
}

对于上面的代码应该没啥问题吧,业务方法上加事务,在处理业务的时候加锁。

但上面这样写法是有问题的,会出现超卖的情况,看下测试结果:模拟1000个并发,抢100商品

5959818e-a3be-11ed-bfe3-dac502259ad0.png59714ed6-a3be-11ed-bfe3-dac502259ad0.png

这里在业务方法开始加了锁,在业务方法结束后释放了锁。但这里的事务提交却不是这样的,有可能在事务提交之前,就已经把锁释放了,这样会导致商品超卖现象。所以加锁的时机很重要!

3. 解决商品超卖

对于上面超卖现象,主要问题出现在事务中锁释放的时机,事务未提交之前,锁已经释放。(事务提交是在整个方法执行完)。如何解决这个问题呢,就是把加锁步骤提前

可以在controller层进行加锁

可以使用Aop在业务方法执行之前进行加锁

3.1 方式一(改进版加锁)

@ApiOperation(value="秒杀实现方式——Lock加锁")
@PostMapping("/start/lock")
publicResultstartLock(longskgId){
//在此处加锁
lock.lock();
try{
log.info("开始秒杀方式一...");
finallonguserId=(int)(newRandom().nextDouble()*(99999-10000+1))+10000;
Resultresult=secondKillService.startSecondKillByLock(skgId,userId);
if(result!=null){
log.info("用户:{}--{}",userId,result.get("msg"));
}else{
log.info("用户:{}--{}",userId,"哎呦喂,人也太多了,请稍后!");
}
}catch(Exceptione){
e.printStackTrace();
}finally{
//在此处释放锁
lock.unlock();
}
returnResult.ok();
}

上面这样的加锁就可以解决事务未提交之前,锁释放的问题,可以分三种情况进行压力测试:

并发数1000,商品100

并发数1000,商品1000

并发数2000,商品1000

对于并发量大于商品数的情况,商品秒杀一般不会出现少卖的请况,但对于并发数小于等于商品数的时候可能会出现商品少卖情况,这也很好理解。

对于没有问题的情况就不贴图了,因为有很多种方式,贴图会太多

59806e02-a3be-11ed-bfe3-dac502259ad0.png

3.2 方式二(AOP版加锁)

对于上面在控制层进行加锁的方式,可能显得不优雅,那就还有另一种方式进行在事务之前加锁,那就是AOP

自定义AOP注解

@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceServiceLock{
Stringdescription()default"";
}

定义切面类

@Slf4j
@Component
@Scope
@Aspect
@Order(1)//order越小越是最先执行,但更重要的是最先执行的最后结束
publicclassLockAspect{
/**
*思考:为什么不用synchronized
*service默认是单例的,并发下lock只有一个实例
*/
privatestaticLocklock=newReentrantLock(true);//互斥锁参数默认false,不公平锁

//Service层切点用于记录错误日志
@Pointcut("@annotation(com.scorpios.secondkill.aop.ServiceLock)")
publicvoidlockAspect(){

}

@Around("lockAspect()")
publicObjectaround(ProceedingJoinPointjoinPoint){
lock.lock();
Objectobj=null;
try{
obj=joinPoint.proceed();
}catch(Throwablee){
e.printStackTrace();
thrownewRuntimeException();
}finally{
lock.unlock();
}
returnobj;
}
}

在业务方法上添加AOP注解

@Override
@ServiceLock//使用Aop进行加锁
@Transactional(rollbackFor=Exception.class)
publicResultstartSecondKillByAop(longskgId,longuserId){

try{
//校验库存
SecondKillsecondKill=secondKillMapper.selectById(skgId);
Integernumber=secondKill.getNumber();
if(number>0){
//扣库存
secondKill.setNumber(number-1);
secondKillMapper.updateById(secondKill);
//创建订单
SuccessKilledkilled=newSuccessKilled();
killed.setSeckillId(skgId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(newTimestamp(System.currentTimeMillis()));
successKilledMapper.insert(killed);

//支付
Paymentpayment=newPayment();
payment.setSeckillId(skgId);
payment.setSeckillId(skgId);
payment.setUserId(userId);
payment.setMoney(40);
payment.setState((short)1);
payment.setCreateTime(newTimestamp(System.currentTimeMillis()));
paymentMapper.insert(payment);
}else{
returnResult.error(SecondKillStateEnum.END);
}
}catch(Exceptione){
thrownewScorpiosException("异常了个乖乖");
}
returnResult.ok(SecondKillStateEnum.SUCCESS);
}

控制层:

@ApiOperation(value="秒杀实现方式二——Aop加锁")
@PostMapping("/start/aop")
publicResultstartAop(longskgId){
try{
log.info("开始秒杀方式二...");
finallonguserId=(int)(newRandom().nextDouble()*(99999-10000+1))+10000;
Resultresult=secondKillService.startSecondKillByAop(skgId,userId);
if(result!=null){
log.info("用户:{}--{}",userId,result.get("msg"));
}else{
log.info("用户:{}--{}",userId,"哎呦喂,人也太多了,请稍后!");
}
}catch(Exceptione){
e.printStackTrace();
}
returnResult.ok();
}

这种方式在对锁的使用上,更高阶、更美观!

3.3 方式三(悲观锁一)

除了上面在业务代码层面加锁外,还可以使用数据库自带的锁进行并发控制。

悲观锁,什么是悲观锁呢?通俗的说,在做任何事情之前,都要进行加锁确认。这种数据库级加锁操作效率较低。

使用for update一定要加上事务,当事务处理完后,for update才会将行级锁解除

如果请求数和秒杀商品数量一致,会出现少卖

@ApiOperation(value="秒杀实现方式三——悲观锁")
@PostMapping("/start/pes/lock/one")
publicResultstartPesLockOne(longskgId){
try{
log.info("开始秒杀方式三...");
finallonguserId=(int)(newRandom().nextDouble()*(99999-10000+1))+10000;
Resultresult=secondKillService.startSecondKillByUpdate(skgId,userId);
if(result!=null){
log.info("用户:{}--{}",userId,result.get("msg"));
}else{
log.info("用户:{}--{}",userId,"哎呦喂,人也太多了,请稍后!");
}
}catch(Exceptione){
e.printStackTrace();
}
returnResult.ok();
}

业务逻辑

@Override
@Transactional(rollbackFor=Exception.class)
publicResultstartSecondKillByUpdate(longskgId,longuserId){
try{
//校验库存-悲观锁
SecondKillsecondKill=secondKillMapper.querySecondKillForUpdate(skgId);
Integernumber=secondKill.getNumber();
if(number>0){
//扣库存
secondKill.setNumber(number-1);
secondKillMapper.updateById(secondKill);
//创建订单
SuccessKilledkilled=newSuccessKilled();
killed.setSeckillId(skgId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(newTimestamp(System.currentTimeMillis()));
successKilledMapper.insert(killed);

//支付
Paymentpayment=newPayment();
payment.setSeckillId(skgId);
payment.setSeckillId(skgId);
payment.setUserId(userId);
payment.setMoney(40);
payment.setState((short)1);
payment.setCreateTime(newTimestamp(System.currentTimeMillis()));
paymentMapper.insert(payment);
}else{
returnResult.error(SecondKillStateEnum.END);
}
}catch(Exceptione){
thrownewScorpiosException("异常了个乖乖");
}finally{
}
returnResult.ok(SecondKillStateEnum.SUCCESS);
}

Dao层

@Repository
publicinterfaceSecondKillMapperextendsBaseMapper{

/**
*将此行数据进行加锁,当整个方法将事务提交后,才会解锁
*@paramskgId
*@return
*/
@Select(value="SELECT*FROMseckillWHEREseckill_id=#{skgId}FORUPDATE")
SecondKillquerySecondKillForUpdate(@Param("skgId")LongskgId);

}

上面是利用for update进行对查询数据加锁,加的是行锁

3.4 方式四(悲观锁二)

悲观锁的第二种方式就是利用update更新命令来加表锁

/**
*UPDATE锁表
*@paramskgId商品id
*@paramuserId用户id
*@return
*/
@Override
@Transactional(rollbackFor=Exception.class)
publicResultstartSecondKillByUpdateTwo(longskgId,longuserId){
try{

//不校验,直接扣库存更新
intresult=secondKillMapper.updateSecondKillById(skgId);
if(result>0){
//创建订单
SuccessKilledkilled=newSuccessKilled();
killed.setSeckillId(skgId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(newTimestamp(System.currentTimeMillis()));
successKilledMapper.insert(killed);

//支付
Paymentpayment=newPayment();
payment.setSeckillId(skgId);
payment.setSeckillId(skgId);
payment.setUserId(userId);
payment.setMoney(40);
payment.setState((short)1);
payment.setCreateTime(newTimestamp(System.currentTimeMillis()));
paymentMapper.insert(payment);
}else{
returnResult.error(SecondKillStateEnum.END);
}
}catch(Exceptione){
thrownewScorpiosException("异常了个乖乖");
}finally{
}
returnResult.ok(SecondKillStateEnum.SUCCESS);
}

Dao层

@Repository
publicinterfaceSecondKillMapperextendsBaseMapper{

/**
*将此行数据进行加锁,当整个方法将事务提交后,才会解锁
*@paramskgId
*@return
*/
@Select(value="SELECT*FROMseckillWHEREseckill_id=#{skgId}FORUPDATE")
SecondKillquerySecondKillForUpdate(@Param("skgId")LongskgId);

@Update(value="UPDATEseckillSETnumber=number-1WHEREseckill_id=#{skgId}ANDnumber>0")
intupdateSecondKillById(@Param("skgId")longskgId);
}

3.5 方式五(乐观锁)

乐观锁,顾名思义,就是对操作结果很乐观,通过利用version字段来判断数据是否被修改

乐观锁,不进行库存数量的校验,直接做库存扣减

这里使用的乐观锁会出现大量的数据更新异常(抛异常就会导致购买失败)、如果配置的抢购人数比较少、比如120:100(人数:商品) 会出现少买的情况,不推荐使用乐观锁。

@ApiOperation(value="秒杀实现方式五——乐观锁")
@PostMapping("/start/opt/lock")
publicResultstartOptLock(longskgId){
try{
log.info("开始秒杀方式五...");
finallonguserId=(int)(newRandom().nextDouble()*(99999-10000+1))+10000;
//参数添加了购买数量
Resultresult=secondKillService.startSecondKillByPesLock(skgId,userId,1);
if(result!=null){
log.info("用户:{}--{}",userId,result.get("msg"));
}else{
log.info("用户:{}--{}",userId,"哎呦喂,人也太多了,请稍后!");
}
}catch(Exceptione){
e.printStackTrace();
}
returnResult.ok();
}
@Override
@Transactional(rollbackFor=Exception.class)
publicResultstartSecondKillByPesLock(longskgId,longuserId,intnumber){

//乐观锁,不进行库存数量的校验,直接
try{
SecondKillkill=secondKillMapper.selectById(skgId);
//剩余的数量应该要大于等于秒杀的数量
if(kill.getNumber()>=number){
intresult=secondKillMapper.updateSecondKillByVersion(number,skgId,kill.getVersion());
if(result>0){
//创建订单
SuccessKilledkilled=newSuccessKilled();
killed.setSeckillId(skgId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(newTimestamp(System.currentTimeMillis()));
successKilledMapper.insert(killed);

//支付
Paymentpayment=newPayment();
payment.setSeckillId(skgId);
payment.setSeckillId(skgId);
payment.setUserId(userId);
payment.setMoney(40);
payment.setState((short)1);
payment.setCreateTime(newTimestamp(System.currentTimeMillis()));
paymentMapper.insert(payment);
}else{
returnResult.error(SecondKillStateEnum.END);
}
}
}catch(Exceptione){
thrownewScorpiosException("异常了个乖乖");
}finally{
}
returnResult.ok(SecondKillStateEnum.SUCCESS);
}
@Repository
publicinterfaceSecondKillMapperextendsBaseMapper{

/**
*将此行数据进行加锁,当整个方法将事务提交后,才会解锁
*@paramskgId
*@return
*/
@Select(value="SELECT*FROMseckillWHEREseckill_id=#{skgId}FORUPDATE")
SecondKillquerySecondKillForUpdate(@Param("skgId")LongskgId);

@Update(value="UPDATEseckillSETnumber=number-1WHEREseckill_id=#{skgId}ANDnumber>0")
intupdateSecondKillById(@Param("skgId")longskgId);

@Update(value="UPDATEseckillSETnumber=number-#{number},version=version+1WHEREseckill_id=#{skgId}ANDversion=#{version}")
intupdateSecondKillByVersion(@Param("number")intnumber,@Param("skgId")longskgId,@Param("version")intversion);
}

乐观锁会出现大量的数据更新异常(抛异常就会导致购买失败),会出现少买的情况,不推荐使用乐观锁

3.6 方式六(阻塞队列)

利用阻塞队类,也可以解决高并发问题。其思想就是把接收到的请求按顺序存放到队列中,消费者线程逐一从队列里取数据进行处理,看下具体代码。

阻塞队列:这里使用静态内部类的方式来实现单例模式,在并发条件下不会出现问题。

//秒杀队列(固定长度为100)
publicclassSecondKillQueue{

//队列大小
staticfinalintQUEUE_MAX_SIZE=100;

//用于多线程间下单的队列
staticBlockingQueueblockingQueue=newLinkedBlockingQueue(QUEUE_MAX_SIZE);

//使用静态内部类,实现单例模式
privateSecondKillQueue(){};

privatestaticclassSingletonHolder{
//静态初始化器,由JVM来保证线程安全
privatestaticSecondKillQueuequeue=newSecondKillQueue();
}

/**
*单例队列
*@return
*/
publicstaticSecondKillQueuegetSkillQueue(){
returnSingletonHolder.queue;
}

/**
*生产入队
*@paramkill
*@throwsInterruptedException
*add(e)队列未满时,返回true;队列满则抛出IllegalStateException(“Queuefull”)异常——AbstractQueue
*put(e)队列未满时,直接插入没有返回值;队列满时会阻塞等待,一直等到队列未满时再插入。
*offer(e)队列未满时,返回true;队列满时返回false。非阻塞立即返回。
*offer(e,time,unit)设定等待的时间,如果在指定时间内还不能往队列中插入数据则返回false,插入成功返回true。
*/
publicBooleanproduce(SuccessKilledkill){
returnblockingQueue.offer(kill);
}
/**
*消费出队
*poll()获取并移除队首元素,在指定的时间内去轮询队列看有没有首元素有则返回,否者超时后返回null
*take()与带超时时间的poll类似不同在于take时候如果当前队列空了它会一直等待其他线程调用notEmpty.signal()才会被唤醒
*/
publicSuccessKilledconsume()throwsInterruptedException{
returnblockingQueue.take();
}

/**
*获取队列大小
*@return
*/
publicintsize(){
returnblockingQueue.size();
}
}

消费秒杀队列:实现ApplicationRunner接口

//消费秒杀队列
@Slf4j
@Component
publicclassTaskRunnerimplementsApplicationRunner{

@Autowired
privateSecondKillServiceseckillService;

@Override
publicvoidrun(ApplicationArgumentsvar){
newThread(()->{
log.info("队列启动成功");
while(true){
try{
//进程内队列
SuccessKilledkill=SecondKillQueue.getSkillQueue().consume();
if(kill!=null){
Resultresult=seckillService.startSecondKillByAop(kill.getSeckillId(),kill.getUserId());
if(result!=null&&result.equals(Result.ok(SecondKillStateEnum.SUCCESS))){
log.info("TaskRunner,result:{}",result);
log.info("TaskRunner从消息队列取出用户,用户:{}{}",kill.getUserId(),"秒杀成功");
}
}
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}).start();
}
}
@ApiOperation(value="秒杀实现方式六——消息队列")
@PostMapping("/start/queue")
publicResultstartQueue(longskgId){
try{
log.info("开始秒杀方式六...");
finallonguserId=(int)(newRandom().nextDouble()*(99999-10000+1))+10000;
SuccessKilledkill=newSuccessKilled();
kill.setSeckillId(skgId);
kill.setUserId(userId);
Booleanflag=SecondKillQueue.getSkillQueue().produce(kill);
//虽然进入了队列,但是不一定能秒杀成功进队出队有时间间隙
if(flag){
log.info("用户:{}{}",kill.getUserId(),"秒杀成功");
}else{
log.info("用户:{}{}",userId,"秒杀失败");
}
}catch(Exceptione){
e.printStackTrace();
}
returnResult.ok();
}

注意:在业务层和AOP方法中,不能抛出任何异常, throw new RuntimeException()这些抛异常代码要注释掉。因为一旦程序抛出异常就会停止,导致消费秒杀队列进程终止!

使用阻塞队列来实现秒杀,有几点要注意:

消费秒杀队列中调用业务方法加锁与不加锁情况一样,也就是seckillService.startSecondKillByAop()、seckillService.startSecondKillByLock()方法结果一样,这也很好理解

当队列长度与商品数量一致时,会出现少卖的现象,可以调大数值

下面是队列长度1000,商品数量1000,并发数2000情况下出现的少卖

5995d896-a3be-11ed-bfe3-dac502259ad0.png

3.7.方式七(Disruptor队列)

Disruptor是个高性能队列,研发的初衷是解决内存队列的延迟问题,在性能测试中发现竟然与I/O操作处于同样的数量级,基于Disruptor开发的系统单线程能支撑每秒600万订单。

//事件生成工厂(用来初始化预分配事件对象)
publicclassSecondKillEventFactoryimplementsEventFactory{

@Override
publicSecondKillEventnewInstance(){
returnnewSecondKillEvent();
}
}
//事件对象(秒杀事件)
publicclassSecondKillEventimplementsSerializable{
privatestaticfinallongserialVersionUID=1L;
privatelongseckillId;
privatelonguserId;

//set/get方法略

}
//使用translator方式生产者
publicclassSecondKillEventProducer{

privatefinalstaticEventTranslatorVarargtranslator=(seckillEvent,seq,objs)->{
seckillEvent.setSeckillId((Long)objs[0]);
seckillEvent.setUserId((Long)objs[1]);
};

privatefinalRingBufferringBuffer;

publicSecondKillEventProducer(RingBufferringBuffer){
this.ringBuffer=ringBuffer;
}

publicvoidsecondKill(longseckillId,longuserId){
this.ringBuffer.publishEvent(translator,seckillId,userId);
}
}
//消费者(秒杀处理器)
@Slf4j
publicclassSecondKillEventConsumerimplementsEventHandler{


privateSecondKillServicesecondKillService=(SecondKillService)SpringUtil.getBean("secondKillService");

@Override
publicvoidonEvent(SecondKillEventseckillEvent,longseq,booleanbool){
Resultresult=secondKillService.startSecondKillByAop(seckillEvent.getSeckillId(),seckillEvent.getUserId());
if(result.equals(Result.ok(SecondKillStateEnum.SUCCESS))){
log.info("用户:{}{}",seckillEvent.getUserId(),"秒杀成功");
}
}
}
publicclassDisruptorUtil{

staticDisruptordisruptor;

static{
SecondKillEventFactoryfactory=newSecondKillEventFactory();
intringBufferSize=1024;
ThreadFactorythreadFactory=runnable->newThread(runnable);
disruptor=newDisruptor<>(factory,ringBufferSize,threadFactory);
disruptor.handleEventsWith(newSecondKillEventConsumer());
disruptor.start();
}

publicstaticvoidproducer(SecondKillEventkill){
RingBufferringBuffer=disruptor.getRingBuffer();
SecondKillEventProducerproducer=newSecondKillEventProducer(ringBuffer);
producer.secondKill(kill.getSeckillId(),kill.getUserId());
}
}
@ApiOperation(value="秒杀实现方式七——Disruptor队列")
@PostMapping("/start/disruptor")
publicResultstartDisruptor(longskgId){
try{
log.info("开始秒杀方式七...");
finallonguserId=(int)(newRandom().nextDouble()*(99999-10000+1))+10000;
SecondKillEventkill=newSecondKillEvent();
kill.setSeckillId(skgId);
kill.setUserId(userId);
DisruptorUtil.producer(kill);
}catch(Exceptione){
e.printStackTrace();
}
returnResult.ok();
}

经过测试,发现使用Disruptor队列队列,与自定义队列有着同样的问题,也会出现超卖的情况,但效率有所提高。

4. 小结

对于上面七种实现并发的方式,做一下总结:

一、二方式是在代码中利用锁和事务的方式解决了并发问题,主要解决的是锁要加载事务之前

三、四、五方式主要是数据库的锁来解决并发问题,方式三是利用for upate对表加行锁,方式四是利用update来对表加锁,方式五是通过增加version字段来控制数据库的更新操作,方式五的效果最差

六、七方式是通过队列来解决并发问题,这里需要特别注意的是,在代码中不能通过throw抛异常,否则消费线程会终止,而且由于进队和出队存在时间间隙,会导致商品少卖

上面所有的情况都经过代码测试,测试分一下三种情况:

并发数1000,商品数100

并发数1000,商品数1000

并发数2000,商品数1000

思考:分布式情况下如何解决并发问题呢?下次继续试验。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • MySQL
    +关注

    关注

    1

    文章

    831

    浏览量

    26779
  • AOP
    AOP
    +关注

    关注

    0

    文章

    40

    浏览量

    11124

原文标题:实现高并发秒杀的七种方式

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于阿里云Serverless架构下函数计算的最新应用场景详解(二)

    摘要: Serverless概念是近年来特别火的一个技术概念,基于这种架构能构建出很多应用场景,适合各行各业,只要对轻计算、高弹性、无状态等场景有诉求的用户都可以通过本文普及一些基础
    发表于 01-25 11:46

    消息队列的应用场景

    、流量削锋  流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛!  应用场景秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加
    发表于 06-23 10:19

    如何去实现一种基于SpringMVC的电商并发秒杀系统设计

    参考博客Java并发秒杀系统API目录业务场景要解决的问题Redis的使用业务场景首页倒计时秒杀
    发表于 01-03 07:50

    ATC'22顶会论文RunD:高密并发的轻量级 Serverless 安全容器运行时 | 龙蜥技术

    并发创建 cgroup 会导致同步时延,尤其在高密场景下带来的高调度开销。为此我们提出了 RunD — 超轻量级安全容器运行时,通过
    发表于 09-05 15:18

    HarmonyOS如何使用异步并发能力进行开发

    }`); } } myAsyncFunction(); 2、 单次I/O任务开发指导 Promise和async/await提供异步并发能力,适用于单次I/O任务的场景开发,本文以使用异步进行单次文件写入为例提供指导
    发表于 09-22 17:35

    如何通过多线程并发设计提高应用程序的性能

    这里我们简单总结了一下,在现代多处理器或多内核环境下,如何通过多线程并发设计提高我们应用程序的性能和响应性。
    的头像 发表于 09-28 02:13 5374次阅读

    高性能接口设计准则

    并发经常会发生在有大活跃用户量,用户聚集的业务场景中,如:秒杀活动,定时领取红包等。
    发表于 08-03 17:37 1268次阅读
    高性能接口设计准则

    解密并发业务场景下典型的秒杀系统的架构

    中,就更别提如何构建并发系统了! 究竟什么样的系统算是并发系统?今天,我们就一起解密并发
    的头像 发表于 11-17 10:32 2326次阅读
    解密<b class='flag-5'>高</b><b class='flag-5'>并发</b>业务<b class='flag-5'>场景</b>下典型的<b class='flag-5'>秒杀</b>系统的架构

    【源码版】基于SpringMVC的电商并发秒杀系统设计思路

    参考博客Java并发秒杀系统API目录业务场景要解决的问题Redis的使用业务场景首页倒计时秒杀
    发表于 01-12 10:23 0次下载
    【源码版】基于SpringMVC的电商<b class='flag-5'>高</b><b class='flag-5'>并发</b><b class='flag-5'>秒杀</b>系统设计思路

    服务器的并发能力如何提升?

    服务器的并发能力如何提升? 服务器并发能力体现着服务器在单位时间内的很强数据处理能力,一般来说,如果企业的互联网业务需要面对大量的同时在线请求,那么就需要高
    的头像 发表于 03-17 17:07 1065次阅读

    如何控制秒杀商品页面购买按钮的点亮

    售空;(4)一般是定时上架;(5)时间短、瞬时并发;   2 秒杀技术挑战 假设某网站秒杀活动只推出一件商品,预计会吸引1万人参加活动,
    的头像 发表于 06-29 11:12 904次阅读
    如何控制<b class='flag-5'>秒杀</b><b class='flag-5'>商品</b>页面购买按钮的点亮

    工业物联网平台如何应对并发应用场景

    面对的巨大挑战。对此,数之能提供并发、官翻机接入的工业物联网平台,可以适应并发场景应用需求。
    的头像 发表于 09-06 14:21 673次阅读

    java结合redis秒杀功能

    。本文将介绍如何结合Java和Redis实现秒杀功能,以及如何应对并发场景下的挑战。 一、秒杀
    的头像 发表于 12-04 11:06 660次阅读

    并发系统的艺术:如何在流量洪峰中游刃有余

    前言 我们常说的三并发可用、高性能,这些技术是构建现代互联网应用程序所必需的。对于京东618备战来说,所有的中台系统服务,无疑都是围绕着三
    的头像 发表于 08-05 13:43 348次阅读
    <b class='flag-5'>高</b><b class='flag-5'>并发</b>系统的艺术:如何在流量洪峰中游刃有余

    并发物联网云平台是什么

    并发物联网云平台是一种能够处理大量设备同时连接并进行数据交换的云计算平台。这种平台通常被设计用来应对来自数以万计甚至数十亿计的物联网设备的并发请求,保证系统的稳定性和响应速度。 首先,从技术层面
    的头像 发表于 08-13 13:50 316次阅读