SpringBoot項(xiàng)目中集成Resilience4j實(shí)現(xiàn)彈性設(shè)計(jì)的方法

在現(xiàn)代分布式系統(tǒng)中,服務(wù)之間的依賴關(guān)系日益復(fù)雜。一個微服務(wù)可能依賴于多個其他服務(wù)、數(shù)據(jù)庫或外部 API。當(dāng)其中一個依賴項(xiàng)出現(xiàn)故障(如網(wǎng)絡(luò)延遲、服務(wù)宕機(jī)、超時等)時,若沒有適當(dāng)?shù)娜蒎e機(jī)制,整個調(diào)用鏈可能會雪崩式崩潰,導(dǎo)致系統(tǒng)不可用。為了解決這一問題,彈性設(shè)計(jì)(Resilience) 成為構(gòu)建高可用系統(tǒng)的關(guān)鍵能力。
在 Java 生態(tài)中,Resilience4j 是一款輕量級、函數(shù)式、受 Netflix Hystrix 啟發(fā)但更現(xiàn)代化的容錯庫。它專為 Java 8 和函數(shù)式編程設(shè)計(jì),支持 熔斷器(Circuit Breaker)、限流器(Rate Limiter)、重試(Retry)、隔板(Bulkhead)、時間限制器(TimeLimiter) 等多種彈性模式,并且天然支持響應(yīng)式編程(如 Reactor、RxJava)。
而 Spring Boot 作為當(dāng)前最流行的 Java 微服務(wù)框架,提供了強(qiáng)大的自動配置和注解驅(qū)動開發(fā)體驗(yàn)。將 Resilience4j 與 Spring Boot 集成,可以極大簡化彈性策略的實(shí)現(xiàn),讓開發(fā)者專注于業(yè)務(wù)邏輯,而非底層容錯細(xì)節(jié)。
本文將深入講解如何在 Spring Boot 項(xiàng)目中快速集成 Resilience4j,重點(diǎn)介紹其自動配置機(jī)制、核心注解的使用方式,并通過豐富的代碼示例展示各種彈性模式的實(shí)際應(yīng)用。無論你是初學(xué)者還是有經(jīng)驗(yàn)的開發(fā)者,都能從中獲得實(shí)用的實(shí)踐指導(dǎo)。
為什么選擇 Resilience4j?
在 Resilience4j 出現(xiàn)之前,Netflix Hystrix 是 Java 社區(qū)中最知名的容錯庫。然而,Hystrix 自 2018 年起已進(jìn)入維護(hù)模式,不再積極開發(fā)。相比之下,Resilience4j 具有以下顯著優(yōu)勢:
- ? 輕量級:無任何外部依賴(除 SLF4J 外),jar 包體積小。
- ? 函數(shù)式設(shè)計(jì):基于 Java 8 的
Function、Supplier等函數(shù)式接口,易于組合。 - ? 模塊化架構(gòu):按需引入所需模塊(如只用熔斷器,無需引入限流器)。
- ? 響應(yīng)式友好:原生支持 Project Reactor、RxJava 等響應(yīng)式流。
- ? 指標(biāo)暴露完善:可無縫集成 Micrometer,進(jìn)而對接 Prometheus + Grafana。
- ? Spring Boot 官方支持:通過
spring-cloud-starter-circuitbreaker-resilience4j提供自動配置。
?? 官方文檔地址:https://resilience4j.readme.io/
Spring Boot 集成 Resilience4j 的準(zhǔn)備工作
添加依賴
要在 Spring Boot 項(xiàng)目中使用 Resilience4j,首先需要在 pom.xml 中添加相關(guān)依賴。推薦使用 Spring Cloud 提供的 starter,它會自動處理版本兼容性問題。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.0</version> <!-- 或最新穩(wěn)定版 -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Resilience4j 核心 Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!-- 可選:用于指標(biāo)監(jiān)控 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>
?? 注意:Spring Cloud 版本需與 Spring Boot 版本匹配。例如,Spring Boot 3.x 對應(yīng) Spring Cloud 2022.0.0+??蓞⒖?Spring Cloud 官方版本對照表。
啟用自動配置
Spring Boot 的自動配置機(jī)制會在檢測到 Resilience4j 相關(guān)類路徑存在時,自動創(chuàng)建以下關(guān)鍵 Bean:
Resilience4JCircuitBreakerFactoryResilience4JRetryFactoryResilience4JRateLimiterFactoryResilience4JBulkheadFactory
這些工廠類用于創(chuàng)建對應(yīng)的彈性組件實(shí)例。你無需手動配置,除非需要自定義行為。
此外,若你使用了 @EnableCircuitBreaker 注解(來自 Spring Cloud Commons),它也會被自動識別并啟用熔斷功能。不過在較新版本中,該注解已非必需,因?yàn)樽詣优渲靡炎銐蛑悄堋?/p>
核心概念與組件概覽
在深入代碼前,先理解 Resilience4j 的幾個核心組件:
| 組件 | 作用 | 類比 |
|---|---|---|
| CircuitBreaker(熔斷器) | 當(dāng)失敗率達(dá)到閾值時,自動“熔斷”后續(xù)請求,避免雪崩 | 家庭電路中的保險絲 |
| Retry(重試) | 在失敗后自動重試操作,可配置重試次數(shù)、間隔、退避策略 | 打電話沒人接,稍后再打 |
| RateLimiter(限流器) | 限制單位時間內(nèi)的請求數(shù),防止系統(tǒng)過載 | 地鐵安檢口每秒只放行 5 人 |
| Bulkhead(隔板) | 限制并發(fā)執(zhí)行的線程數(shù)或信號量,隔離資源 | 船艙的水密隔艙,一艙進(jìn)水不影響其他艙 |
| TimeLimiter(時間限制器) | 為異步操作設(shè)置超時時間 | 限時答題,超時自動交卷 |
這些組件可以單獨(dú)使用,也可以組合使用,形成強(qiáng)大的彈性策略。例如:先重試 → 再熔斷 → 同時限流。
使用 @CircuitBreaker 實(shí)現(xiàn)熔斷保護(hù)
熔斷器是最常用的彈性模式。它通過監(jiān)控失敗率,在達(dá)到閾值時打開熔斷器,拒絕后續(xù)請求一段時間(半開狀態(tài)后嘗試恢復(fù))。
基礎(chǔ)配置
首先,在 application.yml 中配置熔斷器參數(shù):
resilience4j:
circuitbreaker:
instances:
backendA:
failure-rate-threshold: 50 # 失敗率閾值(%)
minimum-number-of-calls: 5 # 觸發(fā)統(tǒng)計(jì)的最小請求數(shù)
wait-duration-in-open-state: 5s # 熔斷后等待多久進(jìn)入半開狀態(tài)
permitted-number-of-calls-in-half-open-state: 3 # 半開狀態(tài)下允許的請求數(shù)
sliding-window-size: 10 # 滑動窗口大?。ㄓ涗涀罱?N 次調(diào)用)
sliding-window-type: COUNT_BASED # 滑動窗口類型:COUNT_BASED 或 TIME_BASED
automatic-transition-from-open-to-half-open-enabled: true
這里定義了一個名為 backendA 的熔斷器實(shí)例。
使用注解
在 Service 方法上使用 @CircuitBreaker 注解:
@Service
public class OrderService {
private final RestTemplate restTemplate;
public OrderService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@CircuitBreaker(name = "backendA", fallbackMethod = "getDefaultOrder")
public String getOrderDetails(String orderId) {
// 模擬調(diào)用遠(yuǎn)程服務(wù)
return restTemplate.getForObject("http://localhost:8081/api/orders/" + orderId, String.class);
}
// 回退方法:簽名必須與原方法一致(參數(shù) + 異常類型)
public String getDefaultOrder(String orderId, Exception ex) {
log.warn("Fallback triggered for order {}: {}", orderId, ex.getMessage());
return "Default order details for " + orderId;
}
}
?? 注意:
name必須與配置文件中的實(shí)例名一致。fallbackMethod指定回退方法名,該方法必須在同一類中。- 回退方法的參數(shù)列表必須包含原方法的所有參數(shù) + 一個 Throwable 類型參數(shù)(放在最后)。
熔斷器狀態(tài)流轉(zhuǎn)
熔斷器有三種狀態(tài):
- CLOSED(關(guān)閉):正常調(diào)用,記錄成功/失敗。
- OPEN(打開):失敗率超標(biāo),直接拒絕所有請求,拋出
CallNotPermittedException。 - HALF_OPEN(半開):等待一段時間后,允許少量請求試探。若成功則關(guān)閉,否則重新打開。
我們可以通過 Mermaid 圖清晰展示這一狀態(tài)機(jī):
測試熔斷行為
假設(shè)遠(yuǎn)程服務(wù) http://localhost:8081 不可用,連續(xù)調(diào)用 getOrderDetails:
- 前 5 次:實(shí)際調(diào)用,全部失敗。
- 第 6 次:因失敗率 100% > 50%,熔斷器打開,直接走 fallback。
- 等待 5 秒后:第 7 次調(diào)用會進(jìn)入 HALF_OPEN 狀態(tài),嘗試真實(shí)調(diào)用。
- 若成功 → 熔斷器關(guān)閉。
- 若失敗 → 重新打開。
使用 @Retry 實(shí)現(xiàn)自動重試
網(wǎng)絡(luò)抖動或臨時故障很常見,重試機(jī)制能有效提升成功率。
配置重試策略
在 application.yml 中:
resilience4j:
retry:
instances:
backendA:
max-attempts: 3 # 最大重試次數(shù)(含首次)
wait-duration: 1s # 重試間隔
enable-exponential-backoff: true # 啟用指數(shù)退避
exponential-backoff-multiplier: 2 # 退避倍數(shù):1s, 2s, 4s...
retry-exceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
ignore-exceptions:
- org.springframework.web.client.HttpClientErrorException.BadRequest
使用 @Retry 注解
@Service
public class PaymentService {
@Retry(name = "backendA", fallbackMethod = "processPaymentFallback")
public String processPayment(String paymentId) {
// 模擬不穩(wěn)定支付網(wǎng)關(guān)
if (Math.random() < 0.7) {
throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "Payment gateway error");
}
return "Payment processed: " + paymentId;
}
public String processPaymentFallback(String paymentId, Exception ex) {
return "Payment failed after retries: " + ex.getMessage();
}
}
?? 重試邏輯:
- 首次調(diào)用失敗 → 等待 1s → 第二次重試
- 若再失敗 → 等待 2s → 第三次重試
- 若仍失敗 → 觸發(fā) fallback
與熔斷器組合使用
你可以在同一個方法上同時使用 @CircuitBreaker 和 @Retry:
@CircuitBreaker(name = "backendA", fallbackMethod = "fallback")
@Retry(name = "backendA")
public String callUnstableService() {
// ...
}
執(zhí)行順序是:先重試 → 重試全部失敗后 → 觸發(fā)熔斷判斷。
使用 @RateLimiter 實(shí)現(xiàn)限流
限流用于保護(hù)系統(tǒng)不被突發(fā)流量壓垮。
配置限流規(guī)則
resilience4j:
ratelimiter:
instances:
backendA:
limit-for-period: 5 # 每個周期允許的請求數(shù)
limit-refresh-period: 10s # 周期長度
timeout-duration: 0 # 獲取許可的等待超時(0=立即失?。?
這意味著:每 10 秒最多處理 5 個請求,即 0.5 QPS。
使用 @RateLimiter 注解
@RestController
public class ApiController {
@RateLimiter(name = "backendA")
@GetMapping("/api/data")
public String getData() {
return "Data at " + Instant.now();
}
}
當(dāng)請求超過速率限制時,會拋出 RequestNotPermitted 異常。你可以通過全局異常處理器返回友好提示:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RequestNotPermitted.class)
public ResponseEntity<String> handleRateLimitExceeded() {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
.body("Too many requests. Please try again later.");
}
}
?? 注意:
@RateLimiter默認(rèn)是同步阻塞的。若timeout-duration > 0,線程會等待直到獲得許可;若為 0,則立即拒絕。
使用 @Bulkhead 實(shí)現(xiàn)資源隔離
隔板模式用于限制并發(fā)數(shù),防止單個慢服務(wù)耗盡所有線程。
配置隔板
Resilience4j 支持兩種隔板:
- 線程池隔板(ThreadPoolBulkhead):為每個服務(wù)分配獨(dú)立線程池。
- 信號量隔板(SemaphoreBulkhead):通過計(jì)數(shù)器限制并發(fā)(默認(rèn))。
配置信號量隔板:
resilience4j:
bulkhead:
instances:
backendA:
max-concurrent-calls: 3 # 最大并發(fā)數(shù)
使用 @Bulkhead 注解
@Service
public class ReportService {
@Bulkhead(name = "backendA", fallbackMethod = "generateReportFallback")
public String generateReport(String reportId) {
// 模擬耗時報告生成
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Report " + reportId + " generated";
}
public String generateReportFallback(String reportId, BulkheadFullException ex) {
return "Report generation is busy. Try again later.";
}
}
當(dāng)并發(fā)調(diào)用超過 3 個時,后續(xù)請求會立即觸發(fā) fallback,拋出 BulkheadFullException。
?? 隔板 vs 線程池:
- 信號量隔板輕量,適合 I/O 密集型任務(wù)(如 HTTP 調(diào)用)。
- 線程池隔板適合 CPU 密集型任務(wù),但開銷較大。
組合使用多種彈性模式
在實(shí)際場景中,往往需要組合多種策略。例如:
“對支付服務(wù)調(diào)用:先限流 → 再重試 3 次 → 若仍失敗則熔斷 → 同時限制并發(fā)不超過 5”
可通過注解疊加實(shí)現(xiàn):
@CircuitBreaker(name = "paymentService", fallbackMethod = "fallback")
@Retry(name = "paymentService")
@RateLimiter(name = "paymentService")
@Bulkhead(name = "paymentService")
public String callPaymentService(String paymentId) {
// ...
}
執(zhí)行順序由 Spring AOP 的代理機(jī)制決定,通常為:
@RateLimiter(最外層)@Bulkhead@Retry@CircuitBreaker(最內(nèi)層)
但更推薦使用 編程式組合 以明確控制順序:
@Service
public class PaymentOrchestrator {
private final CircuitBreaker circuitBreaker;
private final Retry retry;
private final RateLimiter rateLimiter;
private final Bulkhead bulkhead;
public PaymentOrchestrator(CircuitBreakerRegistry cbRegistry,
RetryRegistry retryRegistry,
RateLimiterRegistry rlRegistry,
BulkheadRegistry bulkheadRegistry) {
this.circuitBreaker = cbRegistry.circuitBreaker("paymentService");
this.retry = retryRegistry.retry("paymentService");
this.rateLimiter = rlRegistry.rateLimiter("paymentService");
this.bulkhead = bulkheadRegistry.bulkhead("paymentService");
}
public String callPaymentService(String paymentId) {
Supplier<String> supplier = () -> doCall(paymentId);
// 組合:限流 → 隔板 → 重試 → 熔斷
Supplier<String> decorated = Decorators.ofSupplier(supplier)
.withRateLimiter(rateLimiter)
.withBulkhead(bulkhead)
.withRetry(retry)
.withCircuitBreaker(circuitBreaker)
.decorate();
try {
return decorated.get();
} catch (Exception e) {
return fallback(paymentId, e);
}
}
private String doCall(String paymentId) {
// 實(shí)際調(diào)用邏輯
return restTemplate.postForObject(...);
}
private String fallback(String paymentId, Exception ex) {
return "Fallback for " + paymentId;
}
}
這種方式雖然代碼稍多,但邏輯清晰、可測試性強(qiáng)。
指標(biāo)監(jiān)控與可視化
彈性組件的行為需要可觀測性。Resilience4j 通過 Micrometer 暴露指標(biāo),可接入 Prometheus + Grafana。
暴露指標(biāo)端點(diǎn)
確保已添加 micrometer-registry-prometheus 依賴,并在 application.yml 中啟用:
management:
endpoints:
web:
exposure:
include: health,info,prometheus
metrics:
tags:
application: ${spring.application.name}
訪問 http://localhost:8080/actuator/prometheus 可看到類似指標(biāo):
resilience4j_circuitbreaker_state{kind="closed",name="backendA",} 1.0
resilience4j_circuitbreaker_failure_rate{name="backendA",} 0.0
resilience4j_retry_calls_total{kind="failed",name="backendA",} 2.0
resilience4j_ratelimiter_available_permissions{name="backendA",} 5.0
Grafana 儀表盤
你可以導(dǎo)入官方提供的 Resilience4j Grafana Dashboard,實(shí)時監(jiān)控各組件狀態(tài)。
?? 示例指標(biāo)含義:
resilience4j_circuitbreaker_state: 1=closed, 0=open, 0.5=half_openresilience4j_retry_calls_total{kind="successful_with_retry"}: 重試后成功的次數(shù)
常見問題與最佳實(shí)踐
1. 回退方法必須在同一類中嗎?
是的。當(dāng)前 Spring AOP 代理機(jī)制要求 fallback 方法與目標(biāo)方法在同一類。若需跨類,建議使用編程式調(diào)用。
2. 如何動態(tài)調(diào)整配置?
Resilience4j 支持運(yùn)行時修改配置(通過 Registry)。結(jié)合 Spring Cloud Config 或 Apollo,可實(shí)現(xiàn)動態(tài)刷新:
@RefreshScope
@Component
public class DynamicConfigService {
private final CircuitBreakerRegistry registry;
public void updateFailureRate(String name, float rate) {
CircuitBreakerConfig config = registry.getCircuitBreaker(name).getCircuitBreakerConfig();
CircuitBreakerConfig newConfig = CircuitBreakerConfig.from(config)
.failureRateThreshold(rate)
.build();
registry.getCircuitBreaker(name).transitionToForcedOpenState(); // 先強(qiáng)制打開
registry.replace(name, newConfig); // 替換配置
registry.getCircuitBreaker(name).transitionToClosedState(); // 關(guān)閉
}
}
3. 異常處理粒度
默認(rèn)情況下,所有異常都會被視為失敗。但你可以通過配置指定哪些異常應(yīng)觸發(fā)熔斷/重試:
resilience4j:
circuitbreaker:
instances:
backendA:
record-exceptions:
- org.springframework.web.client.HttpServerErrorException
ignore-exceptions:
- BusinessException
4. 避免過度重試
重試雖好,但需謹(jǐn)慎:
- 不要對冪等性未知的操作重試(如創(chuàng)建訂單)。
- 設(shè)置合理的最大重試次數(shù)(通常 2~3 次)。
- 使用指數(shù)退避避免瞬間重試風(fēng)暴。
5. 測試彈性行為
使用單元測試驗(yàn)證 fallback 和狀態(tài)轉(zhuǎn)換:
@Test
void testCircuitBreakerFallback() {
// 模擬遠(yuǎn)程服務(wù)始終失敗
when(restTemplate.getForObject(any(), eq(String.class)))
.thenThrow(new RuntimeException("Service down"));
// 調(diào)用多次觸發(fā)熔斷
for (int i = 0; i < 6; i++) {
orderService.getOrderDetails("123");
}
// 第 7 次應(yīng)直接走 fallback
String result = orderService.getOrderDetails("123");
assertThat(result).contains("Default order");
}
高級特性:自定義裝飾器與事件監(jiān)聽
自定義裝飾器
除了內(nèi)置注解,你還可以創(chuàng)建自定義裝飾器:
public class LoggingDecorator {
public static <T> Supplier<T> withLogging(Supplier<T> supplier, String operation) {
return () -> {
long start = System.currentTimeMillis();
try {
T result = supplier.get();
log.info("{} succeeded in {}ms", operation, System.currentTimeMillis() - start);
return result;
} catch (Exception e) {
log.error("{} failed after {}ms", operation, System.currentTimeMillis() - start, e);
throw e;
}
};
}
}
// 使用
Supplier<String> decorated = Decorators.ofSupplier(() -> service.call())
.withCircuitBreaker(cb)
.decorate();
Supplier<String> logged = LoggingDecorator.withLogging(decorated, "callService");
事件監(jiān)聽
監(jiān)聽熔斷器狀態(tài)變化:
@PostConstruct
public void registerCircuitBreakerListener() {
CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("backendA");
cb.getEventPublisher()
.onStateTransition(event ->
log.info("CircuitBreaker {} transitioned from {} to {}",
event.getCircuitBreakerName(),
event.getStateTransition().getFromState(),
event.getStateTransition().getToState()));
}
事件類型包括:ERROR, SUCCESS, STATE_TRANSITION, IGNORED_ERROR 等。
總結(jié)
Resilience4j 與 Spring Boot 的集成,為 Java 微服務(wù)提供了強(qiáng)大而簡潔的彈性能力。通過自動配置和注解驅(qū)動,開發(fā)者可以輕松實(shí)現(xiàn):
- ?? 熔斷保護(hù):防止級聯(lián)故障
- ?? 智能重試:應(yīng)對臨時故障
- ?? 流量控制:保障系統(tǒng)穩(wěn)定性
- ?? 資源隔離:避免單點(diǎn)拖垮全局
關(guān)鍵在于合理配置 + 組合使用 + 監(jiān)控反饋。不要盲目添加所有彈性策略,而應(yīng)根據(jù)業(yè)務(wù)場景選擇最適合的組合。例如:
- 對于關(guān)鍵寫操作(如支付):重試 + 熔斷 + 隔板
- 對于非關(guān)鍵讀操作(如推薦):限流 + 熔斷 + 快速失敗
最后,記住:彈性不是銀彈。它只是提升系統(tǒng)可用性的手段之一,還需結(jié)合良好的架構(gòu)設(shè)計(jì)、充分的測試和完善的監(jiān)控體系。
希望本文能幫助你在 Spring Boot 項(xiàng)目中高效、安全地應(yīng)用 Resilience4j,構(gòu)建真正 resilient 的系統(tǒng)!??
到此這篇關(guān)于SpringBoot項(xiàng)目中集成Resilience4j實(shí)現(xiàn)彈性設(shè)計(jì)的方法的文章就介紹到這了,更多相關(guān)SpringBoot集成Resilience4j內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis框架迭代器模式實(shí)現(xiàn)原理解析
這篇文章主要介紹了MyBatis框架迭代器模式實(shí)現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03
springboot+vue實(shí)現(xiàn)頁面下載文件
這篇文章主要為大家詳細(xì)介紹了springboot+vue實(shí)現(xiàn)頁面下載文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-12-12
Java中的數(shù)組基礎(chǔ)知識學(xué)習(xí)教程
這篇文章主要介紹了Java中的數(shù)組基礎(chǔ)知識學(xué)習(xí)教程,文中同時也整理了Java對數(shù)字類型的支持狀況及Number類中的方法,需要的朋友可以參考下2016-02-02
java Class文件內(nèi)部結(jié)構(gòu)解析過程詳解
java class的文件結(jié)構(gòu),java class文件結(jié)構(gòu)是基于字節(jié)流的,用unicode進(jìn)行編碼,下面說說java Class文件內(nèi)部結(jié)構(gòu)分析2013-11-11
如何將IDEA打成jar包并在windows后臺運(yùn)行
在本篇文章里小編給大家分享的是關(guān)于如何將IDEA打成jar包并在windows后臺運(yùn)行知識點(diǎn),需要的朋友們可以學(xué)習(xí)參考下。2019-08-08
dubbo新手學(xué)習(xí)之事件通知實(shí)踐教程
這篇文章主要給大家介紹了關(guān)于dubbo新手學(xué)習(xí)之事件通知實(shí)踐的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

