億級の流量シーンでHTTPインターフェースの制限はどうなりますか?分かりました.


ここでは、ユーザー定義の注釈パッケージを用いてトークンバケット制限アルゴリズムに基づいてインターフェース制限ストリームを実現するWebインターフェース制限ストリームを実現する.
注釈を使わずにインターフェースの流れ制限を実現します.
プロジェクトを構築する
ここでは、SpringBootプロジェクトを使って、Httpインターフェース限定プロジェクトを構築します.Spring Bootプロジェクトは本質的にまだMavenプロジェクトです.だから、友達は直接にMavenプロジェクトを作成できます.ここのプロジェクト名はmykit-ratelimiter-testです.次に、pom.xmlファイルに次のような依存関係を加えて、プロジェクトをSprigBootプロジェクトとして構築します.

        org.springframework.boot
        spring-boot-starter-parent
        2.2.6.RELEASE
    

    4.0.0
    io.mykit.limiter
    mykit-ratelimiter-test
    1.0.0-SNAPSHOT
    jar
    mykit-ratelimiter-test

    
        28.2-jre
    

    
        
            org.springframework.boot
            spring-boot-starter-test
        

        
            org.springframework.boot
            spring-boot-starter-web
            
                
                    org.springframework.boot
                    spring-boot-starter-tomcat
                
                
                    org.springframework.boot
                    spring-boot-starter-logging
                
            
        

        
            org.springframework.boot
            spring-boot-starter-undertow
        

        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

        
            org.springframework.boot
            spring-boot-starter-data-redis
        

        
            org.aspectj
            aspectjweaver
        

        
            org.springframework.boot
            spring-boot-starter-aop
        

        
            com.google.guava
            guava
            ${guava.version}
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.1
                
                    ${java.version}
                    ${java.version}
                
            
        
    
