nível de interface com base limitando

Em cenários de alta concorrência, temos de dizer três armas: cache, rebaixamento, limitando

Cache: Os dados são armazenados em cache, reduzindo o stress banco de dados, proteção e IO de disco DB

Rebaixamento: proteger o sistema de núcleo / serviço, reduzir sistema de solicitação de serviço de resposta não-núcleo para evitar falhas no sistema causadas por acumulação excessiva de pedidos

Limitando: o limite de pedido de um determinado período de tempo ou tempo de acesso de um sistema de proteção convencional

Aplicações distribuídas Micro Serviços, limitação de corrente, e outra identificação autoridade geral pode ser feita diretamente no gateway, Primavera Nuvem Gateway oferece a RequestRateLimiterGatewayFilterFactory oficial esta classe de script Lua Redis e percebeu a maneira como o token bucket limitar o .nginx pode fazer em nginx limit_req módulo

Este artigo é feito através da limitação da camada de aplicação desta forma Aop

Antes da linha de código e para introduzir um plug-in frame sob o Google a goiaba  https://www.yiibai.com/guava/   além da goiaba, há outras soluções limitando quadro correspondentes, tais como: Ali sentinela (sem contacto), da mola - Zuul-Ratelimit-Nuvem ( https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit ), etc.

Goiaba é baseado em open-source biblioteca Java, que contém muitos da biblioteca do núcleo Google está sendo usado por muitos de seus projetos. Esta biblioteca é facilitar a codificação e reduzir os erros de codificação. Esta biblioteca fornece para a recolha, cache, e primitivas método prático, a simultaneidade, notas comuns, processamento de corda, I / O e suporte de validação. Desde o tutorial original] [Ebey, reimpressão comercial entre em contato com o autor autorizado, reimpressão não comercial por favor reter o link original: https: //www.yiibai.com/guava/
 

Aqui ele será usado LoadingCache e RateLimiter. Algoritmo envolve algum conhecimento (através de token algoritmo / gotejante balde algoritmo), antes de o algoritmo de diferença dois blogueiros e artigos introduziram

LoadingCache ConcurrentMap semelhante, também thread-safe. Mas LoadingCache acrescenta política mais elementos de validade, ConcurrentMap só pode exibir elementos são removidos

Line e cupom:

Ratelimit anotação personalizado

package com.chwl.cn.ann;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value={ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {

	double limitNum() default 100.0;//请求数
	
	boolean ipRestricted() default true; //是否限制同一IP
	
	double ipLimitNum() default 1.0;//同一IP访问的次数(默认每秒)
}

Controlador: até duas solicitações por segundo

@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
	@RateLimit(limitNum=2.0,ipRestricted=true,ipLimitNum=1.0)
	public ProductEntity get(@PathVariable("id") long id) {
		return productService.selectById(id);
	}

AOP: se um sinal é adquirido dentro de um segundo, a mesma versão. Flexibilidade, se não, apenas um máximo de duas solicitações simultâneas por segundo para o Application Interface

package com.chwl.cn.aspect;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.chwl.cn.ann.RateLimit;
import com.chwl.cn.config.cache.ConcurrentHashMapCache;
import com.chwl.cn.utils.result.JsonMsg;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.RateLimiter;

@Component
@Aspect
@Order(0)
public class RateLimitAspect {

	private Logger log = LoggerFactory.getLogger(RateLimitAspect.class);

	// 用来存放不同接口的RateLimiter(key为接口名称,value为RateLimiter)
	private static ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>();

	private static ObjectMapper objectMapper = new ObjectMapper();

	static {
		objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
	}

	@Autowired
	private HttpServletRequest request;
	
	@Autowired
	private HttpServletResponse response;

	@Pointcut("@annotation(com.chwl.cn.ann.RateLimit)")
	public void serviceLimit() {
	}

	@Around("serviceLimit()")
	public Object Around(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
		Object obj = null;
		// 获取拦截的方法名
		Signature sig = joinPoint.getSignature();
		String methodName = sig.getName();
		// 获取拦截的方法名
		MethodSignature msig = (MethodSignature) sig;
		// 返回被织入增加处理目标对象
		Object target = joinPoint.getTarget();
		Method method = msig.getMethod();
		RateLimit annotation = method.getAnnotation(RateLimit.class);
//		RateLimit annotation = target.getClass().getAnnotation(RateLimit.class);
		double limitNum = annotation.limitNum();
		String requestURI = request.getRequestURI();
		// 避免方法名重复导致rateLimiter被覆盖
		String key=requestURI+methodName;
		// 获取rateLimiter
//		ConcurrentHashMapCache cacheManager = ConcurrentHashMapCache.getCacheManagerInstance();
//		cacheManager.init();
		if (!map.containsKey(key)) {
			map.put(key, RateLimiter.create(limitNum));
		} 
		RateLimiter rateLimiter = map.get(key);
		try {
			// 是否能马上获取到令牌或者在1秒之内能获取到1个令牌
			if (rateLimiter.tryAcquire()||rateLimiter.tryAcquire(1,1,TimeUnit.SECONDS)) {
				log.info("他们真的来了");
				// 放行,执行方法
				obj=joinPoint.proceed();
			} else {
				// 限制访问 拒绝了请求(服务降级)
				String result = objectMapper.writeValueAsString(JsonMsg.Error(500, "系统繁忙!"));
				log.error("拒绝了请求:" + result);
				outErrorResult(result);
			}
		} catch (Throwable e) {
		}
		return obj;
	}

	// 将结果返回
	public void outErrorResult(String result) {
		response.setContentType("application/json;charset=UTF-8");
		try (ServletOutputStream outputStream = response.getOutputStream()) {
			outputStream.write(result.getBytes("utf-8"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

O uso concomitante de interface 20 pedidos JMeter

Este é várias vezes solicitações simultâneas é parte limitada

IP continua limitando, exemplos: Interface de dois pedidos por segundo restritor, limitando o acesso a IP uma vez por segundo no negócio real é para limitar o IP atual, e alguns

Ainda assim, o mesmo método de tratamento,

@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
	@RateLimit(limitNum=2.0,ipRestricted=true,ipLimitNum=1.0)
	public ProductEntity get(@PathVariable("id") long id) {
		return productService.selectById(id);
	}

AOP:

package com.chwl.cn.aspect;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.chwl.cn.ann.RateLimit;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.RateLimiter;

@Component
@Aspect
@Order(0)
public class RateLimitAspect {

	private Logger log = LoggerFactory.getLogger(RateLimitAspect.class);

	// 用来存放不同接口的RateLimiter(key为接口名称,value为RateLimiter)
	private static ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>();

	private static ObjectMapper objectMapper = new ObjectMapper();
	
	private RateLimiter rateLimiter;

	static {
		objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
	}

	@Autowired
	private HttpServletRequest request;

	@Autowired
	private HttpServletResponse response;

	@Pointcut("@annotation(com.chwl.cn.ann.RateLimit)")
	public void serviceLimit() {
	}

	@Around("serviceLimit()")
	public Object Around(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
		Object obj = null;
		// 获取拦截的方法名
		Signature sig = joinPoint.getSignature();
		String methodName = sig.getName();
		// 获取拦截的方法名
		MethodSignature msig = (MethodSignature) sig;
		// 返回被织入增加处理目标对象
		Object target = joinPoint.getTarget();
		Method method = msig.getMethod();
		RateLimit annotation = method.getAnnotation(RateLimit.class);
//		RateLimit annotation = target.getClass().getAnnotation(RateLimit.class);
		double limitNum = annotation.limitNum();
		String requestURI = request.getRequestURI();
		// 避免方法名重复导致rateLimiter被覆盖
		String key=requestURI+methodName;
		// 获取rateLimiter
//		ConcurrentHashMapCache cacheManager = ConcurrentHashMapCache.getCacheManagerInstance();
//		cacheManager.init();
		String ip = getIpAddress(request);
		if (!map.containsKey(key)) {
			rateLimiter= RateLimiter.create(limitNum,1,TimeUnit.SECONDS);
			map.put(key,rateLimiter);
		} 
		rateLimiter = map.get(key);
		try {
			if (rateLimiter.tryAcquire()) {
				log.info("他们真的来了");
				boolean ipRestricted = annotation.ipRestricted();
				if(ipRestricted){
					double ipLimitNum = annotation.ipLimitNum();
					//IP限流总次数总是不大于接口总限流次数
					if(ipLimitNum>limitNum){
						ipLimitNum=limitNum;
					}
					//组装ip次数放令牌桶
					String loadingCacheKey=key+ip+"&"+ipLimitNum;
					RateLimiter limiter = caches.get(loadingCacheKey);
					if(limiter.tryAcquire()){
						log.error("来了来了来了");
						// 放行,执行方法
						obj=joinPoint.proceed();
					}else {
						//根据实际业务处理
						log.error("网络连接错误,当前IP请求错误,每个IP每秒最多只能访问"+ipLimitNum+"次");
					}
				}else {
					obj=joinPoint.proceed();
				}
			} else {
				// 限制访问 拒绝了请求(服务降级)
				log.error("拒绝了请求");
			}
		} catch (Throwable e) {
		}
		return obj;
	}

	// 根据IP分不同的令牌桶 目的在于对每个IP的令牌桶RateLimiter本地缓存在loadingcach
	private static LoadingCache<String, RateLimiter> caches = CacheBuilder.newBuilder().maximumSize(1000)
			// 一秒过期
			.expireAfterWrite(1, TimeUnit.SECONDS)
			//通过CacheLoader构建RateLimiter在loadingchahe本地缓存起来,如果不存在则自动新建并缓存,存在直接取出
			.build(new CacheLoader<String, RateLimiter>() {
				@Override
				public RateLimiter load(String key) throws Exception {
					String ipLimitNum = (key.split("&"))[1];
					// 新的IP初始化 (限流每秒ipLimitNum个令牌响应)
					return RateLimiter.create(Double.valueOf(ipLimitNum));
				}
			});

	public static String getIpAddress(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

}

Jmeter teste 10 segmentos simultâneos

Até 2 vezes por segundo, limitando o IP é apenas uma passagem, a autenticação for bem sucedida

 

ConcurrentHashMap pode ser substituído LoadingCache é o mesmo

Estes são limitantes aplicação de interface baseada no monómero ou serviço não é fornecido pelo próprio conjunto, se for distribuído micro-serviços projectar, um agrupamento de serviços, o método acima não funcionar, RateLimiter para aglomerados distribuídos, tais como fadiga. A necessidade de outros programas, anexado ao

Publicado 288 artigos originais · Louvor obteve 88 · vista 430 000 +

Acho que você gosta

Origin blog.csdn.net/ypp91zr/article/details/90678462
Recomendado
Clasificación