ð åŒåºïŒäžå³¡å€§åçæºæ §
æ³è±¡é¿æ±äžçäžå³¡å€§å ðïžïŒ
没æå€§åïŒçŽæ¥å€çïŒïŒ
æŽéšæ¥äžŽ ð§ïžð§ïžð§ïž
â
措氎 ððð
â
æ·¹æ²¡äžæžžååž ð±
æå€§åïŒåå³°å¡«è°·ïŒïŒ
æŽéšæ¥äžŽ ð§ïžð§ïžð§ïž
â
倧åèæ°Ž ðïžïŒæ¶æ¯éåïŒ
â
ååæŸæ°Ž ð§ð§ð§
â
äžæžžå®å
š â
è¿å°±æ¯æ¶æ¯éåçåå³°å¡«è°·äœçšïŒ
æ žå¿ææ³ïŒ
- åå³°ïŒé«å³°æ¶æå请æ±
- å¡«è°·ïŒäœè°·æ¶æ ¢æ ¢å€ç
ð€ ä»ä¹æ¯åå³°å¡«è°·ïŒ
åå³°ïŒPeak ShavingïŒ
é®é¢ïŒæµéçªå¢
ç§ææŽ»åšåŒå§ïŒð
10:00:00 â 100äžè¯·æ±åæ¶å°æ¥ ð¥
â
æå¡åšæäžäœ â 宿º ð
è§£å³ïŒæ¶æ¯éåçŒå²
10:00:00 â 100äžè¯·æ± â æ¶æ¯éåïŒæåïŒðŠ
â
æ
¢æ
¢æ¶è޹ â 1000 QPS â æå¡åšçš³å® â
ææïŒ
- 请æ±å³°åŒïŒ100äž/ç§
- å€çèœåïŒ1000/ç§
- æ¶æ¯éååæäº99.9%çå³°åŒïŒ
å¡«è°·ïŒValley FillingïŒ
é®é¢ïŒæµéäœè°·æ¶èµæºæµªè޹
åæš3ç¹ ð
â
请æ±éåŸå° â æå¡åšé²çœ® ð€
è§£å³ïŒç§¯åçæ¶æ¯ç»§ç»å€ç
çœå€©ç§¯åçæ¶æ¯
â
æäžæ
¢æ
¢å€ç â 填满äœè°·æ¶éŽ â°
â
èµæºå
åå©çš â
ð¯ åå³°å¡«è°·çåç
åçåŸ
è¯·æ±æµéæ²çº¿åŸ
æµé
â
â ð¥ å³°åŒïŒ100äžQPSïŒ
â / \
â / \
â / \ â æ²¡ææ¶æ¯éåïŒçŽæ¥æ¿åå³°åŒ
â / \ ç³»ç»åŽ©æºïŒð
â / \
ââââââââââââââââââââââââ ç³»ç»å€çèœåïŒ1000 QPSïŒ
â \
â __ äœè°·ïŒ100 QPSïŒ
â
âââââââââââââââââââââââââââââââââââ æ¶éŽ
10:00 12:00
ææ¶æ¯éåçæ
åµ
æµé
â
â æ¶æ¯éåæå ðŠ
â / \
â / \
â / \
â / \
â / \
ââââââââââââââââââââââââ çš³å®å€çïŒ1000 QPSïŒâ
â \
â __ ç»§ç»å€ç积å â°
â
âââââââââââââââââââââââââââââââââââ æ¶éŽ
10:00 12:00
åå³°ïŒæåå³°åŒè¯·æ±
å¡«è°·ïŒå©çšäœè°·æ¶éŽå€ç积å
æ°åŠæš¡å
å讟ïŒ
- å³°åŒæµéïŒP = 100äž QPS
- ç³»ç»å€çèœåïŒC = 1000 QPS
- å³°åŒæç»æ¶éŽïŒT = 60ç§
æ²¡ææ¶æ¯éåïŒ
éèŠçæå¡åšæ° = P / C = 100äž / 1000 = 1000å° ð±
ææ¶æ¯éåïŒ
积åçæ¶æ¯æ° = (P - C) à T
= (100äž - 1000) Ã 60
â 6000äžæ¡
å€çå®éèŠçæ¶éŽ = 6000äž / 1000 = 60000ç§ â 16.7å°æ¶
éèŠçæå¡åšæ° = åªé1å°ïŒæå€çèœåé
眮ïŒâ
ç»è®ºïŒæ¶æ¯éåå¯ä»¥èç999å°æå¡åšïŒð°
ð å®é åºçšåºæ¯
åºæ¯1ïŒç§æç³»ç» ð
äžå¡æµçš
çšæ·ç¹å»"ç§æ" ð±ïž
â
请æ±è¿å
¥æ¶æ¯éå ðŠïŒç¬éŽå®æïŒ
â
è¿å"æéäž"æç€º â³
â
åå°æ
¢æ
¢å€ç订å ð
â
å€ç宿ïŒéç¥çšæ· â
代ç å®ç°
ç§ææ¥å£ïŒç产è ïŒïŒ
@RestController
@RequestMapping("/api/seckill")
@Slf4j
public class SeckillController {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String SECKILL_QUEUE = "seckill.queue";
/**
* ç§ææ¥å£
*/
@PostMapping("/order")
public ResponseEntity<?> seckill(
@RequestParam Long productId,
@RequestParam Long userId) {
log.info("çšæ·{}ç§æåå{}", userId, productId);
// â æ£æ¥åºåïŒRedisïŒ
String stockKey = "product:stock:" + productId;
Long stock = redisTemplate.opsForValue().decrement(stockKey);
if (stock < 0) {
// åºåäžè¶³ïŒåæ»
redisTemplate.opsForValue().increment(stockKey);
return ResponseEntity.ok(Result.fail("ååå·²æ¢å
ïŒ"));
}
// â åéå°æ¶æ¯éåïŒåå³°ïŒ
SeckillMessage message = new SeckillMessage();
message.setProductId(productId);
message.setUserId(userId);
message.setTimestamp(System.currentTimeMillis());
rabbitTemplate.convertAndSend(SECKILL_QUEUE, message);
log.info("ç§æè¯·æ±å·²è¿å
¥éå");
// â ç«å³è¿åïŒäžçåŸ
å€ç宿ïŒ
return ResponseEntity.ok(Result.success("æéäžïŒè¯·çšåæ¥ç订å"));
}
/**
* æ¥è¯¢ç§æç»æ
*/
@GetMapping("/result")
public ResponseEntity<?> getSeckillResult(
@RequestParam Long userId,
@RequestParam Long productId) {
String resultKey = String.format("seckill:result:%d:%d", userId, productId);
String result = redisTemplate.opsForValue().get(resultKey);
if (result == null) {
return ResponseEntity.ok(Result.success("æéäž..."));
} else if ("SUCCESS".equals(result)) {
return ResponseEntity.ok(Result.success("ç§ææåïŒ"));
} else {
return ResponseEntity.ok(Result.fail("ç§æå€±èŽ¥ïŒ" + result));
}
}
}
@Data
class SeckillMessage {
private Long productId;
private Long userId;
private Long timestamp;
}
ç§ææ¶è޹è ïŒå€ç订åïŒïŒ
@Component
@Slf4j
public class SeckillConsumer {
@Autowired
private OrderService orderService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* â æ¶èŽ¹ç§ææ¶æ¯ïŒå¡«è°·ïŒ
*
* é
眮ïŒ
* - å¹¶åæ°ïŒ10ïŒæ ¹æ®ç³»ç»å€çèœåé
眮ïŒ
* - prefetchCountïŒ1ïŒæ¯æ¬¡åªå1æ¡æ¶æ¯ïŒ
*/
@RabbitListener(
queues = "seckill.queue",
concurrency = "10" // 10䞪æ¶è޹è
线çš
)
public void processSeckill(SeckillMessage message) {
log.info("å€çç§æè®¢å: userId={}, productId={}",
message.getUserId(), message.getProductId());
String resultKey = String.format("seckill:result:%d:%d",
message.getUserId(), message.getProductId());
try {
// â å建订åïŒæ°æ®åºæäœïŒ
Order order = orderService.createSeckillOrder(
message.getUserId(),
message.getProductId()
);
log.info("ç§æè®¢åå建æå: orderId={}", order.getId());
// ä¿åç»æå°Redis
redisTemplate.opsForValue().set(resultKey, "SUCCESS", 10, TimeUnit.MINUTES);
// åééç¥
notifyUser(message.getUserId(), "ç§ææåïŒè®¢åå·ïŒ" + order.getId());
} catch (InsufficientStockException e) {
log.warn("åºåäžè¶³");
redisTemplate.opsForValue().set(resultKey, "åºåäžè¶³", 10, TimeUnit.MINUTES);
} catch (Exception e) {
log.error("ç§æè®¢åå€ç倱莥", e);
redisTemplate.opsForValue().set(resultKey, "ç³»ç»ç¹å¿ïŒè¯·çšååè¯", 10, TimeUnit.MINUTES);
}
}
private void notifyUser(Long userId, String message) {
// åéçä¿¡ãæšééç¥ç
log.info("éç¥çšæ·{}: {}", userId, message);
}
}
é çœ®æ¶æ¯éåïŒ
@Configuration
public class SeckillQueueConfig {
/**
* â ç§æéå
*
* é
眮ïŒ
* - æä¹
åïŒé²æ¢æ¶æ¯äž¢å€±
* - äžèªåšå é€ïŒéåäžçŽååš
*/
@Bean
public Queue seckillQueue() {
return QueueBuilder.durable("seckill.queue")
// â 讟眮éåæå€§é¿åºŠïŒé²æ¢å
åæº¢åºïŒ
.maxLength(1000000) // æå€100äžæ¡æ¶æ¯
// â è¶
åºé¿åºŠçæ¶æ¯çç¥ïŒæç»
.overflow(QueueBuilder.Overflow.rejectPublish)
.build();
}
}
念坹æ¯
æ²¡ææ¶æ¯éåïŒ
念工å
·ïŒJMeter
å¹¶åçšæ·ïŒ10äž
è¯·æ±æ¶éŽïŒ10ç§
ç»æïŒ
- å¹³åååºæ¶éŽïŒ8ç§
- æåçïŒ30%
- æå¡åšCPUïŒ100%
- æ°æ®åºè¿æ¥ïŒå
šéšèå°œ
- ç»è®ºïŒç³»ç»åŽ©æº ð
ææ¶æ¯éåïŒ
念工å
·ïŒJMeter
å¹¶åçšæ·ïŒ10äž
è¯·æ±æ¶éŽïŒ10ç§
ç»æïŒç§ææ¥å£ïŒïŒ
- å¹³åååºæ¶éŽïŒ50ms â
- æåçïŒ100% â
- æå¡åšCPUïŒ20% â
ç»æïŒè®¢åå€çïŒïŒ
- å€çé床ïŒ1000 QPSïŒçš³å®ïŒ
- å
šéšå€ç宿æ¶éŽïŒ100ç§
- ç»è®ºïŒç³»ç»çš³å®è¿è¡ â
åºæ¯2ïŒè®¢åç³»ç» ðŠ
äžå¡åºæ¯
çµå倧ä¿ïŒå11ïŒð
â
订åéæŽå¢ïŒå¹³æ¶100å
â
订åå€çéŸè·¯é¿ïŒ
- æ£ååºå
- å建订å
- æ¯ä»æµçš
- åéçä¿¡
- æŽæ°ç§¯å
- æšééç¥
æ¶æè®Ÿè®¡
çšæ·äžå
â
Order API
â
ââââââââââââââââââââââââââââââââââââââ
â æ¶æ¯éåïŒåå³°ïŒ â
â â
â âââââââââââââââââââââââââââââââ â
â â å建订åéå â â
â â (order.create.queue) â â
â âââââââââââââââââââââââââââââââ â
â â â
â âââââââââââââââââââââââââââââââ â
â â æ¯ä»éå â â
â â (order.payment.queue) â â
â âââââââââââââââââââââââââââââââ â
â â â
â âââââââââââââââââââââââââââââââ â
â â éç¥éå â â
â â (order.notify.queue) â â
â âââââââââââââââââââââââââââââââ â
ââââââââââââââââââââââââââââââââââââââ
â
å€äžªæ¶è޹è
å¹¶åå€çïŒå¡«è°·ïŒ
代ç å®ç°
äžåæ¥å£ïŒ
@RestController
@RequestMapping("/api/order")
@Slf4j
public class OrderController {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* å建订å
*/
@PostMapping("/create")
public ResponseEntity<?> createOrder(@RequestBody OrderRequest request) {
log.info("æ¶å°äžå请æ±: {}", request);
// â ç®åæ ¡éª
validateOrder(request);
// â çæè®¢åå·
String orderNo = generateOrderNo();
// â åéå°æ¶æ¯éå
OrderMessage message = new OrderMessage();
message.setOrderNo(orderNo);
message.setUserId(request.getUserId());
message.setProductId(request.getProductId());
message.setQuantity(request.getQuantity());
message.setAmount(request.getAmount());
rabbitTemplate.convertAndSend("order.create.exchange", "order.create", message);
log.info("è®¢åæ¶æ¯å·²åé: orderNo={}", orderNo);
// â ç«å³è¿å订åå·
return ResponseEntity.ok(Result.success(orderNo));
}
private void validateOrder(OrderRequest request) {
if (request.getUserId() == null) {
throw new IllegalArgumentException("çšæ·IDäžèœäžºç©º");
}
// ... å
¶ä»æ ¡éª
}
private String generateOrderNo() {
// éªè±ç®æ³çæè®¢åå·
return String.valueOf(System.currentTimeMillis());
}
}
è®¢åæ¶èŽ¹è ïŒéŸåŒå€çïŒïŒ
@Component
@Slf4j
public class OrderConsumer {
@Autowired
private OrderService orderService;
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* â 第1æ¥ïŒå建订å
*/
@RabbitListener(
queues = "order.create.queue",
concurrency = "20-50" // åšæè°æŽæ¶è޹è
æ°é
)
public void createOrder(OrderMessage message) {
log.info("å建订å: orderNo={}", message.getOrderNo());
try {
// å建订åïŒæ°æ®åºæäœïŒ
Order order = orderService.create(message);
log.info("订åå建æå: orderId={}", order.getId());
// â åéå°äžäžäžªéåïŒæ¯ä»ïŒ
PaymentMessage paymentMsg = new PaymentMessage();
paymentMsg.setOrderId(order.getId());
paymentMsg.setAmount(order.getAmount());
rabbitTemplate.convertAndSend("order.payment.exchange", "order.payment", paymentMsg);
} catch (Exception e) {
log.error("订åå建倱莥", e);
// è¿å
¥æ»ä¿¡éå
throw new RuntimeException(e);
}
}
/**
* â 第2æ¥ïŒæ¯ä»å€ç
*/
@RabbitListener(
queues = "order.payment.queue",
concurrency = "10-30"
)
public void processPayment(PaymentMessage message) {
log.info("å€çæ¯ä»: orderId={}", message.getOrderId());
try {
// è°çšæ¯ä»æ¥å£
boolean success = paymentService.pay(message.getOrderId(), message.getAmount());
if (success) {
log.info("æ¯ä»æå: orderId={}", message.getOrderId());
// â åéå°äžäžäžªéåïŒéç¥ïŒ
NotifyMessage notifyMsg = new NotifyMessage();
notifyMsg.setOrderId(message.getOrderId());
notifyMsg.setType("PAYMENT_SUCCESS");
rabbitTemplate.convertAndSend("order.notify.exchange", "order.notify", notifyMsg);
}
} catch (Exception e) {
log.error("æ¯ä»å€ç倱莥", e);
throw new RuntimeException(e);
}
}
/**
* â 第3æ¥ïŒåééç¥
*/
@RabbitListener(
queues = "order.notify.queue",
concurrency = "5-10"
)
public void sendNotification(NotifyMessage message) {
log.info("åééç¥: orderId={}", message.getOrderId());
try {
// åéçä¿¡
smsService.send(message);
// æšééç¥
pushService.send(message);
log.info("éç¥åéæå: orderId={}", message.getOrderId());
} catch (Exception e) {
log.error("éç¥åé倱莥", e);
// éç¥å€±èŽ¥äžåœ±å订åïŒäžæåŒåžž
}
}
}
åºæ¯3ïŒæ¥å¿æ¶éç³»ç» ð
äžå¡åºæ¯
倧åç³»ç»ïŒ1000å°æå¡åš
æ¯å°æå¡åšïŒ1000 QPS
æ¯äžªè¯·æ±ïŒäº§ç10æ¡æ¥å¿
â
æ»æ¥å¿éïŒ1000äžæ¡/ç§ ð¥
â
çŽæ¥åå
¥æ°æ®åºïŒç³»ç»åŽ©æºïŒð
æ¶æè®Ÿè®¡
ââââââââââââââââââââââââââââââââââââââââââââ
â åºçšæå¡åšïŒ1000å°ïŒ â
â â
â App1 â æ¥å¿ â æ¶æ¯éå ðŠ â
â App2 â æ¥å¿ â æ¶æ¯éå ðŠ â
â App3 â æ¥å¿ â æ¶æ¯éå ðŠ â
â ... â
âââââââââââââââ¬âââââââââââââââââââââââââââââ
â
â åå³°ïŒåŒæ¥åéïŒ
ââââââââââââââââââââââââââââââââââââââââââââ
â KafkaïŒæ¶æ¯éåïŒ â
â â
â Topic: application-logs â
â Partitions: 100 â
â Retention: 7 days â
âââââââââââââââ¬âââââââââââââââââââââââââââââ
â
â å¡«è°·ïŒæ¹éæ¶è޹ïŒ
ââââââââââââââââââââââââââââââââââââââââââââ
â æ¥å¿å€çæå¡ïŒ10å°ïŒ â
â â
â Consumer1 â æ¹éåå
¥ â Elasticsearch â
â Consumer2 â æ¹éåå
¥ â Elasticsearch â
â ... â
ââââââââââââââââââââââââââââââââââââââââââââ
代ç å®ç°
æ¥å¿ç产è ïŒ
@Component
@Slf4j
public class LogProducer {
@Autowired
private KafkaTemplate<String, LogMessage> kafkaTemplate;
private static final String TOPIC = "application-logs";
/**
* â åŒæ¥å鿥å¿ïŒåå³°ïŒ
*/
public void sendLog(LogMessage logMessage) {
// åŒæ¥åéïŒäžçåŸ
ååº
kafkaTemplate.send(TOPIC, logMessage.getAppName(), logMessage)
.addCallback(
success -> {
// åéæåïŒå¯éè®°åœïŒ
},
failure -> {
// åé倱莥ïŒè®°åœæ¬å°æ¥å¿ïŒ
log.error("æ¥å¿åé倱莥", failure);
}
);
}
}
æ¥å¿æ¶è޹è ïŒæ¹éå€çïŒïŒ
@Component
@Slf4j
public class LogConsumer {
@Autowired
private ElasticsearchTemplate esTemplate;
/**
* â æ¹éæ¶è޹æ¥å¿ïŒå¡«è°·ïŒ
*/
@KafkaListener(
topics = "application-logs",
groupId = "log-consumer-group",
concurrency = "10" // 10䞪æ¶è޹è
线çš
)
public void consumeLogs(List<ConsumerRecord<String, LogMessage>> records) {
log.info("æ¶å°{}æ¡æ¥å¿", records.size());
// â æ¹éå€çïŒæé«æçïŒ
List<LogMessage> logs = records.stream()
.map(ConsumerRecord::value)
.collect(Collectors.toList());
try {
// â æ¹éåå
¥Elasticsearch
esTemplate.bulkIndex(logs);
log.info("æ¹éåå
¥{}æ¡æ¥å¿æå", logs.size());
} catch (Exception e) {
log.error("æ¹éåå
¥å€±èŽ¥", e);
// éè¯æåå
¥æ»ä¿¡éå
}
}
}
Kafkaæ¶è޹è é 眮ïŒ
spring:
kafka:
consumer:
# â æ¹éæ¶è޹é
眮ïŒå¡«è°·ïŒ
max-poll-records: 1000 # æ¯æ¬¡æå1000æ¡
fetch-min-size: 10240 # æå°æå10KB
fetch-max-wait: 500 # æå€çåŸ
500ms
# æ¹éçå¬åš
listener:
type: batch # æ¹éæš¡åŒ
ð ææå¯¹æ¯
æ§èœå¯¹æ¯
| ææ | æ²¡ææ¶æ¯éå | ææ¶æ¯éå | æå |
|---|---|---|---|
| å³°åŒQPS | 1000ïŒç³»ç»æéïŒ | 100äžïŒéåçŒå²ïŒ | 1000å â |
| å¹³åååºæ¶éŽ | 8ç§ | 50ms | 160å â |
| æåç | 30% | 100% | 3.3å â |
| æå¡åšæ°é | 1000å° | 10å° | èç99% â |
| ç³»ç»çš³å®æ§ | åŽ©æº ð | çš³å®è¿è¡ â | æ ä»· ð |
ææ¬å¯¹æ¯
åè®Ÿåºæ¯ïŒçµå倧ä¿
æ²¡ææ¶æ¯éåïŒ
- æå¡åšïŒ1000å° Ã 5000å
/æ = 500äž/æ
- æ°æ®åºïŒ100䞪å®äŸ à 1äžå
/æ = 100äž/æ
- æ»ææ¬ïŒ600äž/æ ð°ð°ð°
ææ¶æ¯éåïŒ
- æå¡åšïŒ10å° Ã 5000å
/æ = 5äž/æ
- æ°æ®åºïŒ10䞪å®äŸ à 1äžå
/æ = 10äž/æ
- æ¶æ¯éåïŒ5äž/æ
- æ»ææ¬ïŒ20äž/æ ð°
èçææ¬ïŒ580äž/æïŒ97%ïŒ ð
ð¯ åå³°å¡«è°·çæäœ³å®è·µ
1ïžâ£ åç讟眮éåé¿åºŠ ð
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
// â 讟眮æå€§é¿åºŠïŒé²æ¢å
åæº¢åºïŒ
.maxLength(1000000) // æ ¹æ®å®é
æ
åµè°æŽ
// â è¶
åºé¿åºŠççç¥
.overflow(QueueBuilder.Overflow.rejectPublish) // æç»æ°æ¶æ¯
.build();
}
计ç®å ¬åŒïŒ
éåé¿åºŠ = å³°åŒQPS Ã å³°åŒæç»æ¶éŽ Ã å®å
šç³»æ°
äŸåŠïŒ
- å³°åŒQPSïŒ10äž
- å³°åŒæç»æ¶éŽïŒ60ç§
- å®å
šç³»æ°ïŒ2
- éåé¿åºŠ = 10äž Ã 60 à 2 = 1200äž
2ïžâ£ åšæè°æŽæ¶èŽ¹è æ°é ð
@RabbitListener(
queues = "order.queue",
concurrency = "10-50" // â æå°10äžªïŒæå€§50䞪æ¶è޹è
)
public void consumeOrder(OrderMessage message) {
// å€ç订å
}
è°æŽçç¥ïŒ
éåé¿åºŠ < 1000ïŒ10䞪æ¶è޹è
ïŒäœè°·ïŒ
éåé¿åºŠ > 10000ïŒ50䞪æ¶è޹è
ïŒé«å³°ïŒ
3ïžâ£ çæ§åèŠ ð
@Component
@Slf4j
public class QueueMonitor {
@Autowired
private RabbitAdmin rabbitAdmin;
/**
* æ¯å鿣æ¥éåé¿åºŠ
*/
@Scheduled(fixedRate = 60000)
public void monitorQueue() {
Properties props = rabbitAdmin.getQueueProperties("order.queue");
if (props != null) {
Integer messageCount = (Integer) props.get("QUEUE_MESSAGE_COUNT");
log.info("éåé¿åºŠ: {}", messageCount);
// â åèŠéåŒ
if (messageCount > 100000) {
sendAlert("éå积å䞥éïŒ" + messageCount + "æ¡æ¶æ¯");
}
}
}
private void sendAlert(String message) {
// åéåèŠïŒçä¿¡ãé®ä»¶ãééçïŒ
log.error("ðš åèŠ: {}", message);
}
}
4ïžâ£ è®Ÿçœ®æ¶æ¯TTL â°
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
// â æ¶æ¯TTLïŒ30åéïŒ
.ttl(1800000) // è¶
æ¶çæ¶æ¯èªåšå é€
// â é
眮æ»ä¿¡éå
.deadLetterExchange("dlx.exchange")
.build();
}
äœçšïŒ
- 鲿¢æ¶æ¯ç§¯åè¿å€
- åæ¶æž çè¿ææ¶æ¯
- ä¿è¯æ¶ææ§
5ïžâ£ æµæ§çç¥ ðŠ
@Configuration
public class RateLimiterConfig {
@Bean
public RateLimiter rateLimiter() {
// â éæµåšïŒæ¯ç§æå€1000䞪请æ±
return RateLimiter.create(1000);
}
}
@RestController
public class OrderController {
@Autowired
private RateLimiter rateLimiter;
@PostMapping("/order")
public ResponseEntity<?> createOrder(@RequestBody OrderRequest request) {
// â å°è¯è·å什çïŒçåŸ
æå€1ç§ïŒ
boolean acquired = rateLimiter.tryAcquire(1, TimeUnit.SECONDS);
if (!acquired) {
return ResponseEntity.status(429)
.body(Result.fail("ç³»ç»ç¹å¿ïŒè¯·çšååè¯"));
}
// æ£åžžå€ç...
return ResponseEntity.ok(Result.success("订åå·²æäº€"));
}
}
ð é¢è¯é¢éç
Q1: ä»ä¹æ¯åå³°å¡«è°·ïŒ
A: åå³°å¡«è°· = æ¶æ¯éåçæ žå¿äœçšä¹äž
åå³°ïŒ
- é«å³°æ¶æå请æ±å°æ¶æ¯éå
- é¿å ç¬æ¶æµéåå®ç³»ç»
å¡«è°·ïŒ
- äœè°·æ¶ç»§ç»å€ç积åçæ¶æ¯
- å åå©çšç³»ç»èµæº
æ¯å»ïŒåäžå³¡å€§åïŒæŽªæ°Žæ¥äžŽæ¶èæ°ŽïŒå¹³æ¶æ ¢æ ¢æŸæ°Ž
Q2: åå³°å¡«è°·çå žååºçšåºæ¯ïŒ
A: äžå€§åºæ¯ïŒ
-
ç§æç³»ç» ð
- 10äžçšæ·åæ¶ç§æ
- æ¶æ¯éåçŒå²ïŒåå°æ ¢æ ¢å€ç
-
订åç³»ç» ðŠ
- å€§ä¿æéŽè®¢åéæŽå¢
- åé¶æ®µå€çïŒå建订å â æ¯ä» â éç¥
-
æ¥å¿æ¶é ð
- 1000äžæ¡æ¥å¿/ç§
- æ¹éæ¶èŽ¹ïŒæ¹éåå ¥æ°æ®åº
Q3: åŠäœè®Ÿè®¡äžäžªç§æç³»ç»ïŒ
A: æ žå¿ïŒæ¶æ¯éååå³°
// ç§ææ¥å£ïŒç«å³è¿å
@PostMapping("/seckill")
public Result seckill(Long productId, Long userId) {
// 1. æ£æ¥åºåïŒRedisïŒ
Long stock = redisTemplate.decrement("stock:" + productId);
if (stock < 0) {
return Result.fail("å·²æ¢å
");
}
// 2. åéå°æ¶æ¯éå
rabbitTemplate.send("seckill.queue", message);
// 3. ç«å³è¿å
return Result.success("æéäž");
}
// æ¶è޹è
ïŒæ
¢æ
¢å€ç
@RabbitListener(queues = "seckill.queue", concurrency = "10")
public void processSeckill(SeckillMessage msg) {
// å建订åïŒæ°æ®åºæäœïŒ
orderService.create(msg);
}
å ³é®ç¹ïŒ
- åºå颿£ïŒRedisïŒ
- åŒæ¥å€çïŒæ¶æ¯éåïŒ
- ç«å³è¿åïŒçšæ·äœéªïŒ
Q4: éåé¿åºŠåŠäœè®Ÿçœ®ïŒ
A: 计ç®å ¬åŒïŒ
éåé¿åºŠ = å³°åŒQPS Ã å³°åŒæç»æ¶éŽ Ã å®å
šç³»æ°
瀺äŸïŒ
- å³°åŒQPSïŒ10äž
- å³°åŒæç»æ¶éŽïŒ60ç§
- å®å šç³»æ°ïŒ2
- éåé¿åºŠ = 1200äž
é 眮ïŒ
QueueBuilder.durable("order.queue")
.maxLength(12000000) // 1200äž
.overflow(Overflow.rejectPublish) // è¶
åºæç»
.build();
Q5: åŠäœçæ§éå积åïŒ
A: äžç§æ¹æ³ïŒ
- 宿¶ä»»å¡çæ§ïŒ
@Scheduled(fixedRate = 60000)
public void monitorQueue() {
Integer count = rabbitAdmin.getQueueProperties("order.queue")
.get("QUEUE_MESSAGE_COUNT");
if (count > 100000) {
sendAlert("éå积åïŒ" + count);
}
}
-
çæ§å¹³å°ïŒ
- Prometheus + Grafana
- å¯è§åçæ§å€§å±
-
æ¶æ¯éåèªåžŠçæ§ïŒ
- RabbitMQ Management
- Kafka Manager
Q6: åå³°å¡«è°·çäŒçŒºç¹ïŒ
A: äŒç¹ â ïŒ
- æé«ç³»ç»çš³å®æ§ïŒäžäŒè¢«æµéåå®ïŒ
- èçæå¡åšææ¬ïŒæå¹³ååŒé 眮ïŒ
- æåçšæ·äœéªïŒå¿«éååºïŒ
- å åå©çšèµæºïŒäœè°·æ¶éŽä¹åšå€çïŒ
çŒºç¹ âïŒ
- 宿¶æ§äžéïŒåŒæ¥å€çæå»¶è¿ïŒ
- æ¶æå€æåºŠå¢å ïŒåŒå ¥æ¶æ¯éåïŒ
- éèŠçæ§åè¿ç»ŽïŒéå积åïŒ
éçšåºæ¯ïŒ
- æµéæ³¢åšå€§çç³»ç» â
- 坹宿¶æ§èŠæ±äžé«çäžå¡ â
- äžéçšïŒå®æ¶äº€æã宿¶éä¿¡ â
ð¬ æ»ç»
åå³°å¡«è°·å®æŽæµçšåŸ
ââââââââââââââââââââââââââââââââââââââââââââââââââ
â çšæ·è¯·æ±ïŒé«å³°ïŒ â
â â
â ð€ð€ð€ð€ð€ð€ð€ð€ð€ð€ð€ð€ð€ð€ð€ â
â 100äž QPSïŒç¬æ¶å³°åŒïŒð¥ â
âââââââââââââââ¬âââââââââââââââââââââââââââââââââââ
â
â åå³°ïŒåŒæ¥åéïŒ
ââââââââââââââââââââââââââââââââââââââââââââââââââ
â æ¶æ¯éåïŒçŒå²åºïŒðŠ â
â â
â ââââââââââââââââââââââââââââââââââââââââââââ â
â â æ¶æ¯1 æ¶æ¯2 æ¶æ¯3 ... æ¶æ¯N â â
â ââââââââââââââââââââââââââââââââââââââââââââ â
â â
â æåæ¶æ¯ïŒåæå³°åŒ â
â
âââââââââââââââ¬âââââââââââââââââââââââââââââââââââ
â
â å¡«è°·ïŒçš³å®æ¶è޹ïŒ
ââââââââââââââââââââââââââââââââââââââââââââââââââ
â æ¶è޹è
ïŒæç³»ç»èœåå€çïŒ â
â â
â Consumer1 â 1000 QPS â
â Consumer2 â 1000 QPS â
â Consumer3 â 1000 QPS â
â ... â
â â
â çš³å®å€çïŒå¡«æ»¡äœè°·æ¶éŽ â
â
ââââââââââââââââââââââââââââââââââââââââââââââââââ
æ¶æ¯éå = äžå³¡å€§å ðïž
ð æåäœ ïŒ
äœ å·²ç»å®å šææ¡äºæ¶æ¯éåçåå³°å¡«è°·äœçšåå®é åºçšïŒð
æ žå¿èŠç¹ïŒ
- åå³°ïŒé«å³°æ¶æå请æ±ïŒé¿å ç³»ç»åŽ©æº
- å¡«è°·ïŒäœè°·æ¶å€ç积åïŒå åå©çšèµæº
- åºæ¯ïŒç§æã倧ä¿ãæ¥å¿æ¶éçé«å¹¶ååºæ¯
- ææïŒæå100åæ§èœïŒèç99%ææ¬
äžæ¬¡é¢è¯ïŒè¿æ ·åçïŒ
"æ¶æ¯éåçåå³°å¡«è°·æ¯æåšæµéé«å³°æ¶å°è¯·æ±æåå°éåäžïŒé¿å ç¬æ¶æµéåå®ç³»ç»ïŒåšæµéäœè°·æ¶ç»§ç»å€ç积åçæ¶æ¯ïŒå åå©çšç³»ç»èµæºã
å žååºçšåºæ¯æ¯ç§æç³»ç»ã10äžçšæ·åæ¶ç§æïŒåŠæçŽæ¥å€çïŒæ°æ®åºäŒè¢«åå®ãäœ¿çšæ¶æ¯éååïŒç§ææ¥å£ç«å³è¿å'æéäž'ïŒæ¶æ¯è¿å ¥éåïŒåå°10䞪æ¶èŽ¹è æ ¢æ ¢å€çïŒ1000 QPSçš³å®è¿è¡ã
éåé¿åºŠç计ç®å ¬åŒæ¯ïŒå³°åŒQPS Ã å³°åŒæç»æ¶éŽ Ã å®å šç³»æ°ãè¿éèŠé çœ®åšææ¶èŽ¹è æ°éãçæ§åèŠãæ¶æ¯TTLçã
æä»¬é¡¹ç®ç订åç³»ç»åšå€§ä¿æéŽéè¿æ¶æ¯éååå³°ïŒååºæ¶éŽä»8ç§éå°50msïŒæåçä»30%æåå°100%ïŒèçäº99%çæå¡å𿿬ã"
é¢è¯å®ïŒð "é垞奜ïŒäœ 对åå³°å¡«è°·çè§£åŸæ·±å»ïŒèäžæå®é åºçšç»éªïŒ"
æ¬æå® ð¬
äžäžç¯: 192-æ»ä¿¡éåçäœçšåå€ççç¥.md
äžäžç¯: 194-æ¶æ¯éååŠäœä¿è¯æ¶æ¯æåºæ§.md
äœè 泚ïŒåå®è¿ç¯ïŒæéœæ³å»æ°Žå©å±å·¥äœäºïŒðïž
åŠæè¿ç¯æç« å¯¹äœ æåž®å©ïŒè¯·ç»æäžäžªStarâïŒ