亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Boot高可用限流三種實(shí)現(xiàn)解決方案

 更新時(shí)間:2023年08月22日 10:06:23   作者:shepherd111  
限流是對(duì)某一時(shí)間窗口內(nèi)的請(qǐng)求數(shù)進(jìn)行限制,保持系統(tǒng)的可用性和穩(wěn)定性,本文就介紹了Spring Boot高可用限流三種實(shí)現(xiàn)解決方案,具有一定的參考價(jià)值,感興趣的可以了解一下

1.什么是限流

限流是對(duì)某一時(shí)間窗口內(nèi)的請(qǐng)求數(shù)進(jìn)行限制,保持系統(tǒng)的可用性和穩(wěn)定性,防止因流量暴增而導(dǎo)致的系統(tǒng)運(yùn)行緩慢或宕機(jī)。

為什么需要限流

其實(shí)限流思想在生活中隨處可見(jiàn),例如景區(qū)限流,防止人滿(mǎn)為患。熱門(mén)餐飲需要排隊(duì)就餐等。回到互聯(lián)網(wǎng)網(wǎng)絡(luò)上,同樣也是這個(gè)道理,例如某某明星公布了戀情,訪(fǎng)問(wèn)從平時(shí)的50萬(wàn)增加到了500萬(wàn),系統(tǒng)最多可以支撐200萬(wàn)訪(fǎng)問(wèn),那么就要執(zhí)行限流規(guī)則,保證系統(tǒng)是一個(gè)可用的狀態(tài),不至于服務(wù)器崩潰導(dǎo)致所有請(qǐng)求不可用。還有12306購(gòu)票系統(tǒng),每年的618,雙11等場(chǎng)景都需要限流來(lái)抗住高并發(fā)的瞬時(shí)流量,保證系統(tǒng)能正常服務(wù),不被沖垮宕機(jī)癱瘓。

2.限流算法

計(jì)數(shù)器算法

計(jì)數(shù)器限流算法是最為簡(jiǎn)單粗暴的解決方案,主要用來(lái)限制總并發(fā)數(shù),比如數(shù)據(jù)庫(kù)連接池大小、線(xiàn)程池大小、接口訪(fǎng)問(wèn)并發(fā)數(shù)等都是使用計(jì)數(shù)器算法。一般我們會(huì)限制一秒鐘能夠通過(guò)的請(qǐng)求數(shù)。比如我們規(guī)定,對(duì)于A接口來(lái)說(shuō),我們1分鐘的訪(fǎng)問(wèn)次數(shù)不能超過(guò)1000個(gè)。那么我們可以這么做:在一開(kāi)始的時(shí)候,我們可以設(shè)置一個(gè)計(jì)數(shù)器counter,每當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)的時(shí)候, counter就加1,如果counter的值大于1000并且該請(qǐng)求與第一個(gè)請(qǐng)求的間隔時(shí)間還在1分鐘之內(nèi),那么說(shuō)明請(qǐng)求數(shù)過(guò)多; 如果該請(qǐng)求與第一個(gè)請(qǐng)求的間隔時(shí)間大于1分鐘,且counter的值還在限流范圍內(nèi),那么就重置 counter。

代碼實(shí)現(xiàn)如下:

/**
 * 固定窗口時(shí)間算法
 * @return
*/
public class Counter {
  public long timeStamp = System.currentTimeMillis(); // 當(dāng)前時(shí)間
  public int reqCount = 0; // 初始化計(jì)數(shù)器
  public final int limit = 1000; // 時(shí)間窗口內(nèi)最大請(qǐng)求數(shù)
  public final long interval = 1000 * 60; // 時(shí)間窗口ms
?
  public boolean limit() {
    long now = System.currentTimeMillis();
    if (now < timeStamp + interval) {
      // 在時(shí)間窗口內(nèi)
      reqCount++;
      // 判斷當(dāng)前時(shí)間窗口內(nèi)是否超過(guò)最大請(qǐng)求控制數(shù)
      return reqCount <= limit;
    } else {
      timeStamp = now;
      // 超時(shí)后重置
      reqCount = 1;
      return true;
    }
  }
}

