<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    linugb118--java space

    Java

    #

    Mule ESB 實戰

    ESB

    1.下載
    http://www.mulesoft.org/documentation/display/MULE2INTRO/Home

    下載Mule ESB 2.2.1 Full Dist.

    2.zip 文件

    3.安裝所需要的環境
    http://www.mulesoft.org/documentation/display/MULE2INTRO/Installing+Mule
    java1.5+

    Mule Build Tool有三種方式
    Ant/Maven/Mule IDE
    下面以Maven 為例進行操作

    4.設置環境變量
    比如

    Linux/UNIX

    export JAVA_HOME=/opt/java/jdk
    export MAVEN_HOME=/opt/apache/maven-2.0.9
    export MAVEN_OPTS='-Xmx512m -XX:MaxPermSize=256m'
    export MULE_HOME=/opt/mule
    export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin:$MULE_HOME/bin


    Windows

    set JAVA_HOME=C:\Program Files\Java\jdk
    set MAVEN_HOME=C:\Apache\maven-2.0.9
    set MAVEN_OPTS=-Xmx512m -XX:MaxPermSize=256m
    set MULE_HOME=C:\Mule
    set PATH=%PATH%;%JAVA_HOME%\bin;%MAVEN_HOME%\bin;%MULE_HOME%\bin
       
    Note: If you will run Mule as a Windows service, you must create system environment variables instead of user environment variables.

    創建一個Maven repository 目錄,比如c:\.m2\repository 在windows系統上,如果windows 不讓創建.m2文件夾,請用mkdir目錄創建
    打開Maven conf 目錄,比如c:\apache-maven-2.0.9\conf;打開setting.xml 文件,查找localRepository
    將注釋去除或者添加一段
    <localRepository>c:/.m2/repository</localRepository>

    5.run Hello Word
    進入windows cmd 命令行,分別查看ant/maven是否正常

    在%MULE_HOME%\lib\user 中查看是否存在mule-example-hello.jar
    如果不存在 那么我們需要ant 或者 mvn 來build jar
    我對mvn不是很熟悉,就用ant,在MULE_HOME%\examples下可以看到hello的例子
    下面有build.xml 和pom.xml
    查看build.xml 會發現,執行ant setup就能build hello project
    在下來就要運行mule,hello文件夾下面的readme有描述

    ------------------
       Linux / Unix
        ------------
        mule -config file:conf/hello-config.xml
        mule -config file:conf/hello-http-config.xml
         or
        export MULE_LIB=./conf
        mule -config hello-config.xml
        mule -config hello-http-config.xml

        Windows
        -------
        mule.bat -config file:conf/hello-config.xml
        mule.bat -config file:conf/hello-http-config.xml
         or
        SET MULE_LIB=.\conf
        mule.bat -config hello-config.xml
        mule.bat -config hello-http-config.xml
       如果運行 http config那么通過
        http://localhost:8888/?name=Ross 可訪問
     --------------------------------------------
     
     更方便的是例子下面提供了hello.bat 直接運行該bat 并輸入1或者2為參數就能啟動hello mule
     (需要提醒的是,第一次啟動mule會有一大堆許可文件要你看,你輸入任何鍵回車后多次過后你就能發現mule啟動成功了)
     
     在conf下有hello-config.xml/hello-http-config.xml
     hello-config.xml他們在console端,通過system.in輸入message,然后在system.out輸出
     hello-http-config.xml 表示在瀏覽器中輸入http://localhost:8888/?name=Ross 那么web頁面會現在動態的name
     
    6最簡單構建Mule project
    你可以通過安裝Eclipse Mule IDE
    http://www.mulesoft.org/documentation/display/MULE2INTRO/Quick+Start
    或者Setting Up Eclipse for Use with Maven
    http://www.mulesoft.org/documentation/display/MULE2INTRO/Setting+Up+Eclipse+for+Use+with+Maven

     

     
       


     

    posted @ 2010-08-11 13:55 linugb118 閱讀(3813) | 評論 (0)編輯 收藏

    關于 java.util.concurrent 您不知道的 5 件事,第 2 部分

    并發 Collections 提供了線程安全、經過良好調優的數據結構,簡化了并發編程。然而,在一些情形下,開發人員需要更進一步,思考如何調節和/或限制線程執行。由于 java.util.concurrent 的總體目標是簡化多線程編程,您可能希望該包包含同步實用程序,而它確實包含。

    本文是 第 1 部分 的延續,將介紹幾個比核心語言原語(監視器)更高級的同步結構,但它們還未包含在 Collection 類中。一旦您了解了這些鎖和門的用途,使用它們將非常直觀。

    關于本系列

    您覺得自己懂 Java 編程?事實是,大多數開發人員都只領會到了 Java 平臺的皮毛,所學也只夠應付工作。在本系列 中,Ted Neward 深度挖掘 Java 平臺的核心功能,揭示一些鮮為人知的事實,幫助您解決最棘手的編程困難。

    1. Semaphore

    在一些企業系統中,開發人員經常需要限制未處理的特定資源請求(線程/操作)數量,事實上,限制有時候能夠提高系統的吞吐量,因為它們減少了對特定資源的爭用。盡管完全可以手動編寫限制代碼,但使用 Semaphore 類可以更輕松地完成此任務,它將幫您執行限制,如清單 1 所示:


    清單 1. 使用 Semaphore 執行限制
                import java.util.*;import java.util.concurrent.*;
                public class SemApp
                {
                public static void main(String[] args)
                {
                Runnable limitedCall = new Runnable() {
                final Random rand = new Random();
                final Semaphore available = new Semaphore(3);
                int count = 0;
                public void run()
                {
                int time = rand.nextInt(15);
                int num = count++;
                try
                {
                available.acquire();
                System.out.println("Executing " +
                "long-running action for " +
                time + " seconds... #" + num);
                Thread.sleep(time * 1000);
                System.out.println("Done with #" +
                num + "!");
                available.release();
                }
                catch (InterruptedException intEx)
                {
                intEx.printStackTrace();
                }
                }
                };
                for (int i=0; i<10; i++)
                new Thread(limitedCall).start();
                }
                }
                

    即使本例中的 10 個線程都在運行(您可以對運行 SemApp 的 Java 進程執行 jstack 來驗證),但只有 3 個線程是活躍的。在一個信號計數器釋放之前,其他 7 個線程都處于空閑狀態。(實際上,Semaphore 類支持一次獲取和釋放多個 permit,但這不適用于本場景。)

    2. CountDownLatch

    如果 Semaphore 是允許一次進入一個(這可能會勾起一些流行夜總會的保安的記憶)線程的并發性類,那么 CountDownLatch 就像是賽馬場的起跑門柵。此類持有所有空閑線程,直到滿足特定條件,這時它將會一次釋放所有這些線程。


    清單 2. CountDownLatch:讓我們去賽馬吧!
                import java.util.*;
                import java.util.concurrent.*;
                class Race
                {
                private Random rand = new Random();
                private int distance = rand.nextInt(250);
                private CountDownLatch start;
                private CountDownLatch finish;
                private List<String> horses = new ArrayList<String>();
                public Race(String... names)
                {
                this.horses.addAll(Arrays.asList(names));
                }
                public void run()
                throws InterruptedException
                {
                System.out.println("And the horses are stepping up to the gate...");
                final CountDownLatch start = new CountDownLatch(1);
                final CountDownLatch finish = new CountDownLatch(horses.size());
                final List<String> places =
                Collections.synchronizedList(new ArrayList<String>());
                for (final String h : horses)
                {
                new Thread(new Runnable() {
                public void run() {
                try
                {
                System.out.println(h +
                " stepping up to the gate...");
                start.await();
                int traveled = 0;
                while (traveled < distance)
                {
                // In a 0-2 second period of time....
                Thread.sleep(rand.nextInt(3) * 1000);
                // ... a horse travels 0-14 lengths
                traveled += rand.nextInt(15);
                System.out.println(h +
                " advanced to " + traveled + "!");
                }
                finish.countDown();
                System.out.println(h +
                " crossed the finish!");
                places.add(h);
                }
                catch (InterruptedException intEx)
                {
                System.out.println("ABORTING RACE!!!");
                intEx.printStackTrace();
                }
                }
                }).start();
                }
                System.out.println("And... they're off!");
                start.countDown();
                finish.await();
                System.out.println("And we have our winners!");
                System.out.println(places.get(0) + " took the gold...");
                System.out.println(places.get(1) + " got the silver...");
                System.out.println("and " + places.get(2) + " took home the bronze.");
                }
                }
                public class CDLApp
                {
                public static void main(String[] args)
                throws InterruptedException, java.io.IOException
                {
                System.out.println("Prepping...");
                Race r = new Race(
                "Beverly Takes a Bath",
                "RockerHorse",
                "Phineas",
                "Ferb",
                "Tin Cup",
                "I'm Faster Than a Monkey",
                "Glue Factory Reject"
                );
                System.out.println("It's a race of " + r.getDistance() + " lengths");
                System.out.println("Press Enter to run the race....");
                System.in.read();
                r.run();
                }
                }
                

    注意,在 清單 2 中,CountDownLatch 有兩個用途:首先,它同時釋放所有線程,模擬馬賽的起點,但隨后會設置一個門閂模擬馬賽的終點。這樣,“主” 線程就可以輸出結果。 為了讓馬賽有更多的輸出注釋,可以在賽場的 “轉彎處” 和 “半程” 點,比如賽馬跨過跑道的四分之一、二分之一和四分之三線時,添加 CountDownLatch。

    3. Executor

    清單 1  清單 2 中的示例都存在一個重要的缺陷,它們要求您直接創建 Thread 對象。這可以解決一些問題,因為在一些 JVM 中,創建 Thread 是一項重量型的操作,重用現有 Thread 比創建新線程要容易得多。而在另一些 JVM 中,情況正好相反:Thread 是輕量型的,可以在需要時很容易地新建一個線程。當然,如果 Murphy 擁有自己的解決辦法(他通常都會擁有),那么您無論使用哪種方法對于您最終將部署的平臺都是不對的。

    JSR-166 專家組(參見 參考資料)在一定程度上預測到了這一情形。Java 開發人員無需直接創建 Thread,他們引入了 Executor 接口,這是對創建新線程的一種抽象。如清單 3 所示,Executor 使您不必親自對 Thread 對象執行 new 就能夠創建新線程:


    清單 3. Executor
                Executor exec = getAnExecutorFromSomeplace();
                exec.execute(new Runnable() { ... });
                

    使用 Executor 的主要缺陷與我們在所有工廠中遇到的一樣:工廠必須來自某個位置。不幸的是,與 CLR 不同,JVM 沒有附帶一個標準的 VM 級線程池。

    Executor 實際上 充當著一個提供 Executor 實現實例的共同位置,但它只有 new 方法(例如用于創建新線程池);它沒有預先創建實例。所以您可以自行決定是否希望在代碼中創建和使用 Executor 實例。(或者在某些情況下,您將能夠使用所選的容器/平臺提供的實例。)

    ExecutorService 隨時可以使用

    盡管不必擔心 Thread 來自何處,但 Executor 接口缺乏 Java 開發人員可能期望的某種功能,比如結束一個用于生成結果的線程并以非阻塞方式等待結果可用。(這是桌面應用程序的一個常見需求,用戶將執行需要訪問數據庫的 UI 操作,然后如果該操作花費了很長時間,可能希望在它完成之前取消它。)

    對于此問題,JSR-166 專家創建了一個更加有用的抽象(ExecutorService 接口),它將線程啟動工廠建模為一個可集中控制的服務。例如,無需每執行一項任務就調用一次 execute()ExecutorService 可以接受一組任務并返回一個表示每項任務的未來結果的未來列表。

    4. ScheduledExecutorServices

    盡管 ExecutorService 接口非常有用,但某些任務仍需要以計劃方式執行,比如以確定的時間間隔或在特定時間執行給定的任務。這就是 ScheduledExecutorService 的應用范圍,它擴展了 ExecutorService。

    如果您的目標是創建一個每隔 5 秒跳一次的 “心跳” 命令,使用 ScheduledExecutorService 可以輕松實現,如清單 4 所示:


    清單 4. ScheduledExecutorService 模擬心跳
                import java.util.concurrent.*;
                public class Ping
                {
                public static void main(String[] args)
                {
                ScheduledExecutorService ses =
                Executors.newScheduledThreadPool(1);
                Runnable pinger = new Runnable() {
                public void run() {
                System.out.println("PING!");
                }
                };
                ses.scheduleAtFixedRate(pinger, 5, 5, TimeUnit.SECONDS);
                }
                }

    這項功能怎么樣?不用過于擔心線程,不用過于擔心用戶希望取消心跳時會發生什么,也不用明確地將線程標記為前臺或后臺;只需將所有的計劃細節留給 ScheduledExecutorService

    順便說一下,如果用戶希望取消心跳,scheduleAtFixedRate 調用將返回一個 ScheduledFuture 實例,它不僅封裝了結果(如果有),還擁有一個 cancel 方法來關閉計劃的操作。

    5. Timeout 方法

    為阻塞操作設置一個具體的超時值(以避免死鎖)的能力是 java.util.concurrent 庫相比起早期并發特性的一大進步,比如監控鎖定。

    這些方法幾乎總是包含一個 int/TimeUnit 對,指示這些方法應該等待多長時間才釋放控制權并將其返回給程序。它需要開發人員執行更多工作 — 如果沒有獲取鎖,您將如何重新獲??? — 但結果幾乎總是正確的:更少的死鎖和更加適合生產的代碼。(關于編寫生產就緒代碼的更多信息,請參見 參考資料 中 Michael Nygard 編寫的 Release It!。)

    posted @ 2010-07-23 15:25 linugb118 閱讀(203) | 評論 (0)編輯 收藏

    關于 java.util.concurrent 您不知道的 5 件事

    Concurrent Collections 是 Java™ 5 的巨大附加產品,但是在關于注釋和泛型的爭執中很多 Java 開發人員忽視了它們。此外(或者更老實地說),許多開發人員避免使用這個數據包,因為他們認為它一定很復雜,就像它所要解決的問題一樣。

    事實上,java.util.concurrent 包含許多類,能夠有效解決普通的并發問題,無需復雜工序。閱讀本文,了解 java.util.concurrent 類,比如 CopyOnWriteArrayList  BlockingQueue 如何幫助您解決多線程編程的棘手問題。

    1. TimeUnit

    盡管本質上 不是 Collections 類,但 java.util.concurrent.TimeUnit 枚舉讓代碼更易讀懂。使用 TimeUnit 將使用您的方法或 API 的開發人員從毫秒的 “暴政” 中解放出來。

    TimeUnit 包括所有時間單位,從 MILLISECONDS  MICROSECONDS  DAYS  HOURS,這就意味著它能夠處理一個開發人員所需的幾乎所有的時間范圍類型。同時,因為在列舉上聲明了轉換方法,在時間加快時,將 HOURS 轉換回 MILLISECONDS 甚至變得更容易。

    2. CopyOnWriteArrayList

    創建數組的全新副本是過于昂貴的操作,無論是從時間上,還是從內存開銷上,因此在通常使用中很少考慮;開發人員往往求助于使用同步的 ArrayList。然而,這也是一個成本較高的選擇,因為每當您跨集合內容進行迭代時,您就不得不同步所有操作,包括讀和寫,以此保證一致性。

    這又讓成本結構回到這樣一個場景:需多讀者都在讀取 ArrayList,但是幾乎沒人會去修改它。

    CopyOnWriteArrayList 是個巧妙的小寶貝,能解決這一問題。它的 Javadoc 將 CopyOnWriteArrayList 定義為一個 “ArrayList 的線程安全變體,在這個變體中所有易變操作(添加,設置等)可以通過復制全新的數組來實現”。

    集合從內部將它的內容復制到一個沒有修改的新數組,這樣讀者訪問數組內容時就不會產生同步成本(因為他們從來不是在易變數據上操作)。

    本質上講,CopyOnWriteArrayList 很適合處理 ArrayList 經常讓我們失敗的這種場景:讀取頻繁,但很少有寫操作的集合,例如 JavaBean 事件的 Listeners。

    3. BlockingQueue

    BlockingQueue 接口表示它是一個 Queue,意思是它的項以先入先出(FIFO)順序存儲。在特定順序插入的項以相同的順序檢索 — 但是需要附加保證,從空隊列檢索一個項的任何嘗試都會阻塞調用線程,直到這個項準備好被檢索。同理,想要將一個項插入到滿隊列的嘗試也會導致阻塞調用線程,直到隊列的存儲空間可用。

    BlockingQueue 干凈利落地解決了如何將一個線程收集的項“傳遞”給另一線程用于處理的問題,無需考慮同步問題。Java Tutorial 的 Guarded Blocks 試用版就是一個很好的例子。它構建一個單插槽綁定的緩存,當新的項可用,而且插槽也準備好接受新的項時,使用手動同步和 wait()/notifyAll() 在線程之間發信。(詳見 Guarded Blocks 實現。)

    盡管 Guarded Blocks 教程中的代碼有效,但是它耗時久,混亂,而且也并非完全直觀。退回到 Java 平臺較早的時候,沒錯,Java 開發人員不得不糾纏于這種代碼;但現在是 2010 年 — 情況難道沒有改善?

    清單 1 顯示了 Guarded Blocks 代碼的重寫版,其中我使用了一個 ArrayBlockingQueue,而不是手寫的 Drop


    清單 1. BlockingQueue
                import java.util.*;
                import java.util.concurrent.*;
                class Producer
                implements Runnable
                {
                private BlockingQueue<String> drop;
                List<String> messages = Arrays.asList(
                "Mares eat oats",
                "Does eat oats",
                "Little lambs eat ivy",
                "Wouldn't you eat ivy too?");
                public Producer(BlockingQueue<String> d) { this.drop = d; }
                public void run()
                {
                try
                {
                for (String s : messages)
                drop.put(s);
                drop.put("DONE");
                }
                catch (InterruptedException intEx)
                {
                System.out.println("Interrupted! " +
                "Last one out, turn out the lights!");
                }
                }
                }
                class Consumer
                implements Runnable
                {
                private BlockingQueue<String> drop;
                public Consumer(BlockingQueue<String> d) { this.drop = d; }
                public void run()
                {
                try
                {
                String msg = null;
                while (!((msg = drop.take()).equals("DONE")))
                System.out.println(msg);
                }
                catch (InterruptedException intEx)
                {
                System.out.println("Interrupted! " +
                "Last one out, turn out the lights!");
                }
                }
                }
                public class ABQApp
                {
                public static void main(String[] args)
                {
                BlockingQueue<String> drop = new ArrayBlockingQueue(1, true);
                (new Thread(new Producer(drop))).start();
                (new Thread(new Consumer(drop))).start();
                }
                }

    ArrayBlockingQueue 還體現了“公平” — 意思是它為讀取器和編寫器提供線程先入先出訪問。這種替代方法是一個更有效,但又冒窮盡部分線程風險的政策。(即,允許一些讀取器在其他讀取器鎖定時運行效率更高,但是您可能會有讀取器線程的流持續不斷的風險,導致編寫器無法進行工作。)

    注意 Bug!

    順便說一句,如果您注意到 Guarded Blocks 包含一個重大 bug,那么您是對的 — 如果開發人員在 main() 中的Drop 實例上同步,會出現什么情況呢?

    BlockingQueue 還支持接收時間參數的方法,時間參數表明線程在返回信號故障以插入或者檢索有關項之前需要阻塞的時間。這么做會避免非綁定的等待,這對一個生產系統是致命的,因為一個非綁定的等待會很容易導致需要重啟的系統掛起。

    4. ConcurrentMap

    Map 有一個微妙的并發 bug,這個 bug 將許多不知情的 Java 開發人員引入歧途。ConcurrentMap 是最容易的解決方案。

    當一個 Map 被從多個線程訪問時,通常使用 containsKey() 或者 get() 來查看給定鍵是否在存儲鍵/值對之前出現。但是即使有一個同步的 Map,線程還是可以在這個過程中潛入,然后奪取對 Map 的控制權。問題是,在對 put() 的調用中,鎖在 get() 開始時獲取,然后在可以再次獲取鎖之前釋放。它的結果是個競爭條件:這是兩個線程之間的競爭,結果也會因誰先運行而不同。

    如果兩個線程幾乎同時調用一個方法,兩者都會進行測試,調用 put,在處理中丟失第一線程的值。幸運的是,ConcurrentMap 接口支持許多附加方法,它們設計用于在一個鎖下進行兩個任務:putIfAbsent(),例如,首先進行測試,然后僅當鍵沒有存儲在 Map 中時進行 put。

    5. SynchronousQueues

    根據 Javadoc,SynchronousQueue 是個有趣的東西:

    這是一個阻塞隊列,其中,每個插入操作必須等待另一個線程的對應移除操作,反之亦然。一個同步隊列不具有任何內部容量,甚至不具有 1 的容量。

    本質上講,SynchronousQueue 是之前提過的 BlockingQueue 的又一實現。它給我們提供了在線程之間交換單一元素的極輕量級方法,使用 ArrayBlockingQueue 使用的阻塞語義。在清單 2 中,我重寫了 清單 1 的代碼,使用 SynchronousQueue 替代ArrayBlockingQueue


    清單 2. SynchronousQueue
                import java.util.*;
                import java.util.concurrent.*;
                class Producer
                implements Runnable
                {
                private BlockingQueue<String> drop;
                List<String> messages = Arrays.asList(
                "Mares eat oats",
                "Does eat oats",
                "Little lambs eat ivy",
                "Wouldn't you eat ivy too?");
                public Producer(BlockingQueue<String> d) { this.drop = d; }
                public void run()
                {
                try
                {
                for (String s : messages)
                drop.put(s);
                drop.put("DONE");
                }
                catch (InterruptedException intEx)
                {
                System.out.println("Interrupted! " +
                "Last one out, turn out the lights!");
                }
                }
                }
                class Consumer
                implements Runnable
                {
                private BlockingQueue<String> drop;
                public Consumer(BlockingQueue<String> d) { this.drop = d; }
                public void run()
                {
                try
                {
                String msg = null;
                while (!((msg = drop.take()).equals("DONE")))
                System.out.println(msg);
                }
                catch (InterruptedException intEx)
                {
                System.out.println("Interrupted! " +
                "Last one out, turn out the lights!");
                }
                }
                }
                public class SynQApp
                {
                public static void main(String[] args)
                {
                BlockingQueue<String> drop = new SynchronousQueue<String>();
                (new Thread(new Producer(drop))).start();
                (new Thread(new Consumer(drop))).start();
                }
                }

    實現代碼看起來幾乎相同,但是應用程序有額外獲益:SynchronousQueue 允許在隊列進行一個插入,只要有一個線程等著使用它。

    在實踐中,SynchronousQueue 類似于 Ada 和 CSP 等語言中可用的 “會合通道”。這些通道有時在其他環境中也稱為 “連接”,這樣的環境包括 .NET (見 參考資料)。

    結束語

    當 Java 運行時知識庫提供便利、預置的并發性時,為什么還要苦苦掙扎,試圖將并發性導入到您的 Collections 類?本系列的下一篇文章將會進一步探討 java.util.concurrent 名稱空間的內容。

    posted @ 2010-07-23 15:23 linugb118 閱讀(231) | 評論 (0)編輯 收藏

    Java中finalize

    Java中finalize()
    java這個finalize內置方法,估計很多人不會去用途,如果理解這個方法的用法和含義就能做一些可能我們一起認為不能做的事情。
    在JAVA中有一種垃圾收集器的機制,當它運行時(通常在系統內存低到一定限度時自動運行),會回收不再使用的對象所占用的內存,所以,在JAVA程序中,我們通常只考慮創建對象,而從不關心對象的清除。Finalize()是JAVA為類提供的一種特殊方法。垃圾收集器的工作過程大致是這樣的:一旦垃圾收集器準備好釋放無用對象占用的存儲空間,它首先調用那些對象的finalize()方法,然后才真正回收對象的內存。通過使用finalize(),就可以在垃圾收集器運行期間進行一些特殊的工作。

    你們也就是說,當gc事件啟動時候,他是調用對象的finalize(),來實現真正的回收,那么首先這些對象是沒有用的,最簡單的使用,我可以在finalize()中添加
    system.out.print 來跟蹤系統的回收了那些對象,可以深層次的了解系統對象的使用情況,比如那些對象回收最頻繁等等。

    另外我在網上也看到有人這么用finalize(). 主要是統計在線人。這個網上也有很多,本人自己也做過,但是在logout的時候,有三種情況
    1.點擊程序的logout,這個我們可以監聽到。2.去別的網站 3.關閉瀏覽器。(當然現在第二,第三有些網站也能通過script捕獲到,這里我們不談)
    我們假定傳統的,session在服務端還是存在的,一般是經過服務器端timeout,自動將這個session的對象失效,那么我們在這些對象調用finalize()做一些
    統計就能知道那些人已經離線。
    具體這個例子
    http://www.qqread.com/java/w712250600.html

    這只是一個例子

    我想說的是,我們如果理解了finalize的含義和用途,就能在很多地方用好它,未嘗不是一種新方式。

    posted @ 2010-07-16 17:18 linugb118 閱讀(1827) | 評論 (1)編輯 收藏

    log4j java版如何將日志寫入數據庫

     

    其實如果是直接通過jdbc去連接數據庫,那么下面的鏈接的

    http://www.dankomannhaupt.de/projects/index.html
    的jdbcappender.zip 已經能很方便的實現這個功能,

    但是在現實情況,特別是大型應用,基本都是通過datasource來獲取
    connection,而這個zip中已經明確說了不支持 DataSource,那么我們怎么辦呢?

    我是這樣解決的,對于j2ee的應用本身不管你用spring+hibernate還是c3p0 來獲取
    Datasource,最終他還是得到jdbc中的connection來進行數據庫操作,而jdbcappender
    也是通過connection來操作數據庫,那么思路就是如果通過在你框架中獲取jdbc的connection,
    然后將他set到jdbcapplender中就可以了。

    確定這種思路后,我就需要了解jdbcappender.zip中的代碼,看如果將connection 放入jdbcappender中

    首先 我們看一下如果正常通過jdbc配置,這個jdbcAppender是怎么集成到log4j中的
    下面是jdbcAppender的用法

    public class Log4JTest {
     // Create a category instance for this class
     static Logger logger = Logger.getLogger(Log4JTest.class);

     public static void main(String[] args) {
     
      
      JDBCAppender ja = new JDBCAppender();

      // Set options with method setOption()
      ja.setConnector("org.apache.log4j.jdbcplus.examples.OracleConnectionHandler");
      ja.setUrl("jdbc:oracle:thin:@..");
      ja.setUsername("mex_pr_dev65");
      ja.setPassword("mex_pr_dev65");

      ja.setSqlhandler("org.apache.log4j.jdbcplus.examples.SqlHandler");

      // Add the appender to a category
      
      logger.addAppender(ja);
      logger.debug("debug");
     
      
            }
     }
    }

    上面的類基本分為這么幾個部分
    1.和一般log4j用法一樣, 獲取logger instance
    2.new 一個JDBCAppender
    3.為jdbc設置兩部分參數,一個是jdbc 建立連接的參數,如url等 另一部分是具體insert sql的handler(你可以用handler來體現insert 語句也可以將sql寫入log4j的配置文件,我這里就用handler處理insert語句)
    4.將logger instance中添加剛才設置的JDBCAppender
    5.完成

    從上面的例子來看好像不能set connection,那么后來查看JDBCAppender的源碼發現
    JDBCAppender有setConnectionHandler(interface JDBCConnectionHandler())方法;
    可以通過這個方法能將connection放入jdbcappender中,但必須實現接口DBCConnectionHandler

    接口JDBCConnectionHandler 如下
    public interface JDBCConnectionHandler {

        /**
         * Get a connection
         *
         * @return The Connection value
         * @exception Exception
         *                Description of Exception
         */
        Connection getConnection() throws Exception;

        /**
         * Get a defined connection
         *
         * @param _url
         *            Description of Parameter
         * @param _username
         *            Description of Parameter
         * @param _password
         *            Description of Parameter
         * @return The Connection value
         * @exception Exception
         *                Description of Exception
         */
        Connection getConnection(String _url, String _username, String _password) throws Exception;
    }

    這個時候你發現 我們找了半天Connection的地方原來在這里

    那么我只要實現這個接口的getConnection() 方法就能將connection放入JDBCAppender中,

    現在我們構建一個Handler,我這里用的框架只是Hibernate3

    我需要從hibernate3中獲取connecion就可以了

    public class Cas2HibernateConnectionHandler implements JDBCConnectionHandler {

       //hibernate 的session Factory
       SessionFactory sf = HibernateConnection.getInstance();
     
     public Connection getConnection(){
      Connection con =null;
      try {
       con = sf.openSession().connection();
      
      } catch (Exception e) {
       e.printStackTrace();
      }
      
      return con;
     }

     public Connection getConnection(String arg0, String arg1, String arg2)
       throws Exception {
      // TODO Auto-generated method stub
      return null;
     }

    }

    這就是我的handler,為了讓代碼更清楚,我把我的HibernateConnection也貼出來,這是常見的Sington模式獲取hibernate的SessionFactory
    public class HibernateConnection {

     private static SessionFactory sessionFactoryInstance = null;

     private HibernateConnection() {
     }
     
     //單元測試用的
     synchronized public static SessionFactory getInstance() {
      
       if (sessionFactoryInstance == null) {
        Configuration config;
        try {    
         config = new Configuration().configure(new File("E:\\cas2\\config\\hibernate\\hibernate.cfg.xml"));
         sessionFactoryInstance = config.buildSessionFactory();
        } catch (HibernateException e) {
         e.printStackTrace();
        }
       }

      return sessionFactoryInstance;
     }
    }

    說到這里我順便說一下,我這邊框架用的是hibernate3,還有很多數據庫相關的框架比如hibernate+spring或者c3p0等等,這些都無所謂
    只要找到相應如果獲得jdbc connection的方法,同時構建自己的ConnectionHandler并實現里面的getConnection()就可以了。

    到這個時候數據庫連接已經能獲取了,接下來就需要寫具體的insert語句,這里有兩種方法,一種就是直接在log4j的配置文件中寫
    這個配置文件可以xml,也可以properties,這個網站也都有具體描述怎么做,這么不說了;
    另外一種就是ja.setSqlhandler("org.apache.log4j.jdbcplus.examples.SqlHandler");
    自己實現一個sqlHandler 后set到JDBCAppender中就可以了

    新構建的sqlHander需要實現接口
    public interface JDBCSqlHandler {

     /**
      * Get a sql-statement.
      * Return null or empty string if nothing should be logged.
      *
      * @return The SQL statement. Null if there is nothing to log.
      * @exception Exception
      *                Any Exception
      */
     String getStatement(LoggingEvent event) throws Exception;
    }

    他里面只有一個方法getStatement,里面寫入具體insert 語句就可以了

    public class CasLoginHandler implements JDBCSqlHandler {

     private final int MAX_LENGTH_MESSAGE = 3000;

        public String getStatement(LoggingEvent event) throws Exception {
            // try { throw new Throwable(); } catch (Throwable th) {
            // th.printStackTrace(); }
            LocationInfo locinfo = event.getLocationInformation();
            ThrowableInformation throwableinfo = event.getThrowableInformation();
            StringBuffer throwableStringBuffer = new StringBuffer();
            String locinfoString = "'', '', '', ''";

            if (locinfo != null) {
                locinfoString = "'" + locinfo.getClassName() + "', '" + locinfo.getMethodName()
                        + "', '" + locinfo.getFileName() + "', '" + locinfo.getLineNumber() + "'";
            }
            if (throwableinfo != null) {
                String[] lines = throwableinfo.getThrowableStrRep();
                for (int index = 0; index < lines.length; index++) {
                    throwableStringBuffer = (StringBuffer) throwableStringBuffer.append(lines[index]
                            + "\r\n");
                }
            }

            StringBuffer sb = new StringBuffer();
            sb.append("Insert into UM_SYS_LOG (ID, LOG_DATE, LOG_LEVEL, LOGGER, OPERATOR, APPLICATION_NAME, MESSAGE_KEY, LOG_MESSAGE)Values(");
            sb.append("SEQ_UM_SYS_LOG.nextval,");
            sb.append("TO_DATE('");
            sb.append(DateFormatUtil.formDateToyyyyMMdd24(new Date(event.timeStamp)));
            sb.append("','YYYY-DD-MM HH24:mi:SS')");
            sb.append(",'");
            sb.append(event.getLevel().toString());
            sb.append("','");
            sb.append(event.getLoggerName());
            sb.append("','bgao','CAS2','login sucess','user bgao loginSuccess')");

            return sb.toString();
        }

    }

    這是我構建的CasLoginHandler。
    然后將該Handler 設置到JDBCAppender中就行了
    ja.setSqlhandler("com.bgao.log.CasLogHandler");


    這樣整個將log日志寫入數據庫就完成了。當然下面還有問題可以供思考,一般log.debug("msg");這種寫法所有的日志信息都是以一條msg輸出
    ,比如"張三在127.0.0.1機器查詢表×××" ;如何簡單得將一句話insert到不同字段,這個有很多方式,可以思考一下哪種更方便,更簡潔。

     

     

     

     

    posted @ 2010-07-09 15:25 linugb118 閱讀(2865) | 評論 (1)編輯 收藏

    memcached window版服務器端安裝并測試

    我最近研究這個memcache 發現這個東東,版本以及名稱很多,有點混亂,這兩天研究下來我是這么梳理的,不知道對不對,和大家一起分享
    Memcached 是分布式cache,他有服務端和client端,核心版本是在Linux上運行
    官方網站為 http://memcached.org/ 對應的wiki在google上
    http://code.google.com/p/memcached/ 其實現在memcached的相關文檔和代碼都在google.code上了
    對應Linux 上版本的維護的挺好,但是對于windows版本的就很糟糕了,可能因為大型應用大多是Unix或其變種

    我在google上搜索了半天找到下面基本版本
    windows的就有
    http://jehiah.cz/projects/memcached-win32/
    http://www.splinedancer.com/memcached-win32/ 基于上面的win32
    win-1.2.6版本可以在
    http://code.jellycan.com/memcached/ 找到

    雖然這三個地方都是memcached for windows 而且還是不同人寫的,但是他們感覺好像都有版本約定,比較有序,
    比如http://jehiah.cz/projects/memcached-win32/ 好像是1.1--1.2.1
    http://www.splinedancer.com/memcached-win32/ 本身網站上就寫了
    This is a port of memcached to the win32 architecture by Kenneth Dalgleish, based on Kronuz's 1.2.1 port
    是基于上面的1.2.1寫的 他有的版本是1.2.4
    而http://code.jellycan.com/memcached/ 版本是1.2.5 1.2.6
    這種有序是我猜想的,也有可能是他們都是根據核心 memcached 進行編譯為win32版本,而win32的版本根據核心memcached版本來定義的。
    這些只是猜想,反正結論是windows 的memcached server版本比較分散沒有主要維護,但是他們也有各種的版本歷史。如有知道這個歷史內幕的
    請反饋,謝謝。

    我剛剛在google wiki上發現了windows 一起其他os的memcached鏈接
    http://code.google.com/p/memcached/wiki/Start 【Server ports to other platforms--->windows】
    現在是1.4版本了,其實也是鏈接到其他website
    http://labs.northscale.com/memcached-packages/ (又多了一個出處)

    好了,到現在我們不再猜測他的歷史問題,雖然windows 版本很多但是他們的安裝步驟都是一樣的
    我現在從http://labs.northscale.com/memcached-packages/ 下載的是最新的版本memcached-win32-1.4.4-54-g136cb6e.zip

    一,安裝memcached for windows
    1.解壓memcached-win32-1.4.4-54-g136cb6e.zip
    2.將里面的文件放入 E:\memcached\memcached_win32
    3.直接到目錄E:\memcached\memcached_win32 下執行命令 memcached.exe -d install 安裝服務
    4.這個時候去控制面板--》管理工具--》服務 中就能看到一個memcached 的服務
    5.如果卸載服務 那就memcached.exe -d uninstall,除此 還有 start restart命令,具體你可以通過memcached.exe -h 查看幫助

    目前只是可以看到是否安裝了服務,接下來我們應該去測試這個memcached server是否成功
    測試這個memcached 有很多方式,
    本身memcahed 有很多client端http://code.google.com/p/memcached/wiki/Clients
    有C++,java,.net,php 等等
    他們的任何一個client都可以用來測試,我們這里就用php
    首先我們要搭建php 環境
    二,安裝apache和php(這些網上都有介紹)
    1.下載apache2 http://httpd.apache.org/download.cgi
    2.安裝 apache2
    3.下載php5
    http://windows.php.net/download/ 注意要下VC6,VC6支持apache
    而VC9不支持apache 是支持IIS
    4.我們下載zip的這個包(比較綠色)
    5.解壓zip包 主目錄為E:\php5,將“php.ini-recommended”文件備份并更名為“php.ini”。
    6.查看php.ini
      查找“extension_dir”字段,賦值為php解壓路徑中的ext目錄下,如"E:\php5\ext"
      查找 cgi.force_redirect 字串.默認值為1.將其修改為0.并取消前面的;號
    7.分別查找擴展,將其之前的;去掉。
         extension=php_mbstring.dll(寬字符,用于支持PhpMyAdmin,避免出現字符顯示問題)
         extension=php_mcrypt.dll(用于支持PhpMyAdmin)
         這些dll在E:\php5\ext可以找到,這里我們為了連接memcached server 我們需要
         加入一行 ‘extension=php_memcache.dll’一般ext沒有該dll
       請在http://downloads.php.net/pierre/ 中下載相應的memcache.dll 放入到ext文件夾中
       我用的是 php_memcache-5.2-Win32-vc6-x86-20090408.zip
       反正就這幾個memcache dll你都試試(php的這種方式真是不好,應該還有其他方式,這里沒有研究)

    8.配置Apache以支持php5:
         打開apache安裝目錄下的“conf”文件夾,apache的配置主要依靠httpd.conf,用編譯工具打開該文件,修改其中的某些字段:
         (1)Listen 字段, 其后默認值為80,你可以修改該端口值以改變apache服務的端口(不至于和tomcat等工具的端口發生沖突)
         (2)DocumentRoot 這是你自己網頁文件的放置目錄,默認為apache安裝目錄下的“htdoc”文件夾,也可以改為本機上的其他目錄,采用絕對路徑。
               我使用的是:DocumentRoot "D:/phpwork/"(新建的工作目錄)   
    9.設置起始頁:
         這個地方可以照抄我的配置,也可以自己增加需要的起始頁文件名。注意文件名之間用空格隔開,而不是用逗號
         <IfModule dir_module>
          DirectoryIndex index.php index.html default.php default.html index.htm
         </IfModule>
    9.配置php模塊:在#LoadModule(有一排的代碼) 后加上兩句話(此處為我的安裝目錄,可根據自己的安裝情況進行適當的**)
         PHPIniDir "E:/php5/"
         LoadModule php5_module "E:/php5/php5apache2_2.dll"  [這個php5apache2_2.dll 有下載的php5.3版本中就沒有,后來下載了5.2,第一次配php就遇到這種事情,這種模式真的很不好]
    10. 保存httpd.conf文件,重啟Apache 如果成功啟動,在phpwork下新建一個HelloWorld.php文件
             
              <?php
                   echo "HelloWorld!<br>";
                   phpinfo();
              ?>
      在瀏覽器中輸入http://localhost/:你自己設置的端口號/HelloWorld.php.哈哈~~~至此將輸出HelloWorld以及php配置環境變量信息,這就成功了。
     11.測試memcached,在phpwork下新建一個memcacheTest.php文件
     <?php
    $mem = new Memcache;
    $mem->connect("127.0.0.1", 11211);
    $mem->set("key", 'This is a test!', 0, 60);
    $val = $mem->get('key');
    echo $val;
    ?>
    在瀏覽器中輸入http://localhost/:你自己設置的端口號/memcacheTest.php 如果看見This is a test!,那就表示成功了。

     

     

    posted @ 2010-05-13 16:22 linugb118 閱讀(5776) | 評論 (0)編輯 收藏

    【轉】豆瓣架構

     各位觀眾朋友大家好,這里是InfoQ中文站的賴翥翔,現在在首屆QCon北京大會的現場,坐在我旁邊的是來自豆瓣網的洪強寧。強寧你好,向大家介紹一下自己以及自己和豆瓣的聯系。 
    我是大概在06年的3月份加入豆瓣的。當時應該是豆瓣的02號程序員。01號是阿北?,F在是任豆瓣的首席架構師。負責豆瓣技術開發的相關工作。
     我記得在之前社區中有對豆瓣高并發能力的討論,豆瓣現在的用戶數量以及訪問量如何?用了多長時間達到了現在的水平? 
    現在的話,我剛才沒有上網,不知道現在是不是已經達到了300萬用戶,如果還沒有達到的話,馬上就會到了,可能是今天,可能是明天。300萬是指我們的注冊用戶,另外還有千萬級的非注冊用戶。訪問量的話,現在應該是兩千萬每天。
     如果能達到這樣的訪問量,確實說明豆瓣高并發的能力是相當強,我想請您從技術這個角度介紹一下豆瓣網的架構。 
    這個話題比較大一點,我剛才在演講的時候,已經表述這方面的問題了。可以這么說,最簡單的方法來說,豆瓣網可分割成兩大塊:一塊是前端的Web,也就是用戶在瀏覽器訪問的時候會觸發一系列的操作,從數據庫拿出數據,渲染成HTML頁面反饋給用戶,這是前端;另外一塊是后端,在豆瓣有一個很強的數據挖掘團隊,每天把用戶產生的數據進行分析,進行組合,然后產生出用戶推薦,然后放在數據庫里面,前端會實時的抓取這些數據顯示給用戶。
     如果是這樣子,要是讓你重新設計的話,你會覺得有必要改進里面哪些部分嗎? 
    豆瓣(架構)設計現在在WEB這一端主要是用這么幾種技術:前端是nginx和lighttpd,中間是Quixote的Web框架,后面是MySQL以及我們自己開發的DoubanDB。這些除了Quixote都是一些比較流行的、尖端的技術。Quixote稍微老一點,如果要重新設計的話,可能會在這方面做一些考慮。比如Python社區中的Django、Pylons等等都是可以考慮的,那么在豆瓣的內部的話,我們一般是用web.py,很輕量的一個Web框架來做,也是非常不錯的選擇,它可能需要自己做的事情多一點。但是,也不太可能完全重新設計了。
     那如果要緩解高并發所帶來的壓力,Cache的利用肯定是一個非常有效的途徑。那么豆瓣的緩存命中率一般是多大?這方面的策略是怎樣? 
    Memcache命中率一般都在97%左右,應該還算是比較高的。策略其實是比較簡單的,如果每次要去執行一個比較耗時耗資源的操作,比如說去數據庫查詢的話,就會以Python的Object形式存放在Memcache里面,下次再拿這個數據的時候就直接從Cache中拿就行了。這邊選擇什么樣的東西,盡量有一個Guideline,必須是要耗時的,耗資源的,而且是重復使用的。比如它是耗資源的,但是只用一次,Cache也沒有意義。差不多用這種方法保證Cache的東西都是真正有效的,也提高了命中率。
     要提高承受高壓力的流量,另外一個有效的措施是對數據庫來進行分區分片,在這方面豆瓣是怎么做的? 
    豆瓣現在還沒有達到數據庫分片的程度。我們現在最常見的手段是,按照功能分區。我們會把數據表分成幾個獨立的庫,現在是一共有4個庫。每個表都是庫的一個部分,每個庫會有主副兩個。通過這種方式來減輕數據庫的壓力,當然這個是現在的方案,再往后的話,表的行數會增長,到達一定的程度后,還要進行水平分割,這是肯定的。然后我們現在的技術方面,在操作數據庫之前,首先獲取數據庫的游標,有一個方法,這個方法會干所有的事情,我們以后做的時候會從這個方法中進行判斷該從哪取東西。這個架構已經在了,只是現在還沒有做這一步而已。
     數據庫這邊主要采用什么解決方案呢? 
    在數據庫這邊,我們主要用的是MySQL。MySQL有一個問題,大文本字段會影響它的性能。如果數據量過大的話,它會擠占索引的內存。那么現在一個行之有效的方法是,我們另外建立一套可伸縮的Key-Value數據庫,叫做DoubanDB。我們把不需要索引的大文本字段,放到DoubanDB里面去。MySQL只保存需要索引的Relationship這方面的信息。這樣給MySQL數據庫降低了壓力,也就可以保證它的性能。
     比如說像保證數據的安全性,以及數據庫的吞吐量,豆瓣是怎樣的策略呢? 
    首先DoubanDB會把每個數據在三個節點進行備份,任何一個出現故障都不會影響索取數據。MySQL是通過雙Master方案,同時還會帶1到2個slave,所以說在MySQL中我們會有三到四個的備份。這點是可以放心的。
     你剛才說到MySQL的雙Master方案,這方面會不會存在什么問題?比如說同步的問題,等等? 
    在MySQL里面,雙Master方案是一個比較經典的方案,我們現在用它很大一部分是為了解決我們同步延遲的問題。在做切換的時候,會出現同步延遲的問題,但其實MySQL的同步速度還是可以的,在切換的時候,我們會忍受幾秒鐘等待同步的時間。在做腳本的切換的時候,我們會稍微等一下。
     豆瓣的數據表一般是怎么樣的規模? 
    數據表,這個不好說了,因為不同的表都是不一樣的。我們最大的表是“九點”的Entry表,“九點”的爬蟲爬過來的所有的文章,現在應該有四千萬左右的行數。然后其他的上百萬的表也有很多。還有包括收藏表也有千萬級的行數。
     在這種海量數據的情況下,對數據表的就結構變更,一定是一個比較麻煩的問題。常見的情況,比如增加一個新的索引,會導致索引好幾個小時。像豆瓣之前會存在這樣的問題,是怎么解決的呢? 
    這個問題曾經讓我們吃過苦頭,在忽視它的狀況下就去改表,然后就鎖了很長時間。后來我們意識到這個問題,如果有表的改動的話,我們會先在一個測試的庫上試驗一下它的時間長短,是不是在可接受的范圍,如果是可接受的范圍,比如說幾分鐘,就做一個定時任務,在深夜里面去執行。如果耗時是不可忍受的,就必須通過其他技術手段,我們現在的手段一般是建一個新表,這個新表從舊表同步數據,然后再寫數據的時候,也會同步,往兩邊寫,一直到兩邊完全一樣了,再把舊表刪掉,大概是這樣一個方式。
     剛才您好像提過你們設計了自己的DoubanDB,還有一個是DoubanFS,這兩者關系是怎么樣的? 
    首先是先出來的DoubanFS,我們剛開始的時候用MogileFS來解決我們可擴展圖片存儲的問題,由于MogileFS有一個重型數據庫,這成為了它的性能瓶頸。我們為了解決這個問題,開發了DoubanFS,基于哈希來尋找節點。之后,我們又發現了新的問題,數據庫中的大文本字段也會影響性能。所以,我們在DoubanFS的基礎上,換了一個底層,做了一些調整,參照Amazon的dynamo思想,搭建了DoubanDB,把文本字段放在DoubanDB里面。做完之后,又反過來用DoubanDB來實現FS,大致是這么一個過程。
     DoubanFS跟DoubanDB的實現,他們在對于內容的安全性,或者內容的冗余性… 
    都是(備份)三份。這都是可以配置的,現在的配置是3份。
     DoubanDB就是用什么機制實現的? 
    DoubanDB簡單來說是這樣子:你來一個Key,它是Key-Value數據庫,你要寫或讀的時候,通過這個Key來尋找這個值。拿一個Key對它做哈希,通過Consistent哈希方法去查找它在哪個節點上,然后往這個節點上去寫或讀。在這個節點上,順著哈希的wheel順次的找到第二、三個節點,寫的時候會保證這三個節點都寫,讀的時候是任意一個,如果其中一個讀失敗了,會自動切換到下一個。
     您剛才提DoubanDB的話,是采用的技術是? 
    DoubanDB的底層存儲用的是TokyoCabinet,是一個很輕量級、高效的Key-Value數據庫。我們在它的基礎之上,做了分布式,用這種方式來實現的。
     實際上有一些其他的方案可以解決,比如說像Berkeley DB(簡稱BDB)、CouchDB等等,你們為什么要選擇TokyoCabinet? 
    最簡單的原因是由于它足夠快,實際上BDB跟它比較類似,BDB更加強大一些。對我們而言,我們在這邊就是需要一個可靠、高效的Key-Value存儲,這兩個其實是我們都可以替換的,只要統一下接口就可以。CouchDB的話就是另外一個東西了,它是一個文檔型數據庫,它不僅僅做了一個Key-Value的工作,它還在這上面做了很多其他的事情,比如它有View的概念,可以進行query。這些TokyoCabinet是沒有的,而我們暫時也不需要這些功能。CouchDB是一個很有意思的數據庫,我們可能會在其他方面(應用),我們也在研究它。
     從我們剛才的討論中,Web前端你用了nginx又用了lighttpd。它們都是非常流行的前端,這兩種方案經常打架,豆瓣為什么把它們融合在一塊? 
    這是歷史原因。我們其實沒有刻意地去傾向某一個。這兩個都是非常優秀的Web Server,都很輕量,都很高效。最開始的時候我們用的是lighttpd,然后是因為出現過一些問題,其實不是lighttpd的問題,但當時我們懷疑可能是lighttpd有問題,就嘗試了一下nginx,覺得這個也不錯,然后這個結構就保留下來了。nginx對開發者和用戶的友好性都更好一些。我舉個例子,比如說重啟,其實在豆瓣的Web Server是經常要重啟的,我們會有一個健康檢查的腳本,定時的檢查網站是不是正常,如果覺得不正常的話,就會做一些保護措施,其中就包括重啟。lighttpd的重啟,是一個很粗暴的Kill。Nginx是一個reload的方案,會先把手頭的事情做完了再重啟。這樣會好很多,而且它會在重啟之前會幫你做一些好的事情。所以,現在我們用Nginx越來越多。Nginx的配置文件也比lighttpd寫起來更舒服一些。
     豆瓣現在有一個龐大的用戶群體,針對這樣一些海量數據做好數據挖掘,肯定不是一件容易的事情,能從技術這個角度講講挖掘的實現嗎? 
    在豆瓣專門有一個算法團隊,他們的主要工作就是數據挖掘。這邊講技術實現的話,可能就講不完了。只能講一些大概,數據挖掘是怎么和前端結合起來的,讓用戶看見的。每天用戶在豆瓣上的操作都會產生很多數據,在豆瓣上面看到的東西,收藏的東西,都會存在數據庫或是訪問日志。每天這些信息都會傳到算法團隊的機器上,然后會從這個數據中建立一個稀疏矩陣,你看過什么,干過什么。他們維護了一個很高效的稀疏矩陣運算庫,然后用它來做各種各樣的嘗試,去看是否能得到好的結果,一旦發現這個結果很好,就會把它寫到數據庫里面。然后用戶在訪問的時候,前端從數據庫中取出推薦給你的數據,然后把這些數據做一些過濾(比如你讀過的東西就不再給你展現了)、調整,最后展現給用戶。基本上是這么一個邏輯。
     從剛才你所描述的內容,可以發現豆瓣其實是一個應用非常多的,幾乎用的都是開源框架吧? 
    全部是開源的。
     我相信你們從社區的智慧以及各方面都會獲取很多東西,我不知道豆瓣對開源社區是不是也做了一些回饋? 
    是有的,我們最大的回饋形式是patch。我們用很多的開源軟件,這當中就不可避免的有各種各樣的問題,我們會嘗試通過自己的努力解決這些問題,把我們的解決方案反饋給開發者。比較典型的像libmemcached,是一個C的memcached客戶端。現在也是非常火的,基本是一個官方的C的客戶端。它其實有很多bug,我們在使用的時候發現,去修正它?,F在我們的團隊成員里面有直接就是它的開發成員。比如說像Python的Mako模板,也是用的人非常多的模板。我們也在使用,使用起來發現它的性能稍微弱一些,我們也花了精力對它進行了優化,這個優化現在也是被接受了,在Mako的后來版本發布出來了。然后豆瓣自己也有一些開源的項目,最主要的開源的項目是豆瓣API的訪問客戶端,這個是在google code上面,也有很多志愿者參與進來,幫我們一起修改。然后從另外一個方面來說,豆瓣和國內的開源社區也有緊密的聯系。豆瓣的上線通知就是發在開源組織CPUG的郵件列表里面的,豆瓣的很多成員也是CPUG的成員,會在郵件列表里面去幫助回答問題,討論問題,這也是一種回饋的方式。
     豆瓣的開發團隊是怎么樣的? 
    我們現在開發團隊這邊是11個人,有全職有兼職,還是比較放松。我們采用的是敏捷的方法,但是也不是完全的一模一樣的方式。在豆瓣內部,我們盡可能地去發揮每個人的創造力。比如,在豆瓣作息是自由的,你可以自己決定什么時候來,什么時候走。比如你想在家里面靜下心來寫code,你可以往郵件列表里面發條消息說,我今天不過來了,就可以在家里面。每天會有很多的討論,我們在豆瓣的辦公室是一個獨立的區域。在這個區域里面有白板,大家可以隨時討論。然后每周我們會有一個技術交流會議,大家輪流來發表一下自己最近在看一些什么東西,有什么心得,跟大家分享一下,這些都促進團隊的溝通與發展的,很有用的東西。
     看來豆瓣是一個相當開放、技術和興趣驅動的團隊。 
    我們希望一直保持這樣的樣子。
     那現場的觀眾有沒有什么問題?其他記者:我是接著社區那個話題問一下,豆瓣現在有了很多的積累,有很多東西都已經成形了,有沒有考慮說開放一些項目?
    我們是有這個計劃的。比如說DoubanDB,實際上我們在創立這個項目的時候,就是說這個項目我們做出來后是要開源的,到現在還沒開源,是因為這個項目還在變化之中。由于開發的時間上的限制,所以現在還和豆瓣本身的數據綁得太緊,我們而且也是在不斷地調整,現在還在調整的過程當中。找一個合適時機,我們會把它跟的豆瓣的數據剝離出來,成為一個可以獨立地去安裝、運行的應用的時候,就會把它拿出來,我想應該很快就能夠做到這點。
     非常感謝強寧接受我們的采訪,也恭喜今天在大會的演講上面取得了非常圓滿的成功。 
    謝謝。

    posted @ 2010-03-15 13:48 linugb118 閱讀(200) | 評論 (0)編輯 收藏

    架構話題

    演化架構和緊急設計: 演化架構

    敏捷架構的考慮和技術

    Neal Ford, 軟件架構師, ThoughtWorks Inc.

     

    簡介: 這一期的 演化架構和緊急設計 將會解決演化架構相關的各種主題,包括設計和架構之間的重要區別(以及如何區分兩者),您在創建企業級架構時遇到的某些問題,以及面向服務的架構中靜態類型和動態類型的區別。

    查看本系列更多內容

    發布日期: 2010 年 3 月 01 日
    級別: 中級 其他語言版本: 英文

    1 star2 stars3 stars4 stars5 stars 平均分 (共 5 個評分 )

     

    本系列的第一期 中,我推薦了軟件世界中的一些架構定義。無論如何,如果您已經閱讀過本系列,您會注意到我花費了大部分時間在設計上。我之所以這么做是基于以下幾個原因:首先,在當前緊急設計尚未被廣泛關注時,軟件世界里存在很多架構定義(良莠不齊);其次,在設計方面很多問題都有具體的、不受環境限制的解決方案。架構往往還涉及到很多組織內的物理和邏輯基礎設施,使其難以獨立談起。

    關于本系列

    系列 旨在從全新的視角來介紹經常討論但是又難以理解的軟件架構和設計概念。通過具體示例,Neal Ford 將幫助您在演化架構緊急設計 的靈活實踐中打下堅實的基礎。通過將重要的架構和設計決定推遲到最后責任時刻,您可以防止不必要的復雜度降低軟件項目的質量。

    這一期填補了敏捷構架材料缺失的空白。在此我討論的是如何分辨架構和設計,涵蓋了一些廣泛的架構考慮,然后通過討論版本控制端點,淺談敏捷的面向服務架構(SOA)。

    分辨架構和設計

    Martin Fowler 對架構的定義(來自和他的對話中)是我認為最好的:

    架構就是完成之后很難更改的東西。所以這種東西應該盡可能越少越好。

    您可以想象一下架構和設計之間的交互,如圖 1 中所示的關系:


    圖 1. 架構和設計的關系
    疊盒圖

    一個軟件系統的架構形成是所有其他部分存在的基礎,如 圖 1 中的灰盒所示。設計部分存在于架構之上,如紅盒所示。處于更基礎的地位,架構部分難以移動和替換是因為您不得不移動所有以架構為基礎的部分來適應改變。這一定義使識別設計和架構更為簡單。例如,您所使用的 Web 框架就是一個架構,因為它難以替換。盡管,在那個 Web 框架中您使用不同的設計模式來表述特定的目標,這就表示大部分的正式設計模式是設計,而不是架構的一部分。

    Fowler 所定義的架構的推論是:您應該靈活地構造架構部分,以便能夠更輕松地替換它們(如果真的需要的話)。但是如何才能確保這點呢?這里有個例子。

    許多框架都會試圖誘導您使用其自身的類,而不是 JDK 或者一個開放標準機構(例如 OASIS)提供的更普遍的類。這就是耦合的 “毒販模式”:如果您服從這些誘導,您就只能永遠受制于框架。這些框架采取的普遍方法就是,如果您使用了它們的類,某方面就會變得異常簡單。這方面的完美例子就來自于 Apache Struts Web 框架(見 參考資料)。

    在您的應用程序中包含業務規則和其他非基礎設施代碼的類是域類:它們包含著您的問題領域相關的有趣信息。Sturts 中的一個好助手類就是 ActionForm 類。如果您從 ActionForm 繼承了您的域對象, 您的應用程序就會變得更方便。您可以從參數完成自動表格填充、自動驗證(Web 和服務器層),以及其他便利。您所要做的就只是把 Struts ActionForm 類作為子集,如圖 2 所示:


    圖 2. 使用 Struts ActionForm
    將域類耦合到 Struts 的圖示

    圖 2 中,標簽為 Model 的盒子包含了您的域對象。它擴展了 Struts 的 ActionForm,使得這一結構此后難以改變。如果以后您決定 ScheduleItem 也需要在一個 Swing 應用程序中運行,那就很難辦了。您只剩下兩個難以接受的解決方案:將所有的 Struts 拖拽到 Swing 應用程序中(且不使用它)或者擺脫對 Struts 的依賴。

    較好的替代方案就是采用組合而不是繼承,如圖 3 所示:


    圖 3. 通過組合來對您的域類解耦合
    進行組合而不是繼承的圖示

    在此版本中,域類(黃色部分)包含了一個定義日程項目語義的界面。原始的 ScheduleItem 將實現這個界面,它還可以由 ScheduleItemForm 來實現,使得這兩個類的語義總是保持一致。反過來,ScheduleItemForm 擁有 ScheduleItem 域對象的一個實例,ScheduleItemForm 的所有讀值器和寫值器傳遞到封裝的 ScheduleItem 的底層讀值器和寫值器。這就允許您利用 Struts 的良好特性,同時擺脫該框架的束縛。

    經驗法則是:可以使框架對您有所了解,而您不可以對框架有所了解。只要您可以維持那種關系,您就能避免把自己的代碼耦合到基礎設施中去,這使您能夠更輕易地改變架構和設計。有時可能要多花點功夫來完成這個任務,但是您可以擁有更好的靈活性。Struts 并不是唯一向您提供這種誘惑的框架。幾乎所有的框架都包含將您限制在框架中的幫助工具。如果您在域類中導入來自某個框架或者廠商的數據包,那您以后就有得頭疼了。


    關于架構的考慮

    除了架構的定義,典型的企業設置中還出現了各種廣泛的問題。我將在這里介紹針對其中一些問題的敏捷架構解決方法。

    架構的政治

    當您被提升到架構師職位時,公司政治將是您所要遇到的眾多難題之一。因為架構師 基本上是公司中最高的技術職位,您會成為 IT 部門內發生的所有決策的發言人(和辯護人),無論好壞。事實上,您還常常要因為失敗受到責備,卻不會因為成功而贏得信任。一些新上任的架構師試圖對這些置之不理(當您在技術職位時這也許非常有效),但是在您的新職位這明顯行不通。

    請您記住在許多軟件項目中,溝通比技術更為重要。如果您曾經在某個軟件項目上失敗過,那么請您思考一下失敗的原因:是出于某個技術 原因,還是某些溝通 問題?大部分時間,失敗是因為溝通而不是技術。技術問題有其解決方案。(有時它們很難解決,但總歸有解決方案。)但社會問題就更加復雜和棘手了。Peopleware(見 參考資料)這本書中有這樣一句名言:

    總是存在人的問題。

    即使是您認為應該按部就班,直截了當的技術決策,也會有政治參雜其中,特別是您處于決定是否批準購買某企業工具的職位。(從樂觀的角度看,您可能有機會由某個工具廠家掏腰包打次異國情調的高爾夫。)請記得,作為一名架構師,您不僅需要做出重要的決策,您還必須為這些決策辯護。有時和您交談的人有他們自己的議事日程,這些內容或許在邏輯上行不通,但是在企業政治的考驗面前卻行得通。不要氣餒,您要記清楚最初之所以作出這個決策的原因。

    構建與購買

    大公司中常出現的普遍問題之一就是決定是構建還是購買:針對現在的需求,我們是應該購買 COTS(Commercial Off-the-Shelf Software)還是自己構建?要做出此決策的動機是可以理解的 — 如果公司可以找到一些完全符合自身需要的現成軟件,這樣就節約了時間和金錢。不幸的是,許多軟件廠商理解這一需求,所以編寫可以定制的打包軟件,如果軟件不能完全符合客戶的需要的話。他們意在盡力構建最通用的軟件,因為這樣能適用更多的生態系統。但是越是通用,就越需要定制。所以有時即使很多顧問在,也需要花費很多年才能完成所有的定制代碼。

    是否應該購買 COTS 的問題實際上歸結為另一個問題:業務流程是由軟件在戰略上 還是經費上 支持?如果業務流程僅僅是經費問題,購買 COTS 就合情合理。這類軟件例子包括人力資源、財務、以及其他普通的業務流程。戰略 軟件在您的業務領域給您競爭優勢,這個競爭優勢不能輕易放棄。

    避免陷阱

    請記?。翰⒉皇撬械臉I務流程都是可定制的,它們根據業務不同而千差萬別。不要輕信那些聲稱已經編寫了您要的業務流程的廠商。如果他們真的擁有這樣的流程,他們肯定也在把這些流程賣給您的競爭對手。

    圖 4 所示的流程圖用于幫助您決定是構建還是購買:


    圖 4. 決策是構建還是購買的流程圖
    構建還是購買決策流程圖

    在這個流程圖中,您要做出的第一個決策就是戰略和經費的重要區別。如果需求是戰略性的,您往往需要自己構建解決方案。如果不這么做,您就會將自己置于一個和對手公平競爭的環境中,而不是構建完全符合您現在和將來需求的軟件。打包軟件吹噓其可定制性,但還是有對定制程度的限制。如果您自己編寫,會花費較長的時間,但是您有了一個平臺,在這個平臺上您可以構建將您和對手區分開的軟件。

    流程圖中的第二個決策就是詢問數據包軟件是否能立刻起作用。在購買數據包軟件時常見的一個陷阱就是錯誤估計其適應您的業務流程所需的準確時間;大部分公司都把這個時間錯估了一個數量級。您所需的定制越多,所耗費的時間就越長。更糟糕的是,一些公司還允許改變他們的業務流程來適應軟件。這是一個錯誤,因為無論好壞,您的業務流程都應和對手的有所區別。

    這個決策樹中的第三步就是詢問數據包是否可擴展,這和定制性 剛好相反。可擴展的系統由經過良好定義的方法來擴展功能,而無需一切事先就緒。這些擴展點包括經過良好定義的 APIs、SOAP 調用等等。定制意味著您要通過 “欺騙” 來讓數據包完成您的工作。例如,如果您試圖打開一個 WAR 文件,那么您可以用一個不同的圖像(必須用 index.gif 來命名)來替換用 index.gif 命名的文件,您是進行定制而不是擴展。最終檢驗標準是您的更改是否能夠通過升級。如果是,您就擴展了數據包;如果不是,您就定制了數據包。定制不鼓勵您不斷升級數據包,因為您會意識到對新版本做出相同的改變需要付出多少努力。那么,趨勢就是不進行更新,落后于最新版四、五個版本,這將使您面臨失去對現在正在使用的老版本的支持的危險。

    是經費問題還是戰略問題因公司而異。例如,我曾為一家財務服務公司做過顧問,它的招聘過程被認為是其關鍵戰略優勢之一。他們雇傭最好、最聰明的人,花費大量的時間和精力來尋找適合的人。他們曾就購買 COTS 人力資源系統咨詢過我的意見,我建議他們不要那樣做:為什么要讓自己置身于一個和對手公平競爭的環境呢?最后,他們采納了我的建議,編寫自己的 HR 系統。編寫花費了較長的時間,但一旦完成,他們就有了一個平臺,能夠完成對其對手來說更勞動密集型的任務。招聘對許多組織來說是簡單的經費問題,但對這家公司來說卻是戰略問題。


    架構中的類型控制

    SOA 計劃中經常出現的一個更技術化(更不面向流程)的主題往往和分布式系統中的類型控制和版本控制有關。這就是這類項目中常見的陷阱之一。它之所以常見,不僅因為人們很容易遵循工具廠商鋪好的路,還因為問題需要一段時間才能凸顯出來 — 最嚴重的問題產生于您不了解在項目早期應該知道的東西。

    關于能否用動態類型語言構建 “企業” 系統的爭論已經有了定論,這個結論現在也不能給予什么啟示。然而,這一爭論意味著就端點的類型控制而言,對分布式系統有了重要的考慮。所謂端點,指的是兩個完全不同的系統之間的通信門戶。兩個相互競爭的類型控制樣式是 SOAP 和 Representational State Transfer (REST),前者通常采用諸如 Web Services Description Language (WSDL)這樣的標準來創建一個強類型,而后者適用于類型更寬松的、以文檔為中心的方法(見 參考資料)。SOAP 與 REST 的詳細優缺點不在本文的討論范圍之內;在此我主要想說的是端點層面上寬松類型的好處,這些好處可以使用任一樣式實現。

    更動態的類型控制在端點處是很重要的,因為這些端點會在以不同速度演變的系統之間形成一個已發布的集成 API。您想在那些系統之間避免嚴格耦合的特定簽名(類型和參數名),因為那樣會使通信的雙方都很脆弱、容易崩潰,削弱了您分別對兩個應用程序進行版本升級的能力。

    這里有個例子。在傳統的 SOAP 式集成中,使用的協議類型是 Remote Procedure Call (RPC),并用 WSDL 來定義兩個應用程序間通話的詳細信息,如圖 5 所示:


    圖 5. 在應用程序間使用 RPC 式調用
    RPC 式端點

    RPC 式集成使用 WSDL 來進行一個 “常規” 方法調用,并將其抽象出來發送到 SOAP。這樣,每個類都映射到 WSDL 中的一個類型,包括其所有參數的類型。這種方法將通信雙方強烈耦合到一起,因為它們都依賴 WSDL 來定義發送的內容和預期接收的內容。問題源于這種嚴格的定義。如果您需要修改其中一個應用程序來采用不同的參數或者改變現有的類型,且不能同時更改這兩個應用程序,那又該怎么辦呢?該如何對端點進行版本控制?有幾個方法是可行的,但所有這些方法都有嚴重的妥協之處。例如,您可以用新的 WSDL 定義創建另外一個端點。如果原始端點命名為 addOrder,那么您可以創建另一個端點,命名為 addOrder2。您會看到這種方法前景不妙。不久,您就會有數十個稍有不同、到處包含重復代碼的端點來處理一次性情況,因為一旦發布,就很難預測人們會怎么應用這些集成點。您也可以使用 Universal Description, Discovery, and Integration (UDDI)(或者僅僅是哈希圖)這樣的工具來欺騙端點,但那并不會有很好的效果。根本問題就是端點間的嚴格耦合,那會阻止其按自然、獨立的速度發展演變。

    一種替代方法就是把集成端點當做寬松類型對待,如圖 6 所示:


    圖 6. 在集成端點采用寬松類型控制
    以文檔為中心的集成

    通過將有趣的端點信息傳送到一個文檔內,您可以在通信雙方任意一方主要升級和次要升級過程中保持端點定義不變。您可以靈活選擇,而不是依賴 WSDL 來嚴格定義預期的內容。現在,端點總是接收一個封裝該端點所需類型的文檔。

    要解決端點的版本控制問題,端點要做的第一步就是把文檔解開,確定已經傳輸的內容,并用預期的內容與之協調。我通常聯合使用 Factory 和 Strategy 設計模式(見 參考資料)來確定是否正在獲得預期的內容,如圖 7 所示:


    圖 7. 在端點內解開內容來確定類型
    確定類型的策略

    端點的首要工作就是查看文檔的清單,確定它包含的內容。然后,它用一個庫來實例化適當的策略,將那些信息從文檔中抽出。一旦所有部分都通過驗證(必要時可用 WSDL),反序列化的對象就被傳遞,用于業務處理。

    這種方法有幾個好處。首先,擁有一個帶有兩個正交作業的機制是個壞主意,然而那正是傳統 RPC 所假設的:端點既要負責提供已發布的集成 API,又要負責驗證類型。因為其有兩個行為,所以您可能會弄混代碼,使其更難理解和維護。其次,現在這個端點可以有多個用戶,每個用戶使用稍有差異的版本。只要您有一個策略,您就能夠用相同的端點支持任何版本(包括那些更新緩慢的應用程序的老版本)。這允許您根據需要進行改變,不用擔心這會迫使企業內應用程序的其他部分和您的改變保持一致 —— 它們可以根據自己的進度改變并使用新的文檔版本。

    目前沒有任何工具或者框架允許您輕松地實現這種方法,但是一些額外的前期工作提供了之前提到過的好處。您可以使用 SOAP 或 REST 來實現這個樣式(不過在 REST 中會更容易,因為它本身就是以文檔為中心的)。通過創建一個寬松類型的生態系統,您可以使不同的開發小組按自己的節奏開展工作,從而使整個企業的應用程序使用以最小的摩擦前進。這就是演化架構的精髓:奠定一個基礎來支持盡快實施無摩擦的、不損害功能的變革。


    結束語

    架構是個龐大且復雜的軟件主題;在這部分中,我試圖涉及許多不同的方面,從政治到 SOA 中的端點版本控制的實現細節。在以后的部分中,我將不斷充實這些關于一般架構和新架構方法的想法,幫助您構建一個可發展的 SOA,以免向軟件商支付數百萬美元的高額費用。


    參考資料

    學習

    討論

    關于作者

    Neal Ford 是一家全球性 IT 咨詢公司 ThoughtWorks 的軟件架構師和 Meme Wrangler。他的工作還包括設計和開發應用程序、教材、雜志文章、課件和視頻/DVD 演示,而且他是各種技術書籍的作者或編輯,包括最近的新書 The Productive Programmer 。他主要的工作重心是設計和構建大型企業應用程序。他還是全球開發人員會議上的國際知名演說家。請訪問他的 Web 站點。

    posted @ 2010-03-15 10:46 linugb118 閱讀(206) | 評論 (0)編輯 收藏

    OO四劍客

                                          OO四劍客
       現在面向對象的開發已經基本成為程序員認定的真理,圍繞這個思想很多人做了將原來不是面向對象的東西轉換
    為面向對象的工作,比如面向對象數據庫
    (參考 『http://linugb118.blog.ccidnet.com/blog-htm-do-showone-uid-39808-type-blog-itemid-102097.html』db4o 參考我以前寫的)
    ,還比如or-mapping的出現,等等。
       本人以前也對面向對象的工具以及一些面向對象的產品進行過源碼的研究,后來在看了《Jakarta Commons Cookbook》
    后,我覺得具體可以這么歸納面向對象開發的四個要素:Predicate(斷言),Closure(終結者),Comparator(比較器),Transformer(轉換),
     我們先不解釋這四個要素的含義和用意,我們先來看看非面向對象語言的描述,如果在學校學習過面向過程的
    編程語言如PASCAL的話,大家其實知道,如果需要實現某個功能,那么在一個函數中從頭寫到尾,其實對于計算機
    本身而言,他也是從頭到尾執行的,他本身不會跳轉,只有你給if或者else 這樣的詞語告訴它,那么機器才會根據你的
    意思知道是跳轉到別的地方還是繼續執行。目前跳轉的常用模式就是if(A){do}else{} 以及循環while(A) do{},
      這里開始引入第一個要素Predicate(斷言),什么是Predicate,其實上面的A就是Predicate。在面向過程語言中。
    往往是等于,AND,OR,不等于等一些判斷,而等式鏈接的直接是變量,鏈接符號那么是預先定義的符號,比如==,&&,||, ! 等等
    那么面向對象的話,一切都是對象,首先等式鏈接的兩邊是對象,雖然對象語言中仍然有預先定義的符號,但是我們
    覺得這個不符合面向對象的原則【一切皆是對象】,于是我們要把等式符號也要轉換為對象,我們就引入接口Predicate
    在接口Predicate中
    public interface Predicate
    {
      public boolean evaluate(Object object);
    }

    只有一個方法evaluate,他返回true或者false;
    在common collections中 與等式符號對于的有
    EqualPredicate   ==
    NotPredicate     !=
    TruePredicate    true
    FalsePredicate    false
    OrPredicate      ||
    AndPredicate     &&
    等等
    而本身因為鏈接的是對象,因此和對象相關的特定Predicate有
    IdentityPredicate
    InstanceOfPredicate
    NullPredicateNullIsTruePredicate
    NotNullPredicateNullIsFalsePredicate
    UniquePredicate
    除了上面已經實現的Predicate,而Predicate本身因為是接口,所有用戶也可以自己去實現自己的Predicate
    比如
    public class LaunchPredicate implements Predicate {
    public LaunchPredicate( ) {}
    public boolean evaluate(Object object) {
          if(...)
           {return true}
            return false;
    }}
    另外我們說過Predicate本身是對象,而等式鏈接的對象當然也可以是Predicate對象本身,因此就有了無窮無盡的
    組合,也就是Composite Predicates   
    上面的AndPredicate和OrPredicate就是能鏈接Predicate對象的Predicate,除此之外還有
    AllPredicate, OnePredicate, AnyPredicate, NonePredicate 當然從原則上說其他Predicate的實現也能
    鏈接Predicate對象,但是含義不夠通用。
       前面對照面向過程語言我講的是if/while后面的斷言部分,那么我們現在看看if/while后面do的部分
    在面向過程語言中,do就是一段代碼。那么如果利用面向對象的思想來思考,那么do應該是一個對象,那這個對象
    就是Closure(終結者)

    public interface Closure
    {
      public void execute(java.lang.Object input);
    }
    在接口中,我們可以看出里面只有一個execute接口,用戶可以實現自己的Closure來實現代碼片段想做的事情。
    接下來,有個問題,多個代碼片段可能合成一個片段,那么怎么處理,Closure 同樣可以,只要引入ChainedClosure 
    就可以了,ChainedClosure的用法
    Closure[] cArray = new Closure[] { repairShielding, fuel };
    Closure preLaunch = new ChainedClosure( cArray );
    我們剛才看的只是片段里面沒有if/while 斷言,如果片段里面有if/while 斷言怎么辦,那么這個時候我們
    根據上面講的斷言引入三個特殊的Closure:IfClosure 和WhileClosure/ForClosure
    他們的用法如下
    Predicate isWinning = new Predicate( ){...}
    Closure sell = new Closure( ){...}
    Closure buy = new Closure( ){...}
    Closure stockAction = new IfClosure( isWinning, buy, sell );
    這是一個買賣股票的例子,如果isWinning為true 那么執行買入buy,否則執行賣出;

    Closure drive = new Closure( ) {...}
    Predicate hasFuel = new Predicate( ) {...}
    Closure useAllFuel = new WhileFuel( hasFuel, drive );
    執行循環數字那么就用ForClosure
    Closure driveSome = new ForClosure( 5, drive );
       到這里,我們需要回過來看看對象的斷言,對象怎么能夠相互比較的,其實很簡單,有兩種方法,一種
    方法:繼承Comparable,實現CompareTo方法 另外一種是:用外部的Comparator來比較。
    參考【我寫過一篇文件
    http://linugb118.blog.ccidnet.com/blog-htm-do-showone-uid-39808-type-blog-itemid-102094.html】
    我們這里講的是Comparator,Comparator比較靈活,獨立于對象。
    public interface Comparator
    {
      public int compare(Object o1, Object o2); 
      public boolean equals(Object obj);
    }
    Comparator接口中主要需要實現的是compare方法。你可以自己寫自己的Comparator,當然collections中
    已經有了些常用的Comparator
    如ReverseComparator 反向比較
    如果要給book反向排序,那么就這樣使用Collections.sort( books, reverseComparator );
    多個比較器同時使用就用ComparatorChain;
    如下面例子:
    ComparatorChain comparatorChain = new ComparatorChain( );
    comparatorChain.addComparator( new BeanComparator( "lastName" ) );
    comparatorChain.addComparator( new BeanComparator( "firstName" ) );
    comparatorChain.addComparator( new BeanComparator( "age" ), true );

    除此之外 還有特定的比較器NullComparator和FixedOrderComparator
    他們的用法:
    Comparator nullComparator = new NullComparator( BookComparator );
    String[] medalOrder = {"tin", "bronze", "silver", "gold", "platinum"};
    Comparator medalComparator = new FixedOrderComparator( medalOrder );----按medalOrder指定的順序排
       到現在為止,面向對象的四劍客我們已經講過三個,剩下最后一個是Transformer(轉換)
    public interface Transformer
    {
      public java.lang.Object transform(java.lang.Object input) 
    }
    其實從程序的角度,我覺得Transformer和Closure都是將input 的東西進行處理然后輸出,好像一樣的,
    沒錯,從程序角度是一樣的,但是如果從對象角度來看,他們還是有一些區別,Transformer是將一個對象
    轉換為其他對象或者一個新的對象,一般不太會改變對象只是做一些轉換,而Closure就是處理對象,會在
    上面做很多改變。
    Transformer同樣可以用ChainedTransformer來將多個Transformer鏈起來使用
    Transformer[] chainElements = new Transformer[] { multiply, increment };
    Transformer chain = new ChainedTransformer( chainElements );
    另外Transformer可以和Predicate一起形成條件開關轉換器SwitchTransform
    Predicate[] pArray = new Predicate[] { isOdd, isEven };
    Transformer[] tArray = new Transformer[] { oddTransform, evenTransform };
    Transform predicateTransform = 
    new SwitchTransform( pArray, tArray, new NOPTransformer( ) );
    這里如果isOdd就執行oddTransform,如果isEven就執行evenTransform,默認執行new NOPTransformer() 

       讀完上面的四劍客,估計你應該對面向對象一切皆為對象的理念有了更深刻的理解的。^_^
     










    posted @ 2010-02-02 10:59 linugb118 閱讀(2132) | 評論 (0)編輯 收藏

    高煥堂老師的軟件架構的講座

    上次有幸參加了高煥堂老師的軟件架構的講座:
    總結了下面幾點:
    1.做框架的思想很簡單,就是所謂的雕刻之道,軟件就如一塊大理石,把多余的部分去掉,那就可以了
    再比如如何做汽車的框架,為了滿足汽車能在沙灘上,地面上,山坡上跑,我們只要把輪胎去掉,那么
    剩下的就是框架,做軟件完整的API不要寫,留給空位就行了。
    2.麥肯錫的思路(反向思維):當需要想完成某個目標的時候,往往一般人會想我現在應該先去做什么,然后
    再做什么,這樣的思路往往出來的step by step 只有一種,如果反向思維,從現在目標開始反向推理出前一
    階段的幾種可能性,然后從分別對這幾種可能性再向前推,以此類推可以形成一個樹狀,然后根據先有情況
    去除不能滿足的鏈路,這樣同樣的問題 你的思路和方法以及可選擇的路線就多很多,往往不是一條。這就是
    反向思維
    3.如果把軟件生產比作工廠,請問軟件工廠的原料是什么? 是需求? 如果回答是需求,那就錯了。
    需求和架構沒有關系。需求是桌面,架構是桌腳,桌腳的要幾個,什么形狀和桌面沒有關系,只要桌腳能支持
    桌面就行了。
    4.架構就像萬里長城,他是保護自己人的,是自己人能安居樂業,外面的多變都被萬里長城擋在外面,
    框架下面的可以多變,沒錢就改版,改版就有錢。
    5.寫框架的是強龍,寫AP的是地頭蛇。買主出現才有地頭蛇。也就是需求出現的時候才有地頭蛇。
    6.軟件哲學,如何讓先寫的call后寫的?引入接口和基類就能完成這個問題
    7.子接口因為都是基礎基類,那么他們之間怎么new,如果他們要new 也就向框架要,這樣才能不違背框架的用意。
    8.框架先不要考慮太多效率的問題,效率的問題在后面慢慢修改,這樣可以減少考慮的因素,更容易理清。
    9.強龍要有主控權,那么框架所做的事情就是能讓強龍能包容改變
    10.如果不想子系統繼承那么就用final 關鍵字
    11.如果兩個類 不要相互繼承,但是要他們相互call,那么就在他們里面分別定義一個方法,相互call
    12.基類告訴子類,讓子類call她,那么子類才能call基類,沒有call子類,那么子類不能先去call她。
    13一般進程process 是不共享的,他們在不同的位置區間,如果要跨進程的call,那么用IPC。而Process
    一般分Main thread;Message Quene;Main Looper。其中Main thread是主線程,他通過Looper 一直查看他的
    MQ,MQ記錄要求做的事情,如果MQ里面有什么事情,那么Main thread 就拿到他把他做掉。
    14.Main thread 主要是處理UI相關的用戶事件,而且一般有時間設置比如每個function不能超過5s。
    15Andriod中是通過IBinder 來實現跨進程的通信。
    16.主線程一定有一個MQ 一個Looper。而小線程沒有,所有小線程從一開始到執行完就結束了,但是小線程
    不能touch UI,只有Main thread 可以touch UI相關的用戶事件。
    17 架構師是在暗室里面抓黑貓,在沒有路的情況下找出一條可行之路,所有沒有步驟可言。
    18 做框架 盡量把人家會抓住你的地方分開,如果實在分不開,可以當壁虎,把壁虎的尾巴給人家抓。
    19 框架的東西盡量要用c++寫,因為c++比起java 安全,快,無反編譯。
    20 做一個系統一定要只要你的控制中心和整合中心,而且他們只有一個并且只有一個連接。控制中心好比大腦
    而整合中心好比骨骼。在控制中心可以增加狀態機來增加控制力和安全性。

    posted @ 2010-01-28 14:29 linugb118 閱讀(3024) | 評論 (4)編輯 收藏

    僅列出標題
    共5頁: 上一頁 1 2 3 4 5 下一頁 

    My Links

    Blog Stats

    常用鏈接

    留言簿(1)

    隨筆檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲电影在线免费观看| 亚洲精品亚洲人成在线播放| 毛片免费视频播放| 精品免费tv久久久久久久| 久久亚洲精品无码网站| 亚洲日韩在线视频| 亚洲av女电影网| 亚洲性猛交XXXX| 亚洲精品A在线观看| 国产亚洲精品免费| 免费观看的a级毛片的网站| 人成午夜免费视频在线观看| 无码中文字幕av免费放dvd| 国产一级黄片儿免费看| 日韩大片在线永久免费观看网站| 亚洲熟妇少妇任你躁在线观看| 亚洲成a人片在线网站| 亚洲男人第一av网站| 亚洲国产精品成人精品无码区| 久久久久一级精品亚洲国产成人综合AV区 | 国产精品免费久久久久久久久| 精品在线免费视频| 亚洲欧美精品午睡沙发| 亚洲国产成人久久一区二区三区| 亚洲国产精品综合久久网各| 亚洲欧洲国产视频| 亚洲成人福利网站| 亚洲一区二区三区国产精品无码| 亚洲毛片免费视频| 亚洲jjzzjjzz在线播放| 亚洲另类图片另类电影| 亚洲天堂2016| 亚洲AV一二三区成人影片| 亚洲一区在线视频| 亚洲日本成本人观看| 国产综合激情在线亚洲第一页 | 免费永久在线观看黄网站| 免费一看一级毛片全播放| 亚洲午夜精品久久久久久浪潮| 久久久久无码专区亚洲av| 亚洲老妈激情一区二区三区|