如何提高系統(tǒng)穩(wěn)定性,減少線上事故?
前言
最近有位球友問(wèn)了我一個(gè)問(wèn)題:如何提高系統(tǒng)穩(wěn)定性,減少線上事故?
確實(shí)系統(tǒng)穩(wěn)定性非常重要。
今天這篇文章跟大家一起聊聊這個(gè)話題,希望對(duì)你會(huì)有所幫助。
一、建立完善的可觀測(cè)體系
可觀測(cè)性是系統(tǒng)穩(wěn)定性的基石。
有些小伙伴在工作中可能遇到過(guò)這樣的情況:系統(tǒng)突然變慢,但查看CPU、內(nèi)存都正常,就是找不到問(wèn)題根源。
1. 多維度監(jiān)控體系
// 使用Spring Boot Actuator實(shí)現(xiàn)健康檢查
@Configuration
publicclass HealthConfig {
@Component
publicclass CustomHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Autowired
private RedisTemplate redisTemplate;
@Override
public Health health() {
// 檢查數(shù)據(jù)庫(kù)連接
if (!checkDatabase()) {
return Health.down()
.withDetail("database", "連接失敗")
.build();
}
// 檢查Redis連接
if (!checkRedis()) {
return Health.down()
.withDetail("redis", "連接異常")
.build();
}
// 檢查磁盤(pán)空間
if (!checkDiskSpace()) {
return Health.down()
.withDetail("disk", "空間不足")
.build();
}
return Health.up().build();
}
private boolean checkDatabase() {
try {
return dataSource.getConnection().isValid(5);
} catch (Exception e) {
returnfalse;
}
}
private boolean checkRedis() {
try {
redisTemplate.opsForValue().get("health_check");
returntrue;
} catch (Exception e) {
returnfalse;
}
}
private boolean checkDiskSpace() {
File file = new File(".");
return file.getFreeSpace() > 1024 * 1024 * 1024; // 剩余1GB以上
}
}
}代碼邏輯解析:
- 通過(guò)實(shí)現(xiàn)HealthIndicator接口,我們可以自定義健康檢查邏輯
- 檢查數(shù)據(jù)庫(kù)連接是否正常
- 檢查Redis等中間件連接狀態(tài)
- 檢查系統(tǒng)資源如磁盤(pán)空間
- 當(dāng)任何組件異常時(shí),立即返回down狀態(tài),便于監(jiān)控系統(tǒng)及時(shí)告警
2. 分布式鏈路追蹤
// 使用Spring Cloud Sleuth實(shí)現(xiàn)鏈路追蹤
@RestController
publicclass OrderController {
privatefinal Tracer tracer;
public OrderController(Tracer tracer) {
this.tracer = tracer;
}
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
// 開(kāi)始新的Span
Span span = tracer.nextSpan().name("createOrder").start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
// 記錄業(yè)務(wù)操作
span.tag("user.id", request.getUserId());
span.tag("order.amount", String.valueOf(request.getAmount()));
// 執(zhí)行訂單創(chuàng)建邏輯
Order order = orderService.createOrder(request);
// 記錄成功結(jié)果
span.event("order.created");
return ResponseEntity.ok(order);
} catch (Exception e) {
// 記錄錯(cuò)誤信息
span.error(e);
throw e;
} finally {
span.finish();
}
}
}鏈路追蹤的價(jià)值:
- 快速定位性能瓶頸
- 可視化服務(wù)調(diào)用關(guān)系
- 分析跨服務(wù)異常
二、構(gòu)建彈性架構(gòu):限流、降級(jí)、熔斷
有些小伙伴在工作中可能遇到過(guò)這樣的場(chǎng)景:大促期間流量激增,系統(tǒng)被瞬間打垮。
這時(shí)候就需要彈性架構(gòu)來(lái)保護(hù)系統(tǒng)。
1. 智能限流策略
// 使用Resilience4j實(shí)現(xiàn)限流
@Configuration
publicclass RateLimitConfig {
@Bean
public RateLimiterRegistry rateLimiterRegistry() {
return RateLimiterRegistry.of(
RateLimiterConfig.custom()
.limitForPeriod(100) // 每秒100個(gè)請(qǐng)求
.limitRefreshPeriod(Duration.ofSeconds(1))
.timeoutDuration(Duration.ofMillis(100))
.build()
);
}
@Service
publicclass OrderService {
privatefinal RateLimiter rateLimiter;
public OrderService(RateLimiterRegistry registry) {
this.rateLimiter = registry.rateLimiter("orderService");
}
@RateLimiter(name = "orderService", fallbackMethod = "createOrderFallback")
public Order createOrder(OrderRequest request) {
// 正常的訂單創(chuàng)建邏輯
return processOrderCreation(request);
}
// 降級(jí)方法
public Order createOrderFallback(OrderRequest request, Exception e) {
// 記錄降級(jí)日志
log.warn("訂單服務(wù)觸發(fā)限流降級(jí), userId: {}", request.getUserId());
// 返回兜底數(shù)據(jù)或拋出業(yè)務(wù)異常
thrownew BusinessException("系統(tǒng)繁忙,請(qǐng)稍后重試");
}
}
}限流策略詳解:
- 固定窗口:簡(jiǎn)單但存在臨界問(wèn)題
- 滑動(dòng)窗口:更精確但實(shí)現(xiàn)復(fù)雜
- 令牌桶:允許突發(fā)流量
- 漏桶:平滑流量,保持穩(wěn)定速率
2. 服務(wù)熔斷機(jī)制
// 熔斷器配置
@Configuration
publicclass CircuitBreakerConfig {
@Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
return CircuitBreakerRegistry.of(
CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失敗率閾值50%
.slowCallRateThreshold(50) // 慢調(diào)用閾值50%
.slowCallDurationThreshold(Duration.ofSeconds(2)) // 2秒以上算慢調(diào)用
.waitDurationInOpenState(Duration.ofSeconds(60)) // 打開(kāi)狀態(tài)等待60秒
.permittedNumberOfCallsInHalfOpenState(10) // 半開(kāi)狀態(tài)允許10個(gè)調(diào)用
.minimumNumberOfCalls(10) // 最小調(diào)用數(shù)
.slidingWindowType(SlidingWindowType.COUNT_BASED)
.slidingWindowSize(10) // 滑動(dòng)窗口大小
.build()
);
}
@Service
publicclass PaymentService {
privatefinal CircuitBreaker circuitBreaker;
public PaymentService(CircuitBreakerRegistry registry) {
this.circuitBreaker = registry.circuitBreaker("paymentService");
}
public PaymentResult processPayment(PaymentRequest request) {
return circuitBreaker.executeSupplier(() -> {
// 調(diào)用支付服務(wù)
return paymentClient.pay(request);
});
}
}
}熔斷器狀態(tài)機(jī):
- CLOSED:正常狀態(tài),請(qǐng)求正常通過(guò)
- OPEN:打開(kāi)狀態(tài),所有請(qǐng)求被拒絕
- HALF_OPEN:半開(kāi)狀態(tài),允許部分請(qǐng)求試探
三、高可用部署策略
有些小伙伴在工作中可能忽略了部署環(huán)節(jié)的重要性,實(shí)際上很多線上事故都發(fā)生在發(fā)布過(guò)程中。
1. 藍(lán)綠部署
# Kubernetes藍(lán)綠部署配置
apiVersion:apps/v1
kind:Deployment
metadata:
name:order-service-v2
spec:
replicas:3
selector:
matchLabels:
app:order-service
version:v2
template:
metadata:
labels:
app:order-service
version:v2
spec:
containers:
-name:order-service
image:order-service:v2.0.0
readinessProbe:
httpGet:
path:/actuator/health
port:8080
initialDelaySeconds:30
periodSeconds:10
livenessProbe:
httpGet:
path:/actuator/health
port:8080
initialDelaySeconds:60
periodSeconds:20
---
apiVersion:v1
kind:Service
metadata:
name:order-service
spec:
selector:
app:order-service
version:v2# 切換流量到新版本
ports:
-port:80
targetPort:8080藍(lán)綠部署優(yōu)勢(shì):
- 快速回滾:只需修改Service的selector
- 零停機(jī)發(fā)布
- 避免版本兼容問(wèn)題
2. 灰度發(fā)布
// 基于流量權(quán)重的灰度發(fā)布
@Component
publicclass GrayReleaseRouter {
@Value("${gray.release.ratio:0.1}")
privatedouble grayRatio;
@Autowired
private HttpServletRequest request;
public boolean shouldRouteToNewVersion() {
String userId = getUserIdFromRequest();
// 基于用戶ID的哈希進(jìn)行分流
int hash = Math.abs(userId.hashCode());
double ratio = (hash % 100) / 100.0;
return ratio < grayRatio;
}
public Object routeRequest(Object request) {
if (shouldRouteToNewVersion()) {
// 轉(zhuǎn)發(fā)到新版本服務(wù)
return callNewVersion(request);
} else {
// 使用老版本服務(wù)
return callOldVersion(request);
}
}
private String getUserIdFromRequest() {
// 從請(qǐng)求中提取用戶ID
return request.getHeader("X-User-Id");
}
}灰度發(fā)布策略:
- 按用戶ID分流
- 按流量比例分流
- 按業(yè)務(wù)參數(shù)分流(如特定城市、用戶等級(jí))
四、數(shù)據(jù)一致性與事務(wù)管理
數(shù)據(jù)不一致是很多線上事故的根源。
有些小伙伴在工作中可能遇到過(guò):訂單扣款成功但庫(kù)存未減少,或者消息重復(fù)消費(fèi)導(dǎo)致數(shù)據(jù)重復(fù)。
1. 分布式事務(wù)解決方案
// 使用Seata實(shí)現(xiàn)分布式事務(wù)
@Service
publicclass OrderServiceImpl implements OrderService {
@GlobalTransactional
@Override
public Order createOrder(OrderRequest request) {
// 1. 創(chuàng)建訂單(本地事務(wù))
Order order = orderMapper.insert(request);
// 2. 扣減庫(kù)存(遠(yuǎn)程服務(wù))
inventoryFeignClient.deduct(request.getProductId(), request.getQuantity());
// 3. 創(chuàng)建積分記錄(遠(yuǎn)程服務(wù))
pointsFeignClient.addPoints(request.getUserId(), request.getAmount());
return order;
}
}
// 庫(kù)存服務(wù)
@Service
publicclass InventoryServiceImpl implements InventoryService {
@Transactional
@Override
public void deduct(String productId, Integer quantity) {
// 檢查庫(kù)存
Inventory inventory = inventoryMapper.selectByProductId(productId);
if (inventory.getStock() < quantity) {
thrownew BusinessException("庫(kù)存不足");
}
// 扣減庫(kù)存
inventoryMapper.deductStock(productId, quantity);
// 記錄庫(kù)存變更日志
inventoryLogMapper.insert(new InventoryLog(productId, quantity));
}
}分布式事務(wù)模式:
- 2PC:強(qiáng)一致,但性能較差
- TCC:高性能,但實(shí)現(xiàn)復(fù)雜
- SAGA:長(zhǎng)事務(wù)場(chǎng)景,最終一致
- 本地消息表:簡(jiǎn)單可靠,應(yīng)用廣泛
2. 消息隊(duì)列的可靠投遞
// 本地消息表實(shí)現(xiàn)最終一致性
@Service
@Transactional
publicclass OrderServiceWithLocalMessage {
@Autowired
private OrderMapper orderMapper;
@Autowired
private MessageLogMapper messageLogMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
public void createOrder(OrderRequest request) {
// 1. 創(chuàng)建訂單
Order order = orderMapper.insert(request);
// 2. 記錄本地消息
MessageLog messageLog = new MessageLog();
messageLog.setMessageId(UUID.randomUUID().toString());
messageLog.setContent(buildMessageContent(order));
messageLog.setStatus(MessageStatus.PENDING);
messageLogMapper.insert(messageLog);
// 3. 發(fā)送消息到MQ
try {
rabbitTemplate.convertAndSend("order.exchange",
"order.created", messageLog.getContent());
// 4. 更新消息狀態(tài)為已發(fā)送
messageLogMapper.updateStatus(messageLog.getMessageId(),
MessageStatus.SENT);
} catch (Exception e) {
// 發(fā)送失敗,消息狀態(tài)保持PENDING,由定時(shí)任務(wù)重試
log.error("消息發(fā)送失敗", e);
}
}
// 消息重試定時(shí)任務(wù)
@Scheduled(fixedDelay = 60000) // 每分鐘執(zhí)行一次
public void retryFailedMessages() {
List<MessageLog> failedMessages = messageLogMapper
.selectByStatus(MessageStatus.PENDING);
for (MessageLog message : failedMessages) {
try {
rabbitTemplate.convertAndSend("order.exchange",
"order.created", message.getContent());
messageLogMapper.updateStatus(message.getMessageId(),
MessageStatus.SENT);
} catch (Exception e) {
log.error("重試消息發(fā)送失敗: {}", message.getMessageId(), e);
}
}
}
}可靠消息要點(diǎn):
- 先持久化消息,再發(fā)送MQ
- 使用定時(shí)任務(wù)補(bǔ)償未發(fā)送的消息
- 消費(fèi)者實(shí)現(xiàn)冪等性
五、容量規(guī)劃與性能優(yōu)化
有些小伙伴在工作中可能等到系統(tǒng)出現(xiàn)性能問(wèn)題才開(kāi)始優(yōu)化,實(shí)際上容量規(guī)劃應(yīng)該前置。
1. 壓力測(cè)試與容量評(píng)估
// 使用JMH進(jìn)行基準(zhǔn)測(cè)試
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
publicclass OrderServiceBenchmark {
private OrderService orderService;
private OrderRequest request;
@Setup
public void setup() {
orderService = new OrderService();
request = new OrderRequest("user123", "product456", 2, 100.0);
}
@Benchmark
public void createOrderBenchmark() {
orderService.createOrder(request);
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(OrderServiceBenchmark.class.getSimpleName())
.forks(1)
.warmupIterations(2)
.measurementIterations(3)
.build();
new Runner(options).run();
}
}容量規(guī)劃步驟:
- 基準(zhǔn)測(cè)試:獲取單機(jī)性能指標(biāo)
- 壓力測(cè)試:找到系統(tǒng)瓶頸點(diǎn)
- 容量計(jì)算:根據(jù)業(yè)務(wù)目標(biāo)計(jì)算所需資源
- 預(yù)留buffer:通常預(yù)留30%-50%的冗余
2. 數(shù)據(jù)庫(kù)性能優(yōu)化
-- 慢查詢(xún)分析
EXPLAINANALYZE
SELECT o.*, u.username, p.product_name
FROM orders o
LEFTJOINusers u ON o.user_id = u.user_id
LEFTJOIN products p ON o.product_id = p.product_id
WHERE o.create_time BETWEEN'2023-01-01'AND'2023-12-31'
AND o.status = 'COMPLETED'
AND u.city = '北京'
ORDERBY o.amount DESC
LIMIT100;
-- 索引優(yōu)化建議
-- 1. 復(fù)合索引覆蓋常用查詢(xún)條件
CREATEINDEX idx_orders_user_time ON orders(user_id, create_time);
-- 2. 覆蓋索引避免回表
CREATEINDEX idx_orders_covering ON orders(status, create_time, amount)
INCLUDE (user_id, product_id);
-- 3. 函數(shù)索引優(yōu)化復(fù)雜條件
CREATEINDEX idx_orders_month ON orders(EXTRACT(MONTHFROM create_time));數(shù)據(jù)庫(kù)優(yōu)化策略:
- 讀寫(xiě)分離:主庫(kù)寫(xiě),從庫(kù)讀
- 分庫(kù)分表:水平拆分大表
- 索引優(yōu)化:避免全表掃描
- 查詢(xún)優(yōu)化:減少JOIN,避免SELECT *
六、應(yīng)急預(yù)案與故障處理
即使做了萬(wàn)全準(zhǔn)備,故障仍然可能發(fā)生。有些小伙伴在工作中可能因?yàn)闆](méi)有預(yù)案而手忙腳亂。
1. 故障預(yù)案庫(kù)
// 自動(dòng)化故障切換
@Component
publicclass AutoFailoverHandler {
@Autowired
private CircuitBreakerRegistry circuitBreakerRegistry;
@Autowired
private RedisTemplate redisTemplate;
@EventListener
public void handleDatabaseFailure(DatabaseDownEvent event) {
log.warn("數(shù)據(jù)庫(kù)故障,啟用降級(jí)策略");
// 1. 打開(kāi)熔斷器,防止請(qǐng)求堆積
CircuitBreaker circuitBreaker = circuitBreakerRegistry
.circuitBreaker("databaseService");
circuitBreaker.transitionToOpenState();
// 2. 啟用本地緩存模式
redisTemplate.opsForValue().set("degradation.mode", "true");
// 3. 發(fā)送告警通知
alertService.sendCriticalAlert("數(shù)據(jù)庫(kù)故障,已啟用降級(jí)模式");
}
// 定時(shí)檢查數(shù)據(jù)庫(kù)恢復(fù)
@Scheduled(fixedRate = 30000)
public void checkDatabaseRecovery() {
if (isDatabaseRecovered()) {
log.info("數(shù)據(jù)庫(kù)已恢復(fù),關(guān)閉降級(jí)模式");
redisTemplate.delete("degradation.mode");
CircuitBreaker circuitBreaker = circuitBreakerRegistry
.circuitBreaker("databaseService");
circuitBreaker.transitionToClosedState();
}
}
}2. 故障演練
// 混沌工程故障注入
@RestController
publicclass ChaosController {
@PostMapping("/chaos/inject")
public String injectChaos(@RequestBody ChaosConfig config) {
switch (config.getFaultType()) {
case"latency":
// 注入延遲
injectLatency(config.getDuration(), config.getLatencyMs());
break;
case"exception":
// 注入異常
injectException(config.getDuration(), config.getExceptionRate());
break;
case"memory":
// 消耗內(nèi)存
consumeMemory(config.getMemoryMb());
break;
default:
thrownew IllegalArgumentException("不支持的故障類(lèi)型");
}
return"故障注入成功";
}
private void injectLatency(Duration duration, long latencyMs) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
AtomicBoolean enabled = new AtomicBoolean(true);
// 設(shè)置延遲
ThreadLocalRandom random = ThreadLocalRandom.current();
AspectJProxyFactory factory = new AspectJProxyFactory(new Object());
factory.addAspect(new LatencyAspect(enabled, latencyMs, random));
// 定時(shí)關(guān)閉
executor.schedule(() -> enabled.set(false),
duration.toMillis(), TimeUnit.MILLISECONDS);
}
}總結(jié)
接下來(lái),給大家總結(jié)一下提高系統(tǒng)穩(wěn)定性的幾個(gè)核心要點(diǎn):
1. 預(yù)防為主
- 完善監(jiān)控:建立多層次監(jiān)控體系
- 容量規(guī)劃:提前評(píng)估系統(tǒng)容量
- 代碼質(zhì)量:嚴(yán)格的代碼審查和測(cè)試
2. 快速發(fā)現(xiàn)
- 智能告警:基于機(jī)器學(xué)習(xí)的異常檢測(cè)
- 鏈路追蹤:快速定位問(wèn)題根因
- 日志分析:集中式日志管理
3. 快速恢復(fù)
- 彈性架構(gòu):限流、降級(jí)、熔斷
- 自動(dòng)化運(yùn)維:一鍵回滾、自動(dòng)擴(kuò)容
- 應(yīng)急預(yù)案:完善的故障處理流程
4. 持續(xù)改進(jìn)
- 故障復(fù)盤(pán):每次事故都要深度分析
- 混沌工程:主動(dòng)發(fā)現(xiàn)系統(tǒng)弱點(diǎn)
- 技術(shù)債管理:定期償還技術(shù)債務(wù)
記住,系統(tǒng)穩(wěn)定性是一個(gè)持續(xù)改進(jìn)的過(guò)程,沒(méi)有終點(diǎn)。
每個(gè)團(tuán)隊(duì)都應(yīng)該根據(jù)自身情況,選擇最適合的穩(wěn)定性建設(shè)路徑。




