但是,這種上面的固定時(shí)間窗口算法有一個(gè)很明顯的臨界問(wèn)題:假設(shè)限流閥值為5個(gè)請(qǐng)求,單位時(shí)間窗口是1s,如果我們?cè)趩挝粫r(shí)間內(nèi)的前0.8-1s和1-1.2s,分別并發(fā)5個(gè)請(qǐng)求。雖然都沒(méi)有超過(guò)閥值,但是如果算0.8-1.2s,則并發(fā)數(shù)高達(dá)10,已經(jīng)超過(guò)單位時(shí)間1s不超過(guò)5閥值的定義啦。

滑動(dòng)窗口限流算法

滑動(dòng)窗口限流解決固定窗口臨界值的問(wèn)題。它將單位時(shí)間周期分為n個(gè)小周期,分別記錄每個(gè)小周期內(nèi)接口的訪(fǎng)問(wèn)次數(shù),并且根據(jù)時(shí)間滑動(dòng)刪除過(guò)期的小周期。如下圖所示:展示了滑動(dòng)窗口算法對(duì)時(shí)間區(qū)間的劃分

假設(shè)單位時(shí)間還是1s,滑動(dòng)窗口算法把它劃分為5個(gè)小周期,也就是滑動(dòng)窗口(單位時(shí)間)被劃分為5個(gè)小格子。每格表示0.2s。每過(guò)0.2s,時(shí)間窗口就會(huì)往右滑動(dòng)一格。然后呢,每個(gè)小周期,都有自己獨(dú)立的計(jì)數(shù)器,如果請(qǐng)求是0.83s到達(dá)的,0.8~1.0s對(duì)應(yīng)的計(jì)數(shù)器就會(huì)加1。

假設(shè)我們1s內(nèi)的限流閥值還是5個(gè)請(qǐng)求,0.81.0s內(nèi)(比如0.9s的時(shí)候)來(lái)了5個(gè)請(qǐng)求,落在黃色格子里。時(shí)間過(guò)了1.0s這個(gè)點(diǎn)之后,又來(lái)5個(gè)請(qǐng)求,落在紫色格子里。如果是固定窗口算法,是不會(huì)被限流的,但是滑動(dòng)窗口的話(huà),每過(guò)一個(gè)小周期,它會(huì)右移一個(gè)小格。過(guò)了1.0s這個(gè)點(diǎn)后,會(huì)右移一小格,當(dāng)前的單位時(shí)間段是0.21.2s,這個(gè)區(qū)域的請(qǐng)求已經(jīng)超過(guò)限定的5了,已觸發(fā)限流啦,實(shí)際上,紫色格子的請(qǐng)求都被拒絕啦。

