java高併發處理

      在java web項目開發者,最難解決的是高併發問題,我爲搞併發解決方案,想出了一個解決方案。

     a.應用層面:讀寫分離、緩存、隊列、集羣、令牌、系統拆分、隔離、系統升級(可水平擴容方向)。

     b.時間換空間:降低單次請求時間,這樣在單位時間內系統併發就會提升。

     c.空間換時間:拉長整體處理業務時間,換取後臺系統容量空間。

      1.使用緩存服務器

      使用Redis作爲緩存服務器的,剛開始的時候會滿足需要,隨着項目的增大緩存數據的增多就會查詢和插入更慢這時就要考慮Redis集羣方案了

使用Redis分佈式要保證數據都能能夠平均的緩存到每一臺機器,首先想到的做法是對數據進行分片,因爲Redis是key-value存儲的,首先想到的是Hash分片,可能的做法是對key進行哈希運算,得到一個long值對分佈式的數量取模會得到一個一個對應數據庫的一個映射,沒有讀取就可以定位到這臺數據庫,那麼速度但然會提升了。

但是取模的hash算法是有問題的如果集羣數量不變的話沒有什麼問題,一旦增加一臺機器或者一臺機器掛掉,導致機器數量變化,就會導致計算的出的數據庫映射亂掉,不能正確存取數據了。

因爲這個問題引入我們說的一致性哈希算法,這個哈希算法具有的特徵

1.均衡性:也有人把它定義爲平衡性,是指哈希的結果能夠儘可能分佈到所有的節點中去,這樣可以有效的利用每個節點上的資源。

2.單調性:對於單調性有很多翻譯讓我非常的不解,而我想要的是當節點數量變化時哈希的結果應儘可能的保護已分配的內容不會被重新分派到新的節點。

3.分散性和負載:這兩個其實是差不多的意思,就是要求一致性哈希算法對 key 哈希應儘可能的避免重複。

一致性哈希就數據結構是創建一個排序的環形數據結構,有許多個區域,先讓每一臺服務器都分佈環上,取每一個服務器的特效做哈希運行,得到的值放進環中,進行排序這樣就能根據哈希特徵找到對應的真是服務器,能夠讓把服務器平均的分佈到環上。



第一個特徵均衡性:就是儘量的讓數據平均的分部到每一個服務器,不讓某臺機器壓力特別打,或者乾脆沒活幹,因爲這個原因,我們的每一個服務器都添加幾個虛擬服務器,比如真是服務器叫node1那麼第一個服務器的虛擬服務器就叫node1-1,node1-2…,根據這些特徵進行哈希運算也分佈到環中,這樣就能把服務器平均的分佈到環中。


第二個特徵單調性:因爲服務器都在環中,數據的key進行哈希運算得到一個值,跟環中的服務器的哈希值進行比較,取離當前值最接近的哈希值對象的服務器,這樣就是獲取服務器的原理了,我們是做了一個偷懶的工作,服務器哈希進行排序,以順時針方式得到一個剛好大於key哈希的服務器。
單調性是在不管添加節點還是刪除節點,原來對應的服務器不變,因爲這個環很大,服務器是零星分佈的,這樣增加或者刪除一個節點只有受影響的都是當前節點,但是key對應的數據庫是不變的,也不能說不變,是把變化變得儘可能的小。

