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

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

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

    別以為是那些軟件開發定律,別以為是開發出那些特殊用途的軟件,別以為是軟件設計技術本身。只有一條真理決定了一個軟件程序員的成功還是失敗。由于堅持這個真理,一個資深的程序員能在一天的時間里學會一門新的編程語言,而由于不堅持這條真理,一個初級的程序員用十年時間也只能掙到一份糊口的錢、永遠是來實現別人的設計、永遠不夠優秀而得不到晉升的機會。這條真理讓你看清了差的程序員和好的程序員的不同之處,好的程序員和偉大的程序員的不同之處,偉大的程序員和能通過自己的技術創造出一個億萬美元價值的程序帝國的超級程序員的不同之處。

    不是什么復雜的道理,不是什么難懂的理論。不是具有什么天賦或“編程超能力“才能做到的事情。最終成為的是一個優秀的程序員還是一個很爛的程序員,這跟你的出身一點關系都沒有。

    而真正的原因只有一個,唯一的一個:

    對所做的事情的理解越深,你就會做的越好。

    超級程序員跟那些平庸的、一般的程序員比起來,對自己要做的事情的理解要深的多的多。這就是原因。

    要想成為一名出色的程序員,你所要做的就是完全的理解要在做的事情。

    有人會說,該知道的我都知道了。而對說這話的人的驗證就是看他們能有應用他們知道的知識的能力。是否他能夠構造出完美的系統架構,讓人們能輕松的維護?是否他能在不皺眉頭的情況下把一個普通程序員毫無可能解決的問題輕松解決掉?是否他能在被詢問時能用最簡單的概念把任何問題都闡述明白?如果能夠,那他就是一個杰出的程序員,他能很好的理解了他在做的事情。

    然而,盡管這些人看起來已經“無所不知”,很多的程序員(包括我)都感覺他們仍然在知識的海洋里奮斗不已。有如此多的東西需要去學習,一個人幾乎要花費他畢生的心力去學習,但仍然很難說能掌握計算機知識的90%。

    而這場持久戰中的秘密武器、戰勝計算機知識的亞瑟王的神劍,就是透徹理解。對你的領域里的基礎知識理解的越好,你就越容易提升到更高的層次。你對這一層次的知識理解的越好,你就更容易掌握下一層次,以此類推。一旦你從最簡單最基礎的知識走到最高級最復雜的理論,你可以從頭再過一遍,此時你會驚奇的發現,在最低最底的底層,竟然還有那么多知識需要學習。

    看起來這個道理實在是太簡單,難以受到重視,但事實就是這樣。通往杰出的程序員的道路就是完全的深入的理解,從掌握精通最基本的知識開始,從而逐漸牢固掌握更高級的知識。

    我不想騙你—這是一個很長的路程。但你是值得去做的。在路的盡頭,你會突然發現,自己神奇的成為了一位資深的程序員,受到所有人的尊敬。你能成為一位神奇的程序員,任何事情都難不倒的程序員,讓其他程序員都羨慕的程序員。誰能預料到呢?我不能告訴你你該做什么或能成為什么。但我可以告訴你我發現一些真實的道理和有價值的東西。怎么去做全在于自己。

    -Max

    posted @ 2013-06-24 14:24 小馬歌 閱讀(225) | 評論 (0)編輯 收藏
     
    本文將探討單例模式的各種情況,并給出相應的建議。單例模式應該是設計模式中比較簡單的一個,但是在多線程并發的環境下使用卻是不那么簡單了。
    首先看最原始的單例模式。
    1 package xylz.study.singleton;
    2 
    3 public class Singleton {
    4 
    5     private static Singleton instance = null;
    6 
    7     private Singleton() {
    8     }
    9 
    10     public static Singleton getInstance() {
    11         if (instance == null) {
    12             instance = new Singleton();
    13         }
    14         return instance;
    15     }
    16 }
    17 

    顯然這個寫法在單線程環境下非常好,但是多線程會導致多個實例出現,這個大家都能理解。
    最簡單的改造方式是添加一個同步鎖。
    1 package xylz.study.singleton;
    2 
    3 public class SynchronizedSingleton {
    4 
    5     private static SynchronizedSingleton instance = null;
    6 
    7     private SynchronizedSingleton() {
    8     }
    9 
    10     public static synchronized SynchronizedSingleton getInstance() {
    11         if (instance == null) {
    12             instance = new SynchronizedSingleton();
    13         }
    14         return instance;
    15     }
    16 }
    17 

    顯然上面的方法避免了并發的問題,但是由于我們只是在第一次構造對象的時候才需要同步,以后就不再需要同步,所以這里不可避免的有性能開銷。于是將鎖去掉采用靜態的屬性來解決同步鎖的問題。
    1 package xylz.study.singleton;
    2 
    3 public class StaticSingleton {
    4 
    5     private static StaticSingleton instance = new StaticSingleton();
    6 
    7     private StaticSingleton() {
    8     }
    9 
    10     public static StaticSingleton getInstance() {
    11         return instance;
    12     }
    13 }
    14 

    上面的方法既沒有鎖又解決了性能問題,看起來已經滿足需求了。但是追求“完美”的程序員想延時加載對象,希望在第一次獲取的時候才構造對象,于是大家非常聰明的進行改造,也即非常出名的雙重檢查鎖機制出來了。
    1 package xylz.study.singleton;
    2 
    3 public class DoubleLockSingleton {
    4 
    5     private static DoubleLockSingleton instance = null;
    6 
    7     private DoubleLockSingleton() {
    8     }
    9 
    10     public static DoubleLockSingleton getInstance() {
    11         if (instance == null) {
    12             synchronized (DoubleLockSingleton.class) {
    13                 if (instance == null) {
    14                     instance = new DoubleLockSingleton();
    15                 }
    16             }
    17         }
    18         return instance;
    19     }
    20 }
    21 


    雙重鎖機制看起來非常巧妙的避免了上面的問題。但是真的是這樣的嗎?文章《雙重檢查鎖定及單例模式》中談到了非常多演變的雙重鎖機制帶來的問題,包括比較難以理解的指令重排序機制等。總之就是雙重檢查鎖機制仍然對導致錯誤問題而不是性能問題。

    一種避免上述問題的解決方案是使用volatile關鍵字,此關鍵字保證對一個對象修改后能夠立即被其它線程看到,也就是避免了指令重排序和可見性問題。參考文章

    指令重排序與happens-before法則。

    所以上面的寫法就變成了下面的例子。

    package xylz.study.singleton;

    public class DoubleLockSingleton {

        private static volatile DoubleLockSingleton instance = null;

        private DoubleLockSingleton() {
        }

        public static DoubleLockSingleton getInstance() {
            if (instance == null) {
                synchronized (DoubleLockSingleton.class) {
                    if (instance == null) {
                        instance = new DoubleLockSingleton();
                    }
                }
            }
            return instance;
        }
    }


    于是繼續改造,某個牛人利用JVM的特性來解決上述問題,具體哪個牛人我忘記了,但是不是下面文章的作者。
    (1)《Java theory and practice: Fixing the Java Memory Model, Part 2
    (2)《Initialize-On-Demand Holder Class and Singletons

    1 package xylz.study.singleton;
    2 
    3 public class HolderSingleton {
    4 
    5     private static class HolderSingletonHolder {
    6 
    7         static HolderSingleton instance = new HolderSingleton();
    8     }
    9 
    10     private HolderSingleton() {
    11         //maybe throw an Exception when doing something 
    12     }
    13 
    14     public static HolderSingleton getInstance() {
    15         return HolderSingletonHolder.instance;
    16     }
    17 }
    18 




    上述代碼看起來解決了上面單例模式遇到的所有問題,而且實際上工作的很好,沒有什么問題。但是卻有一個致命的問題,如果第11行拋出了一個異常,也就是第一次構造函數失敗將導致永遠無法再次得到構建對象的機會。
    使用下面的代碼測試下。
    1 package xylz.study.singleton;
    2 
    3 public class HolderSingletonTest {
    4 
    5     private static class HolderSingletonHolder {
    6 
    7         static HolderSingletonTest instance = new HolderSingletonTest();
    8     }
    9 
    10     private static boolean init = false;
    11     
    12     private HolderSingletonTest() {
    13         //maybe throw an Exception when doing something 
    14         if(!init) {
    15             init=true;
    16             throw new RuntimeException("fail");
    17         }
    18     }
    19 
    20     public static HolderSingletonTest getInstance() {
    21         return HolderSingletonHolder.instance;
    22     }
    23     public static void main(String[] args) {
    24         for(int i=0;i<3;i++) {
    25             try {
    26                 System.out.println(HolderSingletonTest.getInstance());
    27             } catch (Exception e) {
    28                 System.err.println("one->"+i);
    29                 e.printStackTrace();
    30             }catch(ExceptionInInitializerError err) {
    31                 System.err.println("two->"+i);
    32                 err.printStackTrace();
    33             }catch(Throwable t) {
    34                 System.err.println("three->"+i);
    35                 t.printStackTrace();
    36             }
    37         }
    38     }
    39 }
    40 
    很不幸將得到以下輸出:
    1 two->0
    2 java.lang.ExceptionInInitializerError
    3     at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
    4     at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
    5 Caused by: java.lang.RuntimeException: fail
    6     at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:16)
    7     at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:12)
    8     at xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder.<clinit>(HolderSingletonTest.java:7)
    9      2 more
    10 three->1
    11 java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
    12     at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
    13     at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
    14 three->2
    15 java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
    16     at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
    17     at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
    18 

    很顯然我們想著第一次加載失敗第二次能夠加載成功,非常不幸,JVM一旦加載某個類失敗將認為此類的定義有問題,將來不再加載,這樣就導致我們沒有機會再加載。目前看起來沒有辦法避免此問題。如果要使用JVM的類加載特性就必須保證類加載一定正確,否則此問題將比并發和性能更嚴重。如果我們的類需要初始話那么就需要想其它辦法避免在構造函數中完成。看起來像是又回到了老地方,難道不是么?

    總之,結論是目前沒有一個十全十美的單例模式,而大多數情況下我們只需要滿足我們的需求就行,沒必有特意追求最“完美”解決方案。
    原文[http://www.imxylz.info/p/177.html]
    posted @ 2013-05-22 13:58 小馬歌 閱讀(185) | 評論 (0)編輯 收藏
     


    1.1內存分配方面

    :一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式是類似于鏈表??赡苡玫降年P鍵字如下:new、malloc、delete、free等等。

    :由編譯器(Compiler)自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧。

    1.2申請方式方面:

    :需要程序員自己申請,并指明大小。在c中malloc函數如p1 = (char *)malloc(10);在C++中用new運算符,但是注意p1、p2本身是在棧中的。因為他們還是可以認為是局部變量。

    :由系統自動分配。 例如,聲明在函數中一個局部變量 int b;系統自動在棧中為b開辟空間。

    1.3系統響應方面:

    :操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,并將該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣代碼中的delete語句才能正確的釋放本內存空間。另外由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。

    :只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。

    1.4大小限制方面:

    :是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。

    :在Windows下, 棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是固定的(是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。

    1.5效率方面:

    :是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便,另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。但是速度快,也最靈活。

    :由系統自動分配,速度較快。但程序員是無法控制的。

    1.6存放內容方面:

    :一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。

    :在函數調用時第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可執行語句)的地址然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧,然后是函數中的局部變量。 注意: 靜態變量是不入棧的。當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。

    1.7存取效率方面:

    :char *s1 = "Hellow Word";是在編譯時就確定的;

    :char s1[] = "Hellow Word"; 是在運行時賦值的;用數組比用指針速度要快一些,因為指針在底層匯編中需要用edx寄存器中轉一下,而數組在棧上直接讀取。


    小結

    1、靜態變量不入棧。 
    2、棧由編譯器自動分配和釋放。棧中存放局部變量和參數,函數調用結束后,局部變量先出棧,然后是參數。 
    3、數組比用指針速度要快一些,因為指針在底層匯編中需要用edx寄存器中轉一下,而數組在棧上直接讀取。 
    4、堆是由程序員通過new、malloc、free、delete等指令進行分配和釋放。如果程序員沒有進行釋放,程序結束時可能有OS回收。 
    5、堆是由new分配的內存,速度較慢;棧是由系統自動分配,速度較快。 
    6、比如存放在棧里面的數組,是在運行時賦值。而存在堆里面的指針數據,是在編譯時就確定的。

    附:

    一. 在c中分為這幾個存儲區

    1.棧 - 由編譯器自動分配釋放
    2.堆 - 一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收
    3.全局區(靜態區),全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。- 程序結束釋放
    4.另外還有一個專門放常量的地方。- 程序結束釋放
                                                                                                                                                  
    在函數體中定義的變量通常是在棧上,用malloc, calloc, realloc等分配內存的函數分配得到的就是在堆上。在所有函數體外定義的是全局量,加了static修飾符后不管在哪里都存放在全局區(靜態區),在所有函數體外定義的static變量表示在該文件中有效,不能extern到別的文件用,在函數體內定義的static表示只在該函數體內有效。另外,函數中的"adgfdf"這樣的字符串存放在常量區。比如:

    int a = 0//全局初始化區
    char *p1; //全局未初始化區
    void main()
    {
        int b; //
        char s[] = "abc"; //
        char *p2; //
        char *p3 = "123456"; //123456{post.content}在常量區,p3在棧上
        static int c = 0; //全局(靜態)初始化區
         p1 = (char *)malloc(10); //分配得來得10字節的區域在堆區
         p2 = (char *)malloc(20); //分配得來得20字節的區域在堆區
         strcpy(p1, "123456");
        //123456{post.content}放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一塊
    }


    二.在C++中,內存分成5個區,他們分別是堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區
    1.棧,
    就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變量的存儲區。里面的變量通常是局部變量、函數參數等。
    2.堆,就是那些由new分配的內存塊,他們的釋放編譯器不去管,由我們的應用程序去控制,一般一個new就要對應一個delete。如果程序員沒有釋放掉,那么在程序結束后,操作系統會自動回收。
    3.自由存儲區,就是那些由malloc等分配的內存塊,他和堆是十分相似的,不過它是用free來結束自己的生命的。
    4.全局/靜態存儲區,全局變量和靜態變量被分配到同一塊內存中,在以前的C語言中,全局變量又分為初始化的和未初始化的,在C++里面沒有這個區分了,他們共同占用同一塊內存區。
    5.常量存儲區,這是一塊比較特殊的存儲區,他們里面存放的是常量,不允許修改(當然,你要通過非正當手段也可以修改)

    三. 談談堆與棧的關系與區別
    具體地說,現代計算機(串行執行機制),都直接在代碼底層支持棧的數據結構。這體現在,有專門的寄存器指向棧所在的地址,有專門的機器指令完成數據入棧出棧的操作。這種機制的特點是效率高,支持的數據有限,一般是整數,指針,浮點數等系統直接支持的數據類型,并不直接支持其他的數據結構。因為棧的這種特點,對棧的使用在程序中是非常頻繁的。對子程序的調用就是直接利用棧完成的。機器的call指令里隱含了把返回地址推入棧,然后跳轉至子程序地址的操作,而子程序中的ret指令則隱含從堆棧中彈出返回地址并跳轉之的操作。C/C++中的自動變量是直接利用棧的例子,這也就是為什么當函數返回時,該函數的自動變量自動失效的原因。 

    和棧不同,堆的數據結構并不是由系統(無論是機器系統還是操作系統)支持的,而是由函數庫提供的?;镜膍alloc/realloc/free 函數維護了一套內部的堆數據結構。當程序使用這些函數去獲得新的內存空間時,這套函數首先試圖從內部堆中尋找可用的內存空間,如果沒有可以使用的內存空間,則試圖利用系統調用來動態增加程序數據段的內存大小,新分配得到的空間首先被組織進內部堆中去,然后再以適當的形式返回給調用者。當程序釋放分配的內存空間時,這片內存空間被返回內部堆結構中,可能會被適當的處理(比如和其他空閑空間合并成更大的空閑空間),以更適合下一次內存分配申請。這套復雜的分配機制實際上相當于一個內存分配的緩沖池(Cache),使用這套機制有如下若干原因:
    1. 系統調用可能不支持任意大小的內存分配。有些系統的系統調用只支持固定大小及其倍數的內存請求(按頁分配);這樣的話對于大量的小內存分類來說會造成浪費。
    2. 系統調用申請內存可能是代價昂貴的。系統調用可能涉及用戶態和核心態的轉換。
    3. 沒有管理的內存分配在大量復雜內存的分配釋放操作下很容易造成內存碎片。

    堆和棧的對比
    從以上知識可知,棧是系統提供的功能,特點是快速高效,缺點是有限制,數據不靈活;而棧是函數庫提供的功能,特點是靈活方便,數據適應面廣泛,但是效率有一定降低。棧是系統數據結構,對于進程/線程是唯一的;堆是函數庫內部數據結構,不一定唯一。不同堆分配的內存無法互相操作。??臻g分靜態分配和動態分配兩種。靜態分配是編譯器完成的,比如自動變量(auto)的分配。動態分配由alloca函數完成。棧的動態分配無需釋放(是自動的),也就沒有釋放函數。為可移植的程序起見,棧的動態分配操作是不被鼓勵的!堆空間的分配總是動態的,雖然程序結束時所有的數據空間都會被釋放回系統,但是精確的申請內存/ 釋放內存匹配是良好程序的基本要素。

        1.碎片問題:對于堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因為棧是先進后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個內存塊從棧中間彈出,在他彈出之前,在他上面的后進的棧內容已經被彈出,詳細的可以>參考數據結構,這里我們就不再一一討論了。
        2.生長方向:對于堆來講,生長方向是向上的,也就是向著內存地址增加的方向;對于棧來講,它的生長方向是向下的,是向著內存地址減小的方向增長。
        3.分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。動態分配由alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。
        4.分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很復雜的,例如為了分配一塊內存,庫函數會按照一定的算法(具體的算法可以參考數據結構/操作系統)在堆內存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內存碎片太多),就有可能調用系統功能去增加程序數據段的內存空間,這樣就有機會分到足夠大小的內存,然后進行返回。顯然,堆的效率比棧要低得多。

        明確區分堆與棧:
        在bbs上,堆與棧的區分問題,似乎是一個永恒的話題,由此可見,初學者對此往往是混淆不清的,所以我決定拿他第一個開刀。
        首先,我們舉一個例子:

    void f()

        int* p=new int[5];
    }

    這條短短的一句話就包含了堆與棧,看到new,我們首先就應該想到,我們分配了一塊堆內存,那么指針p呢?他分配的是一塊棧內存,所以這句話的意思就是:在棧內存中存放了一個指向一塊堆內存的指針p。在程序會先確定在堆中分配內存的大小,然后調用operator new分配內存,然后返回這塊內存的首地址,放入棧中,他在VC6下的匯編代碼如下:
        00401028    push         14h
        0040102A    call            operator new (00401060)
        0040102F    add          esp,4
        00401032    mov          dword ptr [ebp-8],eax
        00401035    mov          eax,dword ptr [ebp-8]
        00401038    mov          dword ptr [ebp-4],eax
        這里,我們為了簡單并沒有釋放內存,那么該怎么去釋放呢?是delete p么?澳,錯了,應該是delete []p,這是為了告訴編譯器:我刪除的是一個數組,VC6就會根據相應的Cookie信息去進行釋放內存的工作。
        好了,我們回到我們的主題:堆和棧究竟有什么區別?
        主要的區別由以下幾點:
        1、管理方式不同;
        2、空間大小不同;
        3、能否產生碎片不同;
        4、生長方向不同;
        5、分配方式不同;
        6、分配效率不同;
        管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產生memory leak。
        空間大小:一般來講在32位系統下,堆內存可以達到4G的空間,從這個角度來看堆內存幾乎是沒有什么限制的。但是對于棧來講,一般都是有一定的空間大小的,例如,在VC6下面,默認的棧空間大小是1M(好像是,記不清楚了)。當然,我們可以修改:
        打開工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然后在Reserve中設定堆棧的最大值和commit。
    注意:reserve最小值為4Byte;commit是保留在虛擬內存的頁文件里面,它設置的較大會使棧開辟較大的值,可能增加內存的開銷和啟動時間。
        堆和棧相比,由于大量new/delete的使用,容易造成大量的內存碎片;由于沒有專門的系統支持,效率很低;由于可能引發用戶態和核心態的切換,內存的申請,代價變得更加昂貴。所以棧在程序中是應用最廣泛的,就算是函數的調用也利用棧去完成,函數調用過程中的參數,返回地址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。

    另外對存取效率的比較:
    代碼:

    char s1[] = "aaaaaaaaaaaaaaa";
    char *s2 = "bbbbbbbbbbbbbbbbb";

    aaaaaaaaaaa是在運行時刻賦值的;
    而bbbbbbbbbbb是在編譯時就確定的;
    但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快
    比如:

    void main()
    {
        char a = 1;
        char c[] = "1234567890";
        char *p ="1234567890";
         a = c[1];
         a = p[1];
        return;
    }

    對應的匯編代碼
    10: a = c[1];
    00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
    0040106A 88 4D FC mov byte ptr [ebp-4],cl
    11: a = p[1];
    0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
    00401070 8A 42 01 mov al,byte ptr [edx+1]
    00401073 88 45 FC mov byte ptr [ebp-4],al
    第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字符,顯然慢了.
        無論是堆還是棧,都要防止越界現象的發生(除非你是故意使其越界),因為越界的結果要么是程序崩潰,要么是摧毀程序的堆、棧結構,產生以想不到的結果,就算是在你的程序運行過程中,沒有發生上面的問題,你還是要小心,說不定什么時候就崩掉,編寫穩定安全的代碼才是最重要的

    posted @ 2013-05-20 16:40 小馬歌 閱讀(282) | 評論 (0)編輯 收藏
     

    淺談CSRF攻擊方式

    2009-04-09 22:44 by hyddd, 18997 閱讀, 39 評論, 收藏編輯

    一.CSRF是什么?

      CSRF(Cross-site request forgery),中文名稱:跨站請求偽造,也被稱為:one click attack/session riding,縮寫為:CSRF/XSRF。

    二.CSRF可以做什么?

      你這可以這么理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義發送惡意請求。CSRF能夠做的事情包括:以你名義發送郵件,發消息,盜取你的賬號,甚至于購買商品,虛擬貨幣轉賬......造成的問題包括:個人隱私泄露以及財產安全。

    三.CSRF漏洞現狀

      CSRF這種攻擊方式在2000年已經被國外的安全人員提出,但在國內,直到06年才開始被關注,08年,國內外的多個大型社區和交互網站分別爆出CSRF漏洞,如:NYTimes.com(紐約時報)、Metafilter(一個大型的BLOG網站),YouTube和百度HI......而現在,互聯網上的許多站點仍對此毫無防備,以至于安全業界稱CSRF為“沉睡的巨人”。

    四.CSRF的原理

      下圖簡單闡述了CSRF攻擊的思想:

      

      從上圖可以看出,要完成一次CSRF攻擊,受害者必須依次完成兩個步驟

      1.登錄受信任網站A,并在本地生成Cookie

      2.在不登出A的情況下,訪問危險網站B。

      看到這里,你也許會說:“如果我不滿足以上兩個條件中的一個,我就不會受到CSRF的攻擊”。是的,確實如此,但你不能保證以下情況不會發生:

      1.你不能保證你登錄了一個網站后,不再打開一個tab頁面并訪問另外的網站。

      2.你不能保證你關閉瀏覽器了后,你本地的Cookie立刻過期,你上次的會話已經結束。(事實上,關閉瀏覽器不能結束一個會話,但大多數人都會錯誤的認為關閉瀏覽器就等于退出登錄/結束會話了......)

      3.上圖中所謂的攻擊網站,可能是一個存在其他漏洞的可信任的經常被人訪問的網站。

     

      上面大概地講了一下CSRF攻擊的思想,下面我將用幾個例子詳細說說具體的CSRF攻擊,這里我以一個銀行轉賬的操作作為例子(僅僅是例子,真實的銀行網站沒這么傻:>)

      示例1:

      銀行網站A,它以GET請求來完成銀行轉賬的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000

      危險網站B,它里面有一段HTML的代碼如下:

      <img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

      首先,你登錄了銀行網站A,然后訪問危險網站B,噢,這時你會發現你的銀行賬戶少了1000塊......

      為什么會這樣呢?原因是銀行網站A違反了HTTP規范,使用GET請求更新資源。在訪問危險網站B的之前,你已經登錄了銀行網站A,而B中的<img>以GET的方式請求第三方資源(這里的第三方就是指銀行網站了,原本這是一個合法的請求,但這里被不法分子利用了),所以你的瀏覽器會帶上你的銀行網站A的Cookie發出Get請求,去獲取資源“http://www.mybank.com/Transfer.php?toBankId=11&money=1000”,結果銀行網站服務器收到請求后,認為這是一個更新資源操作(轉賬操作),所以就立刻進行轉賬操作......

      示例2:

      為了杜絕上面的問題,銀行決定改用POST請求完成轉賬操作。

      銀行網站A的WEB表單如下:  

      <form action="Transfer.php" method="POST">
        <p>ToBankId: <input type="text" name="toBankId" /></p>
        <p>Money: <input type="text" name="money" /></p>
        <p><input type="submit" value="Transfer" /></p>
      </form>

      后臺處理頁面Transfer.php如下:

    復制代碼
      <?php
        session_start();
        if (isset($_REQUEST['toBankId'&& isset($_REQUEST['money']))
        {
            buy_stocks(
    $_REQUEST['toBankId'], $_REQUEST['money']);
        }
      ?>
    復制代碼

      危險網站B,仍然只是包含那句HTML代碼:

      <img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

      和示例1中的操作一樣,你首先登錄了銀行網站A,然后訪問危險網站B,結果.....和示例1一樣,你再次沒了1000塊~T_T,這次事故的原因是:銀行后臺使用了$_REQUEST去獲取請求的數據,而$_REQUEST既可以獲取GET請求的數據,也可以獲取POST請求的數據,這就造成了在后臺處理程序無法區分這到底是GET請求的數據還是POST請求的數據。在PHP中,可以使用$_GET和$_POST分別獲取GET請求和POST請求的數據。在JAVA中,用于獲取請求數據request一樣存在不能區分GET請求數據和POST數據的問題。

      示例3:

      經過前面2個慘痛的教訓,銀行決定把獲取請求數據的方法也改了,改用$_POST,只獲取POST請求的數據,后臺處理頁面Transfer.php代碼如下:

    復制代碼
      <?php
        
    session_start();
        
    if (isset($_POST['toBankId'&& isset($_POST['money']))
        {
            buy_stocks(
    $_POST['toBankId'], $_POST['money']);
        }
      
    ?>
    復制代碼

      然而,危險網站B與時俱進,它改了一下代碼:

    復制代碼
    <html>
      <head>
        <script type="text/javascript">
          function steal()
          {
                   iframe 
    = document.frames["steal"];
                   iframe.document.Submit(
    "transfer");
          }
        </script>
      </head>

      
    <body onload="steal()">
        <iframe name="steal" display="none">
          <form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php">
            
    <input type="hidden" name="toBankId" value="11">
            
    <input type="hidden" name="money" value="1000">
          
    </form>
        </iframe>
      </body>
    </html>
    復制代碼

    如果用戶仍是繼續上面的操作,很不幸,結果將會是再次不見1000塊......因為這里危險網站B暗地里發送了POST請求到銀行!

      總結一下上面3個例子,CSRF主要的攻擊模式基本上是以上的3種,其中以第1,2種最為嚴重,因為觸發條件很簡單,一個<img>就可以了,而第3種比較麻煩,需要使用JavaScript,所以使用的機會會比前面的少很多,但無論是哪種情況,只要觸發了CSRF攻擊,后果都有可能很嚴重。

      理解上面的3種攻擊模式,其實可以看出,CSRF攻擊是源于WEB的隱式身份驗證機制!WEB的身份驗證機制雖然可以保證一個請求是來自于某個用戶的瀏覽器,但卻無法保證該請求是用戶批準發送的!

    五.CSRF的防御

      我總結了一下看到的資料,CSRF的防御可以從服務端客戶端兩方面著手,防御效果是從服務端著手效果比較好,現在一般的CSRF防御也都在服務端進行。

      1.服務端進行CSRF防御

      服務端的CSRF方式方法很多樣,但總的思想都是一致的,就是在客戶端頁面增加偽隨機數。

      (1).Cookie Hashing(所有表單都包含同一個偽隨機值):

      這可能是最簡單的解決方案了,因為攻擊者不能獲得第三方的Cookie(理論上),所以表單中的數據也就構造失敗了:>

      <?php
        //構造加密的Cookie信息
        $value = “DefenseSCRF”;
        setcookie(”cookie”, $value, time()+3600);
      ?>

      在表單里增加Hash值,以認證這確實是用戶發送的請求。

    復制代碼
      <?php
        $hash = md5($_COOKIE['cookie']);
      ?>
      <form method=”POST” action=”transfer.php”>
        <input type=”text” name=”toBankId”>
        <input type=”text” name=”money”>
        <input type=”hidden” name=”hash” value=<?=$hash;?>>
        <input type=”submit” name=”submit” value=”Submit”>
      </form>
    復制代碼

      然后在服務器端進行Hash值驗證

    復制代碼
          <?php
            if(isset($_POST['check'])) {
                 
    $hash = md5($_COOKIE['cookie']);
                 if($_POST['check'== $hash) {
                      doJob();
                 } 
    else {
            //...

                 }
            } 
    else {
          //...

            }
          
    ?>
    復制代碼

      這個方法個人覺得已經可以杜絕99%的CSRF攻擊了,那還有1%呢....由于用戶的Cookie很容易由于網站的XSS漏洞而被盜取,這就另外的1%。一般的攻擊者看到有需要算Hash值,基本都會放棄了,某些除外,所以如果需要100%的杜絕,這個不是最好的方法。
      (2).驗證碼

      這個方案的思路是:每次的用戶提交都需要用戶在表單中填寫一個圖片上的隨機字符串,厄....這個方案可以完全解決CSRF,但個人覺得在易用性方面似乎不是太好,還有聽聞是驗證碼圖片的使用涉及了一個被稱為MHTML的Bug,可能在某些版本的微軟IE中受影響。

      (3).One-Time Tokens(不同的表單包含一個不同的偽隨機值)

      在實現One-Time Tokens時,需要注意一點:就是“并行會話的兼容”。如果用戶在一個站點上同時打開了兩個不同的表單,CSRF保護措施不應該影響到他對任何表單的提交??紤]一下如果每次表單被裝入時站點生成一個偽隨機值來覆蓋以前的偽隨機值將會發生什么情況:用戶只能成功地提交他最后打開的表單,因為所有其他的表單都含有非法的偽隨機值。必須小心操作以確保CSRF保護措施不會影響選項卡式的瀏覽或者利用多個瀏覽器窗口瀏覽一個站點。

      以下我的實現:

      1).先是令牌生成函數(gen_token()):

    復制代碼
         <?php
         function gen_token() {
        //這里我是貪方便,實際上單使用Rand()得出的隨機數作為令牌,也是不安全的。
        //這個可以參考我寫的Findbugs筆記中的《Random object created and used only once》
              $token = md5(uniqid(rand(), true));
              
    return $token;
         }
    復制代碼

      2).然后是Session令牌生成函數(gen_stoken()):

    復制代碼
         <?php
         
      function gen_stoken() {
          $pToken = "";
          if($_SESSION[STOKEN_NAME]  == $pToken){
            //沒有值,賦新值
          
      $_SESSION[STOKEN_NAME] = gen_token();
          }    
          else{
            //繼續使用舊的值
          }

           }
         
    ?>
    復制代碼

      3).WEB表單生成隱藏輸入域的函數:  

    復制代碼
         <?php
           function gen_input() {
                gen_stoken();
                echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\”
                     value=\”" . $_SESSION[STOKEN_NAME] . “\”> “;
           }
         ?>
    復制代碼

      4).WEB表單結構:

    復制代碼
         <?php
              
    session_start();
              
    include(”functions.php”);
         
    ?>
         
    <form method=”POST” action=”transfer.php”>
              
    <input type=”text” name=”toBankId”>
              
    <input type=”text” name=”money”>
              
    <? gen_input(); ?>
              
    <input type=”submit” name=”submit” value=”Submit”>
         
    </FORM>
    復制代碼

      5).服務端核對令牌:

      這個很簡單,這里就不再啰嗦了。

      上面這個其實不完全符合“并行會話的兼容”的規則,大家可以在此基礎上修改。

     

      其實還有很多想寫,無奈精力有限,暫且打住,日后補充,如果錯漏,請指出:>

      PS:今天下午寫這篇文檔的時候FF崩潰了一次,寫了一半文章的全沒了,郁悶好久T_T.......

      轉載請說明出處,謝謝[hyddd(http://www.cnblogs.com/hyddd/)]

    posted @ 2013-04-24 17:00 小馬歌 閱讀(246) | 評論 (0)編輯 收藏
     

    分布式領域CAP理論,
    Consistency(一致性), 數據一致更新,所有數據變動都是同步的
    Availability(可用性), 好的響應性能
    Partition tolerance(分區容錯性) 可靠性
    定理:任何分布式系統只可同時滿足二點,沒法三者兼顧。
    忠告:架構師不要將精力浪費在如何設計能滿足三者的完美分布式系統,而是應該進行取舍。
    關系數據庫的ACID模型擁有 高一致性 + 可用性 很難進行分區:
    Atomicity原子性:一個事務中所有操作都必須全部完成,要么全部不完成。
    Consistency一致性. 在事務開始或結束時,數據庫應該在一致狀態。
    Isolation隔離層. 事務將假定只有它自己在操作數據庫,彼此不知曉。
    Durability. 一旦事務完成,就不能返回。
    跨數據庫事務:2PC (two-phase commit), 2PC is the anti-scalability pattern (Pat Helland) 是反可伸縮模式的,JavaEE中的JTA事務可以支持2PC。因為2PC是反模式,盡量不要使用2PC,使用BASE來回避。
    BASE模型反ACID模型,完全不同ACID模型,犧牲高一致性,獲得可用性或可靠性:
    Basically Available基本可用。支持分區失敗(e.g. sharding碎片劃分數據庫)
    Soft state軟狀態 狀態可以有一段時間不同步,異步。
    Eventually consistent最終一致,最終數據是一致的就可以了,而不是時時高一致。
    BASE思想的主要實現有
    1.按功能劃分數據庫
    2.sharding碎片 
    BASE思想主要強調基本的可用性,如果你需要High 可用性,也就是純粹的高性能,那么就要以一致性或容錯性為犧牲,BASE思想的方案在性能上還是有潛力可挖的。
    現在NoSQL運動豐富了拓展了BASE思想,可按照具體情況定制特別方案,比如忽視一致性,獲得高可用性等等,NOSQL應該有下面兩個流派:
    1. Key-Value存儲,如Amaze Dynamo等,可根據CAP三原則靈活選擇不同傾向的數據庫產品。
    2. 領域模型 + 分布式緩存 + 存儲 (Qi4j和NoSQL運動),可根據CAP三原則結合自己項目定制靈活的分布式方案,難度高。
    這兩者共同點:都是關系數據庫SQL以外的可選方案,邏輯隨著數據分布,任何模型都可以自己持久化,將數據處理和數據存儲分離,將讀和寫分離,存儲可以是異步或同步,取決于對一致性的要求程度。
    不同點:NOSQL之類的Key-Value存儲產品是和關系數據庫頭碰頭的產品BOX,可以適合非Java如PHP RUBY等領域,是一種可以拿來就用的產品,而領域模型 + 分布式緩存 + 存儲是一種復雜的架構解決方案,不是產品,但這種方式更靈活,更應該是架構師必須掌握的。

    posted @ 2013-04-23 16:18 小馬歌 閱讀(231) | 評論 (0)編輯 收藏
     

    主要闡述以下內容:

    磁盤的內部結構及實現機制
    分區的結構及實現機制
    塊的構造及原理
    扇區的構造及結構

     

    由于機械硬盤的訪問機制為移動磁頭,并等待磁碟旋轉,因此在設計磁盤時,需要考慮如何組織數據為順序訪問,并且最大限度提供一次順序訪問盡可能多的數據

    由于塊大小設計直影響到性能、內存、空間等因素,因此需要合理設置塊大小,同時需要合理的為業務擴展預留升級空間

    機械磁盤讀寫原理

     

    機械硬盤獲取數據的方式為:

    1、通過查找meta數據(圖中的Data Meta,它是用于描述數據的數據,所以稱為數據元,通常這些數據會被Cache在磁盤Cache或OS Cache中),捕獲數據存儲物理區域。包括:磁頭指向的磁道位置、磁道的起始、結束位置等,其中還包含了數據塊的標記,如使用狀態,分區、塊、卷、扇區等等存儲細節等,是磁盤運作機制的核心組件

    2、驅動磁碟旋轉(服務器通常磁盤會一直旋轉,但為了省電及減少對驅動軸的損耗,通常會對旋轉進行優化,即空閑時降低磁盤旋轉速度或停止轉動,但重新驅動磁盤也會消耗大量的功耗,廠家進行了很多節能減排的優化措施,為綠色環保做了不少貢獻)

    3、將磁頭(圖中的head)移動到指定的磁道,并從指定的起始位置開始讀取bit流,一直至指定的結束位置,并將信號傳送至處理程序,最后轉化為OS識別的數據


    機械硬盤核心組件:

    1、磁盤控制器:內部包含用于控制多磁碟并行訪問的機制,包括磁頭移動、盤片旋轉、供電、緩存、寫保護等,是整個磁盤的核心組件

    2、分區(LUN):機械硬盤要被使用,通常先要被分區,并基于分區進行格式化,格式化將產生Meta數據,Meta數據通常會占用部分磁盤空間??臻g大小取決于Block大小、分區量,Block越小,需要消耗的空間,用于于索引磁盤數據,接下來我們將還會介紹塊及扇區的組合方式,多碟磁盤中,磁盤控制器將在每塊磁碟中劃分一塊空間用于該分區使用,達到并行訪問的目的,提升響應速度(通常某些訪問需要集中訪問的數據都集中在某些分區中),此處的分區不同于OS中的分區,此處為物理分區

    3、塊:塊由扇區構成,塊大小決定了數據訪問的性能,如果大數據如果存儲在小塊中會導致浪費大量的數據元空間,并消耗更多的Cache、更多的尋道時間(數據是被分散再分區中的各個位置),所以當應用數據塊比較小的時候,我們建議將數據劃分成更大的塊,提升性能,但如果塊劃的太大,會導致存儲空間的浪費,當這些不需要的數據被LOAD到應用中時,同樣會消耗額外的OS內存,通常我們建議根據業務的類型,在應用層選擇合適的數據集大小,并設置合理的磁盤塊大小

    塊由扇區構成,扇區直接構建再磁碟的磁面上,每個扇區為512byte,業績意味著4KB的塊大小將需要8個扇區組成(通常Linux設置塊大小為4K),但再某些數據庫應用中我們將數據庫的數據設置為更大,如8K、16K、64K,日志設置為更大,如8K、16K、64K等,結構如下

     

    4、扇區:扇區是磁盤存儲的單元,扇區顧名思義,機械硬盤是圓形的,通過分區格式化后將得到一個一個的扇形結構,一條磁道的存儲空間被格式化后將的到大量的扇形結構,磁盤的扇區大小為512byte,其在磁盤的結構如下圖:


    圖中我們可以看到扇區結構非常復雜,包含數據區域、扇區分界標識區域、扇區間隙區域,磁盤在處理數據時為了更好的保護數據及容錯、設計了扇區分界標識及扇區間隙(當然遠遠不止如此)

    posted @ 2013-04-23 09:56 小馬歌 閱讀(272) | 評論 (0)編輯 收藏
     

    Hadoop是Apache的一個項目(http://hadoop.apache.org/),它是一個實現了MapReduce計算模型的可以運用于大型集群并行計算的分布式并行計算編程框架。

    目前,整個Hadoop家族由以下幾個子項目組成:

    Hadoop Common
    Hadoop體系最底層的一個模塊,為Hadoop各子項目提供各種工具,如:配置文件和日志操作等。

    Avro
    Avro是doug cutting主持的RPC項目,有點類似Google的protobuf和Facebook的thrift。avro用來做以后hadoop的RPC,使hadoop的RPC模塊通信速度更快、數據結構更緊湊。

    Chukwa
    Chukwa是基于Hadoop的大集群監控系統,由yahoo貢獻。

    HBase
    基于Hadoop Distributed File System,是一個開源的,基于列存儲模型的分布式數據庫。

    HDFS
    分布式文件系統

    Hive
    hive類似CloudBase,也是基于hadoop分布式計算平臺上的提供data warehouse的sql功能的一套軟件。使得存儲在hadoop里面的海量數據的匯總,即席查詢簡單化。hive提供了一套QL的查詢語言,以sql為基礎,使用起來很方便。

    MapReduce
    實現了MapReduce編程框架

    Pig
    Pig是SQL-like語言,是在MapReduce上構建的一種高級查詢語言,把一些運算編譯進MapReduce模型的Map和Reduce中,并且用戶可以定義自己的功能。Yahoo網格運算部門開發的又一個克隆Google的項目Sawzall。

    ZooKeeper
    Zookeeper是Google的Chubby一個開源的實現。它是一個針對大型分布式系統的可靠協調系統,提供的功能包括:配置維護、名字服務、分布式同步、組服務等。ZooKeeper的目標就是封裝好復雜易出錯的關鍵服務,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。

    posted @ 2013-04-22 12:39 小馬歌 閱讀(290) | 評論 (0)編輯 收藏
     
    到http://nginx.org/en/download.html下載最新版本的Nginx并安裝.
    一 下載并安裝pcre庫ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
       tar zxvf pcre-8.30.tar.gz
       ./configure     make    make install
    二 安裝openssl 
       yum -y install openssl openssl-devel
    三 下載tcp_proxy_module
    到 https://github.com/yaoweibin/nginx_tcp_proxy_module 下載
    四 安裝nginx
    export NGINX_VERSION=1.2.1
    curl -O http://nginx.org/downlad/nginx-$NGINX_VERSION.tar.gz
    tar -xvzf nginx-$NGINX_VERSION.tar.gz
    cd nginx-$NGINX_VERSION
    patch -p1 < ../nginx_tcp_proxy_module/tcp.patch
    ./configure --add-module=../nginx_tcp_proxy_module/
    sudo make && make install
     
    啟動nginx上時,服務無法啟動,出現libpcre.so.1 not found的錯誤,解決方法如下:
    先執行下述命令,查看
    ---#ldd $(which /usr/sbin/nginx)
    顯示如下:
        linux-vdso.so.1 =>  (0x00007fff7e9db000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fe4629d0000)
        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fe462799000)
        libpcre.so.1 => not found//果然沒找到
        libz.so.1 => /lib64/libz.so.1 (0x00007fe462582000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fe4621e1000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fe462bfa000)
        libfreebl3.so => /lib64/libfreebl3.so (0x00007fe461f7e000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fe461d7a000)
    執行如下:
     ----#cd /lib64
     ----#ln -s libpcre.so.0.0.1 libpcre.so.1
    再次查看一下:
     ----#ldd $(which /usr/sbin/nginx)
    顯示已經ok了:
        linux-vdso.so.1 =>  (0x00007fff4d7ff000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb06f13e000)
        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fb06ef07000)
        libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fb06ecda000)
        libz.so.1 => /lib64/libz.so.1 (0x00007fb06eac4000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fb06e723000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fb06f368000)
        libfreebl3.so => /lib64/libfreebl3.so (0x00007fb06e4c0000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fb06e2bc000)
     
    為websocket應用實現負載均衡 http://cnodejs.org/topic/4f16442ccae1f4aa270010b3 
    Reverse Proxy Web Sockets with Nginx and Socket.IO http://www.letseehere.com/reverse-proxy-web-sockets
    posted @ 2013-04-18 17:38 小馬歌 閱讀(499) | 評論 (0)編輯 收藏
     

    前言: 


    websocket相信經常逛cnode社區的孩紙們都知道..具體API使用方式也再熟悉不過了..使用nodejs開發的websocket服務端也是品種繁多..github上總有你喜歡的..平時耍耍當然沒問題..如果真要是承載一個生產環境服務的核心..總會有些問題需要你去解決. 

    不可避免的問題: 

    按照一個web請求占用線程數為參照..我們可以把nodejs稱之為單線程語言..而java的servlet這種應該就是多線程語言了..我們可以想象在高并發情況下..單線程語言的運行風險還是蠻高的..畢竟如果一個請求出事那么整個線程(進程)就退出了..于是乎停止服務了..為了規避風險..我們常常會使用負載均衡技術..保證整個系統的對外正常服務.. 

    解決方案: 

    負載均衡方案目前來講..用apache的也不多了吧..普遍的解決方案是使用nginx配置多個upstream.實現負載均衡..例子如下: 
    http{ 
    upstream http_sr {
    server 192.168.0.2:8080;
    server 192.168.0.3:8080;
    }
    server {
    listen 80 ;
    proxy_pass http_sr;
    }
    }

    這樣對于部署在192.168.0.2和3這兩臺機器上http服務..就通過這臺nginx服務器實現了負載均衡...但是websocket的服務端nginx的http反向代理是不能支持的.從websocket的specs我們可以很明確的其實基于tcp協議的..http協議雖然也是基于tcp..它們都使用了tcp的握手方式..但是nginx的http_proxy_pass是不能支持websocket的.. 

    于是我們可以尋根問主..讓nginx支持tcp_proxy_pass..那websocket負載均衡的問題不就迎刃而解了..nginx有豐富的第三方擴展..一頓搜索在github上找到了yaoweibin老師的nginx_tcp_proxy_module 

      

    二話不說下載此模塊..重新編譯nginx(此過程略吧大家應該都懂) ..修改nginx配置文件加入下面conf: 
    tcp { 
    upstream websocket {
    server 192.168.0.2:8080;
    server 192.168.0.3:8080;
    check interval=3000 rise=2 fall=5 timeout=1000;
    }
    server {
    listen 80;
    proxy_pass websocket;
    }
    }

    這個module的作者在description寫到: 
     The motivation of writing these modules is Nginx's high performance and 
    robustness. At first, I developed this module just for general TCP
    proxy. And now, this module is frequently used in websocket reverse
    proxying.

    目前基本就是用來做websocket反向代理的..測試后確實是支持的..非常感謝module的開發者另外值得注意的是你要在nginx里同時配置tcp和http..那么它們是不能listen同一端口的..
    posted @ 2013-04-18 17:38 小馬歌 閱讀(2266) | 評論 (0)編輯 收藏
     

    nginx_tcp_proxy_module 為 Nginx 增加對 TCP 的反向代理支持,提供連接有效性檢測和狀態監控。

    配置示例:

    upstream cluster {
        # simple round-robin
        server 127.0.0.1:3306;
        server 127.0.0.1:1234;

        check interval=3000 rise=2 fall=5 timeout=1000;

        #check interval=3000 rise=2 fall=5 timeout=1000 type=ssl_hello;

        #check interval=3000 rise=2 fall=5 timeout=1000 type=http;
        #check_http_send "GET / HTTP/1.0\r\n\r\n";
        #check_http_expect_alive http_2xx http_3xx;
    }

    add the feature of tcp proxy with nginx, with health check and status monitor — More...

    http://yaoweibin.github.com/nginx_tcp_proxy_module

    Issues
    #74may be a debug in ngx_tcp_upstream_check_broken_connection?by chenbk85  2013-04-17
    #73reverse proxy tcp and http on the same portby beurdouche  2013-03-26
    #72TCP error_logby splitice  2013-03-24
    #31basic auth htpsswdby manguz  2013-03-21
    #71upstream ssl suppoprt!by sleets  2013-03-13

    master分支代碼最近更新:2013-03-28

    posted @ 2013-04-18 17:19 小馬歌 閱讀(332) | 評論 (0)編輯 收藏
    僅列出標題
    共95頁: First 上一頁 26 27 28 29 30 31 32 33 34 下一頁 Last 
     
    主站蜘蛛池模板: 亚洲国产aⅴ成人精品无吗| 亚洲精品中文字幕无码AV| 国产成人1024精品免费| 亚洲无av在线中文字幕| 国产精品久久永久免费| 美女的胸又黄又www网站免费| 亚洲精品成人网站在线观看| 成年在线网站免费观看无广告 | 四虎影视永久免费观看网址| 国产VA免费精品高清在线| 国产精品免费久久久久久久久| 一级做α爱过程免费视频| 亚洲影视自拍揄拍愉拍| 亚洲一区二区视频在线观看| 69式互添免费视频| 国产福利免费视频 | 5555在线播放免费播放| 九九久久国产精品免费热6| 牛牛在线精品观看免费正| 亚洲AV无码久久久久网站蜜桃| 亚洲人成网77777色在线播放| 精品亚洲综合在线第一区| 国产免费小视频在线观看| 免费大香伊蕉在人线国产 | 人体大胆做受免费视频| 在线观看日本亚洲一区| 亚洲成AV人片在WWW色猫咪| 婷婷久久久亚洲欧洲日产国码AV| 国产真实伦在线视频免费观看| 18禁止看的免费污网站| 好男人www免费高清视频在线| 日韩免费高清大片在线| 国产精品亚洲专区无码唯爱网| 亚洲欧洲精品一区二区三区| 国产国拍亚洲精品mv在线观看| 老色鬼久久亚洲AV综合| 亚洲色爱图小说专区| 亚洲精品亚洲人成在线观看麻豆| 亚洲欧洲日产国码久在线| 亚洲精品国产日韩| 久久精品国产亚洲av麻豆图片|