TIPS: 當(dāng)滑動(dòng)窗口的格子周期劃分的越多,那么滑動(dòng)窗口的滾動(dòng)就越平滑,限流的統(tǒng)計(jì)就會(huì)越精確。

 /**
     * 單位時(shí)間劃分的小周期(單位時(shí)間是1分鐘,10s一個(gè)小格子窗口,一共6個(gè)格子)
     */
    private int SUB_CYCLE = 10;
    /**
     * 每分鐘限流請(qǐng)求數(shù)
     */
    private int thresholdPerMin = 100;
    /**
     * 計(jì)數(shù)器, k-為當(dāng)前窗口的開(kāi)始時(shí)間值秒,value為當(dāng)前窗口的計(jì)數(shù)
     */
    private final TreeMap<Long, Integer> counters = new TreeMap<>();
   /**
     * 滑動(dòng)窗口時(shí)間算法實(shí)現(xiàn)
     */
    boolean slidingWindowsTryAcquire() {
        long currentWindowTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) / SUB_CYCLE * SUB_CYCLE; //獲取當(dāng)前時(shí)間在哪個(gè)小周期窗口
        int currentWindowNum = countCurrentWindow(currentWindowTime); //當(dāng)前窗口總請(qǐng)求數(shù)
        //超過(guò)閥值限流
        if (currentWindowNum >= thresholdPerMin) {
            return false;
        }
        //計(jì)數(shù)器+1
        counters.get(currentWindowTime)++;
        return true;
    }
   /**
    * 統(tǒng)計(jì)當(dāng)前窗口的請(qǐng)求數(shù)
    */
    private int countCurrentWindow(long currentWindowTime) {
        //計(jì)算窗口開(kāi)始位置
        long startTime = currentWindowTime - SUB_CYCLE* (60s/SUB_CYCLE-1);
        int count = 0;
        //遍歷存儲(chǔ)的計(jì)數(shù)器
        Iterator<Map.Entry<Long, Integer>> iterator = counters.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, Integer> entry = iterator.next();
            // 刪除無(wú)效過(guò)期的子窗口計(jì)數(shù)器
            if (entry.getKey() < startTime) {
                iterator.remove();
            } else {
                //累加當(dāng)前窗口的所有計(jì)數(shù)器之和
                count =count + entry.getValue();
            }
        }
        return count;
    }

滑動(dòng)窗口算法雖然解決了固定窗口的臨界問(wèn)題,但是一旦到達(dá)限流后,請(qǐng)求都會(huì)直接暴力被拒絕

漏桶算法

漏桶算法思路很簡(jiǎn)單,我們把水比作是請(qǐng)求,漏桶比作是系統(tǒng)處理能力極限,水先進(jìn)入到漏桶里,漏桶里的水按一定速率流出,當(dāng)流出的速率小于流入的速率時(shí),由于漏桶容量有限,后續(xù)進(jìn)入的水直接溢出(拒絕請(qǐng)求),以此實(shí)現(xiàn)限流。

令牌桶算法

令牌桶算法則是一個(gè)存放固定容量令牌的桶,按照固定速率往桶里添加令牌。桶中存放的令牌數(shù)有最大上限,超出之后就被丟棄或者拒絕。當(dāng)流量或者網(wǎng)絡(luò)請(qǐng)求到達(dá)時(shí),每個(gè)請(qǐng)求都要獲取一個(gè)令牌,如果能夠獲取到,則直接處理,并且令牌桶刪除一個(gè)令牌。如果獲取不同,該請(qǐng)求就要被限流,要么直接丟棄,要么在緩沖區(qū)等待。

令牌桶和漏桶算法區(qū)別:

1)令牌桶是按照固定速率往桶中添加令牌,請(qǐng)求是否被處理需要看桶中令牌是否足夠,當(dāng)令牌數(shù)減為零時(shí)則拒絕新的請(qǐng)求;漏桶則是按照常量固定速率流出請(qǐng)求,流入請(qǐng)求速率任意,當(dāng)流入的請(qǐng)求數(shù)累積到漏桶容量時(shí),則新流入的請(qǐng)求被拒絕;

2)令牌桶限制的是平均流入速率,允許突發(fā)請(qǐng)求,只要有令牌就可以處理,支持一次拿3個(gè)令牌,4個(gè)令牌;漏桶限制的是常量流出速率,即流出速率是一個(gè)固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2,從而平滑突發(fā)流入速率;

3)令牌桶允許一定程度的突發(fā),而漏桶主要目的是平滑流出速率;

3.guava實(shí)現(xiàn)限流

Google開(kāi)源工具包Guava提供了限流工具類(lèi)RateLimiter,該類(lèi)基于令牌桶算法實(shí)現(xiàn)流量限制,使用十分方便,而且十分高效。RateLimiter提供了令牌桶算法實(shí)現(xiàn):平滑突發(fā)限流(SmoothBursty)和平滑預(yù)熱限流(SmoothWarmingUp)。
依賴(lài)包

     <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>29.0-jre</version>
        </dependency>

使用示例

/**
 * @author fjzheng
 * @version 1.0
 * @date 2021/9/1 10:38
 */
