日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > 單片機(jī) > 架構(gòu)師社區(qū)
[導(dǎo)讀]這里,我們實(shí)現(xiàn)Web接口限流,具體方式為:使用自定義注解封裝基于令牌桶限流算法實(shí)現(xiàn)接口限流。 不使用注解實(shí)現(xiàn)接口限流 搭建項(xiàng)目 這里,我們使用SpringBoot項(xiàng)目來(lái)搭建Http接口限流項(xiàng)目,SpringBoot項(xiàng)目本質(zhì)上還是一個(gè)Maven項(xiàng)目。所以,小伙伴們可以直接創(chuàng)建

這里,我們實(shí)現(xiàn)Web接口限流,具體方式為:使用自定義注解封裝基于令牌桶限流算法實(shí)現(xiàn)接口限流。

不使用注解實(shí)現(xiàn)接口限流

搭建項(xiàng)目

這里,我們使用SpringBoot項(xiàng)目來(lái)搭建Http接口限流項(xiàng)目,SpringBoot項(xiàng)目本質(zhì)上還是一個(gè)Maven項(xiàng)目。所以,小伙伴們可以直接創(chuàng)建一個(gè)Maven項(xiàng)目,我這里的項(xiàng)目名稱為mykit-ratelimiter-test。接下來(lái),在pom.xml文件中添加如下依賴使項(xiàng)目構(gòu)建為一個(gè)SpringBoot項(xiàng)目。

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>io.mykit.limiter</groupId>
    <artifactId>mykit-ratelimiter-test</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>mykit-ratelimiter-test</name>

    <properties>
        <guava.version>28.2-jre</guava.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version><!--$NO-MVN-MAN-VER$-->
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

可以看到,我在項(xiàng)目中除了引用了SpringBoot相關(guān)的Jar包外,還引用了guava框架,版本為28.2-jre。

創(chuàng)建核心類

這里,我主要是模擬一個(gè)支付接口的限流場(chǎng)景。首先,我們定義一個(gè)PayService接口和MessageService接口。PayService接口主要用于模擬后續(xù)的支付業(yè)務(wù),MessageService接口模擬發(fā)送消息。接口的定義分別如下所示。

  • PayService

package io.mykit.limiter.service;
import java.math.BigDecimal;
/**
 * @author binghe
 * @version 1.0.0
 * @description 模擬支付
 */

public interface PayService {
    int pay(BigDecimal price);
}
  • MessageService

package io.mykit.limiter.service;
/**
 * @author binghe
 * @version 1.0.0
 * @description 模擬發(fā)送消息服務(wù)
 */

public interface MessageService {
    boolean sendMessage(String message);
}

接下來(lái),創(chuàng)建二者的實(shí)現(xiàn)類,分別如下。

  • MessageServiceImpl

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 模擬實(shí)現(xiàn)發(fā)送消息
 */

@Service
public class MessageServiceImpl implements MessageService {
    private final Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
    @Override
    public boolean sendMessage(String message) {
        logger.info("發(fā)送消息成功===>>" + 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;
    }
}

由于是模擬支付和發(fā)送消息,所以,我在具體實(shí)現(xiàn)的方法中打印出了相關(guān)的日志,并沒(méi)有實(shí)現(xiàn)具體的業(yè)務(wù)邏輯。

接下來(lái),就是創(chuàng)建我們的Controller類PayController,在PayController類的接口pay()方法中使用了限流,每秒鐘向桶中放入2個(gè)令牌,并且客戶端從桶中獲取令牌,如果在500毫秒內(nèi)沒(méi)有獲取到令牌的話,我們可以則直接走服務(wù)降級(jí)處理。

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 測(cè)試接口限流
 */

@RestController
public class PayController {
    private final Logger logger = LoggerFactory.getLogger(PayController.class);
    /**
     * RateLimiter的create()方法中傳入一個(gè)參數(shù),表示以固定的速率2r/s,即以每秒2個(gè)令牌的速率向桶中放入令牌
     */

    private RateLimiter rateLimiter = RateLimiter.create(2);

