Java讀取Level-1行情dbf文件極致優化(1)

最近架構一個項目,實現行情的接入和分發,須要達到極致的低時延特性,這對於證券系統是很是重要的。接入的行情源是能夠配置,既能夠是Level-1,也能夠是Level-2或其餘第三方的源。雖然Level-1行情沒有Level-2快,可是做爲系統支持的行情源,咱們仍是須要優化它,使得從文件讀取,到用戶經過socket收到行情,端到端的時延儘量的低。本文主要介紹對level-1行情dbf文件讀取的極致優化方案。相信對其餘的dbf文件讀取應該也有借鑑意義。html

 

Level-1行情是由行情小站,定時每隔幾秒把dbf文件(上海是show2003.dbf,深圳是sjshq.dbf)更新一遍,用新的行情替換掉舊的。咱們的目標就是,在新文件完成更新後,在最短期內將文件讀取到內存,把每一行轉化爲對象,把每一個列轉化爲對應的數據類型。java

 

咱們一共採用了6種優化方式。windows

 

優化一:採用內存硬盤(RamDisk)

內存硬盤能夠極大地提升文件的讀寫速度,level-1行情的讀寫是應用內存硬盤的絕好狀況:服務器

1,咱們能夠把行情小站的行情文件地址配置在內存硬盤上。這樣能夠加速行情小站寫文件的速度。架構

2,本系統再從內存硬盤讀取,又能夠加快讀取速度。eclipse

3,內存硬盤掉電後會丟失文件,這裏咱們基本不在意這個缺點,由於行情文件原本就是臨時的,若是有持久化的須要,大部份內存硬盤也支持持久化的功能。socket

 

Windows上有不少虛擬內存硬盤的軟件(本系統部署在windows服務器上,由於行情小站是在windows上的)。性能仍是有差異的,我主要基於如下這篇文章《12 RAM Disk Software Benchmarked for Fastest Read and Write Speed》,本身再作了一點點測試,選擇了Bond Disc這個軟件。其地址是http://www.bonddisc.com/,使用挺簡單的,這裏就不詳細介紹了。性能

 

測試下來,採用RamDisk讀取並無比從普通的硬盤讀取快不少,多是由於行情dbf文件自己就很小,只有1~2M,而且咱們採用NIO形式的文件讀取自己就比較快。可是使用RamDisk仍是頗有意義的 --測試

1,因爲行情小站直接把dbf文件寫入內存硬盤,能夠加速很多。優化

2,若是在硬盤繁忙時,因爲硬盤是串行的,採用內存硬盤在這種狀況下能夠避免瓶頸,保持穩定的讀取低時延。

 

優化二:採用JNotify,用通知替代輪詢

因爲行情小站會不斷的更新行情dbf文件,咱們的系統須要探測到一旦行情文件被更新,就當即讀取。傳統的策略是不斷輪詢行情文件的狀態,若是發現行情文件的最後修改日期(或者再加上文件大小)改變時,就認爲文件被更新。可是這種方式既低效,時延又高且不穩定。假設即便把輪詢時間設置爲10ms一次(這意味着1秒鐘就要輪詢100次), 平均時延也要5ms。

 

咱們能夠用通知來替代輪詢。這裏咱們採用JNotify庫,這個庫的下載地址是:

http://jnotify.sourceforge.net/

JNotify庫支持Windows,Linux和MacOS,容許咱們監視一個文件夾,當這個文件夾下的文件被增刪改時,發起回調通知。代碼示例以下:

public void addWatcher(String hangqingFolder, String hangqingFile) throws Exception {

        // watch mask, specify events you care about,
        // or JNotify.FILE_ANY for all events.
        int mask = // JNotify.FILE_CREATED |
                    // JNotify.FILE_DELETED |
                JNotify.FILE_MODIFIED; // 咱們只須要註冊修改事件
        // JNotify.FILE_RENAMED;

        // watch subtree?
        boolean watchSubtree = false;

        // add actual watch

        watchId = JNotify.addWatch(hangqingFolder, mask, watchSubtree, new JNotifyListener() {

            public void fileRenamed(int wd, String rootPath, String oldName, String newName) {
                // do nothing.
            }

            public void fileModified(int wd, String rootPath, String name) {

                if (!hangqingFile.equalsIgnoreCase(name)) //若是是修改的其餘文件,則忽略
                    return;
                
                readHangqingFile(hangqingFolder + File.separator + hangqingFile);

            }

            public void fileDeleted(int wd, String rootPath, String name) {
                // do nothing
            }

            public void fileCreated(int wd, String rootPath, String name) {
                // do nothing
            }
        });
    }

 

以上代碼:

1,咱們只要監視文件修改,所以只要設置mask = JNotify.FILE_MODIFIED

2,不須要遞歸地監視子目錄,設置watchSubtree = false

3,因爲監視的是文件夾,而不是文件,在fileModified方法中,咱們要判斷修改的是否是咱們關心的文件(即行情文件),若是不是,則忽略。若是是,就調用readHangqingFile開始讀取。

 

JNotify是基於操做系統API實現的,即便用JNI實現的,所以除了jar文件,還包含.dll文件和.so文件。用eclipse開發時,咱們須要指定這些本地庫的目錄,以下圖所示:

image

 

部署時,須要將本地庫放在執行根目錄下,或者用-Djava.library.path=/native/library/path 指定本地庫的位置。

 

採用JNotify,用(基於操做系統的)通知而不是輪詢,能夠很是快地發現文件被更新,根據測試時間<1ms (我以爲應該遠小於1ms,可是因爲文件修改時間單位是毫秒,沒辦法更精確的測量)。

 

待續。。。

 

Binhua Liu原創文章,轉載請註明原地址http://www.cnblogs.com/Binhua-Liu/p/5609396.html

相關文章
相關標籤/搜索