第三個特徵分散性和負載:指服務器在環中儘可能的分散,儘可能的讓數據平均分佈到不同的服務器,我們就是使用虛擬節點的方式解決的。

  1. public final class MurmurHash {      
  2.       
  3.     public MurmurHash() {      
  4.       
  5.     }      
  6.       
  7.     private byte[] toBytesWithoutEncoding(String str) {      
  8.       
  9.         int len = str.length();      
  10.         int pos = 0;      
  11.         byte[] buf = new byte[len << 1];      
  12.         for (int i = 0; i < len; i++) {      
  13.       
  14.             char c = str.charAt(i);      
  15.             buf[pos++] = (byte) (c & 0xFF);      
  16.             buf[pos++] = (byte) (c >> 8);      
  17.         }      
  18.         return buf;      
  19.     }      
  20.       
  21.     public int hashcode(String str) {      
  22.         byte[] bytes = toBytesWithoutEncoding(str);      
  23.         return hash32(bytes, bytes.length);      
  24.     }      
  25.       
  26.     /**   
  27.      *  * Generates 32 bit hash from byte array of the given length and  * seed.   
  28.      *  *  * @param data byte array to hash  * @param length length of the array   
  29.      * to hash  * @param seed initial seed value  * @return 32 bit hash of the   
  30.      * given array     
  31.      */      
  32.     public int hash32(final byte[] data, int length, int seed) {      
  33.         // ‘m’ and ‘r’ are mixing constants generated offline.      
  34.         // They’re not really ‘magic’, they just happen to work well.      
  35.         final int m = 0x5bd1e995;      
  36.         final int r = 24;      
  37.         // Initialize the hash to a random value      
  38.         int h = seed ^ length;      
  39.         int length4 = length / 4;      
  40.         for (int i = 0; i < length4; i++) {      
  41.             final int i4 = i * 4;      
  42.             int k = (data[i4 + 0] & 0xff) + ((data[i4 + 1] & 0xff) << 8)      
  43.                     + ((data[i4 + 2] & 0xff) << 16)      
  44.                     + ((data[i4 + 3] & 0xff) << 24);      
  45.             k *= m;      
  46.             k ^= k >>> r;      
  47.             k *= m;      
  48.             h *= m;      
  49.             h ^= k;      
  50.         }      
  51.         // Handle the last few bytes of the input array      
  52.         switch (length % 4) {      
  53.         case 3:      
  54.             h ^= (data[(length & ~3) + 2] & 0xff) << 16;      
  55.         case 2:      
  56.             h ^= (data[(length & ~3) + 1] & 0xff) << 8;      
  57.         case 1:      
  58.             h ^= (data[length & ~3] & 0xff);      
  59.             h *= m;      
  60.         }      
  61.         h ^= h >>> 13;      
  62.         h *= m;      
  63.         h ^= h >>> 15;      
  64.         return h;      
  65.     }      
  66.       
  67.     /**   
  68.      *  * Generates 32 bit hash from byte array with default seed value.  *  * @param   
  69.      * data byte array to hash  * @param length length of the array to hash  * @return   
  70.      * 32 bit hash of the given array     
  71.      */      
  72.     public int hash32(final byte[] data, int length) {      
  73.         return hash32(data, length, 0x9747b28c);      
  74.     }      
  75.           
  76.     public int hash32(final String data) {      
  77.         byte[] bytes = toBytesWithoutEncoding(data);      
  78.         return hash32(bytes, bytes.length, 0x9747b28c);      
  79.     }      
  80.       
  81.     /**   
  82.      *  * Generates 64 bit hash from byte array of the given length and seed.  *   
  83.      *  * @param data byte array to hash  * @param length length of the array to   
  84.      * hash  * @param seed initial seed value  * @return 64 bit hash of the   
  85.      * given array     
  86.      */      
  87.     public long hash64(final byte[] data, int length, int seed) {      
  88.         final long m = 0xc6a4a7935bd1e995L;      
  89.         final int r = 47;      
  90.         long h = (seed & 0xffffffffl) ^ (length * m);      
  91.         int length8 = length / 8;      
  92.         for (int i = 0; i < length8; i++) {      
  93.             final int i8 = i * 8;      
  94.             long k = ((long) data[i8 + 0] & 0xff)      
  95.                     + (((long) data[i8 + 1] & 0xff) << 8)      
  96.                     + (((long) data[i8 + 2] & 0xff) << 16)      
  97.                     + (((long) data[i8 + 3] & 0xff) << 24)      
  98.                     + (((long) data[i8 + 4] & 0xff) << 32)      
  99.                     + (((long) data[i8 + 5] & 0xff) << 40)      
  100.                     + (((long) data[i8 + 6] & 0xff) << 48)      
  101.                     + (((long) data[i8 + 7] & 0xff) << 56);      
  102.             k *= m;      
  103.             k ^= k >>> r;      
  104.             k *= m;      
  105.             h ^= k;      
  106.             h *= m;      
  107.         }      
  108.         switch (length % 8) {      
  109.         case 7:      
  110.             h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48;      
  111.         case 6:      
  112.             h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40;      
  113.         case 5:      
  114.             h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32;      
  115.         case 4:      
  116.             h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24;      
  117.         case 3:      
  118.             h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16;      
  119.         case 2:      
  120.             h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8;      
  121.         case 1:      
  122.             h ^= (long) (data[length & ~7] & 0xff);      
  123.             h *= m;      
  124.         }      
  125.         ;      
  126.         h ^= h >>> r;      
  127.         h *= m;      
  128.         h ^= h >>> r;      
  129.         return h;      
  130.     }      
  131.       
  132.     /**   
  133.      *  * Generates 64 bit hash from byte array with default seed value.  *  * @param   
  134.      * data byte array to hash  * @param length length of the array to hash  * @return   
  135.      * 64 bit hash of the given string     
  136.      */      
  137.     public long hash64(final byte[] data, int length) {      
  138.         return hash64(data, length, 0xe17a1465);      
  139.     }      
  140.           
  141.           
  142.     public long hash64(final String data) {      
  143.         byte[] bytes = toBytesWithoutEncoding(data);      
  144.         return hash64(bytes, bytes.length);      
  145.     }      
  146. }      