    @Autowired
    private MessageService messageService;
    @Autowired
    private PayService payService;
    @RequestMapping("/boot/pay")
    public String pay(){
        //記錄返回接口
        String result = "";
        //限流處理,客戶端請(qǐng)求從桶中獲取令牌,如果在500毫秒沒(méi)有獲取到令牌,則直接走服務(wù)降級(jí)處理
        boolean tryAcquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);
        if (!tryAcquire){
            result = "請(qǐng)求過(guò)多,降級(jí)處理";
            logger.info(result);
            return result;
        }
        int ret = payService.pay(BigDecimal.valueOf(100.0));
        if(ret > 0){
            result = "支付成功";
            return result;
        }
        result = "支付失敗,再試一次吧...";
        return result;
    }
}

最后,我們來(lái)創(chuàng)建mykit-ratelimiter-test項(xiàng)目的核心啟動(dòng)類,如下所示。

package io.mykit.limiter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author binghe
 * @version 1.0.0
 * @description 項(xiàng)目啟動(dòng)類
 */

@SpringBootApplication
public class MykitLimiterApplication {

    public static void main(String[] args){
        SpringApplication.run(MykitLimiterApplication.class, args);
    }
}

至此,我們不使用注解方式實(shí)現(xiàn)限流的Web應(yīng)用就基本完成了。

運(yùn)行項(xiàng)目

項(xiàng)目創(chuàng)建完成后,我們來(lái)運(yùn)行項(xiàng)目,運(yùn)行SpringBoot項(xiàng)目比較簡(jiǎn)單,直接運(yùn)行MykitLimiterApplication類的main()方法即可。

項(xiàng)目運(yùn)行成功后,我們?cè)跒g覽器地址欄輸入鏈接:http://localhost:8080/boot/pay。頁(yè)面會(huì)輸出“支付成功”的字樣,說(shuō)明項(xiàng)目搭建成功了。如下所示。

億級(jí)流量場(chǎng)景下如何為HTTP接口限流?看完我懂了?。? >
   </figure>
   <p style=此時(shí),我只訪問(wèn)了一次,并沒(méi)有觸發(fā)限流。接下來(lái),我們不停的刷瀏覽器,此時(shí),瀏覽器會(huì)輸出“支付失敗,再試一次吧…”的字樣,如下所示。

億級(jí)流量場(chǎng)景下如何為HTTP接口限流?看完我懂了!!

在PayController類中還有一個(gè)sendMessage()方法,模擬的是發(fā)送消息的接口,同樣使用了限流操作,具體代碼如下所示。

@RequestMapping("/boot/send/message")
public String sendMessage(){
    //記錄返回接口
    String result = "";
    //限流處理,客戶端請(qǐng)求從桶中獲取令牌,如果在500毫秒沒(méi)有獲取到令牌,則直接走服務(wù)降級(jí)處理
    boolean tryAcquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);
    if (!tryAcquire){
        result = "請(qǐng)求過(guò)多,降級(jí)處理";
        logger.info(result);
        return result;
    }
    boolean flag = messageService.sendMessage("恭喜您成長(zhǎng)值+1");
    if (flag){
        result = "消息發(fā)送成功";
        return result;
    }
    result = "消息發(fā)送失敗,再試一次吧...";
    return result;
}

sendMessage()方法的代碼邏輯和運(yùn)行效果與pay()方法相同,我就不再瀏覽器訪問(wèn) http://localhost:8080/boot/send/message 地址的訪問(wèn)效果了,小伙伴們可以自行驗(yàn)證。

不使用注解實(shí)現(xiàn)限流缺點(diǎn)

通過(guò)對(duì)項(xiàng)目的編寫,我們可以發(fā)現(xiàn),當(dāng)在項(xiàng)目中對(duì)接口進(jìn)行限流時(shí),不使用注解進(jìn)行開發(fā),會(huì)導(dǎo)致代碼出現(xiàn)大量冗余,每個(gè)方法中幾乎都要寫一段相同的限流邏輯,代碼十分冗余。

如何解決代碼冗余的問(wèn)題呢?我們可以使用自定義注解進(jìn)行實(shí)現(xiàn)。

使用注解實(shí)現(xiàn)接口限流

使用自定義注解,我們可以將一些通用的業(yè)務(wù)邏輯封裝到注解的切面中,在需要添加注解業(yè)務(wù)邏輯的方法上加上相應(yīng)的注解即可。針對(duì)我們這個(gè)限流的實(shí)例來(lái)說(shuō),可以基于自定義注解實(shí)現(xiàn)。

實(shí)現(xiàn)自定義注解

實(shí)現(xiàn),我們來(lái)創(chuàng)建一個(gè)自定義注解,如下所示。

