Spring Boot高可用限流三種實(shí)現(xiàn)解決方案
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)餐飲需要排隊(duì)就餐等?;氐交ヂ?lián)網(wǎng)網(wǎng)絡(luò)上,同樣也是這個(gè)道理,例如某某明星公布了戀情,訪問(wèn)從平時(shí)的50萬(wàn)增加到了500萬(wàn),系統(tǒng)最多可以支撐200萬(wàn)訪問(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ù)連接池大小、線程池大小、接口訪問(wèn)并發(fā)數(shù)等都是使用計(jì)數(shù)器算法。一般我們會(huì)限制一秒鐘能夠通過(guò)的請(qǐng)求數(shù)。比如我們規(guī)定,對(duì)于A接口來(lái)說(shuō),我們1分鐘的訪問(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)接口的訪問(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)窗口的話,每過(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提供了限流工具類RateLimiter,該類基于令牌桶算法實(shí)現(xiàn)流量限制,使用十分方便,而且十分高效。RateLimiter提供了令牌桶算法實(shí)現(xiàn):平滑突發(fā)限流(SmoothBursty)和平滑預(yù)熱限流(SmoothWarmingUp)。
依賴包
<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 "";
?
/**
* 最多的訪問(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
endnginx+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ù)用戶的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)文章希望大家以后多多支持腳本之家!
- 基于SpringBoot+Redis實(shí)現(xiàn)一個(gè)簡(jiǎn)單的限流器
- SpringBoot單機(jī)限流的實(shí)現(xiàn)
- SpringBoot使用Redis對(duì)用戶IP進(jìn)行接口限流的項(xiàng)目實(shí)踐
- SpringBoot使用Redis對(duì)用戶IP進(jìn)行接口限流的示例詳解
- SpringBoot Redis用注釋實(shí)現(xiàn)接口限流詳解
- SpringBoot如何使用自定義注解實(shí)現(xiàn)接口限流
- 使用SpringBoot?+?Redis?實(shí)現(xiàn)接口限流的方式
- Springboot+Redis實(shí)現(xiàn)API接口限流的示例代碼
相關(guān)文章
SpringBoot通過(guò)自定義注解實(shí)現(xiàn)配置類的自動(dòng)注入的實(shí)現(xiàn)
本文主要介紹了SpringBoot通過(guò)自定義注解實(shí)現(xiàn)配置類的自動(dòng)注入的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
java實(shí)現(xiàn)簡(jiǎn)單五子棋小游戲(1)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單五子棋小游戲的第一部分,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
Java知識(shí)點(diǎn)歸納 —給Java新手的一些建議(新手必看)
以下簡(jiǎn)單介紹了下我對(duì)于這些java基本知識(shí)點(diǎn)和技術(shù)點(diǎn)的一些看法和心得,這些內(nèi)容都源自于我這些年來(lái)使用java的一些總結(jié)2016-05-05
Spring事務(wù)管理下synchronized鎖失效問(wèn)題的解決方法
這篇文章主要給大家介紹了關(guān)于Spring事務(wù)管理下synchronized鎖失效問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03
JAVA項(xiàng)目swing轉(zhuǎn)javafx語(yǔ)法規(guī)則以及示例代碼
這篇文章主要介紹了JAVA項(xiàng)目swing轉(zhuǎn)javafx語(yǔ)法規(guī)則以及示例代碼的相關(guān)資料,文中詳細(xì)講解了主類繼承、窗口創(chuàng)建、布局管理、控件替換、事件監(jiān)聽(tīng)、線程安全、圖標(biāo)設(shè)置、顯示窗口等內(nèi)容,需要的朋友可以參考下2025-12-12
SpringSecurity Oauth2訪問(wèn)令牌續(xù)期問(wèn)題
這篇文章主要介紹了SpringSecurity Oauth2訪問(wèn)令牌續(xù)期問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04