public final class MurmurHash {    

    public MurmurHash() {    

    }    

    private byte[] toBytesWithoutEncoding(String str) {    

        int len = str.length();    
        int pos = 0;    
        byte[] buf = new byte[len << 1];    
        for (int i = 0; i < len; i++) {    

            char c = str.charAt(i);    
            buf[pos++] = (byte) (c & 0xFF);    
            buf[pos++] = (byte) (c >> 8);    
        }    
        return buf;    
    }    

    public int hashcode(String str) {    
        byte[] bytes = toBytesWithoutEncoding(str);    
        return hash32(bytes, bytes.length);    
    }    

    /**  
     *  * Generates 32 bit hash from byte array of the given length and  * seed.  
     *  *  * @param data byte array to hash  * @param length length of the array  
     * to hash  * @param seed initial seed value  * @return 32 bit hash of the  
     * given array    
     */    
    public int hash32(final byte[] data, int length, int seed) {    
        // 'm' and 'r' are mixing constants generated offline.    
        // They're not really 'magic', they just happen to work well.    
        final int m = 0x5bd1e995;    
        final int r = 24;    
        // Initialize the hash to a random value    
        int h = seed ^ length;    
        int length4 = length / 4;    
        for (int i = 0; i < length4; i++) {    
            final int i4 = i * 4;    
            int k = (data[i4 + 0] & 0xff) + ((data[i4 + 1] & 0xff) << 8)    
                    + ((data[i4 + 2] & 0xff) << 16)    
                    + ((data[i4 + 3] & 0xff) << 24);    
            k *= m;    
            k ^= k >>> r;    
            k *= m;    
            h *= m;    
            h ^= k;    
        }    
        // Handle the last few bytes of the input array    
        switch (length % 4) {    
        case 3:    
            h ^= (data[(length & ~3) + 2] & 0xff) << 16;    
        case 2:    
            h ^= (data[(length & ~3) + 1] & 0xff) << 8;    
        case 1:    
            h ^= (data[length & ~3] & 0xff);    
            h *= m;    
        }    
        h ^= h >>> 13;    
        h *= m;    
        h ^= h >>> 15;    
        return h;    
    }    

    /**  
     *  * Generates 32 bit hash from byte array with default seed value.  *  * @param  
     * data byte array to hash  * @param length length of the array to hash  * @return  
     * 32 bit hash of the given array    
     */    
    public int hash32(final byte[] data, int length) {    
        return hash32(data, length, 0x9747b28c);    
    }    

    public int hash32(final String data) {    
        byte[] bytes = toBytesWithoutEncoding(data);    
        return hash32(bytes, bytes.length, 0x9747b28c);    
    }    

    /**  
     *  * Generates 64 bit hash from byte array of the given length and seed.  *  
     *  * @param data byte array to hash  * @param length length of the array to  
     * hash  * @param seed initial seed value  * @return 64 bit hash of the  
     * given array    
     */    
    public long hash64(final byte[] data, int length, int seed) {    
        final long m = 0xc6a4a7935bd1e995L;    
        final int r = 47;    
        long h = (seed & 0xffffffffl) ^ (length * m);    
        int length8 = length / 8;    
        for (int i = 0; i < length8; i++) {    
            final int i8 = i * 8;    
            long k = ((long) data[i8 + 0] & 0xff)    
                    + (((long) data[i8 + 1] & 0xff) << 8)    
                    + (((long) data[i8 + 2] & 0xff) << 16)    
                    + (((long) data[i8 + 3] & 0xff) << 24)    
                    + (((long) data[i8 + 4] & 0xff) << 32)    
                    + (((long) data[i8 + 5] & 0xff) << 40)    
                    + (((long) data[i8 + 6] & 0xff) << 48)    
                    + (((long) data[i8 + 7] & 0xff) << 56);    
            k *= m;    
            k ^= k >>> r;    
            k *= m;    
            h ^= k;    
            h *= m;    
        }    
        switch (length % 8) {    
        case 7:    
            h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48;    
        case 6:    
            h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40;    
        case 5:    
            h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32;    
        case 4:    
            h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24;    
        case 3:    
            h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16;    
        case 2:    
            h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8;    
        case 1:    
            h ^= (long) (data[length & ~7] & 0xff);    
            h *= m;    
        }    
        ;    
        h ^= h >>> r;    
        h *= m;    
        h ^= h >>> r;    
        return h;    
    }    