@Slf4j
@RestController
@RequestMapping("/test")
@ResponseResultBody
public class TestController {
    /**
     * 限流策略 :1秒鐘2個(gè)請(qǐng)求
     */
    private final RateLimiter limiter = RateLimiter.create(2.0);
    private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    @GetMapping("/rateLimit")
    public String testLimiter() {
        //500毫秒內(nèi),沒(méi)拿到令牌,就直接進(jìn)入服務(wù)降級(jí)
        boolean tryAcquire = limiter.tryAcquire(50, TimeUnit.MILLISECONDS);
        if (!tryAcquire) {
            log.warn("進(jìn)入服務(wù)降級(jí),時(shí)間{}", LocalDateTime.now().format(dtf));
            return "當(dāng)前排隊(duì)人數(shù)較多,請(qǐng)稍后再試!";
        }
?
        log.info("獲取令牌成功,時(shí)間{}", LocalDateTime.now().format(dtf));
        return "請(qǐng)求成功";
    }
?
}

以上用到了RateLimiter的2個(gè)核心方法:create()、tryAcquire(),以下為詳細(xì)說(shuō)明

  • acquire() 獲取一個(gè)令牌, 該方法會(huì)阻塞直到獲取到這一個(gè)令牌, 返回值為獲取到這個(gè)令牌花費(fèi)的時(shí)間
  • acquire(int permits) 獲取指定數(shù)量的令牌, 該方法也會(huì)阻塞, 返回值為獲取到這 permits 個(gè)令牌花費(fèi)的時(shí)間
  • tryAcquire() 判斷時(shí)候能獲取到令牌, 如果不能獲取立即返回 false
  • tryAcquire(int permits) 獲取指定數(shù)量的令牌, 如果不能獲取立即返回 false
  • tryAcquire(long timeout, TimeUnit unit) 判斷能否在指定時(shí)間內(nèi)獲取到令牌, 如果不能獲取立即返回 false
  • tryAcquire(int permits, long timeout, TimeUnit unit) 判斷能否在指定時(shí)間內(nèi)獲取到permits個(gè)令牌,如果不能則返回false。

這時(shí)候調(diào)用上面的測(cè)試接口,一秒鐘只能通過(guò)2個(gè)接口,同時(shí)每隔0.5s產(chǎn)生一個(gè)令牌,調(diào)用過(guò)于頻繁賊會(huì)被限制。但是上面的使用方式不夠優(yōu)雅,因?yàn)槲覀冃枰诿總€(gè)需要限流的接口重復(fù)使用tryAcquire()方法,然后根據(jù)是否獲取到令牌做邏輯判斷

優(yōu)雅使用

所謂優(yōu)雅就是統(tǒng)一處理,使用aop實(shí)現(xiàn)一個(gè)攔截器即可,如下所示:
首先我們的創(chuàng)建一個(gè)注解,該注解可以配置限流信息

/**
 * @author fjzheng
 * @version 1.0
 * @date 2021/10/25 00:06
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface RateLimit {
    /**
     * 資源的key,唯一
     * 作用:不同的接口,不同的流量控制
     */
    String key() default "";
?
    /**
     * 最多的訪(fǎng)問(wèn)限制次數(shù)
     */
    double permitsPerSecond () ;
?
    /**
     * 獲取令牌最大等待時(shí)間
     */
    long timeout();
?
    /**
     * 獲取令牌最大等待時(shí)間,單位(例:分鐘/秒/毫秒) 默認(rèn):毫秒
     */
    TimeUnit timeunit() default TimeUnit.MILLISECONDS;
?
    /**
     * 得不到令牌的提示語(yǔ)
     */
    String msg() default "系統(tǒng)繁忙,請(qǐng)稍后再試.";
?
}

然后使用aop攔截帶有RateLimit注解的方法,進(jìn)行統(tǒng)一處理即可。

/**
 * @author fjzheng
 * @version 1.0
 * @date 2021/10/25 00:10
 */