見られます.プロジェクトの中でSpringBoot関連のJarバッグを引用したほか、guavaのフレームを引用しました.バージョンは28.2-jreです.
コアクラスを作成
ここで、私は主にお支払いインターフェースの制限シーンをシミュレーションします.まず、PayServiceインターフェースとMessage Serviceインターフェースを定義します.PayServiceインターフェースは主に後続の支払い業務をシミュレーションするために用いられ、Message Serviceインターフェースはメッセージをアナログで送る.インターフェースの定義はそれぞれ以下の通りです.
  • PayService
  • package io.mykit.limiter.service;
    import java.math.BigDecimal;
    /**
     * @author binghe
     * @version 1.0.0
     * @description     
     */
    public interface PayService {
        int pay(BigDecimal price);
    }
    
  • メッセンジャー
  • package io.mykit.limiter.service;
    /**
     * @author binghe
     * @version 1.0.0
     * @description         
     */
    public interface MessageService {
        boolean sendMessage(String message);
    }
    
    次に、2つの実装クラスを作成します.それぞれは以下の通りです.
  • Message ServiceImpl
  • package io.mykit.limiter.service.impl;
    import io.mykit.limiter.service.MessageService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Service;
    /**
     * @author binghe
     * @version 1.0.0
     * @description         
     */
    @Service
    public class MessageServiceImpl implements MessageService {
        private final Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
        @Override
        public boolean sendMessage(String message) {
            logger.info("      ===>>" + message);
            return true;
        }
    }
    
  • PayServiceImpl
  • package io.mykit.limiter.service.impl;
    import io.mykit.limiter.service.PayService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Service;
    import java.math.BigDecimal;
    /**
     * @author binghe
     * @version 1.0.0
     * @description     
     */
    @Service
    public class PayServiceImpl implements PayService {
        private final Logger logger = LoggerFactory.getLogger(PayServiceImpl.class);
        @Override
        public int pay(BigDecimal price) {
            logger.info("    ===>>" + price);
            return 1;
        }
    }
    
    アナログ決済とメッセージ送信ですので、具体的に実現した方法で関連ログを印刷しました.具体的な業務ロジックが実現されていません.
    次に、私達のController類PayControllerを作成し、PayController類のインターフェースpay()方法で限流を使用して、一秒ごとに2つのトークンをバケットに入れて、クライアントはバケットからトークンを取得します.500ミリ秒以内にトークンを取得していないなら、直接にサービスダウングレード処理を行うことができます.
    PayControllerのコードは以下の通りです.
    package io.mykit.limiter.controller;
    import com.google.common.util.concurrent.RateLimiter;
    import io.mykit.limiter.service.MessageService;
    import io.mykit.limiter.service.PayService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.math.BigDecimal;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description       
     */
    @RestController
    public class PayController {
        private final Logger logger = LoggerFactory.getLogger(PayController.class);
        /**
         * RateLimiter create()         ,        2r/s,    2             
         */
        private RateLimiter rateLimiter = RateLimiter.create(2);
    
        @Autowired
        private MessageService messageService;
        @Autowired
        private PayService payService;
        @RequestMapping("/boot/pay")
        public String pay(){
            //      
            String result = "";
            //    ,            ,   500         ,          
            boolean tryAcquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);
            if (!tryAcquire){
                result = "    ,    ";
                logger.info(result);
                return result;
            }
            int ret = payService.pay(BigDecimal.valueOf(100.0));
            if(ret > 0){
                result = "    ";
                return result;
            }
            result = "    ,     ...";
            return result;
        }
    }
    
    最後に、mykit-ratelimiter-testプロジェクトのコアスタートクラスを作成します.
    package io.mykit.limiter;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description      
     */
    @SpringBootApplication
    public class MykitLimiterApplication {
    
        public static void main(String[] args){
            SpringApplication.run(MykitLimiterApplication.class, args);
        }
    }
    
    これまで,注釈方式を用いて,制限流を実現するWebアプリケーションを使わずにほぼ完成した.
    プロジェクトを実行
    プロジェクトの作成が完了したら、プロジェクトを実行します.Spring Bootプロジェクトを実行するのは比較的簡単です.
    プロジェクトが成功したら、ブラウザのアドレスバーにリンクを入力します.http://localhost:8080/boot/pay.ページは「支払成功」という文字を出力します.プロジェクトの構築が成功したと説明します.下記のとおりです
    亿级流量场景下如何为HTTP接口限流?看完我懂了!!_第1张图片
    この時、私は一回しか訪問しませんでした.制限電流をトリガしませんでした.続いて、私達はひっきりなしにブラウザを使って、この時、ブラウザーは“支払って失敗して、もう一回試みるようにしましょう…”の文字を出力して、下の通りです.
    亿级流量场景下如何为HTTP接口限流?看完我懂了!!_第2张图片
    PayControllerクラスにはもう一つのsendMessage()方法があります.シミュレーションはメッセージを送信するインターフェースであり、同様にフロー制限操作を使用しています.具体的なコードは下記の通りです.
    @RequestMapping("/boot/send/message")
    public String sendMessage(){
        //      
        String result = "";
        //    ,            ,   500         ,          
        boolean tryAcquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);
        if (!tryAcquire){
            result = "    ,    ";
            logger.info(result);
            return result;
        }
        boolean flag = messageService.sendMessage("      +1");
        if (flag){
            result = "      ";
            return result;
        }
        result = "      ,     ...";
        return result;
    }
    
    sendMessage()メソッドのコードロジックと実行効果はpay()方法と同じです.ブラウザからアクセスしません.http://localhost:8080/boot/send/message 住所の訪問効果は、仲間が自分で検証できます.
    注解を使わず限流欠点を実現する
    プロジェクトの編纂を通じて、プロジェクトの中でインターフェースを制限する時、注釈を使わずに開発すると、コードに大量の冗長性が現れます.各方法の中ではほとんど同じ制限流論理を書きます.コードは非常に冗長です.
    コード冗長の問題はどう解決しますか?私たちはカスタマイズした注釈を使って実現できます.
    注解を使ってインターフェースの流れ制限を実現します.
    ユーザー定義の注釈を使って、一般的な業務ロジックを注解の切断面に封入できます.注解業務ロジックを追加する必要がある方法に、相応の注釈をつければいいです.この制限された流れの例については、カスタマイズされた注釈に基づいて実装することができる.
    カスタムコメントを実行
    下記のようにカスタムコメントを作成します.
    package io.mykit.limiter.annotation;
    import java.lang.annotation.*;
    /**
     * @author binghe
     * @version 1.0.0
     * @description           
     */
    @Target(value = ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyRateLimiter {
        //           
        double rate();
        //             
        long timeout() default 0;
    }
    
    カスタム注解うどんの実現
    次に、私たちはまた、次のように切麺類MyRateLimiterAspectを実現します.
    package io.mykit.limiter.aspect;
    
    import com.google.common.util.concurrent.RateLimiter;
    import io.mykit.limiter.annotation.MyRateLimiter;
    import org.aspectj.lang.ProceedingJoinPoint;
    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.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description        
     */
    @Aspect
    @Component
    public class MyRateLimiterAspect {
    
        private RateLimiter rateLimiter = RateLimiter.create(2);
    
        @Pointcut("execution(public * io.mykit.limiter.controller.*.*(..))")
        public void pointcut(){
    
        }
    
        /**
         *       
         */
        @Around("pointcut()")
        public Object process(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
            MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
    
            //             @MyRateLimiter  
            MyRateLimiter myRateLimiter = signature.getMethod().getDeclaredAnnotation(MyRateLimiter.class);
            if(myRateLimiter == null){
                //      ,      
                return proceedingJoinPoint.proceed();
            }
            //        
            //       
            double rate = myRateLimiter.rate();
            //            
            long timeout = myRateLimiter.timeout();
    
            //      
            rateLimiter.setRate(rate);
    
            //             
            boolean tryAcquire = rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS);
            if(!tryAcquire){
                //    
                fullback();
                return null;
            }
            //     ,    
            return proceedingJoinPoint.proceed();
    
        }
    
        /**
         *     
         */
        private void fullback() {
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            PrintWriter writer = null;
            try {
                writer =  response.getWriter();
                writer.println("   ,      ?");
                writer.flush();;
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(writer != null){
                    writer.close();
                }
            }
        }
    }
    
    カスタムカットの機能は比較的簡単です.詳しくは言いませんが、「氷河技術」のWeChat公式アカウントについて質問してもいいです.
    次に、PayControllerクラスのsendMessage()方法を改造して、修正後の方法セグメントコードを下記に示します.
    @MyRateLimiter(rate = 1.0, timeout = 500)
    @RequestMapping("/boot/send/message")
    public String sendMessage(){
        //      
        String result = "";
        boolean flag = messageService.sendMessage("      +1");
        if (flag){
            result = "      ";
            return result;
        }
        result = "      ,     ...";
        return result;
    }
    
    展開プロジェクトを実行
    配置項目は比較的簡単で、MykitLimiter Appleication類のメーンメソッドを実行するだけでいいです.ここでは、簡単なために、ブラウザから直接にリンク先を入力して訪問します.
    効果は以下の通りです.
    亿级流量场景下如何为HTTP接口限流?看完我懂了!!_第3张图片
    続いて、私達は絶えずブラウザを更新します.メッセージの送信に失敗しました.もう一度試してみます.
    亿级流量场景下如何为HTTP接口限流?看完我懂了!!_第4张图片
    限流アルゴリズムに基づく限流の欠点
    上で紹介した制限流方式はすべて単一機配備の環境にしか使えません.もし複数のサーバーにアプリケーションを配置して分散式、クラスタ化するなら、上の制限流の方式は適用されません.この時、分散式の制限流を使用する必要があります.分散的なシーンでは、どのようにしてフロー制限操作を実現するかについては、次の編で紹介します.
              +       ,        ,        :
    
    
    
            ▼
    
        ,    ,