    /**  
     *  * Generates 64 bit hash from byte array with default seed value.  *  * @param  
     * data byte array to hash  * @param length length of the array to hash  * @return  
     * 64 bit hash of the given string    
     */    
    public long hash64(final byte[] data, int length) {    
        return hash64(data, length, 0xe17a1465);    
    }    


    public long hash64(final String data) {    
        byte[] bytes = toBytesWithoutEncoding(data);    
        return hash64(bytes, bytes.length);    
    }    
}    


2.動靜態資源分離,緩解tomcat服務器壓力

 nginx 這個輕量級、高性能的 web server 主要可以幹兩件事情:

  〉直接作爲http server(代替apache,對PHP需要FastCGI處理器支持);
  〉另外一個功能就是作爲反向代理服務器實現負載均衡

  以下我們就來舉例說明如何使用 nginx 實現負載均衡。因爲nginx在處理併發方面的優勢,現在這個應用非常常見。當然了Apache的 mod_proxy和mod_cache結合使用也可以實現對多臺app server的反向代理和負載均衡,但是在併發處理方面apache還是沒有 nginx擅長。

  1)環境:

  a. 我們本地是Windows系統,然後使用VirutalBox安裝一個虛擬的Linux系統
  在本地的Windows系統上分別安裝nginx(偵聽8080端口)和apache(偵聽80端口)。在虛擬的Linux系統上安裝apache(偵聽80端口)。
  這樣我們相當於擁有了1臺nginx在前端作爲反向代理服務器;後面有2臺apache作爲應用程序服務器(可以看作是小型的server cluster。;-) );

  b. nginx用來作爲反向代理服務器,放置到兩臺apache之前,作爲用戶訪問的入口;
  nginx僅僅處理靜態頁面,動態的頁面(php請求)統統都交付給後臺的兩臺apache來處理。
  也就是說,可以把我們網站的靜態頁面或者文件放置到nginx的目錄下;動態的頁面和數據庫訪問都保留到後臺的apache服務器上。

  c. 如下介紹兩種方法實現server cluster的負載均衡。
  我們假設前端nginx(爲127.0.0.1:80)僅僅包含一個靜態頁面index.html;
  後臺的兩個apache服務器(分別爲localhost:80和158.37.70.143:80),一臺根目錄放置phpMyAdmin文件夾和test.php(裏面測試代碼爲print “server1“;),另一臺根目錄僅僅放置一個test.php(裏面測試代碼爲 print “server2“;)。

  2)針對不同請求 的負載均衡:

  a. 在最簡單地構建反向代理的時候 (nginx僅僅處理靜態不處理動態內容,動態內容交給後臺的apache server來處理),我們具體的設置爲:在nginx.conf中修改:
  複製代碼 代碼如下:

  location ~ .php$ {
  proxy_pass 158.37.70.143:80 ;
  }

  〉 這樣當客戶端訪問localhost:8080/index.html的時候,前端的nginx會自動進行響應;
  〉當用戶訪問localhost:8080/test.php的時候(這個時候nginx目錄下根本就沒有該文件),但是通過上面的設置 location ~ .php$(表示正則表達式匹配以.php結尾的文件,詳情參看location是如何定義和匹配的 http://wiki.nginx.org/NginxHttpCoreModule) ,nginx服務器會自動pass給 158.37.70.143的apache服務器了。該服務器下的test.php就會被自動解析,然後將html的結果頁面返回給nginx,然後 nginx進行顯示(如果nginx使用memcached模塊或者squid還可以支持緩存),輸出結果爲打印server2。

發佈了16 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章