網(wǎng)站如何帶來流量鄭州網(wǎng)絡(luò)推廣排名
還在補(bǔ)充,這幾天工作忙,閑了會把答案附上去,也歡迎各位大佬評論區(qū)討論
1.不用分布式鎖如何防重復(fù)提交
方法 1:基于唯一請求 ID(冪等 Token)
思路:前端生成 一個唯一的 requestId(如 UUID或者能表示本次請求的唯一標(biāo)識字段),每次提交請求時帶上它。
后端使用 Redis存儲 requestId,如果已經(jīng)存在,則拒絕處理。
實現(xiàn):前端:每次提交請求時,生成一個 requestId:
javascript
const requestId = crypto.randomUUID(); // 生成唯一 ID
axios.post('/api/submit', { data, requestId });
后端java
@Autowired
private StringRedisTemplate redisTemplate;public ResponseEntity<String> submitRequest(String requestId, Data data) {Boolean isDuplicate = redisTemplate.opsForValue().setIfAbsent("request:" + requestId, "1", 10, TimeUnit.MINUTES);if (Boolean.FALSE.equals(isDuplicate)) {return ResponseEntity.status(HttpStatus.CONFLICT).body("重復(fù)提交");}try {// 處理業(yè)務(wù)邏輯process(data);return ResponseEntity.ok("提交成功");} finally {redisTemplate.delete("request:" + requestId); // 可選:若確保冪等性,可不刪除}
}
優(yōu)點: ? 適用于分布式系統(tǒng)
? 性能好(基于 Redis 操作)
? Token 過期時間 防止長期占用
方法 2:數(shù)據(jù)庫唯一索引
思路:讓數(shù)據(jù)庫的唯一索引防止重復(fù)提交,常用于訂單號、業(yè)務(wù)唯一鍵。
實現(xiàn)
數(shù)據(jù)庫表添加唯一索引:
sql
ALTER TABLE orders ADD UNIQUE (order_no);
后端插入數(shù)據(jù):
try {orderMapper.insert(order);
} catch (DuplicateKeyException e) {return ResponseEntity.status(HttpStatus.CONFLICT).body("重復(fù)提交");
}
優(yōu)點: ? 數(shù)據(jù)庫級防重,最可靠
? 適合訂單、支付等業(yè)務(wù)場景
? 性能受限于數(shù)據(jù)庫,高并發(fā)需優(yōu)化
方法 3:前端按鈕防抖
思路:提交后禁用按鈕,直到返回響應(yīng),防止用戶快速點擊。
實現(xiàn)
document.getElementById("submit-btn").addEventListener("click", function() {this.disabled = true; // 禁用按鈕axios.post('/api/submit', { data }).then(response => alert(response.data)).finally(() => this.disabled = false); // 請求完成后恢復(fù)
});
優(yōu)點: ? 簡單易行,無需改后端
? 前端可繞過,不適用于高安全性場景
方法 4:悲觀鎖(數(shù)據(jù)庫行鎖)
思路:通過 SELECT … FOR UPDATE 加行鎖,確保事務(wù)內(nèi)數(shù)據(jù)不會被其他請求修改。
實現(xiàn)
@Transactional
public void submitOrder(Long orderId) {Order order = orderMapper.selectByIdForUpdate(orderId); // 加鎖if (order.getStatus() != OrderStatus.PENDING) {throw new IllegalStateException("訂單已處理");}order.setStatus(OrderStatus.PROCESSED);orderMapper.updateById(order);
}
優(yōu)點: ? 確保單線程執(zhí)行,避免重復(fù)
? 數(shù)據(jù)庫性能受影響,不適用于高并發(fā)
總結(jié)
)
推薦方案:
高并發(fā)系統(tǒng):冪等 Token + Redis ?
數(shù)據(jù)庫事務(wù)業(yè)務(wù):唯一索引 / 狀態(tài)機(jī) ?
簡單防重:前端按鈕防抖 ?
如果并發(fā)壓力大,可以結(jié)合多種方案,例如:
Redis 冪等 Token + 數(shù)據(jù)庫唯一索引 🚀
前端防抖 + 后端冪等 Token 🔥
2.redis的哨兵模式是如何選舉的
Redis 的哨兵模式(Sentinel)用于監(jiān)控 Redis 服務(wù)器,并在主服務(wù)器(Master)宕機(jī)時自動執(zhí)行故障轉(zhuǎn)移(Failover)。哨兵模式的選舉過程主要發(fā)生在主服務(wù)器不可用時,選舉一個新的主服務(wù)器。以下是選舉的具體步驟:
- 發(fā)現(xiàn)主服務(wù)器故障
每個哨兵(Sentinel)會定期向主服務(wù)器和從服務(wù)器發(fā)送 PING 命令,檢查它們的狀態(tài)。
如果多個哨兵在 down-after-milliseconds 時間內(nèi)沒有收到主服務(wù)器的響應(yīng),就會認(rèn)為它 主觀下線(Subjectively Down, sDown)。
當(dāng) 大多數(shù) 哨兵都認(rèn)定主服務(wù)器宕機(jī)時,主服務(wù)器就會進(jìn)入 客觀下線(Objectively Down, oDown) 狀態(tài),觸發(fā)故障轉(zhuǎn)移。 - 哨兵選舉領(lǐng)導(dǎo)者
當(dāng)主服務(wù)器 oDown 后,需要一個哨兵來執(zhí)行故障轉(zhuǎn)移,因此哨兵之間需要進(jìn)行 Raft 算法 類似的選舉流程:
所有哨兵都可以參與選舉,嘗試成為領(lǐng)導(dǎo)者(Leader)。
每個哨兵向其他哨兵發(fā)送 SENTINEL is-master-down-by-addr 請求,詢問它們是否同意自己成為領(lǐng)導(dǎo)者。
其他哨兵如果還沒有投票,則會同意投票給該請求的哨兵。
如果一個哨兵獲得超過一半的票數(shù)(多數(shù)派),就會成為領(lǐng)導(dǎo)者。
3. 選擇新的主服務(wù)器
領(lǐng)導(dǎo)者哨兵會從現(xiàn)有的從服務(wù)器中選擇一個最合適的來提升為新的主服務(wù)器:
選擇 復(fù)制進(jìn)度最接近主服務(wù)器 的從服務(wù)器(偏移量最大)。
如果多個從服務(wù)器復(fù)制進(jìn)度相同,選擇 ID 最小 的。
如果沒有合適的從服務(wù)器,則失敗,等待下一輪選舉。
4. 執(zhí)行故障轉(zhuǎn)移
領(lǐng)導(dǎo)者哨兵發(fā)送 slaveof no one 命令,讓選中的從服務(wù)器成為新的主服務(wù)器。
讓其他從服務(wù)器執(zhí)行 slaveof <new_master>,將它們的主服務(wù)器指向新的主服務(wù)器。
更新配置信息,廣播新的主服務(wù)器地址給所有 Redis 客戶端。
5. 恢復(fù)監(jiān)控
整個集群穩(wěn)定后,哨兵繼續(xù)監(jiān)控新的主服務(wù)器和從服務(wù)器,準(zhǔn)備處理下一次故障。
通過這種選舉機(jī)制,Redis 的哨兵模式能夠自動檢測主服務(wù)器故障,并確保集群能夠繼續(xù)運行。
3.線程池的原理
-
線程池的概念
線程池(Thread Pool)是一種用于管理和復(fù)用線程的技術(shù),它維護(hù)了一組可復(fù)用的線程,避免了頻繁創(chuàng)建和銷毀線程的開銷,從而提高程序的執(zhí)行效率。 -
線程池的核心組成
線程池主要由以下幾個核心部分組成:
線程隊列(BlockingQueue)
用于存放等待執(zhí)行的任務(wù)。常見的隊列類型:
無界隊列(LinkedBlockingQueue):適用于任務(wù)量大但不會超出系統(tǒng)資源的情況。
有界隊列(ArrayBlockingQueue):適用于控制任務(wù)數(shù)量,防止資源耗盡。
優(yōu)先隊列(PriorityBlockingQueue):任務(wù)可根據(jù)優(yōu)先級執(zhí)行。
核心線程數(shù)(Core Pool Size)
線程池初始化后,線程數(shù)不超過核心線程數(shù)時,即使空閑,也不會銷毀。
最大線程數(shù)(Maximum Pool Size)
當(dāng)任務(wù)數(shù)量超過核心線程數(shù)時,線程池可以臨時創(chuàng)建額外的線程,但不會超過最大線程數(shù)。
任務(wù)拒絕策略(Rejection Policy)
當(dāng)線程池達(dá)到最大線程數(shù)且任務(wù)隊列已滿時,新任務(wù)將被拒絕。常見策略:
AbortPolicy(默認(rèn)):拋出異常。
CallerRunsPolicy:調(diào)用線程自己執(zhí)行任務(wù),降低任務(wù)提交速率。
DiscardPolicy:直接丟棄任務(wù),不處理也不拋出異常。
DiscardOldestPolicy:丟棄隊列中最早的任務(wù),再嘗試執(zhí)行新任務(wù)。
線程工廠(ThreadFactory)
用于創(chuàng)建線程,可以自定義線程命名、設(shè)置守護(hù)線程等。
存活時間(Keep Alive Time)
當(dāng)線程數(shù)超過核心線程數(shù)時,多余的空閑線程會在超過該時間后被銷毀。
3. 線程池的工作流程
任務(wù)提交到線程池。
如果當(dāng)前運行線程數(shù)小于核心線程數(shù),直接創(chuàng)建新線程執(zhí)行任務(wù)。
如果核心線程數(shù)已滿,則任務(wù)進(jìn)入隊列等待。
若隊列已滿且線程數(shù)小于最大線程數(shù),則創(chuàng)建新線程執(zhí)行任務(wù)。
若線程數(shù)達(dá)到最大值且任務(wù)隊列也滿了,則觸發(fā)任務(wù)拒絕策略。
線程執(zhí)行完任務(wù)后,若線程數(shù)超過核心線程數(shù),多余的空閑線程會在 keepAliveTime 超過后被銷毀。
4. 線程池的優(yōu)點
提高性能:減少線程創(chuàng)建和銷毀的開銷。
提高資源利用率:合理分配線程,避免資源浪費。
控制并發(fā):防止創(chuàng)建過多線程導(dǎo)致系統(tǒng)資源耗盡。
5. 線程池的應(yīng)用
在 Java 中,ExecutorService 提供了常見的線程池實現(xiàn):
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {System.out.println("任務(wù)執(zhí)行:" + Thread.currentThread().getName());
});
executor.shutdown();
- 自定義線程池
使用 ThreadPoolExecutor 自定義線程池:
ExecutorService threadPool = new ThreadPoolExecutor(2, // 核心線程數(shù)5, // 最大線程數(shù)60, // 線程空閑存活時間TimeUnit.SECONDS, // 時間單位new LinkedBlockingQueue<>(10), // 任務(wù)隊列Executors.defaultThreadFactory(), // 線程工廠new ThreadPoolExecutor.AbortPolicy() // 拒絕策略
);
4.spring的spi和dubbo的spi機(jī)制是什么?
Spring 的 SPI(Service Provider Interface) 主要基于 Java 原生的 java.util.ServiceLoader 機(jī)制,并在此基礎(chǔ)上進(jìn)行了增強(qiáng)。它主要用于加載和擴(kuò)展 Spring 組件,如 SpringFactoriesLoader。
1. Java SPI 機(jī)制
Java 提供 java.util.ServiceLoader,用于加載 META-INF/services/ 目錄下的服務(wù)配置文件。
這個配置文件的命名規(guī)則是接口的全限定名,內(nèi)容是具體的實現(xiàn)類。
示例
定義 SPI 接口
public interface MyService {void execute();
}//提供實現(xiàn)
public class MyServiceImpl implements MyService {@Overridepublic void execute() {System.out.println("MyServiceImpl executed");}
}
在 META-INF/services/ 目錄下,創(chuàng)建文件 com.example.MyService,內(nèi)容如下:
com.example.MyServiceImpl
使用 ServiceLoader 進(jìn)行加載:
ServiceLoader<MyService> loader = ServiceLoader.load(MyService.class);
for (MyService service : loader) {service.execute();
}
2. Spring 的 SPI
Spring 在 Java SPI 機(jī)制基礎(chǔ)上,增加了 SpringFactoriesLoader,用于從 META-INF/spring.factories 文件中加載擴(kuò)展組件。
Spring SPI 加載方式:
主要通過 org.springframework.core.io.support.SpringFactoriesLoader 進(jìn)行加載。
讀取 META-INF/spring.factories 配置文件,該文件的內(nèi)容格式為:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
在 Spring Boot 自動裝配(AutoConfiguration)中,大量使用了該機(jī)制。
示例
1.在 spring.factories 文件中配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyAutoConfiguration
2.MyAutoConfiguration 類:
@Configuration
public class MyAutoConfiguration {@Beanpublic MyService myService() {return new MyServiceImpl();}
}
3.Spring Boot 在啟動時,會通過 SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader) 自動加載 MyAutoConfiguration 類,從而完成自動配置。
3.Dubbo 的 SPI 機(jī)制
Dubbo 也使用了 SPI 機(jī)制,但相比 Java SPI 進(jìn)行了增強(qiáng),主要特點包括:
自定義 SPI 機(jī)制:Dubbo 提供了 ExtensionLoader,替代 Java 的 ServiceLoader,支持 AOP、IoC 和自適應(yīng)擴(kuò)展等功能。
META-INF/dubbo/ 目錄:Dubbo 的 SPI 機(jī)制約定配置文件放在 META-INF/dubbo/ 或 META-INF/services/ 目錄下。
@SPI 注解:Dubbo 允許在接口上使用 @SPI 進(jìn)行標(biāo)注,指明默認(rèn)實現(xiàn)。
支持 @Adaptive 注解:Dubbo 支持自適應(yīng)擴(kuò)展,能夠根據(jù)參數(shù)動態(tài)選擇實現(xiàn)類。
- Dubbo SPI 使用方式
1>定義 SPI 接口
import org.apache.dubbo.common.extension.SPI;@SPI("defaultImpl") // 指定默認(rèn)實現(xiàn)
public interface MyService {void execute();
}
2>提供多個實現(xiàn)
public class DefaultImpl implements MyService {@Overridepublic void execute() {System.out.println("Executing Default Implementation");}
}public class AdvancedImpl implements MyService {@Overridepublic void execute() {System.out.println("Executing Advanced Implementation");}
}
3>配置 META-INF/dubbo/com.example.MyService
defaultImpl=com.example.DefaultImpl
advancedImpl=com.example.AdvancedImpl
4>通過 Dubbo 的 ExtensionLoader 加載
ExtensionLoader<MyService> loader = ExtensionLoader.getExtensionLoader(MyService.class);
MyService service = loader.getDefaultExtension(); // 獲取默認(rèn)實現(xiàn)
service.execute();MyService advancedService = loader.getExtension("advancedImpl"); // 獲取指定實現(xiàn)
advancedService.execute();
- Dubbo SPI 的增強(qiáng)點
默認(rèn)實現(xiàn):@SPI(“defaultImpl”) 指定默認(rèn)實現(xiàn)。
自動注入:Dubbo 的擴(kuò)展點支持 IoC,可以自動注入依賴。
自適應(yīng)擴(kuò)展:@Adaptive 允許根據(jù)運行時參數(shù)動態(tài)選擇實現(xiàn)。
Wrapper 擴(kuò)展:支持 AOP 方式的擴(kuò)展,如日志增強(qiáng)。
總結(jié)
Spring SPI 主要用于 Spring Boot 自動裝配。
Dubbo SPI 主要用于服務(wù)擴(kuò)展,提供了更強(qiáng)的動態(tài)適配能力。
Dubbo SPI 機(jī)制在性能、可擴(kuò)展性和動態(tài)適應(yīng)方面,比 Spring 和 Java SPI 機(jī)制更加強(qiáng)大。
5.流量激增,大批量數(shù)據(jù)如何處理?
- 數(shù)據(jù)分片與分區(qū)
數(shù)據(jù)庫分片(Sharding):將數(shù)據(jù)拆分到多個數(shù)據(jù)庫實例上,減少單個數(shù)據(jù)庫的壓力。
分區(qū)表(Partitioning):對大表進(jìn)行分區(qū)存儲,提高查詢性能,例如按時間、地域等劃分。 - 高效的緩存策略
CDN(內(nèi)容分發(fā)網(wǎng)絡(luò)):對于靜態(tài)資源(圖片、視頻、文件等),使用CDN分發(fā)可減少源站壓力。
Redis/Memcached:對于熱點數(shù)據(jù),將查詢結(jié)果緩存,減少數(shù)據(jù)庫訪問次數(shù)。
本地緩存:在應(yīng)用層使用LRU緩存避免頻繁訪問遠(yuǎn)程存儲。 - 異步處理與消息隊列
消息隊列(Kafka、RabbitMQ、RocketMQ):削峰填谷,將高并發(fā)請求轉(zhuǎn)換為異步任務(wù),提高吞吐量。
任務(wù)隊列(Celery、Sidekiq):對于非實時數(shù)據(jù)處理(如日志分析、統(tǒng)計計算),可以異步執(zhí)行。 - 擴(kuò)展架構(gòu)
水平擴(kuò)展(Scale Out):增加服務(wù)器數(shù)量,通過負(fù)載均衡(Nginx、HAProxy)分發(fā)流量。
垂直擴(kuò)展(Scale Up):提升單機(jī)性能,如升級CPU、內(nèi)存、磁盤IO能力。 - 數(shù)據(jù)流處理
流式計算(Flink、Spark Streaming、Storm):用于實時數(shù)據(jù)處理,避免批量計算的延遲問題。
批處理(Hadoop、Spark):適用于大規(guī)模離線分析任務(wù)。 - 數(shù)據(jù)庫優(yōu)化
索引優(yōu)化:合理使用B+樹索引、哈希索引等加速查詢。
SQL優(yōu)化:避免N+1查詢,使用JOIN優(yōu)化查詢結(jié)構(gòu)。
讀寫分離:主從數(shù)據(jù)庫架構(gòu),分離讀寫操作,提高查詢性能。 - 日志及監(jiān)控
ELK(Elasticsearch + Logstash + Kibana):用于日志分析,監(jiān)測異常流量。
Prometheus + Grafana:監(jiān)控系統(tǒng)狀態(tài),及時發(fā)現(xiàn)瓶頸。
通過以上策略,可以高效應(yīng)對流量激增和大批量數(shù)據(jù)處理,提高系統(tǒng)穩(wěn)定性和響應(yīng)速度。
6.rabbitmq如何防止重復(fù)消費,高并發(fā)情況下
- 確保手動 ACK
避免使用自動 ACK,確保消息處理完畢后再確認(rèn)
? 正確做法(手動 ACK)
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body) throws IOException {try {String message = new String(body, "UTF-8");processMessage(message); // 處理消息// 處理完成后手動確認(rèn)channel.basicAck(envelope.getDeliveryTag(), false);} catch (Exception e) {// 處理失敗,拒絕消息并重新放回隊列channel.basicNack(envelope.getDeliveryTag(), false, true);}}
});
📌 說明
autoAck=false (手動 ACK)
basicAck() (成功后確認(rèn)消息已處理)
basicNack() (失敗后重新入隊)
- 業(yè)務(wù)層去重
即使 RabbitMQ 保障**“至少一次”(At Least Once)**投遞,仍可能發(fā)生重復(fù)消費,因此需要在業(yè)務(wù)層去重。
? 方法 1:數(shù)據(jù)庫唯一約束
可以使用數(shù)據(jù)庫的唯一索引字段,如訂單號 order_id:
INSERT INTO orders (order_id, user_id, amount)
VALUES ('msg_123456', 'user_001', 100)
ON DUPLICATE KEY UPDATE order_id=order_id;
如果 order_id 已存在,則不會插入新的記錄。
? 方法 2:Redis 去重
使用 Redis SETNX(防止短時間內(nèi)重復(fù)處理)
public boolean isProcessed(String messageId) {String key = "msg_" + messageId;Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", 60, TimeUnit.SECONDS);return success != null && !success;
}public void processMessage(String message) {if (isProcessed(message)) {System.out.println("消息已處理,跳過: " + message);return;}// 業(yè)務(wù)邏輯處理
}
- 設(shè)置消息 TTL + 死信隊列(DLX)
防止重復(fù)消費時死循環(huán)
? 設(shè)置消息 TTL
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 60000); // 60s 后消息過期
args.put("x-dead-letter-exchange", "dlx_exchange"); // 指定死信交換機(jī)
channel.queueDeclare("task_queue", true, false, false, args);
? 死信隊列(DLX)
當(dāng)消息失敗多次后,可以轉(zhuǎn)移到死信隊列,避免重復(fù)消費:
args.put("x-dead-letter-routing-key", "dlx_routing_key");
- 限制消費者并發(fā)數(shù)
如果消費者并發(fā)過高,可能會導(dǎo)致 RabbitMQ 消息重復(fù)投遞,可以限制每個消費者最多只能預(yù)取 1 條消息:
channel.basicQos(1); // 每個消費者一次只處理 1 條消息
- 生產(chǎn)者端消息確認(rèn)
RabbitMQ 提供 Confirm 機(jī)制,確保消息不會丟失
channel.confirmSelect(); // 開啟發(fā)布確認(rèn)模式channel.basicPublish("", "task_queue", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes("UTF-8"));// 等待確認(rèn)
if (!channel.waitForConfirms()) {System.out.println("消息發(fā)送失敗");
}
總結(jié)
? 手動 ACK 機(jī)制(避免自動 ACK,確保消息處理完畢后才確認(rèn))
? 業(yè)務(wù)去重(數(shù)據(jù)庫唯一索引,Redis SETNX)
? TTL + 死信隊列(防止重復(fù)消費導(dǎo)致的死循環(huán))
? 限制并發(fā)數(shù)(basicQos(1) 限制每個消費者最多消費 1 條)
? 發(fā)布確認(rèn)(waitForConfirms() 確保消息可靠投遞)
使用這些優(yōu)化方法,可以有效防止 RabbitMQ 在 高并發(fā)情況下的重復(fù)消費,保障消息一致性和系統(tǒng)穩(wěn)定性!🚀
7消息堆積如何處理?
在 RabbitMQ 高并發(fā)環(huán)境下,如果消息處理速度趕不上生產(chǎn)速度,容易導(dǎo)致 消息堆積,最終可能引發(fā) 隊列溢出、服務(wù)器崩潰 等問題。這里介紹 消息堆積的原因分析 以及 有效的解決方案。
? 解決方案
- 增加消費者并發(fā)數(shù)
方法:
增加 消費者實例(多個 Worker 并發(fā)消費)
調(diào)整 預(yù)取數(shù) prefetch_count,避免一個消費者一次性接收過多消息
📌 示例(Java 代碼:增加并發(fā)消費者)
channel.basicQos(10); // 每個消費者最多處理 10 條消息
這樣可以讓消費者更快地處理消息,減少隊列堆積。
- 使用消息分片 & 負(fù)載均衡
當(dāng)單個隊列壓力過大時,可以通過 多隊列 + 負(fù)載均衡 分擔(dān)流量:
方法 1:按 消息特性 分片(如訂單隊列、支付隊列、庫存隊列)
方法 2:多個消費者監(jiān)聽 不同的隊列 處理同一類任務(wù)
方法 3:使用 交換機(jī)(Exchange) 進(jìn)行路由
📌 示例(不同消費者監(jiān)聽不同的隊列)
channel.queueDeclare("order_queue", true, false, false, null);
channel.queueDeclare("payment_queue", true, false, false, null);
優(yōu)點:避免某一個隊列因負(fù)載過大而導(dǎo)致 RabbitMQ 掛掉。
- 啟用 RabbitMQ 集群
如果 RabbitMQ 單節(jié)點性能不足,可以使用 RabbitMQ 集群,提高系統(tǒng)的吞吐能力:
鏡像隊列模式:保證高可用
分布式隊列模式:多個 RabbitMQ 節(jié)點共同處理消息
📌 示例(啟用集群)
rabbitmqctl join_cluster rabbit@node2
優(yōu)點:多個 RabbitMQ 服務(wù)器 分擔(dān)流量,避免單點壓力過大。
- 配置消息 TTL + 死信隊列
如果消息長時間堆積,可以設(shè)置 消息過期時間(TTL),并把未處理的消息轉(zhuǎn)入 死信隊列(DLX)。
📌 示例(配置 TTL + DLX)
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 60000); // 消息存活 60s
args.put("x-dead-letter-exchange", "dlx_exchange"); // 過期消息轉(zhuǎn)入死信隊列
channel.queueDeclare("task_queue", true, false, false, args);
優(yōu)點:
- 限制消息存活時間,防止無用消息無限堆積。
- 過期消息進(jìn)入 死信隊列,避免影響正常業(yè)務(wù)。
- 限流(限流+降級)
如果生產(chǎn)者產(chǎn)生消息過快,可以限制生產(chǎn)速度,或直接丟棄部分非核心消息。
限流方式:
- RabbitMQ basic.qos 控制流速
- Kafka 或 Redis 作為緩沖層
- 應(yīng)用層限流(如令牌桶、漏桶算法)
📌 示例(使用 basic.qos 進(jìn)行流量控制)
channel.basicQos(5); // 限制每個消費者每次最多消費 5 條
優(yōu)點:可以防止 RabbitMQ 被瞬時高并發(fā)流量壓垮。
- 監(jiān)控 & 自動擴(kuò)展
- 使用 RabbitMQ 監(jiān)控工具(Prometheus + Grafana / RabbitMQ Management)
- 設(shè)置自動擴(kuò)容機(jī)制(Kubernetes HPA)
📌 示例(監(jiān)控 RabbitMQ 隊列長度)
rabbitmqctl list_queues name messages_ready messages_unacknowledged
如果隊列長度超過閾值,可以 動態(tài)增加消費者,提高處理能力。
🔥 總結(jié)
如果你的 RabbitMQ 消息已經(jīng)堆積過多,可以:
- 臨時增加消費者實例(短期緩解)
- 快速清理過期/無用消息(避免資源占滿)
- 優(yōu)化消息分片 & 負(fù)載均衡(長期解決)
8.用過那些監(jiān)控ivm的工具?
IVM(Intelligent Virtual Machine,智能虛擬機(jī))監(jiān)控 需要關(guān)注 CPU、內(nèi)存、磁盤、網(wǎng)絡(luò) 以及 應(yīng)用服務(wù) 的健康狀態(tài)。常見的 IVM 監(jiān)控工具有:
? 如何實現(xiàn) IVM 監(jiān)控
- 使用 Prometheus + Grafana 監(jiān)控 IVM
架構(gòu):
- Node Exporter(采集虛擬機(jī) CPU、內(nèi)存、磁盤、網(wǎng)絡(luò))
- Prometheus(存儲 & 報警)
- Grafana(可視化)
📌 步驟 1?? 安裝 Node Exporter(虛擬機(jī)監(jiān)控)
wget https://github.com/prometheus/node_exporter/releases/latest/download/node_exporter-linux-amd64.tar.gz
tar -xzf node_exporter-linux-amd64.tar.gz
cd node_exporter-*
./node_exporter &
2?? 配置 Prometheus 采集虛擬機(jī)數(shù)據(jù) 編輯 prometheus.yml:
yaml
scrape_configs:- job_name: 'ivm-monitor'static_configs:- targets: ['192.168.1.100:9100'] # 監(jiān)控 IVM 的 IP
3?? 運行 Prometheus
./prometheus --config.file=prometheus.yml
4?? 配置 Grafana 可視化
- 在 Grafana 添加數(shù)據(jù)源(Prometheus)
- 使用 Node Exporter 監(jiān)控儀表盤展示 CPU、內(nèi)存、磁盤使用率
- 使用 Zabbix 監(jiān)控 IVM
1?? 安裝 Zabbix Agent 到 IVM
sudo apt install zabbix-agent
sudo systemctl start zabbix-agent
sudo systemctl enable zabbix-agent
2?? 配置 Zabbix Server 在 /etc/zabbix/zabbix_agentd.conf 添加:
Server=192.168.1.200 # Zabbix 服務(wù)器 IP
3?? 在 Zabbix Web UI 中添加 IVM 主機(jī)
- 配置 CPU、內(nèi)存、網(wǎng)絡(luò)監(jiān)控
- 設(shè)置告警閾值(如 CPU 使用率 > 80% 發(fā)送告警)
- 使用 ELK 監(jiān)控 IVM 日志
1?? 安裝 Filebeat 采集日志
sudo apt install filebeat
2?? 配置 Filebeat 發(fā)送到 Elasticsearch
output.elasticsearch:hosts: ["192.168.1.200:9200"]
3?? 使用 Kibana 可視化 IVM 日志
🚀 總結(jié)
- 大規(guī)模 IVM 監(jiān)控 👉 Prometheus + Grafana
- 企業(yè)級穩(wěn)定監(jiān)控 👉 Zabbix / Nagios
- 日志監(jiān)控 👉 ELK
- SaaS 監(jiān)控 👉 Datadog / New Relic
如果是 高并發(fā)分布式 IVM 監(jiān)控,推薦 Prometheus + Grafana,結(jié)合 告警 & 自動擴(kuò)展,確保系統(tǒng)穩(wěn)定!💡
9.常用的設(shè)計模式有哪些?怎么用的?
設(shè)計模式(Design Patterns) 是軟件開發(fā)中的常見問題解決方案,主要分為 三大類:
- 創(chuàng)建型模式(解決對象創(chuàng)建問題)
- 結(jié)構(gòu)型模式(解決類和對象的組合問題)
- 行為型模式(解決對象交互問題)
🔹 1. 創(chuàng)建型模式
? 1.1 單例模式(Singleton)
作用:確保一個類只有一個實例,并提供全局訪問點
應(yīng)用場景:數(shù)據(jù)庫連接池、線程池、日志系統(tǒng)
📌 示例(懶漢式,線程安全)
public class Singleton {private static volatile Singleton instance; // 防止指令重排private Singleton() {} // 私有構(gòu)造函數(shù),防止外部實例化public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}
? 1.2 工廠模式(Factory Pattern)
作用:提供一個創(chuàng)建對象的接口,而不是直接實例化類
應(yīng)用場景:數(shù)據(jù)庫連接、日志記錄器
📌 示例
// 1. 定義接口
interface Product {void create();
}// 2. 具體產(chǎn)品實現(xiàn)
class ConcreteProductA implements Product {public void create() { System.out.println("創(chuàng)建產(chǎn)品 A"); }
}class ConcreteProductB implements Product {public void create() { System.out.println("創(chuàng)建產(chǎn)品 B"); }
}// 3. 工廠類
class Factory {public static Product getProduct(String type) {if ("A".equals(type)) return new ConcreteProductA();else if ("B".equals(type)) return new ConcreteProductB();return null;}
}// 4. 使用工廠
Product product = Factory.getProduct("A");
product.create();
🔹 2. 結(jié)構(gòu)型模式
? 2.1 適配器模式(Adapter Pattern)
作用:把一個接口轉(zhuǎn)換成客戶端期望的另一個接口
應(yīng)用場景:兼容老代碼、新舊接口對接
📌 示例
// 1. 目標(biāo)接口
interface Target {void request();
}// 2. 被適配者(舊接口)
class Adaptee {void specificRequest() { System.out.println("調(diào)用舊接口"); }
}// 3. 適配器
class Adapter implements Target {private Adaptee adaptee = new Adaptee();public void request() { adaptee.specificRequest(); }
}// 4. 使用適配器
Target adapter = new Adapter();
adapter.request();
? 2.2 裝飾器模式(Decorator Pattern)
作用:動態(tài)擴(kuò)展類的功能,而不修改原代碼
應(yīng)用場景:日志增強(qiáng)、數(shù)據(jù)加密、I/O 流
📌 示例
// 1. 定義接口
interface Component {void operation();
}// 2. 具體組件
class ConcreteComponent implements Component {public void operation() { System.out.println("基礎(chǔ)功能"); }
}// 3. 裝飾器基類
class Decorator implements Component {protected Component component;public Decorator(Component component) { this.component = component; }public void operation() { component.operation(); }
}// 4. 具體裝飾器
class ConcreteDecorator extends Decorator {public ConcreteDecorator(Component component) { super(component); }public void operation() {super.operation();System.out.println("擴(kuò)展功能");}
}// 5. 使用裝飾器
Component decorated = new ConcreteDecorator(new ConcreteComponent());
decorated.operation();
🔹 3. 行為型模式
? 3.1 觀察者模式(Observer Pattern)
作用:一對多依賴,當(dāng)一個對象狀態(tài)改變,所有依賴對象自動更新
應(yīng)用場景:事件監(jiān)聽、消息推送
📌 示例
import java.util.ArrayList;
import java.util.List;// 1. 觀察者接口
interface Observer {void update(String message);
}// 2. 具體觀察者
class User implements Observer {private String name;public User(String name) { this.name = name; }public void update(String message) {System.out.println(name + " 收到消息: " + message);}
}// 3. 主題(被觀察者)
class Subject {private List<Observer> observers = new ArrayList<>();public void addObserver(Observer observer) { observers.add(observer); }public void notifyObservers(String message) {for (Observer observer : observers) observer.update(message);}
}// 4. 測試
Subject subject = new Subject();
Observer user1 = new User("張三");
Observer user2 = new User("李四");
subject.addObserver(user1);
subject.addObserver(user2);
subject.notifyObservers("系統(tǒng)升級");
? 3.2 策略模式(Strategy Pattern)
作用:定義一系列算法,讓它們可以互換,避免 if-else 代碼膨脹
應(yīng)用場景:支付方式、數(shù)據(jù)壓縮、權(quán)限驗證
📌 示例
// 1. 策略接口
interface Strategy {void execute();
}// 2. 具體策略
class ConcreteStrategyA implements Strategy {public void execute() { System.out.println("執(zhí)行策略 A"); }
}class ConcreteStrategyB implements Strategy {public void execute() { System.out.println("執(zhí)行策略 B"); }
}// 3. 上下文(使用策略)
class Context {private Strategy strategy;public void setStrategy(Strategy strategy) { this.strategy = strategy; }public void executeStrategy() { strategy.execute(); }
}// 4. 測試
Context context = new Context();
context.setStrategy(new ConcreteStrategyA());
context.executeStrategy();
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy();
🔥 總結(jié)
10.xxl-job如果任務(wù)處理不完怎么辦?
🔍 任務(wù)堆積的原因分析
- 任務(wù)執(zhí)行時間過長(業(yè)務(wù)邏輯耗時過多、數(shù)據(jù)庫查詢慢)
- 任務(wù)并發(fā)數(shù)太少(單個執(zhí)行器線程數(shù)限制)
- 任務(wù)調(diào)度間隔過短(調(diào)度頻率高,任務(wù)還未執(zhí)行完就來了新的)
- 任務(wù)失敗未重試(導(dǎo)致部分任務(wù)一直未完成)
- 執(zhí)行器負(fù)載過高(CPU、內(nèi)存資源耗盡)
? 解決方案
- 增加任務(wù)并發(fā)數(shù)
如果執(zhí)行器并發(fā)數(shù)太少,任務(wù)處理能力有限,可以通過 線程池 提高并發(fā)。
📌 方法 1:配置 XXL-Job 線程池
修改 XXL-Job 執(zhí)行器配置,提高 xxl.job.executor.logretentiondays
xxl.job.executor.logretentiondays=30
xxl.job.executor.threadpool=20
Java 代碼示例(使用線程池)
@XxlJob("parallelJobHandler")
public void parallelJobHandler() throws Exception {ExecutorService executorService = Executors.newFixedThreadPool(10); // 增加線程池for (int i = 0; i < 10; i++) {executorService.submit(() -> {System.out.println("處理任務(wù):" + Thread.currentThread().getName());});}executorService.shutdown();
}
- 增加執(zhí)行器機(jī)器
如果任務(wù)量特別大,單個 Executor(執(zhí)行器) 處理不過來,可以 水平擴(kuò)展,增加多臺機(jī)器。
📌 方法:配置 XXL-Job 多個執(zhí)行器
- 在 Nginx 或負(fù)載均衡 中配置多個 XXL-Job 執(zhí)行器
- 在 xxl-job-admin 配置多個 Executor
- 動態(tài)擴(kuò)容:可以使用 Kubernetes(K8s)+ HPA 自動擴(kuò)展實例
📌 示例(多個執(zhí)行器注冊)
xxl.job.executor.address=http://192.168.1.100:9999,http://192.168.1.101:9999
- 任務(wù)分片(Sharding)
如果任務(wù)處理不完,可以 拆分任務(wù),讓多個執(zhí)行器同時處理不同數(shù)據(jù)片段。
📌 方法:使用 XXL-Job 自帶的 Sharding
@XxlJob("shardingJobHandler")
public void shardingJobHandler() throws Exception {int shardIndex = XxlJobHelper.getShardIndex(); // 當(dāng)前執(zhí)行器分片索引int shardTotal = XxlJobHelper.getShardTotal(); // 總分片數(shù)System.out.println("執(zhí)行分片:" + shardIndex + "/" + shardTotal);
}
作用:如果有 10000 條數(shù)據(jù),可以分 10 片,每個執(zhí)行器處理 1000 條,提升任務(wù)吞吐量。
- 任務(wù)超時 & 限流
如果任務(wù)執(zhí)行時間過長,導(dǎo)致后續(xù)任務(wù)堆積,可以:
設(shè)置任務(wù)超時時間
限制最大并發(fā)數(shù)
📌 方法:配置 XXL-Job 超時 & 并發(fā)
@XxlJob(value = "timeoutJobHandler", init = "initMethod", destroy = "destroyMethod", timeout = 5000, concurrent = false)
public void timeoutJobHandler() throws Exception {Thread.sleep(6000); // 模擬超時
}
timeout = 5000:超時 5 秒后強(qiáng)制結(jié)束任務(wù)
concurrent = false:不允許并發(fā)執(zhí)行,防止任務(wù)堆積
- 失敗重試
如果任務(wù)失敗,可能會導(dǎo)致數(shù)據(jù)未處理完。可以開啟 失敗重試,讓 XXL-Job 自動嘗試執(zhí)行。
📌 方法:在 XXL-Job 控制臺配置
- 重試次數(shù):默認(rèn) 3 次
- 失敗策略:
-
- 失敗重試(RETRY)
-
- 失敗報警(FAIL_ALARM)
-
- 丟棄后續(xù)任務(wù)(DISCARD_LATER)
- 任務(wù)排隊(消息隊列)
如果任務(wù)量超大,可以引入 消息隊列(MQ) 進(jìn)行流量削峰。
📌 方法:使用 RabbitMQ / Kafka 進(jìn)行異步處理
@XxlJob("mqJobHandler")
public void mqJobHandler() throws Exception {String message = "任務(wù)數(shù)據(jù)";rabbitTemplate.convertAndSend("taskQueue", message);
}
讓 XXL-Job 只負(fù)責(zé)寫入 MQ,執(zhí)行器異步消費,防止任務(wù)堆積。
🔥 總結(jié)
📌 綜合優(yōu)化:
? 短期優(yōu)化 👉 線程池、任務(wù)分片、超時處理
? 長期優(yōu)化 👉 負(fù)載均衡、多執(zhí)行器、異步消息隊列
如果 XXL-Job 任務(wù)一直堆積,建議結(jié)合 任務(wù)分片 + 多執(zhí)行器 + MQ 方案,確保高并發(fā)場景下任務(wù)穩(wěn)定運行!🚀
11.spring事務(wù)失效情況,傳播機(jī)制,如何保證事務(wù)不失效
🔹 1. Spring 事務(wù)失效的常見原因
在 Spring 中,事務(wù)由 @Transactional 注解管理,但在某些情況下事務(wù)可能會失效。常見失效情況包括:
🔹 2. 事務(wù)傳播機(jī)制(Transaction Propagation)
Spring 事務(wù)提供 7 種事務(wù)傳播機(jī)制,主要用于嵌套調(diào)用時事務(wù)的行為控制:
📌 示例
@Service
public class OrderService {@Transactional(propagation = Propagation.REQUIRED)public void createOrder() {userService.addUser(); // 事務(wù)加入當(dāng)前事務(wù)paymentService.processPayment(); // 事務(wù)加入當(dāng)前事務(wù)}
}
🔹 3. 如何保證事務(wù)不失效
為了確保事務(wù)能夠正常生效,推薦采取以下措施:
? 1. @Transactional 方法必須是 public
📌 錯誤示例(私有方法無效)
@Service
public class MyService {@Transactionalprivate void saveData() { // ? 無效// 事務(wù)不會生效}
}
📌 正確示例
@Service
public class MyService {@Transactionalpublic void saveData() { // ? 必須是 public// 事務(wù)正常生效}
}
? 2. 確保 @Transactional 方法是由 Spring 托管的 Bean 調(diào)用
📌 錯誤示例(內(nèi)部調(diào)用失效)
@Service
public class MyService {@Transactionalpublic void methodA() {methodB(); // ? 內(nèi)部調(diào)用,事務(wù)失效}@Transactionalpublic void methodB() {// 事務(wù)不會生效}
}
📌 正確示例
@Service
public class MyService {@Transactionalpublic void methodA() {((MyService) AopContext.currentProxy()).methodB(); // ? 通過代理調(diào)用}@Transactionalpublic void methodB() {// 事務(wù)正常生效}
}
? 3. 事務(wù)異常必須是 RuntimeException
📌 錯誤示例(捕獲異常后事務(wù)不回滾)
java
復(fù)制
編輯
@Transactional
public void updateData() {
try {
// 業(yè)務(wù)邏輯
} catch (Exception e) { // ? 事務(wù)不會回滾
e.printStackTrace();
}
}
📌 正確示例
java
復(fù)制
編輯
@Transactional
public void updateData() {
try {
// 業(yè)務(wù)邏輯
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // ? 手動回滾
}
}
? 4. 多線程事務(wù)控制
📌 錯誤示例(子線程不繼承事務(wù))
@Transactional
public void process() {new Thread(() -> saveData()).start(); // ? 事務(wù)不會生效
}
📌 正確示例
@Transactional
public void process() {CompletableFuture.runAsync(() -> saveData(), Executors.newFixedThreadPool(5)); // ? 線程池事務(wù)
}
? 5. 事務(wù)嵌套 & Propagation.REQUIRES_NEW
如果主事務(wù)和子事務(wù)相互獨立,可以使用 REQUIRES_NEW。
📌 示例
@Service
public class OrderService {@Transactional(propagation = Propagation.REQUIRED)public void createOrder() {paymentService.processPayment(); // 事務(wù) A}
}@Service
public class PaymentService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void processPayment() {// 事務(wù) B,獨立提交,不受主事務(wù)影響}
}
🔥 總結(jié)
📌 最佳實踐:
- 所有 @Transactional 方法必須是 public
- 確保方法是由 Spring 管理的 Bean 調(diào)用(避免內(nèi)部調(diào)用)
- 拋出的異常必須是 RuntimeException,或手動 setRollbackOnly()
- 多線程時要手動綁定事務(wù)
- 事務(wù)傳播機(jī)制要合理選擇(REQUIRES_NEW / NESTED 等)