흐름 제한 실현 및 해결 방안

9995 단어 JAVA 개발
1. 흐름 제한 작업:
  • 왜 흐름을 제한하는지는 사용자가 악의적으로 인터페이스를 갱신하는 것을 방지하는 것이다. 외부 서버에 배치되고 우리가 웹socket의 인터페이스를 이용하여 실현한 것이기 때문에 회사가 하드웨어에 대한 업그레이드를 하지 않아 프로그램의 시간 붕괴를 초래했다. 이 문제를 해결하기 위해 회사의 거물들에게 방법을 가르쳐 주고 흐름을 제한하는 조작을 하도록 한다.하지만 결국 원인을 찾았습니다. 처리량은 1만 6 정도입니다. 테스트 서버로 테스트를 했습니다. 제가 개발한 노트북은 압축 테스트를 했습니다. 도구는 Jmeter입니다. 결과적으로 제 컴퓨터가 응답하지 않고 끊겼습니다. 서버가 아직 끊기지 않았습니다.
  • 흐름을 제한하는 방법은 흔히 볼 수 있는 흐름을 제한한다. 1. 넷플릭스의hystrix2, 아리계 개원의sentinel3, 흐름을 제한하고 높은 병렬 인터페이스를 처리하기 위한 방식: , , , , kafka, 등 기술 차원: 1. 같은 요청이 있는지 판단하기 위해 자신의 캐시를 통해 같은 요청을 막을 수 있다. 2, 부하 균형, 예를 들어nginx3,캐시 데이터베이스로 핫이슈 데이터 get을 캐시에 저장합니다. redis, ES4, 연결 탱크 업무 차원을 잘 사용합니다. 1. 상호작용을 추가하고 줄을 서서 기다립니다
  • 2. 응용 단계 흐름 제한과 흐름 제한 실현: 방법 1. 구글의guava를 사용하여 영패통 알고리즘 실현: 매끄러운 돌발 흐름 제한(SmoothBursty), 매끄러운 예열 흐름 제한(SmoothWarming Up) 실현
            
            
                com.google.guava
                guava
                23.0
            
    
    
    package com.citydo.dialogue.controller;
    
    import com.google.common.util.concurrent.RateLimiter;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Collections;
    
    @RestController
    public class HomeController {
    
        //  1 10 
         private RateLimiter limiter = RateLimiter.create(10.0);
        //RateLimiter.create(doublepermitsPerSecond, long warmupPeriod, TimeUnit unit);
        //RateLimiter limiter = RateLimiter.create(5, 1000,  TimeUnit.MILLISECONDS);
        //permitsPerSecond:      
        // warmupPeriod:          
      
    
        @GetMapping("/test/{name}")
        public String Test(@PathVariable("name") String name){
            //  RateLimiter,  permits 
            final double acquire = limiter.acquire();
            System.out.println("--------"+acquire);
            // double 0
            if(acquire>=(-1e-6)&&acquire<=(1e-6)){
                return name;
            }else{
                return " ";
            }
        }
    }
    

    이것은 약간 QPS 데이터 제어와 유사하다. QPS가 특정한 한도값을 초과할 때 데이터 제어를 하는 조치를 취한다.직접 거부: 방식은 기본 데이터 제어 방식입니다. QPS가 임의의 규칙의 한도값을 초과하면 새로운 요청은 즉시 거부됩니다. 거부 방식은 Exception을 던지거나 404를 되돌려줍니다.이런 방식은 시스템 처리 능력에 대해 확실하게 알고 있는 상황에서 예를 들어 압력 측정을 통해 시스템의 정확한 수위를 확정했을 때 적용된다.
    방법 2. redis 증가 1을 한 번 요청하면 키는sentinel: 、Warm Up、 또는 하나IP+ 일 수 있습니다. 없으면 만들 수 있습니다. 만료 시간 설정 차단기를 설정해야 합니다.
    package com.citydo.dialogue.config;
    
    import com.citydo.dialogue.service.AccessLimitInterceptor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    /**
     *  
     * @author nick
     */
    
    @Configuration
    public class InterceptorConfig extends WebMvcConfigurationSupport {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //addPathPatterns  
            registry.addInterceptor(new AccessLimitInterceptor())
                    // 
                    .addPathPatterns("/**");
                   //swagger2   .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
                   //.excludePathPatterns("/*")
                   // 
        }
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/");
        }
    }
    
    

    차단 방법
    package com.citydo.dialogue.service;
    
    import com.citydo.dialogue.entity.AccessLimit;
    import com.citydo.dialogue.utils.IpUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.concurrent.TimeUnit;
    
    public class AccessLimitInterceptor implements HandlerInterceptor {
    
        // RedisTemplate redis
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();
                if (!method.isAnnotationPresent(AccessLimit.class)) {
                    return true;
                }
                AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
                if (accessLimit == null) {
                    return true;
                }
                int limit = accessLimit.limit();
                int sec = accessLimit.sec();
                String key = IpUtil.getIpAddr(request) + request.getRequestURI();
                // 
                String formatDate=new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
                //String key="request_"+formatDate;
                Integer maxLimit = redisTemplate.opsForValue().get(key);
                if (maxLimit == null) {
                    //set 
                    redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);
                } else if (maxLimit < limit) {
                    redisTemplate.opsForValue().set(key, maxLimit + 1, sec, TimeUnit.SECONDS);
                } else {
                    output(response, " !");
                    return false;
                }
            }
            return true;
        }
    
        public void output(HttpServletResponse response, String msg) throws IOException {
            response.setContentType("application/json;charset=UTF-8");
            ServletOutputStream outputStream = null;
            try {
                outputStream = response.getOutputStream();
                outputStream.write(msg.getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                outputStream.flush();
                outputStream.close();
            }
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
        }
    }
    

    메모로 설정할 수도 있고 매개 변수를 직접 추가할 수도 있습니다
    package com.citydo.dialogue.entity;
    
    import java.lang.annotation.*;
    
    @Inherited
    @Documented
    @Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AccessLimit {
        //   sec 
        int limit() default 5;
        //   
        int sec() default 5;
    }
    

    redis 구성 작성
    package com.citydo.dialogue.config;
    
    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.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    /**
     *  redis 
     *  
     * @author nick
     */
    @Configuration
    public class RedisConfig {
        /**
         *  
         */
        @Bean
        public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate template = new RedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            template.setKeySerializer(new StringRedisSerializer());
            template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
            template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
            template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
            template.afterPropertiesSet();
            return template;
        }
    }
    

    방법 3. 분포식 유동 제한, 분포식 유동 제한의 가장 관건적인 것은 유동 제한 서비스를 원자화하는 것이다. 해결 방안은 + 또는 redis+lua 기술로 실현할 수 있다.
    방법 4. 연못화 기술을 사용하여 총 자원 수: 연결 연못, 스레드 연못을 제한할 수 있다.예를 들어 모든 응용 프로그램에 분배된 데이터베이스 연결이 100이라면 본 응용 프로그램은 최대 100개의 자원을 사용할 수 있으며 기다리거나 던질 수 있는 이상을 초과할 수 있다.
    메서드 5. Tomcat을 사용한 경우 Connector의 구성에는 다음과 같은 몇 가지 매개변수가 있습니다.
    maxThreads: 
    Tomcat    , , 。
    maxConnections: 
     ,   。
    acceptCount: 
      Tomcat  ,   ,   ,   。
    

    메서드 6, Java의 AtomicLong을 사용하여 코드를 나타내는 인터페이스의 총 동시/요청 수를 제한합니다.
    try{
        if(atomic.incrementAndGet() >  ) {
            // 
        } else {
            // 
        }
    } finally {
        atomic.decrementAndGet();
    }
    

    메서드 7, 어떤 인터페이스의 시간 창 요청 수를 Guava의 Cache로 제한하여 코드를 표시합니다.
    LoadingCache counter = CacheBuilder.newBuilder()
        .expireAfterWrite(2, TimeUnit.SECONDS)
        .build(newCacheLoader() {
            @Override
            public AtomicLong load(Long seconds) throws Exception {
                return newAtomicLong(0);
            }
        });
    longlimit =1000;
    while(true) {
        //  
        long currentSeconds = System.currentTimeMillis() /1000;
        if(counter.get(currentSeconds).incrementAndGet() > limit) {
            System.out.println(" : " + currentSeconds);
            continue;
        }
        //  
    }
    

    어떤 목적이든 흐름을 제한하기 위해서다.참조:https://blog.csdn.net/fanrenxiang/article/details/80683378참조:https://mp.weixin.qq.com/s/2_oDGJiI1GhaNYnaeL7Qpg소스 코드:https://github.com/863473007/springboot_current_limiting

    좋은 웹페이지 즐겨찾기