From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-biz-common/jnpf-audit-sdk/src/main/java/jnpf/audit/sdk/AuditSpoolLock.java |  328 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 328 insertions(+), 0 deletions(-)

diff --git a/jnpf-biz-common/jnpf-audit-sdk/src/main/java/jnpf/audit/sdk/AuditSpoolLock.java b/jnpf-biz-common/jnpf-audit-sdk/src/main/java/jnpf/audit/sdk/AuditSpoolLock.java
new file mode 100644
index 0000000..1cebdb1
--- /dev/null
+++ b/jnpf-biz-common/jnpf-audit-sdk/src/main/java/jnpf/audit/sdk/AuditSpoolLock.java
@@ -0,0 +1,328 @@
+package jnpf.audit.sdk;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.DisposableBean;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.file.Files;
+
+/**
+ * spool 鍙屽眰閿佸崗璁紙Codex 鎵规浜屽楠� #1/#2 缁堝眬淇锛涙壒娆′簩涓夎疆 #A/#D 鍔犲浐锛夈��
+ *
+ * <p>{@link AuditSpoolWriter} 涓� {@link AuditSpoolReplayer} <b>鍏变韩鍚屼竴瀹炰緥</b>锛堢敱
+ * {@code AuditSdkAutoConfiguration} 娉ㄥ叆鍗曚緥锛夛紝鎶婂郊姝ょ殑 spool 鏂囦欢鎿嶄綔涓茶鍖栵紝浠庢牴涓婃秷闄や袱鏉$珵鎬侊細
+ * <ul>
+ *   <li><b>Codex #1锛圕ritical锛�</b>锛歸riter 姣忔 open-append-close 涓� replayer 鐨�
+ *       "璁ら rename + readAllLines" 涔嬮棿鐨勭獥鍙b�斺�攚riter 鎭板湪 replayer 鏀瑰悕鏃ф枃浠跺悗缁忓凡鎵撳紑鐨� FD
+ *       鍐欏叆琚敼鍚� inode锛屼簨浠堕殢 {@code .replaying} 琚垹鑰屾案涔呬涪澶便��</li>
+ *   <li><b>Codex #2锛圛mportant锛�</b>锛氳棰� rename 涓� setLastModified 鍒锋柊绉熺害鏄袱涓嫭绔嬫搷浣滐紝
+ *       瀹炰緥 B 鍙湪瀹炰緥 A rename 鎴愬姛鍚庛�佸埛鏂� mtime 鍓嶆妸瀹冨綋瀛ゅ効鎶㈣蛋銆�</li>
+ * </ul>
+ * 涓ゆ潯绔炴�佺殑姝g‘鎬т粠姝ょ敱鏈攣淇濊瘉锛堜笉鍐嶄緷璧� 60 绉掗潤榛樼獥鍙h繖绉嶆椿璺冨害鍚彂寮忥紱闈欓粯绐楀彛闄嶇骇涓虹函浼樺寲锛夈��
+ *
+ * <h3>涓ゅ眰缁撴瀯</h3>
+ * <ol>
+ *   <li><b>杩涚▼鍐呭眰</b>锛氬崟涓�鐩戣鍣� {@link #monitor}锛寃riter/replayer 鎵�鏈変复鐣屽尯鍏辩敤锛屼繚璇佸悓 JVM 涓茶銆�
+ *       瀹冨悓鏃朵繚璇佸悓 JVM 鍐� {@link FileChannel#tryLock()} 璇锋眰姘镐笉閲嶅彔鈥斺�斿惁鍒� JDK 浼氭姏
+ *       {@link OverlappingFileLockException}锛團ileLock 鏄� JVM 绾ц�岄潪绾跨▼绾э級锛屾晠鏈被鍏堝彇杩涚▼鍐呴攣銆�
+ *       鍦� {@code synchronized} 鍧楀唴鑾�/鏀� FileLock锛屽ぉ鐒舵棤閲嶅彔銆�</li>
+ *   <li><b>璺ㄨ繘绋嬪眰</b>锛歴pool 鐩綍涓嬪父椹荤殑 {@code spool.lock} 鏂囦欢 + {@link FileChannel#lock()} 绯�
+ *       鐙崰 {@link FileLock}銆傞攣鏂囦欢 {@link RandomAccessFile}/{@link FileChannel} 鎯版�ф墦寮�銆佸父椹伙紝
+ *       {@link DisposableBean#destroy()} 鏃堕噴鏀俱��</li>
+ * </ol>
+ *
+ * <h3>闄嶇骇涓庣粓灞�锛堢粷涓嶈涓氬姟/鍙戦�佺嚎绋嬪穿婧冿級</h3>
+ * <ul>
+ *   <li>鎵撳紑閿佹枃浠舵垨 {@code tryLock} 鎶� {@link IOException}锛堜笉鏀寔鐨勬枃浠剁郴缁熺瓑锛夆啋 缃�
+ *       {@link #fileLockDisabled}锛屼箣鍚�<b>涓嶅啀灏濊瘯璺ㄨ繘绋嬮攣</b>锛寋@link #warnDegradeOnce} 鍘婚噸 warn 涓�娆°��</li>
+ *   <li><b>writer 璺緞</b> {@link #lockAndRun(IoRunnable, IoRunnable)}锛氭嬁鍒拌法杩涚▼ FileLock 鈫� 鎵ц
+ *       {@code lockedAction}锛堟甯歌拷鍔犲叡浜椿鍔ㄦ枃浠讹級锛�<b>鏈嬁鍒�</b>锛堣秴鏃�/涓柇/涓嶅彲鐢�/宸查攢姣侊級鈫� 鎵ц
+ *       {@code degradedAction}锛圕odex#A锛氬啓鍞竴鍚� {@code .retry} 搴旀�ユ鈥斺�斾竴浜嬩欢涓�鏂囦欢锛屾棤鍏变韩鍚�=鏃�
+ *       rename 绔炴�侊紝澶╃劧鏄噸鏀惧崗璁殑杈撳叆锛屽畞澶氭枃浠朵笉涓簨浠讹級銆傝繘绋嬪唴 {@link #monitor} 鍏ㄧ▼鎸佹湁銆�</li>
+ *   <li><b>replayer 璺緞</b> {@link #tryLockAndRun}锛氶潪闃诲 {@code tryLock} 鎷夸笉鍒帮紙浠栬繘绋嬫寔鏈夛級灏�<b>璺宠繃鏈疆</b>
+ *       锛堣繑鍥� {@code false}锛屼笅杞啀鏉ワ級锛涢檷绾ф�佷粎杩涚▼鍐呴攣鎵ц杩斿洖 {@code true}锛堣閫�鍖栬矾寰勪粎鍦�"鍗曞疄渚�
+ *       鎷撴墤"涓嬪畨鍏ㄢ�斺�斿瀹炰緥鍏变韩鍚屼竴 spool 鍗锋椂璺ㄨ繘绋嬩簰鏂ュ畬鍏ㄥけ鏁堬紝闇�杩愮淮淇濊瘉涓嶆敮鎸� FileLock 鐨勬枃浠剁郴缁�
+ *       鍙崟瀹炰緥閮ㄧ讲锛屾棦鏈夊缓璁級锛�<b>宸查攢姣佹�佺洿鎺ヨ繑鍥� {@code false}銆佷笉鎵ц action</b>锛圕odex 鍥涜疆
+ *       Critical#1锛岃涓嬩竴鏉★級銆�</li>
+ *   <li><b>Codex#D 缁堟浣�</b> {@link #destroyed}锛歿@link #destroy()} 鍚� {@link #ensureChannelOpen} 鎷掔粷
+ *       閲嶅紑 channel鈥斺�旇繜鍒扮殑鏀跺熬/鍐欏叆璋冪敤锛坽@link #lockAndRun(IoRunnable, IoRunnable)}锛�"濮嬬粓鎵ц"璇箟锛�
+ *       鍙蛋杩涚▼鍐呴攣鎵ц degradedAction锛坽@link #warnPostDestroyOnce} 鍘婚噸 warn 涓�娆★級锛屾潨缁�"宸� close
+ *       鐨� raf/channel 琚噸寮�銆佸仠鏈哄悗鍙堟寔鏈夎法杩涚▼閿�"銆�</li>
+ *   <li><b>Codex 鍥涜疆 Critical#1</b>锛坉estroyed 鎬佽棰嗘湭鎷掔粷锛夛細{@link #tryLockAndRun}锛堣棰�/瀛ゅ効鍥炴敹
+ *       璺緞锛�"鍙欢鍚庡埌涓嬭疆"璇箟锛夊湪 destroyed 鍚�<b>鐩存帴杩斿洖 {@code false}锛屼笉鎵ц action</b>鈥斺�旇�岄潪鍍�
+ *       鏃х増閭f牱閫�鍖栦负"浠呰繘绋嬪唴閿佹墽琛屽苟杩斿洖 true"銆傚仠鏈虹獥鍙e唴涓嶅簲璇ュ啀瀵� spool 鏂囦欢鍋氫换浣曡棰嗭細璁ら
+ *       寤跺悗鍒颁笅涓�杞�/涓嬩竴涓瓨娲诲疄渚嬪鐞嗘棤瀹筹紝鑰屽啓鍏ヨ矾寰� {@link #lockAndRun(IoRunnable, IoRunnable)}
+ *       蹇呴』"濮嬬粓鎵ц"锛堜簨浠朵笉鑳戒涪锛夛紝浜岃�呰涔夋湁鎰忎笉鍚岋紝涓嶅彲娣风敤鍚屼竴濂� destroyed 澶勭悊銆�</li>
+ * </ul>
+ *
+ * <p><b>娈嬩綑澹版槑</b>锛氬崟鏂囦欢閲嶆斁锛坮eadAllLines鈫掔綉缁� deliver鈫掓敹灏撅級瓒� 10 鍒嗛挓浠嶅彲鑳借鍏跺畠杩涚▼褰撳鍎�
+ * 鎶㈣蛋 鈫� 鍚庢灉=閲嶅閲嶆斁锛岃惤搴撴寜 client_event_id 骞傜瓑淇濊瘉涓嶄涪涓嶉噸鍏ュ簱锛屽彲鎺ュ彈銆倇@code spool.lock}
+ * 鑷韩姘镐笉鍙備笌鎵弿/閲嶆斁/鍒犻櫎锛坽@link AuditSpoolReplayer} 鎵弿杩囨护宸叉寜 {@link #LOCK_FILE_NAME} 鎺掗櫎锛夈��
+ */
+@Slf4j
+public class AuditSpoolLock implements DisposableBean {
+
+    /** 璺ㄨ繘绋嬮攣鏂囦欢鍚嶁�斺�斿父椹� spool 鐩綍锛屾案涓嶅弬涓庢壂鎻�/閲嶆斁/鍒犻櫎銆� */
+    static final String LOCK_FILE_NAME = "spool.lock";
+
+    /** writer 璺緞 FileLock 鑾峰彇鐨勬渶闀跨瓑寰咃紙甯﹂噸璇曪級锛涜秴鏃跺悗闄嶇骇涓哄敮涓�鍚嶅簲鎬ユ銆� */
+    private static final long WRITER_LOCK_WAIT_MS = 5_000L;
+    /** 閲嶈瘯杞闂撮殧銆� */
+    private static final long RETRY_SLEEP_MS = 50L;
+
+    /**
+     * 杩涚▼鍐呭眰锛歸riter/replayer 鎵�鏈変复鐣屽尯鍏辩敤鐨勫崟涓�鐩戣鍣紝淇濊瘉鍚� JVM 涓茶锛�
+     * 浜︿繚璇佸悓 JVM 鍐� FileLock 璇锋眰姘镐笉閲嶅彔锛堝惁鍒欐姏 {@link OverlappingFileLockException}锛夈��
+     */
+    private final Object monitor = new Object();
+
+    private final File spoolDir;
+    private final File lockFile;
+
+    /** 璺ㄨ繘绋嬪眰甯搁┗鍙ユ焺锛宒estroy 鏃堕噴鏀撅紱浠呭湪鎸佹湁 {@link #monitor} 鏃惰鍐欍�� */
+    private RandomAccessFile raf;
+    private FileChannel channel;
+
+    /** 闄嶇骇鏍囧織锛欶ileLock 涓嶆敮鎸�/閿佹枃浠舵墦涓嶅紑鏃剁疆浣嶏紝涔嬪悗涓嶅啀灏濊瘯璺ㄨ繘绋嬮攣锛涗粎鍦ㄦ寔鏈� {@link #monitor} 鏃惰鍐欍�� */
+    private boolean fileLockDisabled = false;
+    /** warn 鍘婚噸鏍囧織锛涗粎鍦ㄦ寔鏈� {@link #monitor} 鏃惰鍐欍�� */
+    private boolean degradeWarned = false;
+    /** Codex#D锛氱粓姝綅鈥斺�攄estroy 鍚庢嫆缁濋噸寮� channel锛岃繜鍒拌皟鐢ㄥ彧璧拌繘绋嬪唴閿併�備粎鍦ㄦ寔鏈� {@link #monitor} 鏃跺啓銆� */
+    private volatile boolean destroyed = false;
+    /**
+     * 宸查攢姣佸悗杩熷埌璋冪敤鐨� warn 鍘婚噸鏍囧織锛涗粎鍦ㄦ寔鏈� {@link #monitor} 鏃惰鍐欍�備袱鏉¤皟鐢ㄨ矾寰勫叡鐢ㄤ竴涓幓閲嶅紑鍏筹細
+     * {@link #acquireFileLockWithTimeout()}锛坵riter/{@code lockAndRun} 璺緞锛宒estroyed 鈫� 杩斿洖 {@code null}
+     * 鈫� 璋冪敤鏂硅蛋 degradedAction锛屼粎杩涚▼鍐呴攣鎵ц锛変笌 {@link #tryLockAndRun}锛坮eplayer/璁ら璺緞锛宒estroyed 鈫�
+     * 鐩存帴杩斿洖 {@code false}銆佷笉鎵ц action鈥斺�擟odex 鍥涜疆 Critical#1锛夈��
+     */
+    private boolean postDestroyWarned = false;
+
+    public AuditSpoolLock(String spoolDir) {
+        this.spoolDir = new File(spoolDir);
+        this.lockFile = new File(this.spoolDir, LOCK_FILE_NAME);
+    }
+
+    /** 鍙姏 {@link IOException} 鐨勬棤杩斿洖鍥炶皟锛圝DK8 鍑芥暟寮忔帴鍙o級銆� */
+    @FunctionalInterface
+    public interface IoRunnable {
+        void run() throws IOException;
+    }
+
+    /**
+     * writer 璇箟锛圕odex#A 闄嶇骇闅旂锛夛細<b>濮嬬粓鎵ц</b>锛屼絾鎸夋槸鍚︽寔鏈夎法杩涚▼閿佷簩閫変竴鎵ц銆�
+     *
+     * <p>椤哄簭锛氳繘绋嬪唴 {@link #monitor} 鈫� 灏藉姏鑾� FileLock锛堝甫瓒呮椂閲嶈瘯锛屽叡鏈�澶� {@link #WRITER_LOCK_WAIT_MS}锛夈��
+     * <ul>
+     *   <li>鎷垮埌 FileLock 鈫� 鎵ц {@code lockedAction}锛堟甯歌拷鍔犲叡浜椿鍔ㄦ枃浠讹級锛屽�掑簭閲婃斁銆�</li>
+     *   <li>鏈嬁鍒帮紙瓒呮椂/涓柇/涓嶅彲鐢�/宸查攢姣侊級鈫� 鎵ц {@code degradedAction}锛堝啓鍞竴鍚� {@code .retry} 搴旀�ユ锛夈��
+     *       姝ゆ椂<b>缁濅笉</b>鍐嶈拷鍔犲叡浜椿鍔ㄥ悕鈥斺�斿敮涓�鍚嶆枃浠舵棤鍏变韩銆佹棤 rename 绔炴�侊紝澶╃劧鏄噸鏀惧崗璁殑杈撳叆
+     *       锛圕odex#A锛氬璁′簨浠跺畞鍙鍑犱釜鏂囦欢涔熶笉涓㈠純锛夈��</li>
+     * </ul>
+     *
+     * @param lockedAction   鎸佹湁璺ㄨ繘绋嬮攣鏃舵墽琛岀殑鍔ㄤ綔锛堝叡浜椿鍔ㄦ枃浠惰拷鍔狅級
+     * @param degradedAction 鏈寔閿佹椂鎵ц鐨勯檷绾у姩浣滐紙鍞竴鍚嶅簲鎬ユ鍐欏叆锛�
+     * @throws IOException 鎵�閫夊姩浣滄姏鍑虹殑 IO 寮傚父锛岀敱璋冪敤鏂规寜鍘熸湁鍏滃簳澶勭悊
+     */
+    void lockAndRun(IoRunnable lockedAction, IoRunnable degradedAction) throws IOException {
+        synchronized (monitor) {
+            FileLock fileLock = acquireFileLockWithTimeout();
+            if (fileLock == null) {
+                // Codex#A锛氭湭鎸佹湁璺ㄨ繘绋嬮攣锛堣秴鏃�/涓柇/涓嶅彲鐢�/宸查攢姣侊級鈫� 璧伴檷绾у姩浣滐紙鍐欏敮涓�鍚嶏紝娑堢伃鍏变韩锛�
+                degradedAction.run();
+                return;
+            }
+            try {
+                lockedAction.run();
+            } finally {
+                releaseQuietly(fileLock);
+            }
+        }
+    }
+
+    /**
+     * writer 璇箟渚挎嵎閲嶈浇锛氶檷绾у姩浣滀笌鎸侀攣鍔ㄤ綔<b>鐩稿悓</b>銆�
+     *
+     * <p>閫傜敤浜庢敹灏剧瓑"鎿嶄綔瀵硅薄鏈氨鏄敮涓�鍚�/owner-token 鍚�"鐨勮皟鐢ㄦ柟锛堝 {@link AuditSpoolReplayer} 鐨�
+     * {@code finishReplay}锛氬啓 {@code .retry}/{@code .bad}銆佸垹 owner-token 鐨� {@code .replaying.<uuid>}
+     * 閮芥槸鍞竴鍚嶏紝浠呰繘绋嬪唴閿佸嵆瀹夊叏锛屾棤闇� FileLock 涔熷彲姝g‘钀藉湴锛夈��
+     */
+    void lockAndRun(IoRunnable action) throws IOException {
+        lockAndRun(action, action);
+    }
+
+    /**
+     * replayer 璇箟锛欶ileLock 琚�<b>鍒殑杩涚▼</b>鎸佹湁鍒欒烦杩囷紙杩斿洖 {@code false}锛屼笉鎵ц {@code action}锛屼笅杞啀鏉ワ級銆�
+     *
+     * <p>椤哄簭锛氳繘绋嬪唴 {@link #monitor} 鈫� 宸查攢姣佸垯鐩存帴鎷掔粷 鈫� 闈為樆濉� {@link FileChannel#tryLock()} 鈫� 鎵ц 鈫� 鍊掑簭閲婃斁銆�
+     * 涓嶆敮鎸� FileLock锛堥檷绾ф�侊級鈫� 浠呰繘绋嬪唴閿佹墽琛屽苟杩斿洖 {@code true}锛堜粎鍗曞疄渚嬫嫇鎵戝畨鍏紝瑙佺被 Javadoc锛夈��
+     *
+     * <p><b>Codex 鍥涜疆 Critical#1锛坉estroyed 鎬佽棰嗘湭鎷掔粷锛�</b>锛氭棫鐗� destroyed 涓� fileLockDisabled 璧板悓涓�
+     * 鍒嗘敮鈥斺�斾粎杩涚▼鍐呴攣鎵ц {@code action} 骞惰繑鍥� {@code true}锛屽嵆鍋滄満鍚庝粛浼�"璁ら"銆傝棰�/瀛ゅ効鍥炴敹鏄彲寤跺悗
+     * 鍒颁笅涓�杞垨涓嬩竴涓瓨娲诲疄渚嬪鐞嗙殑鎿嶄綔锛屽仠鏈虹獥鍙e唴娌℃湁蹇呰銆佷篃涓嶅簲璇ュ啀瀵� spool 鏂囦欢鍔ㄦ墜锛屾晠 destroyed
+     * 鍗曠嫭鍒ゆ柇銆佺洿鎺ヨ繑鍥� {@code false}銆�<b>涓嶆墽琛� action</b>鈥斺�斾笌"蹇呴』濮嬬粓鎵ц"鐨勫啓鍏ヨ涔�
+     * {@link #lockAndRun(IoRunnable, IoRunnable)} 鏄庣‘鍖哄垎寮�銆�
+     *
+     * @return {@code true}=宸叉墽琛岋紙鎷垮埌 FileLock 鎴栧凡闄嶇骇锛夛紱{@code false}=FileLock 琚粬杩涚▼鎸佹湁鏈疆璺宠繃锛�
+     *         鎴栧凡閿�姣佺洿鎺ユ嫆缁�
+     * @throws IOException {@code action} 鎶涘嚭鐨� IO 寮傚父
+     */
+    boolean tryLockAndRun(IoRunnable action) throws IOException {
+        synchronized (monitor) {
+            if (destroyed) {
+                // Codex 鍥涜疆 Critical#1锛歞estroyed 鍚庣洿鎺ユ嫆缁濊棰嗭紝涓嶆墽琛� action銆佷笉閫�鍖栦负浠呰繘绋嬪唴閿佹墽琛屻��
+                warnPostDestroyOnce();
+                return false;
+            }
+            ensureChannelOpen();
+            if (fileLockDisabled || channel == null) {
+                // fileLockDisabled锛堜笉鏀寔 FileLock 鐨勬枃浠剁郴缁燂級淇濇寔鍘熸湁閫�鍖栬涓猴細浠呰繘绋嬪唴閿佹墽琛� action銆�
+                // 璇ラ��鍖栬矾寰勪粎鍦�"鍗曞疄渚嬫嫇鎵�"涓嬪畨鍏ㄢ�斺�斿瀹炰緥鍏变韩鍚屼竴 spool 鍗锋椂璺ㄨ繘绋嬩簰鏂ュ畬鍏ㄥけ鏁堬紝
+                // 闇�杩愮淮淇濊瘉涓嶆敮鎸� FileLock 鐨勬枃浠剁郴缁熷彧鍗曞疄渚嬮儴缃诧紙鏃㈡湁寤鸿锛岃绫� Javadoc锛夈��
+                action.run();
+                return true;
+            }
+            FileLock fileLock;
+            try {
+                fileLock = channel.tryLock();   // 闈為樆濉烇紝鏁存枃浠剁嫭鍗�
+            } catch (OverlappingFileLockException overlap) {
+                // 鐞嗚涓嶅彲杈撅細monitor 宸蹭覆琛屽寲鍚� JVM锛孎ileLock 璇锋眰涓嶄細閲嶅彔銆傞槻寰℃�у綋浣�"琚崰"璺宠繃鏈疆銆�
+                return false;
+            } catch (IOException unsupported) {
+                markDegraded(unsupported);   // 涓嶆敮鎸佺殑 FS 绛� 鈫� 闄嶇骇锛屼粎杩涚▼鍐呴攣鎵ц
+                action.run();
+                return true;
+            }
+            if (fileLock == null) {
+                return false;   // 鍒殑杩涚▼鎸佹湁 鈫� 璺宠繃鏈疆
+            }
+            try {
+                action.run();
+                return true;
+            } finally {
+                releaseQuietly(fileLock);
+            }
+        }
+    }
+
+    /**
+     * 甯﹁秴鏃堕噸璇曡幏鍙� FileLock锛涙嬁涓嶅埌杩斿洖 {@code null}锛堣皟鐢ㄦ柟璧伴檷绾у姩浣滐級銆傝皟鐢ㄦ柟蹇呴』鎸佹湁 {@link #monitor}銆�
+     */
+    private FileLock acquireFileLockWithTimeout() {
+        ensureChannelOpen();
+        if (fileLockDisabled || destroyed || channel == null) {
+            if (destroyed) {
+                warnPostDestroyOnce();
+            }
+            return null;   // 宸查檷绾�/宸查攢姣�/鏈紑 channel锛氳皟鐢ㄦ柟璧伴檷绾у姩浣�
+        }
+        long deadline = System.currentTimeMillis() + WRITER_LOCK_WAIT_MS;
+        while (true) {
+            try {
+                FileLock lock = channel.tryLock();
+                if (lock != null) {
+                    return lock;
+                }
+            } catch (OverlappingFileLockException overlap) {
+                // 鐞嗚涓嶅彲杈撅紙monitor 涓茶鍖栵級锛涘綋浣滅灛鏃讹紝钀藉叆閲嶈瘯/瓒呮椂閫昏緫
+            } catch (IOException unsupported) {
+                markDegraded(unsupported);   // 涓嶆敮鎸� 鈫� 闄嶇骇
+                return null;
+            }
+            if (System.currentTimeMillis() >= deadline) {
+                // 瓒呮椂 鈫� 璧伴檷绾у姩浣滐紙鍞竴鍚嶅簲鎬ユ锛屽璁′簨浠跺畞鍙鏂囦欢涔熶笉涓級锛涗笉缃� fileLockDisabled锛堜粬杩涚▼鏈鎸佹湁锛屽睘鐬椂锛�
+                warnDegradeOnce("FileLock 鑾峰彇瓒呮椂(" + WRITER_LOCK_WAIT_MS + "ms)锛屾湰娆″啓鍏ラ檷绾т负鍞竴鍚嶅簲鎬ユ");
+                return null;
+            }
+            try {
+                Thread.sleep(RETRY_SLEEP_MS);
+            } catch (InterruptedException ie) {
+                Thread.currentThread().interrupt();
+                return null;   // 琚腑鏂� 鈫� 璧伴檷绾у姩浣滐紙鍞竴鍚嶅簲鎬ユ锛夛紝灏藉揩瀹屾垚鍐欏叆
+            }
+        }
+    }
+
+    /**
+     * 鎯版�ф墦寮� {@code spool.lock} 甯搁┗鍙ユ焺锛涙墦寮�澶辫触鍗抽檷绾с��<b>宸查攢姣佸垯鎷掔粷閲嶅紑</b>锛圕odex#D锛夈��
+     * 璋冪敤鏂瑰繀椤绘寔鏈� {@link #monitor}銆�
+     */
+    private void ensureChannelOpen() {
+        if (channel != null || fileLockDisabled || destroyed) {
+            return;   // Codex#D锛歞estroyed 鏃朵笉閲嶅紑 channel锛岃繜鍒拌皟鐢ㄨ蛋杩涚▼鍐呴攣
+        }
+        try {
+            if (!spoolDir.exists()) {
+                Files.createDirectories(spoolDir.toPath());
+            }
+            raf = new RandomAccessFile(lockFile, "rw");
+            channel = raf.getChannel();
+        } catch (IOException openFail) {
+            markDegraded(openFail);   // 鏃犳硶鎵撳紑閿佹枃浠� 鈫� 浠呰繘绋嬪唴閿�
+        }
+    }
+
+    /** 缃案涔呴檷绾ф爣蹇楀苟 warn 涓�娆°�傝皟鐢ㄦ柟蹇呴』鎸佹湁 {@link #monitor}銆� */
+    private void markDegraded(Throwable cause) {
+        fileLockDisabled = true;
+        warnDegradeOnce("璺ㄨ繘绋� FileLock 涓嶅彲鐢紝闄嶇骇锛堝啓鍏ヨ蛋鍞竴鍚嶅簲鎬ユ/閲嶆斁璧颁粎杩涚▼鍐呴攣锛�: " + cause);
+    }
+
+    /** warn 鍘婚噸銆傝皟鐢ㄦ柟蹇呴』鎸佹湁 {@link #monitor}銆� */
+    private void warnDegradeOnce(String msg) {
+        if (!degradeWarned) {
+            degradeWarned = true;
+            log.warn("[audit] spool 閿侀檷绾�: {}", msg);
+        }
+    }
+
+    /** Codex#D锛氬凡閿�姣佸悗杩熷埌璋冪敤鐨� warn 鍘婚噸銆傝皟鐢ㄦ柟蹇呴』鎸佹湁 {@link #monitor}銆� */
+    private void warnPostDestroyOnce() {
+        if (!postDestroyWarned) {
+            postDestroyWarned = true;
+            log.warn("[audit] spool 閿佸凡閿�姣侊紝杩熷埌璋冪敤鎸夎矾寰勯檷绾э紙鍐欏叆璺緞=浠呰繘绋嬪唴閿佹墽琛岋紝"
+                    + "璁ら璺緞=鐩存帴鎷掔粷锛屼笉鍐嶉噸寮�璺ㄨ繘绋� channel锛�");
+        }
+    }
+
+    private void releaseQuietly(FileLock fileLock) {
+        if (fileLock == null) {
+            return;
+        }
+        try {
+            fileLock.release();
+        } catch (IOException releaseFail) {
+            log.warn("[audit] spool FileLock 閲婃斁澶辫触: {}", releaseFail.toString());
+        }
+    }
+
+    /**
+     * Codex#D锛氱疆缁堟浣嶃�侀噴鏀捐法杩涚▼鍙ユ焺銆傜疆 {@link #destroyed} 鍚� {@link #ensureChannelOpen} 姘镐笉鍐嶉噸寮�
+     * channel锛岃繜鍒扮殑鏀跺熬/鍐欏叆璋冪敤鍙蛋杩涚▼鍐呴攣锛堜笉浼氬洜 {@code ensureChannelOpen} 澶嶆椿涓�涓仠鏈哄悗浠嶆寔鏈夌殑璺ㄨ繘绋嬮攣锛夈��
+     */
+    @Override
+    public void destroy() {
+        synchronized (monitor) {
+            destroyed = true;
+            closeQuietly(channel);
+            closeQuietly(raf);
+            channel = null;
+            raf = null;
+        }
+    }
+
+    private static void closeQuietly(Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignore) {
+                // 鍋滄満閲婃斁锛屽拷鐣�
+            }
+        }
+    }
+}

--
Gitblit v1.8.0