1. [Tips] 用 “磁碟鏡像" 更換硬碟 #2, Windows 2003 的跨距磁區

    上一篇,介紹了如何用windows server内建的磁碟鏡像 (Mirror) 更換硬碟後,這次剛好有windows 2003,就拿來試了一下...

    廢話就不多說了,先來看一下 2003 的步驟,再來看看跟 2008 / 2008R2 差在那裡:

    1. 原本的樣子,磁碟1 (8.00GB) 是舊的硬碟,磁碟2 (16.00GB) 是要換上來的新硬碟:
      image001

    2. 磁碟1 + 磁碟2 做成鏡像 (MIRROR):
      image002


      等待重新同步化 (Resync) 完成:
      image003


    3. Resync 完成後,移除鏡像:
      image004


      移除鏡像完成後的狀態:
      image005 

    4. 用 2003 的延伸磁區,把磁碟2後面沒用到的空間也併進 D: 來
      image006


      合併之後的狀態:
      image007 



    整個步驟跟前一篇都差不多,主要都是靠鏡像(MIRROR)搬完資料後,再把新硬碟多的可用空間併進來。唯一的差別就在這裡: 2003 的 "延伸磁區" (英文: extend volume) 是可以把兩個硬碟,或是兩個分割區併在一起使用。在磁碟管理員還是看的到這些分割區的存在。這就有點像 JBOD (Just a Bunch Of Disks) 的模式。

    不過在 2008 之後 (其實 vista 也是),這個功能就改了。原本的名字 extend volumn 現在改成直接擴大原分割區,把原分割區後面可用的空間都納進來,就像你砍掉再重建一個大的分割區一樣,只是資料會留著不會掉。當然有 extend 也有相對的 shrink volume, 這功能會把分割區縮小,騰出空間來讓你多切一個分割區...。而原本 JBOD 模式的功能,則改為 span disk, 用起來效果就如上圖一樣,當然你願意的話,也可以把多顆硬碟 (可以不同容量) 通通併成一個來使用,最多可以併到 32 顆硬碟...。

    沒經過這次更換硬碟,還真沒發現 2008 / vista 總算內建這組 extend / shrink volume 的功能進來。雖然很陽春,不過已經很實用了,在過去這種動作是得搬出像 partition magic 這類軟體才能做的到,而這種東西每次用起來心裡都會毛毛的,深怕一不小心就把資料都給毀了...。

    這篇小品文章就記到這裡,希望有幫到需要的人 :D

    2010/03/11 Tips 技術隨筆 有的沒的

  2. [Tips] 用 “磁碟鏡像" 無痛更換硬碟

    老是寫一堆像外星文,沒人看的懂的 multi-threading 文章,偶爾也來換換口味吧。前陣子把 SERVER 的兩顆 750GB HDD (RAID1) 升級了一下,升級成兩顆 1.5TB HDD (RAID1)。更換硬碟是小事,不過這個硬碟上有些服務,如網站,資料庫,還有一些重要資料,跟分享資料夾,想一想要更換也是挺囉唆的...。

    想了幾個辦法,不過都不符合我既懶又挑毛病的個性... 原本考慮的更換方式有:

    1. 硬上... 新硬碟裝上去,檔案COPY過去,能停的服務就停掉,花一堆時間搬資料,然後再恢復原服務 (如 SQL、IIS等等)。至於比較麻煩的,像是目錄分享的,只好移掉再重建。中斷服務的時間就是從關機裝硬碟,一直到全部完成為止。
    2. 用 disk clone 的工具,如 true image / ghost 之類的軟體。不過這樣通常得停機做 clone, 750GB 也是要執行好一段時間,加上我這次買的是 Advanced Format 的硬碟,用這類工具會有效能的問題,事後還得校正回來… ouch, 算了...

    想來想去,我用的是 windows server, 有內建的 Mirror set, 就拿來用一用好了。我真正作的是把 mirror set 的兩顆都升級,不過為了簡化說明,我底下的例子就只以替換一般的硬碟就好,反正道理是一樣的。

    說穿了不值錢,就是用 mirror 的磁碟複製特性,加上 extend volume 的功能,我除了需要關機裝上新硬碟之外,其它包含資料複製的所有時間,原服務都不用中斷 (當然速度會慢一點),所有服務的設定也都不用修改,算是既無腦又防呆的完美方案... 只要簡單的按幾下滑鼠就可以達成我的目標。

     

    直接來看看怎麼作的吧! 很簡單,先利用 mirror 把資料轉移到新硬碟... 然後中斷 mirror, 再用 extend volume 把磁區大小調大就可以收工了。來看分解步驟:

     

    1. 我原本的磁碟組態是長這個樣子 (圖我是事後用 VM 模擬的),其中 Disk 1 (8.00GB) 就是我要換掉的...
      image 


    2. 關機裝上一顆新的硬碟 Disk 2 之後,變成這樣 (Disk 2 (16.00GB) 是新的硬碟):
      image


    3. 把 disk1 / disk2 做成磁碟鏡像之後,就變成這樣:
       image


    4. 鏡像做好,Resync 完成後,就可以中斷鏡像了。中斷之後變成這個樣子:
      image 


    5. 目前為止,看來磁碟轉移已經完成了,剩下就是想辦法把後面的空間吃進來。接下來的就用 Extend Volume 括大 D: 的大小:
      image 
      image

     

    之後就大功告成了。這方法不但簡單,而且整個過程中,全程 D:\ 都可以正常的使用。除了 (1) --> (2) 需要關機裝硬碟之外, (2) ~ (5) 全程,放在 D:\ 的 SQL DB,IIS 網站,還有 pagefile 通通都正常運作中。有了 windows server 的磁碟陣列還真是好用啊 :D

    不過,事情也是有黑暗面的... 這個方法是有幾個小缺點啦...

    1. 被迫使用 "動態磁碟" :
      dynamic disk 其實不是什麼缺點啦,不過你要是會用到其它 OS,像 linux 之類的,或是用其它的磁碟管理軟體,可能就不認得了。這是缺點之一..
    2. 只有 windows server 可以使用:
      desktop os (windows 2000 pro, xp, vista, win7) 都只支援部份的磁碟管理功能,這作法關鍵的 mirror 是不支援的... 只能乾瞪眼 @@
    3. Extend Volume 只適用 windows 2008 以上的版本:
      記得 windows 2003 只支援到 span volume, 在 disk manager 裡還是會顯示兩個 partition, 只不過只會有一個磁碟機代號,容量會是兩個加起來的而已。一樣啦,是不會有什麼大問題,看起來不大爽而已 XD

    偶爾換個口味,貼些小品文章,這邊我也不是很專業,有啥更好的作法也歡迎留 comment 啊 :D

    2010/03/06 Tips 技術隨筆 敗家 有的沒的

  3. 升級到 BlogEngine.NET 1.6.0.0 了!

    1.6.0.0 出來一陣子了,不過到過年才有空升級... 主要的原因只有一個,就是最近 spam comments 實在太多了 =_=,新版對於這類問題的處理比較像樣一點..

    其它改進還有 nested comments 跟其它一堆改進,就不一一列出來了,有興趣的人可到官方網站去看看。

    試了一下,升級後沒啥大問題,除了 CSS 有點走樣之外... 如果各位有發現什麼地方漏掉了,請再通知我 :D

    祝大家新年快樂 :D

    2010/02/19 BlogEngine.NET 有的沒的

  4. [設計案例] 清除Cache物件 #2. Create Custom CacheDependency

    上一篇廢話了這麼多,其實重點只有一個,我這次打算利用 CacheDependency 的機制,只要一聲令下,我想移除的 cache item 就會因為 CacheDependency 的關係自動失效,而不用很辛苦的拿著 cache key 一個一個移除。 我的想法是用 tags 的概念,建立起一套靠某個 tag 就能對應到一組 cache item,然後將它移除。開始之前先來想像一下 code 寫好長什麼樣子:

    透過 tags 來控制 cache items 的範例程式[copy code]
            static void Main(string[] args)
            {
                string[] urls = new string[] {
                    "http://columns.chicken-house.net/",
                    // 共 50 組網址... 略
                };
    
                foreach (string url in urls)
                {
                    DownloadData(new Uri(url));
                }
    
                Console.ReadLine();
                TaggingCacheDependency.DependencyDispose("funp.com");
                Console.ReadLine();
            }
    
            private static void Info(string key, object value, CacheItemRemovedReason reason)
            {
                    Console.WriteLine("Remove: {0}", key);
            }
    
            private static byte[] DownloadData(Uri sourceURL)
            {
                byte[] buffer = (byte[])HttpRuntime.Cache[sourceURL.ToString()];
    
                if (buffer == null)
                {
                    // 直接到指定網址下載。略...
                    buffer = null;
    
                    HttpRuntime.Cache.Add(
                        sourceURL.ToString(),
                        buffer,
                        new TaggingCacheDependency(sourceURL.Host, sourceURL.Scheme),
                        Cache.NoAbsoluteExpiration,
                        TimeSpan.FromSeconds(600),
                        CacheItemPriority.NotRemovable,
                        Info);
                }
    
                return buffer;
            }
        }
    
        這段 sample code 做的事很簡單,程式準備了 50 個網址清單,用 for-loop 一個一個下載。下載的 method: DownloadData(Uri sourceURL) 會先檢查 cache 是否已經有資料,沒有才真正下載 (不過下載的細節不是本篇要講的,所以就直接略過了...)。 而主程式的最後一行,則是想要把指定網站 ( funp.com ) 下載的所有資料,都從 cache 移除。為了方便觀看程式結果,我特地加上了 callback method, 當 cache item 被移除時, 會在畫面顯示資訊: image 由執行結果來看,果然被移出 cache 的都是來在 funp.com 的網址... 接著來看看程式碼中出現的 TaggingCacheDependecny 是怎麼實作的。相關的 code 如下:
    TaggingCacheDependency 的實作 [copy code]
        public class TaggingCacheDependency : CacheDependency
        {
            private static Dictionary<string, List<TaggingCacheDependency>> _lists = new Dictionary<string, List<TaggingCacheDependency>>();
    
            public TaggingCacheDependency(params string[] tags)
            {
                foreach (string tag in tags)
                {
                    if (_lists.ContainsKey(tag) == false)
                    {
                        _lists.Add(tag, new List<TaggingCacheDependency>());
                    }
                    _lists[tag].Add(this);
                }
                this.SetUtcLastModified(DateTime.MinValue);
                this.FinishInit();
            }
    
            public static void DependencyDispose(string tag)
            {
                if (_lists.ContainsKey(tag) == true)
                {
                    foreach (TaggingCacheDependency tcd in _lists[tag])
                    {
                        tcd.NotifyDependencyChanged(null, EventArgs.Empty);
                    }
                    _lists[tag].Clear();
                    _lists.Remove(tag);
                }
            }
        }
      30行不到... 其實程式很簡單,TaggingCacheDependency 繼承自 CacheDependency, 額外宣告一個靜態的 Dictionary<string, List<TaggingCacheDependency>> 來處理各個標簽及 TaggingCacheDependency 的關係,剩下的就沒什麼了。呼叫 DependencyDispose( ) 就可以通知 .NET Cache 機制,將相關的 cache item 移除。 用法很簡單,當你要把任何物件放進 cache 時,只要用 TaggingCacheDependency 物件來標示它的 tag:
    把物件加進 Cache, 配上 TaggingCacheDependency ...[copy code]
                    HttpRuntime.Cache.Add(
                        sourceURL.ToString(),
                        buffer,
                        new TaggingCacheDependency(sourceURL.Host, sourceURL.Scheme),
                        Cache.NoAbsoluteExpiration,
                        TimeSpan.FromSeconds(600),
                        CacheItemPriority.NotRemovable,
                        Info);
    在這個例子裡 (line 4), 直接在 TaggingCacheDependency 物件的 constructor 上直接標上 tags, 在此例是直接把網址的 hostname, scheme 兩個部份當作 tag, 未來就可以依照這兩種資訊直接讓 cache 裡的相關物件失效。 而要下令讓 Cache 內有標上某個 tag 的 cache item 失效,只要這行:  
    將標為 "funp.com" 的 cache item 設為失效的 cache item[copy code]
                TaggingCacheDependency.DependencyDispose("funp.com");
      結果就會如同上面的程式範例一樣,還留在 cache 的該網址下載資料,在這一瞬間通通都會被清掉...   用這種方式,是不是比拿到 key 再去呼叫 Cache.Remove( key ) 的方式簡單多了呢? 同時也能夠更快速的處理複雜的移除機制。其實運用 tagging 的方式只是一例,需要的話你也可以設計合適的 CacheDependency 類別。 以下是本篇文章的兩個附加參考檔案:
    Download File - URL清單
     

    2009/12/19 設計案例: 清除 Cache 物件 .NET C# MSDN Tips 技術隨筆 有的沒的 物件導向

  5. [設計案例] 清除Cache物件 #1. 問題與作法

    每次心裡有什麼好點子想寫出來時,第一關就卡在想不出個好標題... 想來想去的標題,怎麼看就是既不顯眼又不聳動... 果然是個老實的工程師性格 =_= ...  這次要講的,是 .NET HttpRuntime 裡提供的 Cache 物件的操作心得。這個東西我想不用我多作介紹,大家都用到爛掉了吧? 不過好用歸好用,有個老問題其實一直困擾著我很久了...

    " 我該怎麼手動的把某個物件從 cache 裡移除? "

    老實說,這問題蠻沒水準的... 老叫別人要翻 MSDN,我自己怎麼沒翻? 不不... 容我花點篇幅先說明一下問題。Cache物件,是個典型的 Dictionary 型態的應用 (雖然它沒有 implement interface: IDictionary… ), 透過 key 就可以拿到 cached item. 要從 cache 裡移除某個 item, 簡單的很,只要用 Remove 這個 method, 一行就搞定了:

    從 key 移除指定的 cache item[copy code]
       1:  HttpRuntime.Cache.Remove(“cache-key”);

    別小看這一行,實作起來障礙還不少。首先,你得額外去記著 cache key 的值。當你要移除的 cache item 有多個的時後,或是移除的 items 之間的關係有點複雜時,這些 code 就不怎麼漂亮了。下一個問題是:

    " 我該如何得知所有存在 Cache 內的 keys 有那些? "

    這個問題單純的多,那些把 intelligent sense 當購物網站的人 (平常不看文件,只會按下 . 然後挑個順眼 method 來用的人),可能這次就碰壁了... Cache 物件不像一般的 Dictionary 一樣,有提供 Keys 這樣的 property ... 它藏在 GetEnumerator 這 method 內,它會把所有的 keys 給巡一遍,你需要所有的 keys 的話,可以這樣用:

    跑過 cache 裡每一個 key[copy code]
       1:  foreach (string key in HttpRuntime.Cache) { 
       2:      // … 
       3:  }

    不過這樣的風險也是蠻高的,誰曉得你拿到 key 後的下一秒,這個 cache item 還在不在 cache 內?

     

     

     

    --------------------------------------------------------------

    本文正式開始! 哈哈,前面那一段只是廢話 + 碎碎唸,現在才是正題。前面想表達的只是,因為 cache 的不確定性 (資料隨時都會被 remove), 操作起來變的要格外小心, 即使它用起來像一般的 Dictionary 一樣。

    我舉個案例,來說明我應用 cache 的情況。假如我想實作一個簡單的 web browser, 透過網路下載資源是很慢的動作,每種 browser 都會有某種程度的 cache 機制。我們就拿 Cache 物件替代 IE 的 "temporary internet files” 目錄吧。這時很簡單,只要用 URL 當作 KEY,下載的 content 就當物件塞進去就好...

    不過事情沒那麼簡單。如果程式運作了一陣子,我想提供使用者手動清除 "部份" cache 的功能的話,那該怎麼辦? 我舉幾種情況:

    1. 從 cache 裡刪除所有從某個特定網站 (如: columns.chicken-house.net) 下載的資料
    2. 從 cache 裡刪除所有特定類型的資料 (如: content-type 為 image/jpeg 的圖檔)
    3. 從 cache 裡刪除所有透過特定 protocol (如: https) 下載的資料

    這樣的要求應該不算過份吧? 用前面提到的兩種作法,你會想哭吧 XD .. 用這些基礎,你大概只能選這幾種作法 (各位網友有好作法也記得提供一下):

    1. 自己另外管理所有下載過的 URL, 用盡各種適合的資料結構,讓你可以順利的挑出這些 match 的 key, 然後移除它。

      缺點: 都作這麼多,你乾脆自己重寫個 cache 機制好了... 何況時間一久,你管理的 key, 那些對應的資料搞不好老早就通通從 cache 裡清掉了...
    2. 聰明一點,用 regular expression … 從 GetEnumerator( ) 一筆一筆過濾出要移除的 URL, 然後清掉它...

      缺點: 這作法只會檢查還留在 cache 內的 URL,不過這樣的 cache 隨便也有成千上萬個,每次都要 looping 掃一次實在不怎麼好看... 有違處女座有潔癖的個性...

     

    這些方法 code 寫起來實在不怎麼漂亮,我就不寫 sample code 了,請各位自行想像一下寫起來的樣子。抱歉,如果你用的正好是上面的作法... 那請多包含... :D   這些都是 workable 的作法,但是看起來就是沒什麼設計感;程式可以動,不過就效能、簡潔、可讀性、美感來看,就是覺的不夠精緻 @@。跟朋友討論到這個問題時,我想到一個爛主意...

    " 用蠢方法,這些 cache item 先分好類,每一類去關聯一個檔案,設 CacheDependency … 要清掉時去 touch 一下這個檔案,一整組的物件就會自動被清出 cache 了…。”

    老實說,我覺的這是個既聰明又愚蠢的作法。聰明的是它很漂亮的解決我要如何移除某一群 item 的問題...,愚蠢的是這種單純程式內可以解決的事,竟然要繞到外面不必要的 file system I/O 動作... 而這通常是最慢的...

     

    --

    咳,寫太晚,實際的程式碼明天待續...

    2009/12/19 設計案例: 清除 Cache 物件 .NET ASP.NET C# MSDN Tips 技術隨筆 物件導向