Na vida real, a função de pico é mais comum, como 12306 obtenção de ingressos, atividades de pico do sistema de comércio eletrônico etc. O chamado pico, da perspectiva do negócio de aplicativos, significa que vários usuários "competem" por um determinado recurso em um curto período de tempo. O recurso aqui é uma commodity na maioria dos cenários de pico; do ponto de vista técnico, é uma combinação de vários threads. Recursos para operar. Portanto, para realizar a função spike, é necessário controlar a competição do segmento por recursos, não apenas para garantir alta eficiência e simultaneidade, mas também para garantir a correção da operação e atender às reais necessidades do negócio.
As ideias de otimização para pico são:
- Escreva na memória.
- Realize o processamento assíncrono multithread.
- Realize o processamento distribuído.
[Exemplo] Um exemplo de uso de multi-threading para atingir 1000 pessoas matando 100 telefones celulares em segundos.
(1) No arquivo de informações de configuração pom.xml, adicione as dependências do iniciador Redis e do cliente Jedis:
<!-- Redis启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.4.0</version>
</dependency>
<!-- Jedis客户端依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
(2) Implementação de SecondKill (SecondKill.java): Crie vários threads e use a função de transação do Redis para realizar a segunda função de eliminação.
package com.pjb.seckill;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import java.util.List;
/**
* 秒杀抢购
* @author pan_junbiao
**/
public class SecondKill implements Runnable
{
String key = "iphone";
Jedis jedis = new Jedis("127.0.0.1",6379);
String userInfo;
public SecondKill(String userInfo)
{
this.userInfo = userInfo;
}
@Override
public void run()
{
try
{
jedis.watch(key); //watchkeys
String val = jedis.get(key);
int valint = Integer.valueOf(val);
if(valint<=100 && valint>=1)
{
//1、使用MULTI命令开启事务
Transaction tx = jedis.multi();
//2、事务命令入队
tx.incrBy(key,-1);
//3、使用EXEC命令执行事务
//提交事务。如果此时watchkeys被改动了,则返回null
List<Object> list = tx.exec();
if(list==null || list.size()==0)
{
String failuserinfo = "fail_" + userInfo;
String failinfo = "用户:" + failuserinfo + "商品争抢失败,抢购失败";
System.out.println(failinfo);
//抢购失败业务逻辑
jedis.setnx(failuserinfo,failinfo);
}
else
{
for(Object succ : list)
{
String succuserinfo = "succ_" + succ.toString() + "_" + userInfo;
String succinfo = "用户:" + succuserinfo + " 抢购成功,当前抢购成功人数:" + (1 -(valint -100));
System.out.println(succinfo);
//抢购成功业务逻辑
jedis.setnx(succuserinfo,succinfo);
}
}
}
else
{
String failuserinfo = "kcfail_" + userInfo;
String failinfo1 = "用户:" + failuserinfo + " 商品被抢购完毕,抢购失败";
System.out.println(failinfo1);
jedis.setnx(failuserinfo,failinfo1);
return;
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
finally
{
jedis.close();
}
}
}
(3) O programa principal (SecondKillTest.java): o programa de entrada da função spike, usado para criar um pool de threads, ao mesmo tempo, gerar um ID de usuário, chamar o código da função spike e concluir a tarefa spike.
package com.pjb.seckill;
import com.pjb.util.RedisHelper;
import redis.clients.jedis.Jedis;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Redis秒杀功能的实现,1000人抢购100部手机
* @author pan_junbiao
**/
public class SecondKillTest
{
public static void main(String[] args)
{
final String key = "iphone";
//20个线程池并发数
ExecutorService executor = Executors.newFixedThreadPool(20);
final Jedis jedis = new Jedis("127.0.0.1",6379);
jedis.del(key); //先删除
jedis.set(key,"100"); //设置起始的抢购数
jedis.close();
for(int i=0; i<1000; i++)
{
String userInfo = "pan_junbiao的博客" + i;
executor.execute(new SecondKill(userInfo));
}
executor.shutdown();
}
}
Resultados de: