億級の流量シーンでHTTPインターフェースの制限はどうなりますか?分かりました.
ここでは、ユーザー定義の注釈パッケージを用いてトークンバケット制限アルゴリズムに基づいてインターフェース制限ストリームを実現するWebインターフェース制限ストリームを実現する.
注釈を使わずにインターフェースの流れ制限を実現します.
プロジェクトを構築する
ここでは、SpringBootプロジェクトを使って、Httpインターフェース限定プロジェクトを構築します.Spring Bootプロジェクトは本質的にまだMavenプロジェクトです.だから、友達は直接にMavenプロジェクトを作成できます.ここのプロジェクト名はmykit-ratelimiter-testです.次に、pom.xmlファイルに次のような依存関係を加えて、プロジェクトをSprigBootプロジェクトとして構築します.
コアクラスを作成
ここで、私は主にお支払いインターフェースの制限シーンをシミュレーションします.まず、PayServiceインターフェースとMessage Serviceインターフェースを定義します.PayServiceインターフェースは主に後続の支払い業務をシミュレーションするために用いられ、Message Serviceインターフェースはメッセージをアナログで送る.インターフェースの定義はそれぞれ以下の通りです. PayService メッセンジャー Message ServiceImpl PayServiceImpl
次に、私達のController類PayControllerを作成し、PayController類のインターフェースpay()方法で限流を使用して、一秒ごとに2つのトークンをバケットに入れて、クライアントはバケットからトークンを取得します.500ミリ秒以内にトークンを取得していないなら、直接にサービスダウングレード処理を行うことができます.
PayControllerのコードは以下の通りです.
プロジェクトを実行
プロジェクトの作成が完了したら、プロジェクトを実行します.Spring Bootプロジェクトを実行するのは比較的簡単です.
プロジェクトが成功したら、ブラウザのアドレスバーにリンクを入力します.http://localhost:8080/boot/pay.ページは「支払成功」という文字を出力します.プロジェクトの構築が成功したと説明します.下記のとおりです
この時、私は一回しか訪問しませんでした.制限電流をトリガしませんでした.続いて、私達はひっきりなしにブラウザを使って、この時、ブラウザーは“支払って失敗して、もう一回試みるようにしましょう…”の文字を出力して、下の通りです.
PayControllerクラスにはもう一つのsendMessage()方法があります.シミュレーションはメッセージを送信するインターフェースであり、同様にフロー制限操作を使用しています.具体的なコードは下記の通りです.
注解を使わず限流欠点を実現する
プロジェクトの編纂を通じて、プロジェクトの中でインターフェースを制限する時、注釈を使わずに開発すると、コードに大量の冗長性が現れます.各方法の中ではほとんど同じ制限流論理を書きます.コードは非常に冗長です.
コード冗長の問題はどう解決しますか?私たちはカスタマイズした注釈を使って実現できます.
注解を使ってインターフェースの流れ制限を実現します.
ユーザー定義の注釈を使って、一般的な業務ロジックを注解の切断面に封入できます.注解業務ロジックを追加する必要がある方法に、相応の注釈をつければいいです.この制限された流れの例については、カスタマイズされた注釈に基づいて実装することができる.
カスタムコメントを実行
下記のようにカスタムコメントを作成します.
次に、私たちはまた、次のように切麺類MyRateLimiterAspectを実現します.
次に、PayControllerクラスのsendMessage()方法を改造して、修正後の方法セグメントコードを下記に示します.
配置項目は比較的簡単で、MykitLimiter Appleication類のメーンメソッドを実行するだけでいいです.ここでは、簡単なために、ブラウザから直接にリンク先を入力して訪問します.
効果は以下の通りです.
続いて、私達は絶えずブラウザを更新します.メッセージの送信に失敗しました.もう一度試してみます.
限流アルゴリズムに基づく限流の欠点
上で紹介した制限流方式はすべて単一機配備の環境にしか使えません.もし複数のサーバーにアプリケーションを配置して分散式、クラスタ化するなら、上の制限流の方式は適用されません.この時、分散式の制限流を使用する必要があります.分散的なシーンでは、どのようにしてフロー制限操作を実現するかについては、次の編で紹介します.
注釈を使わずにインターフェースの流れ制限を実現します.
プロジェクトを構築する
ここでは、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インターフェースはメッセージをアナログで送る.インターフェースの定義はそれぞれ以下の通りです.
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つの実装クラスを作成します.それぞれは以下の通りです.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;
}
}
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.ページは「支払成功」という文字を出力します.プロジェクトの構築が成功したと説明します.下記のとおりです
この時、私は一回しか訪問しませんでした.制限電流をトリガしませんでした.続いて、私達はひっきりなしにブラウザを使って、この時、ブラウザーは“支払って失敗して、もう一回試みるようにしましょう…”の文字を出力して、下の通りです.
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類のメーンメソッドを実行するだけでいいです.ここでは、簡単なために、ブラウザから直接にリンク先を入力して訪問します.
効果は以下の通りです.
続いて、私達は絶えずブラウザを更新します.メッセージの送信に失敗しました.もう一度試してみます.
限流アルゴリズムに基づく限流の欠点
上で紹介した制限流方式はすべて単一機配備の環境にしか使えません.もし複数のサーバーにアプリケーションを配置して分散式、クラスタ化するなら、上の制限流の方式は適用されません.この時、分散式の制限流を使用する必要があります.分散的なシーンでは、どのようにしてフロー制限操作を実現するかについては、次の編で紹介します.
+ , , :
▼
, ,