A otimização da interface é um requisito de negócios durante o processo de desenvolvimento para evitar que um único módulo de microsserviço experimente repentinamente muita simultaneidade e resulte em serviços de usuário instáveis, ou seja, para atingir um número fixo de acessos à mesma interface em um período de tempo fixo.
Durante o processo de desenvolvimento, o redis geralmente é usado como um cache para armazenamento e recuperação rápidos. Dados que exigem um grande número de vezes podem ser armazenados no redis, então podemos usar o redis para obter a otimização da interface?
Primeiro, vamos falar sobre a anotação de anotação personalizada
Sabemos que a estrutura geral das anotações é:
@Target({ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Repeatable
public @interface 注解名{
类型 属性名() default 默认值,
...
}
Target
Serve para especificar onde essa anotação pode ser colocada, METHOD
ou seja, no método, PARAMTERS
no parâmetro e assim por diante.
Retention
é o tempo de vida da anotação especificada, RUNTIME>CLASS>SOURCE
.
Repeatable
Refere-se a se a anotação pode ser reutilizada na mesma classe, método ou propriedade.
Quando especificamos uma anotação, podemos @注解名
usá-la diretamente.
Em segundo lugar, vamos falar sobre AOP novamente
Quando eu estava aprendendo Spring antes, eu disse que existem duas maneiras de usar o aop. Aqui eu descrevo principalmente como usar as anotações do aop. Primeiro de tudo, se quisermos usar o aop, precisamos ter dependências do aop.
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
Aspect é uma anotação de uma classe de aspecto, e a classe marcada se tornará uma classe de aspecto, na qual alguns pontos de corte podem ser definidos e aprimoramentos como borda frontal e traseira do ponto de corte podem ser definidos.
Código-fonte do Aspect:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Aspect {
String value() default "";
}
Adicione o formato da expressão pointcut: execution(切入点方法修饰符 返回值类型 类的全限定名.方法名(..) )
, que *
representa tudo.
3. Caso de aplicação: Realize a limitação de redis simulada
Depois de importar as dependências relevantes, escrevemos anotações personalizadas @InCache
:
/ ElementType.FIELD用于属性,ElementType.PARAMETER用于方法参数,ElementType.METHOD 用于方法
@Target({ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface InCache {
String key() default "";
int value() default 0;
}
No futuro, esta chave e valor podem ser colocados no redis simulado. Obviamente, o redis real adicionará um parâmetro de tempo de expiração ao definir setnx. Como o redis simulado é usado aqui, esse parâmetro de tempo não será adicionado concurrentMap
.
Em seguida, escrevemos a interface básica e sua classe de serviço:
//利用自定义注解和Aop对接口进行节流
@RestController
@CrossOrigin
public class UserController {
@Resource
UserServiceImpl userService;
@GetMapping("login")
@ResponseBody
@InCache(key = "login",value = 3)
public String login(){
Integer value = AopUtil.redis.get("login");
if (value == 0)
return "访问次数刷完了,访问被refuse";
return "第"+(4-value)+"次访问:"+userService.login("account", "password");
}
}
@Component
public class UserServiceImpl {
public String login(String account, String password) {
if (account.equals("account") & password.equals("password")) {
System.out.println("----------user login success -------");
return "success";
}else
return "fail";
}
}
Em seguida, configuramos nosso método de aprimoramento aop para o método login na classe do controlador:
@Aspect
@Component
@Slf4j
public class AopUtil {
//代替redis
public static ConcurrentMap<String,Integer> redis = new ConcurrentHashMap<>();
//定义切入点并使用切面类方法代替
@Pointcut("execution(public String com.example.aop.UserController.login())")
public void interPoint(){}
//利用前置增强检查注解
@Before("interPoint()")
public void checkAnnotation(JoinPoint joinPoint){
//获取参数列表
Object[] params = joinPoint.getArgs();
if (params.length != 0)
System.out.println("切入方法的参数列表为:"+ Arrays.toString(Arrays.stream(params).toArray()));
//获取方法,强行将签名对象类型转换成方法签名对象,仅用于在已知连接点类型为方法时使用
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//根据注解类型获取注解数组,这个直接就是索引加注解对象,可获取注解的属性值
InCache annotation = method.getAnnotation(InCache.class);
if (annotation == null){
System.out.println("该方法没有使用InCache注解!");
return ;
}
String key = annotation.key();
if(redis.containsKey(key)){
log.info("缓存已存在此key,将value进行减一操作");
Integer oldValue = redis.get(key);
if (oldValue > 0){
redis.remove(key);
redis.put(key,oldValue-1);
}else {
log.info("缓存中的此key已经为0,拒绝接口访问数据库!");
}
}else {
log.info("将key 与 value 存入缓存");
redis.put(key,annotation.value());
}
}
}
Desta forma, o número básico de vezes limite pode ser realizado.