package io.mykit.limiter.annotation;
import java.lang.annotation.*;
/**
 * @author binghe
 * @version 1.0.0
 * @description 實(shí)現(xiàn)限流的自定義注解
 */

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRateLimiter {
    //向令牌桶放入令牌的速率
    double rate();
    //從令牌桶獲取令牌的超時(shí)時(shí)間
    long timeout() default 0;
}

自定義注解切面實(shí)現(xiàn)

接下來(lái),我們還要實(shí)現(xiàn)一個(gè)切面類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){
            //程序正常執(zhí)行,執(zhí)行目標(biāo)方法
            return proceedingJoinPoint.proceed();
        }
        //獲取注解上的參數(shù)
        //獲取配置的速率
        double rate = myRateLimiter.rate();
        //獲取客戶端等待令牌的時(shí)間
        long timeout = myRateLimiter.timeout();

        //設(shè)置限流速率
        rateLimiter.setRate(rate);

        //判斷客戶端獲取令牌是否超時(shí)
        boolean tryAcquire = rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS);
        if(!tryAcquire){
            //服務(wù)降級(jí)
            fullback();
            return null;
        }
        //獲取到令牌,直接執(zhí)行
        return proceedingJoinPoint.proceed();

    }

    /**
     * 降級(jí)處理
     */

    private void fullback() {
        response.setHeader("Content-type""text/html;charset=UTF-8");
        PrintWriter writer = null;
        try {
            writer =  response.getWriter();
            writer.println("出錯(cuò)了,重試一次試試?");
            writer.flush();;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(writer != null){
                writer.close();
            }
        }
    }
}

自定義切面的功能比較簡(jiǎn)單,我就不細(xì)說(shuō)了,大家有啥問(wèn)題可以關(guān)注【冰河技術(shù)】微信公眾號(hào)來(lái)進(jìn)行提問(wèn)。

接下來(lái),我們改造下PayController類中的sendMessage()方法,修改后的方法片段代碼如下所示。

@MyRateLimiter(rate = 1.0, timeout = 500)
@RequestMapping("/boot/send/message")
public String sendMessage(){
    //記錄返回接口
    String result = "";
    boolean flag = messageService.sendMessage("恭喜您成長(zhǎng)值+1");
    if (flag){
        result = "消息發(fā)送成功";
        return result;
    }
    result = "消息發(fā)送失敗,再試一次吧...";
    return result;
}

運(yùn)行部署項(xiàng)目

部署項(xiàng)目比較簡(jiǎn)單,只需要運(yùn)行MykitLimiterApplication類下的main()方法即可。這里,為了簡(jiǎn)單,我們還是從瀏覽器中直接輸入鏈接地址來(lái)進(jìn)行訪問(wèn)

效果如下所示。

億級(jí)流量場(chǎng)景下如何為HTTP接口限流?看完我懂了?。? >
   </figure>
   <p style=接下來(lái),我們不斷的刷新瀏覽器。會(huì)出現(xiàn)“消息發(fā)送失敗,再試一次吧..”的字樣,說(shuō)明已經(jīng)觸發(fā)限流操作。

億級(jí)流量場(chǎng)景下如何為HTTP接口限流?看完我懂了?。? >
   </figure>
   <h2 style=基于限流算法實(shí)現(xiàn)限流的缺點(diǎn)

上面介紹的限流方式都只能用于單機(jī)部署的環(huán)境中,如果將應(yīng)用部署到多臺(tái)服務(wù)器進(jìn)行分布式、集群,則上面限流的方式就不適用了,此時(shí),我們需要使用分布式限流。至于在分布式場(chǎng)景下,如何實(shí)現(xiàn)限流操作,我們就在下一篇中進(jìn)行介紹。

特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

億級(jí)流量場(chǎng)景下如何為HTTP接口限流?看完我懂了!!

億級(jí)流量場(chǎng)景下如何為HTTP接口限流?看完我懂了!!

億級(jí)流量場(chǎng)景下如何為HTTP接口限流?看完我懂了!!

長(zhǎng)按訂閱更多精彩▼

億級(jí)流量場(chǎng)景下如何為HTTP接口限流?看完我懂了?。? ></p><p style=如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過(guò)流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問(wèn)題卻十分常見,不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問(wèn)題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問(wèn)題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來(lái)解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