在java web項目開發者,最難解決的是高并發問題,我為搞并發解決方案,想出了一個解決方案。
a.應用層面:讀寫分離、緩存、隊列、集群、令牌、系統拆分、隔離、系統升級(可水平擴容方向)。
b.時間換空間:降低單次請求時間,這樣在單位時間內系統并發就會提升。
c.空間換時間:拉長整體處理業務時間,換取后臺系統容量空間。
使用Redis作為緩存服務器的,剛開始的時候會滿足需要,隨著項目的增大緩存數據的增多就會查詢和插入更慢這時就要考慮Redis集群方案了
使用Redis分布式要保證數據都能能夠平均的緩存到每一臺機器,首先想到的做法是對數據進行分片,因為Redis是key-value存儲的,首先想到的是Hash分片,可能的做法是對key進行哈希運算,得到一個long值對分布式的數量取模會得到一個一個對應數據庫的一個映射,沒有讀取就可以定位到這臺數據庫,那么速度但然會提升了。
但是取模的hash算法是有問題的如果集群數量不變的話沒有什么問題,一旦增加一臺機器或者一臺機器掛掉,導致機器數量變化,就會導致計算的出的數據庫映射亂掉,不能正確存取數據了。
因為這個問題引入我們說的一致性哈希算法,這個哈希算法具有的特征
1.均衡性:也有人把它定義為平衡性,是指哈希的結果能夠盡可能分布到所有的節點中去,這樣可以有效的利用每個節點上的資源。
2.單調性:對于單調性有很多翻譯讓我非常的不解,而我想要的是當節點數量變化時哈希的結果應盡可能的保護已分配的內容不會被重新分派到新的節點。
3.分散性和負載:這兩個其實是差不多的意思,就是要求一致性哈希算法對 key 哈希應盡可能的避免重復。一致性哈希就數據結構是創建一個排序的環形數據結構,有許多個區域,先讓每一臺服務器都分布環上,取每一個服務器的特效做哈希運行,得到的值放進環中,進行排序這樣就能根據哈希特征找到對應的真是服務器,能夠讓把服務器平均的分布到環上。第一個特征均衡性:就是盡量的讓數據平均的分部到每一個服務器,不讓某臺機器壓力特別打,或者干脆沒活干,因為這個原因,我們的每一個服務器都添加幾個虛擬服務器,比如真是服務器叫node1那么第一個服務器的虛擬服務器就叫node1-1,node1-2…,根據這些特征進行哈希運算也分布到環中,這樣就能把服務器平均的分布到環中。第二個特征單調性:因為服務器都在環中,數據的key進行哈希運算得到一個值,跟環中的服務器的哈希值進行比較,取離當前值最接近的哈希值對象的服務器,這樣就是獲取服務器的原理了,我們是做了一個偷懶的工作,服務器哈希進行排序,以順時針方式得到一個剛好大于key哈希的服務器。單調性是在不管添加節點還是刪除節點,原來對應的服務器不變,因為這個環很大,服務器是零星分布的,這樣增加或者刪除一個節點只有受影響的都是當前節點,但是key對應的數據庫是不變的,也不能說不變,是把變化變得盡可能的小。第三個特征分散性和負載:指服務器在環中盡可能的分散,盡可能的讓數據平均分布到不同的服務器,我們就是使用虛擬節點的方式解決的。
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。   服務器架構圖
   
??                  </div>                </dd>              </dl>            </div>            <div class="skin_nav">                         <div class="skin_edit"><i class="fa fa-th-list"></i>                 <div class="nav_list nav_list_edit"><a href="/qq_31065001">文章列表</a></div>              </div>            </div>            <div class="rssFix">                <a  class="column"  onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_RSS'])"><i class="fa fa-rss"></i></a>            </div>                    <div id="goTopBtn" class="skin_backTop"><i class="fa fa-chevron-up"></i></div>    </div>    <div class="detail_b">      <p class="reprint_copy" style="display:none" id="copyright">版權聲明:本文為博主原創文章,未經博主允許不得轉載。</p>  
新聞熱點
疑難解答