@Slf4j
@Component
@Aspect
public class RateLimitAop {
    /**
     * 不同的接口,不同的流量控制
     * map的key為 Limiter.key
     */
    private final Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();
?
    @Around("@annotation(com.shepherd.mall.seckill.annotation.RateLimit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //拿limit的注解
        RateLimit limit = method.getAnnotation(RateLimit.class);
        if (limit != null) {
            //key作用:不同的接口,不同的流量控制
            String key=limit.key();
            RateLimiter rateLimiter = null;
            //驗(yàn)證緩存是否有命中key
            if (!limitMap.containsKey(key)) {
                // 創(chuàng)建令牌桶
                rateLimiter = RateLimiter.create(limit.permitsPerSecond());
                limitMap.put(key, rateLimiter);
                log.info("新建了令牌桶={},容量={}",key,limit.permitsPerSecond());
            }
            rateLimiter = limitMap.get(key);
            // 拿令牌
            boolean acquire = rateLimiter.tryAcquire(limit.timeout(), limit.timeunit());
            // 拿不到命令,直接返回異常提示
            if (!acquire) {
                log.debug("令牌桶={},獲取令牌失敗",key);
                this.responseFail(limit.msg());
                return null;
            }
        }
        return joinPoint.proceed();
    }
?
    /**
     * 直接向前端拋出異常
     * @param msg 提示信息
     */
    private void responseFail(String msg)  {
        HttpServletResponse response=((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        ResponseVO<Object> responseVO = ResponseVO.failure(400, msg);
        WebUtil.writeJson(response, responseVO);
    }
}

優(yōu)雅使用示例

/**
 * @author fjzheng
 * @version 1.0
 * @date 2021/9/1 10:38
 */
@Slf4j
@RestController
@RequestMapping("/test")
@ResponseResultBody
public class TestController {
    @GetMapping("/limit2")
    @RateLimit(key = "limit2", permitsPerSecond = 1, timeout = 50, timeunit = TimeUnit.MILLISECONDS, msg = "當(dāng)前排隊(duì)人數(shù)較多,請(qǐng)稍后再試!")
    public String limit2() {
        log.info("令牌桶l(fā)imit2獲取令牌成功");
        return "ok";
    }
}

其測(cè)試結(jié)果和上面的示例是差不多的。
guava的rateLimit限流只能使用與單機(jī)版,如果是分布式系統(tǒng),部署多個(gè)節(jié)點(diǎn)就不行了

4.分布式限流

分布式限流服務(wù)最關(guān)鍵是將限流服務(wù)做成原子化的,防止redis在分步執(zhí)行命令時(shí)部分成功執(zhí)行部分失敗問(wèn)題導(dǎo)致邏輯錯(cuò)誤。lua腳本可以保證操作的原子性

redis+lua實(shí)現(xiàn)

local key = "rate.limit:" .. KEYS[1]
local limit = tonumber(ARGV[1])
local expire_time = ARGV[2]
?
local is_exists = redis.call("EXISTS", key)
if is_exists == 1 then
    if redis.call("INCR", key) > limit then
        return 0
    else
        return 1
    end
else
    redis.call("SET", key, 1)
    redis.call("EXPIRE", key, expire_time)
    return 1
end

nginx+lua實(shí)現(xiàn)

local locks = require "resty.lock"
?
local function acquire()
    local lock =locks:new("locks")
    local elapsed, err =lock:lock("limit_key") --互斥鎖 保證原子特性
    local limit_counter =ngx.shared.limit_counter --計(jì)數(shù)器
?
    local key = "ip:" ..os.time()
    local limit = 5 --限流大小
    local current =limit_counter:get(key)
?
    if current ~= nil and current + 1> limit then --如果超出限流大小
       lock:unlock()
       return 0
    end
    if current == nil then
       limit_counter:set(key, 1, 1) --第一次需要設(shè)置過(guò)期時(shí)間,設(shè)置key的值為1,
--過(guò)期時(shí)間為1秒
    else
        limit_counter:incr(key, 1) --第二次開(kāi)始加1即可
    end
    lock:unlock()
    return 1
end
ngx.print(acquire())

對(duì)于Nginx接入層限流可以使用Nginx自帶了兩個(gè)模塊:連接數(shù)限流模塊ngx_http_limit_conn_module和漏桶算法實(shí)現(xiàn)的請(qǐng)求限流模塊ngx_http_limit_req_module。
控制并發(fā)數(shù):ngx_http_limit_conn_module

http {
    include       mime.types;
    default_type  application/octet-stream;
?
    #cache
    lua_shared_dict dis_cache 128m;
?
    #限流設(shè)置
    limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=2r/s;
?
    #根據(jù)IP地址來(lái)限制,存儲(chǔ)內(nèi)存大小10M
    limit_conn_zone $binary_remote_addr zone=addr:1m;
?
    sendfile        on;
    #tcp_nopush     on;
?
    #keepalive_timeout  0;
    keepalive_timeout  65;
?
    #gzip  on;
?
    server {
        listen       80;
        server_name  localhost;
        location /brand {
            limit_conn addr 2;
            proxy_pass http://192.168.211.1:18081;
        }
?
        location /update_content {
            content_by_lua_file /root/lua/update_content.lua;
        }
?
        location /read_content {
            limit_req zone=contentRateLimit burst=4 nodelay;
            content_by_lua_file /root/lua/read_content.lua;
        }
    }
}
  • limit_conn_zone $binary_remote_addr zone=addr:10m; 表示限制根據(jù)用戶(hù)的IP地址來(lái)顯示,設(shè)置存儲(chǔ)地址為的內(nèi)存大小10M
  • limit_conn addr 2; 表示 同一個(gè)地址只允許連接2次。

控制速率:ngx_http_limit_req_module

user  root root;
worker_processes  1;
?
events {
    worker_connections  1024;
}
?
http {
    include       mime.types;
    default_type  application/octet-stream;
?
    #cache
    lua_shared_dict dis_cache 128m;
?
    #限流設(shè)置
    limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=2r/s;
?
    sendfile        on;
    #tcp_nopush     on;
?
    #keepalive_timeout  0;
    keepalive_timeout  65;
?
    #gzip  on;
?
    server {
        listen       80;
        server_name  localhost;
?
        location /update_content {
            content_by_lua_file /root/lua/update_content.lua;
        }
?
        location /read_content {
            limit_req zone=contentRateLimit burst=4 nodelay;
            content_by_lua_file /root/lua/read_content.lua;
        }
    }
}

burst 譯為突發(fā)、爆發(fā),表示在超過(guò)設(shè)定的處理速率后能額外處理的請(qǐng)求數(shù),當(dāng) rate=10r/s 時(shí),將1s拆成10份,即每100ms可處理1個(gè)請(qǐng)求。
此處,burst=4 ,若同時(shí)有4個(gè)請(qǐng)求到達(dá),Nginx 會(huì)處理第一個(gè)請(qǐng)求,剩余3個(gè)請(qǐng)求將放入隊(duì)列,然后每隔500ms從隊(duì)列中獲取一個(gè)請(qǐng)求進(jìn)行處理。若請(qǐng)求數(shù)大于4,將拒絕處理多余的請(qǐng)求,直接返回503.

不過(guò),單獨(dú)使用 burst 參數(shù)并不實(shí)用。假設(shè) burst=50 ,rate依然為10r/s,排隊(duì)中的50個(gè)請(qǐng)求雖然每100ms會(huì)處理一個(gè),但第50個(gè)請(qǐng)求卻需要等待 50 * 100ms即 5s,這么長(zhǎng)的處理時(shí)間自然難以接受。

因此,burst 往往結(jié)合 nodelay 一起使用。

到此這篇關(guān)于Spring Boot高可用限流三種實(shí)現(xiàn)解決方案的文章就介紹到這了,更多相關(guān)Spring Boot高可用限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決springboot文件上傳提示臨時(shí)文件夾不存在問(wèn)題

    解決springboot文件上傳提示臨時(shí)文件夾不存在問(wèn)題

    這篇文章主要介紹了解決springboot文件上傳提示臨時(shí)文件夾不存在問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Springboot AOP對(duì)指定敏感字段數(shù)據(jù)加密存儲(chǔ)的實(shí)現(xiàn)

    Springboot AOP對(duì)指定敏感字段數(shù)據(jù)加密存儲(chǔ)的實(shí)現(xiàn)

    本篇文章主要介紹了利用Springboot+AOP對(duì)指定的敏感數(shù)據(jù)進(jìn)行加密存儲(chǔ)以及對(duì)數(shù)據(jù)中加密的數(shù)據(jù)的解密的方法,代碼詳細(xì),具有一定的價(jià)值,感興趣的小伙伴可以了解一下
    2021-11-11
  • Java單元測(cè)試工具之JUnit的使用

    Java單元測(cè)試工具之JUnit的使用

    本篇文章主要詳細(xì)介紹單元測(cè)試工具JUnit的使用,文章中有詳細(xì)的代碼實(shí)例,有一定的參考價(jià)值,需要的朋友可以參考閱讀
    2023-04-04
  • PowerJob的TimingStrategyHandler工作流程源碼解讀

    PowerJob的TimingStrategyHandler工作流程源碼解讀

    這篇文章主要為大家介紹了PowerJob的TimingStrategyHandler工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Java線(xiàn)程狀態(tài)及切換、關(guān)閉線(xiàn)程的正確姿勢(shì)分享

    Java線(xiàn)程狀態(tài)及切換、關(guān)閉線(xiàn)程的正確姿勢(shì)分享

    這篇文章主要給大家介紹了關(guān)于Java線(xiàn)程狀態(tài)及切換、關(guān)閉線(xiàn)程的正確姿勢(shì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • MyBatis常用標(biāo)簽大全

    MyBatis常用標(biāo)簽大全

    這篇文章主要介紹了MyBatis常用標(biāo)簽大全的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-12-12
  • 有關(guān)Java中的BeanInfo介紹

    有關(guān)Java中的BeanInfo介紹

    Java的BeanInfo在工作中并不怎么用到,我也是在學(xué)習(xí)spring源碼的時(shí)候,發(fā)現(xiàn)SpringBoot啟動(dòng)時(shí)候會(huì)設(shè)置一個(gè)屬叫"spring.beaninfo.ignore",網(wǎng)上一些地方說(shuō)這個(gè)配置的意思是是否跳過(guò)java BeanInfo的搜索,但是BeanInfo又是什么呢?本文我們將對(duì)此做一個(gè)詳細(xì)介紹
    2021-09-09
  • SpringBoot整合JWT實(shí)戰(zhàn)教程

    SpringBoot整合JWT實(shí)戰(zhàn)教程

    JWT(JSON?Web?Token)是一種用于身份驗(yàn)證和授權(quán)的開(kāi)放標(biāo)準(zhǔn)(RFC?7519),它使用JSON格式傳輸信息,可以在不同系統(tǒng)之間安全地傳遞數(shù)據(jù),這篇文章主要介紹了SpringBoot整合JWT實(shí)戰(zhàn)教程,需要的朋友可以參考下
    2023-06-06
  • Springboot項(xiàng)目啟動(dòng)不加載resources目錄下的文件問(wèn)題

    Springboot項(xiàng)目啟動(dòng)不加載resources目錄下的文件問(wèn)題

    這篇文章主要介紹了Springboot項(xiàng)目啟動(dòng)不加載resources目錄下的文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java函數(shù)式編程(四):在集合中查找元素

    Java函數(shù)式編程(四):在集合中查找元素

    這篇文章主要介紹了Java函數(shù)式編程(四):在集合中查找元素,本文是系列文章的第4篇,其它篇章請(qǐng)參閱相關(guān)文章,需要的朋友可以參考下
    2014-09-09

最新評(píng)論