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

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

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

    一、一般來說nginx 配置文件中對優化比較有作用的為以下幾項:

    1.  worker_processes 8;

    nginx 進程數,建議按照cpu 數目來指定,一般為它的倍數 (如,2個四核的cpu計為8)。

    2.  worker_cpu_affinity 00000001 0000001000000100 00001000 00010000 00100000 01000000 10000000;

    為每個進程分配cpu,上例中將8 個進程分配到8 個cpu,當然可以寫多個,或者將一
    個進程分配到多個cpu。

    3.  worker_rlimit_nofile65535;

    這個指令是指當一個nginx 進程打開的最多文件描述符數目,理論值應該是最多打開文
    件數(ulimit -n)與nginx 進程數相除,但是nginx 分配請求并不是那么均勻,所以最好與ulimit -n的值保持一致。

    現在在linux2.6內核下開啟文件打開數為65535,worker_rlimit_nofile就相應應該填寫65535。

    這是因為nginx調度時分配請求到進程并不是那么的均衡,所以假如填寫10240,總并發量達到3-4萬時就有進程可能超過10240了,這時會返回502錯誤。

    查看linux系統文件描述符的方法:

    [root@web001 ~]# sysctl -a | grep fs.file

    fs.file-max = 789972

    fs.file-nr = 510 0 789972

    4.  useepoll;

    使用epoll 的I/O 模型

    (

    補充說明:

    與apache相類,nginx針對不同的操作系統,有不同的事件模型

         A)標準事件模型
         Select、poll屬于標準事件模型,如果當前系統不存在更有效的方法,nginx會選擇select或poll
         B)高效事件模型  
    Kqueue:使用于 FreeBSD 4.1+, OpenBSD 2.9+, NetBSD2.0 和 MacOS X. 使用雙處理器的MacOS X系統使用kqueue可能會造成內核崩潰。
    Epoll: 使用于Linux內核2.6版本及以后的系統。

    /dev/poll:使用于 Solaris 7 11/99+, HP/UX 11.22+(eventport), IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+。

           Eventport:使用于 Solaris 10. 為了防止出現內核崩潰的問題,有必要安裝安全補丁。

    )

    5.  worker_connections65535;

    每個進程允許的最多連接數, 理論上每臺nginx服務器的最大連接數為worker_processes*worker_connections。

    6.  keepalive_timeout60;

    keepalive 超時時間。

    7.  client_header_buffer_size4k;

    客戶端請求頭部的緩沖區大小,這個可以根據你的系統分頁大小來設置,一般一個請求頭的大小不會超過1k,不過由于一般系統分頁都要大于1k,所以這里設置為分頁大小。 

    分頁大小可以用命令getconf PAGESIZE 取得。

    [root@web001 ~]# getconf PAGESIZE 

    4096

    但也有client_header_buffer_size超過4k的情況,但是client_header_buffer_size該值必須設置為“系統分頁大小”的整倍數。

    8.  open_file_cachemax=65535 inactive=60s;

    這個將為打開文件指定緩存,默認是沒有啟用的,max 指定緩存數量,建議和打開文件數一致,inactive是指經過多長時間文件沒被請求后刪除緩存。

    9.  open_file_cache_valid80s;

    這個是指多長時間檢查一次緩存的有效信息。

    10.  open_file_cache_min_uses1;

    open_file_cache 指令中的inactive參數時間內文件的最少使用次數,如果超過這個數字,文件描述符一直是在緩存中打開的,如上例,如果有一個文件在inactive時間內一次沒被使用,它將被移除。


    二、關于內核參數的優化:

    net.ipv4.tcp_max_tw_buckets = 6000

    timewait 的數量,默認是180000。

    net.ipv4.ip_local_port_range = 1024 65000

    允許系統打開的端口范圍。

    net.ipv4.tcp_tw_recycle = 1

    啟用timewait 快速回收。

    net.ipv4.tcp_tw_reuse = 1

    開啟重用。允許將TIME-WAIT sockets 重新用于新的TCP 連接。

    net.ipv4.tcp_syncookies = 1

    開啟SYN Cookies,當出現SYN 等待隊列溢出時,啟用cookies 來處理。

    net.core.somaxconn = 262144

    web 應用中listen 函數的backlog 默認會給我們內核參數的net.core.somaxconn限制到128,而nginx 定義的NGX_LISTEN_BACKLOG 默認為511,所以有必要調整這個值。

    net.core.netdev_max_backlog = 262144

    每個網絡接口接收數據包的速率比內核處理這些包的速率快時,允許送到隊列的數據包的最大數目。

    net.ipv4.tcp_max_orphans = 262144

    系統中最多有多少個TCP套接字不被關聯到任何一個用戶文件句柄上。如果超過這個數字,孤兒連接將即刻被復位并打印出警告信息。這個限制僅僅是為了防止簡單的DoS攻擊,不能過分依靠它或者人為地減小這個值,更應該增加這個值(如果增加了內存之后)。

    net.ipv4.tcp_max_syn_backlog = 262144

    記錄的那些尚未收到客戶端確認信息的連接請求的最大值。對于有128M內存的系統而言,缺省值是1024,小內存的系統則是128。

    net.ipv4.tcp_timestamps = 0

    時間戳可以避免序列號的卷繞。一個1Gbps的鏈路肯定會遇到以前用過的序列號。時間戳能夠讓內核接受這種“異常”的數據包。這里需要將其關掉。

    net.ipv4.tcp_synack_retries = 1

    為了打開對端的連接,內核需要發送一個SYN 并附帶一個回應前面一個SYN的ACK。也就是所謂三次握手中的第二次握手。這個設置決定了內核放棄連接之前發送SYN+ACK 包的數量。

    net.ipv4.tcp_syn_retries = 1

    在內核放棄建立連接之前發送SYN 包的數量。

    net.ipv4.tcp_fin_timeout = 1

    如果套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間。對端可以出錯并永遠不關閉連接,甚至意外當機。缺省值是60 秒。2.2 內核的通常值是180秒,3你可以按這個設置,但要記住的是,即使你的機器是一個輕載的WEB 服務器,也有因為大量的死套接字而內存溢出的風險,FIN-WAIT-2 的危險性比FIN-WAIT-1 要小,因為它最多只能吃掉1.5K 內存,但是它們的生存期長些。

    net.ipv4.tcp_keepalive_time = 30

    當keepalive 起用的時候,TCP 發送keepalive 消息的頻度。缺省是2 小時。


    三、下面貼一個完整的內核優化設置:

    vi /etc/sysctl.conf CentOS5.5中可以將所有內容清空直接替換為如下內容:

    net.ipv4.ip_forward = 0
    net.ipv4.conf.default.rp_filter = 1
    net.ipv4.conf.default.accept_source_route = 0
    kernel.sysrq = 0
    kernel.core_uses_pid = 1
    net.ipv4.tcp_syncookies = 1
    kernel.msgmnb = 65536
    kernel.msgmax = 65536
    kernel.shmmax = 68719476736
    kernel.shmall = 4294967296
    net.ipv4.tcp_max_tw_buckets = 6000
    net.ipv4.tcp_sack = 1
    net.ipv4.tcp_window_scaling = 1
    net.ipv4.tcp_rmem = 4096 87380 4194304
    net.ipv4.tcp_wmem = 4096 16384 4194304
    net.core.wmem_default = 8388608
    net.core.rmem_default = 8388608
    net.core.rmem_max = 16777216
    net.core.wmem_max = 16777216
    net.core.netdev_max_backlog = 262144
    net.core.somaxconn = 262144
    net.ipv4.tcp_max_orphans = 3276800
    net.ipv4.tcp_max_syn_backlog = 262144
    net.ipv4.tcp_timestamps = 0
    net.ipv4.tcp_synack_retries = 1
    net.ipv4.tcp_syn_retries = 1
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_mem = 94500000 915000000 927000000
    net.ipv4.tcp_fin_timeout = 1
    net.ipv4.tcp_keepalive_time = 30
    net.ipv4.ip_local_port_range = 1024 65000

    使配置立即生效可使用如下命令:
    /sbin/sysctl -p

    四、下面是關于系統連接數的優化

    linux 默認值 open files 和 max user processes 為1024

    #ulimit -n

    1024

    #ulimit –u

    1024

    問題描述: 說明 server 只允許同時打開 1024 個文件,處理 1024個用戶進程

    使用ulimit -a 可以查看當前系統的所有限制值,使用ulimit -n 可以查看當前的最大打開文件數。

    新裝的linux 默認只有1024 ,當作負載較大的服務器時,很容易遇到error: too many open files。因此,需要將其改大。

     

    解決方法:

    使用 ulimit –n 65535 可即時修改,但重啟后就無效了。(注ulimit -SHn 65535 等效 ulimit-n 65535 ,-S 指soft ,-H 指hard)

    有如下三種修改方式:

    1. 在/etc/rc.local 中增加一行 ulimit -SHn 65535
    2. 在/etc/profile 中增加一行 ulimit -SHn 65535
    3. /etc/security/limits.conf最后增加:

    * soft nofile 65535
    * hard nofile 65535
    * soft nproc 65535
    * hard nproc 65535

    具體使用哪種,在 CentOS 中使用第1 種方式無效果,使用第3 種方式有效果,而在Debian 中使用第2種有效果

     # ulimit -n

    65535

    # ulimit -u

    65535

     

    備注:ulimit 命令本身就有分軟硬設置,加-H 就是硬,加-S 就是軟默認顯示的是軟限制

    soft 限制指的是當前系統生效的設置值。 hard 限制值可以被普通用戶降低。但是不能增加。 soft 限制不能設置的比hard 限制更高。 只有 root 用戶才能夠增加 hard 限制值。


    五、下面是一個簡單的nginx 配置文件:

    user www www;
    worker_processes 8;
    worker_cpu_affinity 00000001 00000010 00000100 00001000 0001000000100000
    01000000;
    error_log /www/log/nginx_error.log crit;
    pid /usr/local/nginx/nginx.pid;
    worker_rlimit_nofile 204800;
    events
    {
    use epoll;
    worker_connections 204800;
    }
    http
    {
    include mime.types;
    default_type application/octet-stream;
    charset utf-8;
    server_names_hash_bucket_size 128;
    client_header_buffer_size 2k;
    large_client_header_buffers 4 4k;
    client_max_body_size 8m;
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 60;
    fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2
    keys_zone=TEST:10m
    inactive=5m;
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_buffer_size 4k;
    fastcgi_buffers 8 4k;
    fastcgi_busy_buffers_size 8k;
    fastcgi_temp_file_write_size 8k;
    fastcgi_cache TEST;
    fastcgi_cache_valid 200 302 1h;
    fastcgi_cache_valid 301 1d;
    fastcgi_cache_valid any 1m;
    fastcgi_cache_min_uses 1;
    fastcgi_cache_use_stale error timeout invalid_headerhttp_500;
    open_file_cache max=204800 inactive=20s;
    open_file_cache_min_uses 1;
    open_file_cache_valid 30s;
    tcp_nodelay on;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_types text/plain application/x-javascript text/cssapplication/xml;
    gzip_vary on;
    server
    {
    listen 8080;
    server_name backup.aiju.com;
    index index.php index.htm;
    root /www/html/;
    location /status
    {
    stub_status on;
    }
    location ~ .*\.(php|php5)?$
    {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fcgi.conf;
    }
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
    {
    expires 30d;
    }
    log_format access '$remote_addr -- $remote_user [$time_local]"$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" $http_x_forwarded_for';
    access_log /www/log/access.log access;
    }
    }

    六、關于FastCGI 的幾個指令:

    fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2keys_zone=TEST:10minactive=5m;

    這個指令為FastCGI 緩存指定一個路徑,目錄結構等級,關鍵字區域存儲時間和非活動刪除時間。

    fastcgi_connect_timeout 300;

    指定連接到后端FastCGI 的超時時間。

    fastcgi_send_timeout 300;

    向FastCGI 傳送請求的超時時間,這個值是指已經完成兩次握手后向FastCGI 傳送請求的超時時間。

    fastcgi_read_timeout 300;

    接收FastCGI 應答的超時時間,這個值是指已經完成兩次握手后接收FastCGI 應答的超時時間。

    fastcgi_buffer_size 4k;

    指定讀取FastCGI應答第一部分需要用多大的緩沖區,一般第一部分應答不會超過1k,由于頁面大小為4k,所以這里設置為4k。

    fastcgi_buffers 8 4k;

    指定本地需要用多少和多大的緩沖區來緩沖FastCGI 的應答。

    fastcgi_busy_buffers_size 8k;

    這個指令我也不知道是做什么用,只知道默認值是fastcgi_buffers 的兩倍。

    fastcgi_temp_file_write_size 8k;

    在寫入fastcgi_temp_path 時將用多大的數據塊,默認值是fastcgi_buffers 的兩倍。

    fastcgi_cache TEST

    開啟FastCGI 緩存并且為其制定一個名稱。個人感覺開啟緩存非常有用,可以有效降低CPU 負載,并且防止502 錯誤。

    fastcgi_cache_valid 200 302 1h;
    fastcgi_cache_valid 301 1d;
    fastcgi_cache_valid any 1m;

    為指定的應答代碼指定緩存時間,如上例中將200,302 應答緩存一小時,301 應答緩存1 天,其他為1 分鐘。

    fastcgi_cache_min_uses 1;

    緩存在fastcgi_cache_path 指令inactive 參數值時間內的最少使用次數,如上例,如果在5 分鐘內某文件1次也沒有被使用,那么這個文件將被移除。

    fastcgi_cache_use_stale error timeout invalid_headerhttp_500;

    不知道這個參數的作用,猜想應該是讓nginx 知道哪些類型的緩存是沒用的。以上為nginx 中FastCGI相關參數,另外,FastCGI 自身也有一些配置需要進行優化,如果你使用php-fpm來管理FastCGI,可以修改配置文件中的以下值:

    <valuename="max_children">60</value>

    同時處理的并發請求數,即它將開啟最多60 個子線程來處理并發連接。

    <valuename="rlimit_files">102400</value>

    最多打開文件數。

    <valuename="max_requests">204800</value>

    每個進程在重置之前能夠執行的最多請求數。

    posted @ 2013-02-01 10:44 小馬歌 閱讀(4472) | 評論 (0)編輯 收藏
     
         摘要: 前端時間對公司已有項目JavaScript代碼進行優化,本文的是對優化工作的一個總結,拿出來與大家分享。當然我的優化方式可能并不是最優的,或者說有些不對的地方,請指教?! avaScript優化總結分為以下幾點優化前后對比優化前優化后代碼混亂,同樣功能的函數重復出現在多個地方。如果需要修改實現,需要找到所有的地方。牽一發而動全身模塊化,提取公共接口組織為庫、結構清晰、方便代碼重用、并且能夠游戲防...  閱讀全文
    posted @ 2013-01-24 14:17 小馬歌 閱讀(409) | 評論 (1)編輯 收藏
     
         摘要: from:http://developer.yahoo.com/performance/rules.htmlMinimize HTTP Requeststag: content80% of the end-user response time is spent on the front-end. Most of this time is tied up in downloading all the...  閱讀全文
    posted @ 2013-01-24 14:16 小馬歌 閱讀(272) | 評論 (0)編輯 收藏
     

    在JavaScript開發中,被人問到:null與undefined到底有啥區別?

        一時間不好回答,特別是undefined,因為這涉及到undefined的實現原理。于是,細想之后,寫下本文,請各位大俠拍磚。

        總所周知:null == undefined

        但是:null !== undefined

        那么這兩者到底有啥區別呢?

        請聽俺娓娓道來...

    null

        這是一個對象,但是為空。因為是對象,所以 typeof null  返回 'object' 。

        null 是 JavaScript 保留關鍵字。

        null 參與數值運算時其值會自動轉換為 0 ,因此,下列表達式計算后會得到正確的數值:

        表達式:123 + null    結果值:123

        表達式:123 * null    結果值:0

    undefined

      undefined是全局對象(window)的一個特殊屬性,其值是未定義的。但 typeof undefined 返回 'undefined' 。

          雖然undefined是有特殊含義的,但它確實是一個屬性,而且是全局對象(window)的屬性。請看下面的代碼:

        alert('undefined' in window);   //輸出:true

         var anObj = {};
         alert('undefined' in anObj);    //輸出:false

     
    從中可以看出,undefined是window對象的一個屬性,但卻不是anObj對象的一個屬性。

      注意:盡管undefined是有特殊含義的屬性,但卻不是JavaScript的保留關鍵字。

      undefined參與任何數值計算時,其結果一定是NaN。

      隨便說一下,NaN是全局對象(window)的另一個特殊屬性,Infinity也是。這些特殊屬性都不是JavaScript的保留關鍵字!

    提高undefined性能

      當我們在程序中使用undefined值時,實際上使用的是window對象的undefined屬性。

      同樣,當我們定義一個變量但未賦予其初始值,例如:

        var aValue;

      這時,JavaScript在所謂的預編譯時會將其初始值設置為對window.undefined屬性的引用,

      于是,當我們將一個變量或值與undefined比較時,實際上是與window對象的undefined屬性比較。這個比較過程中,JavaScript會搜索window對象名叫‘undefined'的屬性,然后再比較兩個操作數的引用指針是否相同。

      由于window對象的屬性值是非常多的,在每一次與undefined的比較中,搜索window對象的undefined屬性都會花費時 間。在需要頻繁與undefined進行比較的函數中,這可能會是一個性能問題點。因此,在這種情況下,我們可以自行定義一個局部的undefined變 量,來加快對undefined的比較速度。例如:

        function anyFunc()
        {
            var undefined;          //自定義局部undefined變量
            
            if(x == undefined)      //作用域上的引用比較
            
            
            while(y != undefined)   //作用域上的引用比較
            
        };
        其中,定義undefined局部變量時,其初始值會是對window.undefined屬性值的引用。新定義的局部undefined變量存在與 該函數的作用域上。在隨后的比較操作中,JavaScript代碼的書寫方式沒有任何的改變,但比較速度卻很快。因為作用域上的變量數量會遠遠少于 window對象的屬性,搜索變量的速度會極大提高。

      這就是許多前端JS框架為什么常常要自己定義一個局部undefined變量的原因!

     

    本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/leadzen/archive/2009/02/17/3899392.aspx

     

    第二篇 區別Javascript中的Null與Undefined

    在JavaScript中存在這樣兩種原始類型:Null與Undefined。這兩種類型常常會使JavaScript的開發人員產生疑惑,在什么時候是Null,什么時候又是Undefined?

    Undefined類型只有一個值,即undefined。當聲明的變量還未被初始化時,變量的默認值為undefined。

    Null類型也只有一個值,即null。null用來表示尚未存在的對象,常用來表示函數企圖返回一個不存在的對象。

    js 代碼


    var oValue; 
    alert(oValue == undefined); //output "true"

    這段代碼顯示為true,代表oVlaue的值即為undefined,因為我們沒有初始化它。

    js 代碼


    alert(null == document.getElementById('notExistElement'));

    當頁面上不存在id為”notExistElement”的DOM節點時,這段代碼顯示為”true”,因為我們嘗試獲取一個不存在的對象。

    js 代碼


    alert(typeof undefined); //output "undefined" 
    alert(typeof null); //output "object"

    第一行代碼很容易理解,undefined的類型為Undefined;第二行代碼卻讓人疑惑,為什么null的類型又是Object了呢?其實這 是 JavaScript最初實現的一個錯誤,后來被ECMAScript沿用下來。在今天我們可以解釋為,null即是一個不存在的對象的占位符,但是在實 際編碼時還是要注意這一特性。

    js 代碼


    alert(null == undefined); //output "true"

    ECMAScript認為undefined是從null派生出來的,所以把它們定義為相等的。但是,如果在一些情況下,我們一定要區分這兩個值,那應該怎么辦呢?可以使用下面的兩種方法。

    js 代碼


    alert(null === undefined); //output "false" 
    alert(typeof null == typeof undefined); //output "false"

    使用typeof方法在前面已經講過,null與undefined的類型是不一樣的,所以輸出”false”。而===代表絕對等于,在這里null === undefined輸出false。

    以上是轉載內容:

    document.getElementById('不存在的元素')返回的是null

    document.getElementById('不存在的元素').value返回的是undefined

    jqury-->ajax(post,getJSON)函數中如果{id:undefined}的話,則此參數id不會提交到服務器中,{id:null}會提交到服務器中,如果是將參數直接寫到url中則也會提交到服務器上

    分類: JS
    posted @ 2013-01-24 13:02 小馬歌 閱讀(459) | 評論 (1)編輯 收藏
     
    因為linux系統里都是以文件來表示的,所以在做搞并發的web系統時,修改文件句柄限制那是必須的。 
     
    查看某個進程使用了的文件句柄數 
    # sudo ps -ef | grep java 
    root      1663     1  0 02:37 ?        00:00:43 /opt/bitnami/java/bin/java 
    從結果中可得知進程的pid,然后根據pid查看該進程當前使用的文件句柄數 
    #sudo ls /proc/1663/fd | wc -l 
    1022 
    發現句柄數一直就停留在1022,同時top查看發現load持續增高。于是先查看了下當前進程的句柄數限制。 
    #sudo cat /proc/1663/limits | grep "files" 
    Max open files            1024                1024                files 
    發現限制卡在這了,于是修改/etc/sysctl.conf文件,添加一行如下: 
    fs.file-max=30720 
    保存。并執行 
    #sudo sysctl -p 
    然后再查看就發現已經改過來了。 
    Google發現linux系統里有兩種文件句柄限制,一種是系統級的,一種是用戶級的。 
    修改系統級的:  www.2cto.com
    #echo "30720" > /proc/sys/fs/file-max 
    修改用戶級的: 
    #sudo vi /etc/security/limits.conf 
    增加如下行: 
    * soft nofile 2048 
    * hard nofile 32768 
    星號表示任何用戶,soft/hard表示軟限制、硬限制。修改/etc/pam.d/login文件指定使用PAM 
    session required /lib/security/pam_limits.so 
    不過需要注意的是系統是32位還是64位,若是64位的那就是/lib64/security/pam_limits.so 
    上面的修改后,reboot,再次查看 
    #ulimit -n 
    30720 
    修改成功。
    posted @ 2013-01-10 13:58 小馬歌 閱讀(773) | 評論 (0)編輯 收藏
     

    from:http://blog.jobbole.com/1194/ 
            淘寶網擁有國內最具商業價值的海量數據。截至當前,每天有超過30億的店鋪、商品瀏覽記錄,10億在線商品數,上千萬的成交、收藏和評價數據。如何從這些數據中挖掘出真正的商業價值,進而幫助淘寶、商家進行企業的數據化運營,幫助消費者進行理性的購物決策,是淘寶數據平臺與產品部的使命。

    為此,我們進行了一系列數據產品的研發,比如為大家所熟知的量子統計、數據魔方和淘寶指數等。盡管從業務層面來講,數據產品的研發難度并不高;但在 “海量”的限定下,數據產品的計算、存儲和檢索難度陡然上升。本文將以數據魔方為例,向大家介紹淘寶在海量數據產品技術架構方面的探索。

    淘寶海量數據產品技術架構

    數據產品的一個最大特點是數據的非實時寫入,正因為如此,我們可以認為,在一定的時間段內,整個系統的數據是只讀的。這為我們設計緩存奠定了非常重要的基礎。

    淘寶海量數據產品技術架構
    圖1 淘寶海量數據產品技術架構

     

    按照數據的流向來劃分,我們把淘寶數據產品的技術架構分為五層(如圖1所示),分別是數據源、計算層、存儲層、查詢層和產品層。位于架構頂端的是我們的數據來源層,這里有淘寶主站的用戶、店鋪、商品和交易等數據庫,還有用戶的瀏覽、搜索等行為日志等。這一系列的數據是數據產品最原始的生命力所在。

    在數據源層實時產生的數據,通過淘寶自主研發的數據傳輸組件DataX、DbSync和Timetunnel準實時地傳輸到一個有1500個節點的 Hadoop集群上,這個集群我們稱之為“云梯”,是計算層的主要組成部分。在“云梯”上,我們每天有大約40000個作業對1.5PB的原始數據按照產品需求進行不同的MapReduce計算。這一計算過程通常都能在凌晨兩點之前完成。相對于前端產品看到的數據,這里的計算結果很可能是一個處于中間狀態的結果,這往往是在數據冗余與前端計算之間做了適當平衡的結果。

    不得不提的是,一些對實效性要求很高的數據,例如針對搜索詞的統計數據,我們希望能盡快推送到數據產品前端。這種需求再采用“云梯”來計算效率將是比較低的,為此我們做了流式數據的實時計算平臺,稱之為“銀河”。“銀河”也是一個分布式系統,它接收來自TimeTunnel的實時消息,在內存中做實時計算,并把計算結果在盡可能短的時間內刷新到NoSQL存儲設備中,供前端產品調用。

    容易理解,“云梯”或者“銀河”并不適合直接向產品提供實時的數據查詢服務。這是因為,對于“云梯”來說,它的定位只是做離線計算的,無法支持較高的性能和并發需求;而對于“銀河”而言,盡管所有的代碼都掌握在我們手中,但要完整地將數據接收、實時計算、存儲和查詢等功能集成在一個分布式系統中,避免不了分層,最終仍然落到了目前的架構上。

    為此,我們針對前端產品設計了專門的存儲層。在這一層,我們有基于MySQL的分布式關系型數據庫集群MyFOX和基于HBase的NoSQL存儲集群Prom,在后面的文字中,我將重點介紹這兩個集群的實現原理。除此之外,其他第三方的模塊也被我們納入存儲層的范疇。

    存儲層異構模塊的增多,對前端產品的使用帶來了挑戰。為此,我們設計了通用的數據中間層——glider——來屏蔽這個影響。glider以HTTP協議對外提供restful方式的接口。數據產品可以通過一個唯一的URL獲取到它想要的數據。

    以上是淘寶海量數據產品在技術架構方面的一個概括性的介紹,接下來我將重點從四個方面闡述數據魔方設計上的特點。

    關系型數據庫仍然是王道

    關系型數據庫(RDBMS)自20世紀70年代提出以來,在工業生產中得到了廣泛的使用。經過三十多年的長足發展,誕生了一批優秀的數據庫軟件,例如Oracle、MySQL、DB2、Sybase和SQL Server等。

    圖2 MyFOX中的數據增長曲線
    圖2 MyFOX中的數據增長曲線

     

    盡管相對于非關系型數據庫而言,關系型數據庫在分區容忍性(Tolerance to Network Partitions)方面存在劣勢,但由于它強大的語義表達能力以及數據之間的關系表達能力,在數據產品中仍然占據著不可替代的作用。

    淘寶數據產品選擇MySQL的MyISAM引擎作為底層的數據存儲引擎。在此基礎上,為了應對海量數據,我們設計了分布式MySQL集群的查詢代理層——MyFOX,使得分區對前端應用透明。

    圖3 MyFOX的數據查詢過程
    圖3 MyFOX的數據查詢過程

     

    目前,存儲在MyFOX中的統計結果數據已經達到10TB,占據著數據魔方總數據量的95%以上,并且正在以每天超過6億的增量增長著(如圖2所示)。這些數據被我們近似均勻地分布到20個MySQL節點上,在查詢時,經由MyFOX透明地對外服務(如圖3所示)。

    圖4 MyFOX節點結構
    圖4 MyFOX節點結構

     

    值得一提的是,在MyFOX現有的20個節點中,并不是所有節點都是“平等”的。一般而言,數據產品的用戶更多地只關心“最近幾天”的數據,越早的數據,越容易被冷落。為此,出于硬件成本考慮,我們在這20個節點中分出了“熱節點”和“冷節點”(如圖4所示)。

    顧名思義,“熱節點”存放最新的、被訪問頻率較高的數據。對于這部分數據,我們希望能給用戶提供盡可能快的查詢速度,所以在硬盤方面,我們選擇了每分鐘15000轉的SAS硬盤,按照一個節點兩臺機器來計算,單位數據的存儲成本約為4.5W/TB。相對應地,“冷數據”我們選擇了每分鐘7500轉的 SATA硬盤,單碟上能夠存放更多的數據,存儲成本約為1.6W/TB。

    將冷熱數據進行分離的另外一個好處是可以有效降低內存磁盤比。從圖4可以看出,“熱節點”上單機只有24GB內存,而磁盤裝滿大約有 1.8TB(300 * 12 * 0.5 / 1024),內存磁盤比約為4:300,遠遠低于MySQL服務器的一個合理值。內存磁盤比過低導致的后果是,總有一天,即使所有內存用完也存不下數據的索引了——這個時候,大量的查詢請求都需要從磁盤中讀取索引,效率大打折扣。
    NoSQL是SQL的有益補充

    在MyFOX出現之后,一切都看起來那么完美,開發人員甚至不會意識到MyFOX的存在,一條不用任何特殊修飾的SQL語句就可以滿足需求。這個狀態持續了很長一段時間,直到有一天,我們碰到了傳統的關系型數據庫無法解決的問題——全屬性選擇器(如圖5所示)。

    圖5 全屬性選擇器
    圖5 全屬性選擇器

     

    這是一個非常典型的例子。為了說明問題,我們仍然以關系型數據庫的思路來描述。對于筆記本電腦這個類目,用戶某一次查詢所選擇的過濾條件可能包括 “筆記本尺寸”、“筆記本定位”、“硬盤容量”等一系列屬性(字段),并且在每個可能用在過濾條件的屬性上,屬性值的分布是極不均勻的。在圖5中我們可以看到,筆記本電腦的尺寸這一屬性有著10個枚舉值,而“藍牙功能”這個屬性值是個布爾值,數據的篩選性非常差。

    在用戶所選擇的過濾條件不確定的情況下,解決全屬性問題的思路有兩個:一個是窮舉所有可能的過濾條件組合,在“云梯”上進行預先計算,存入數據庫供查詢;另一個是存儲原始數據,在用戶查詢時根據過濾條件篩選出相應的記錄進行現場計算。很明顯,由于過濾條件的排列組合幾乎是無法窮舉的,第一種方案在現實中是不可取的;而第二種方案中,原始數據存儲在什么地方?如果仍然用關系型數據庫,那么你打算怎樣為這個表建立索引?

    這一系列問題把我們引到了“創建定制化的存儲、現場計算并提供查詢服務的引擎”的思路上來,這就是Prometheus(如圖6所示)。

    圖6 Prom的存儲結構
    圖6 Prom的存儲結構

     

    從圖6可以看出,我們選擇了HBase作為Prom的底層存儲引擎。之所以選擇HBase,主要是因為它是建立在HDFS之上的,并且對于 MapReduce有良好的編程接口。盡管Prom是一個通用的、解決共性問題的服務框架,但在這里,我們仍然以全屬性選擇為例,來說明Prom的工作原理。這里的原始數據是前一天在淘寶上的交易明細,在HBase集群中,我們以屬性對(屬性與屬性值的組合)作為row-key進行存儲。而row-key 對應的值,我們設計了兩個column-family,即存放交易ID列表的index字段和原始交易明細的data字段。在存儲的時候,我們有意識地讓每個字段中的每一個元素都是定長的,這是為了支持通過偏移量快速地找到相應記錄,避免復雜的查找算法和磁盤的大量隨機讀取請求。

    圖7 Prom查詢過程
    圖7 Prom查詢過程

     

    圖7用一個典型的例子描述的Prom在提供查詢服務時的工作原理,限于篇幅,這里不做詳細描述。值得一提的是,Prom支持的計算并不僅限于求和 SUM運算,統計意義上的常用計算都是支持的。在現場計算方面,我們對Hbase進行了擴展,Prom要求每個節點返回的數據是已經經過“本地計算”的局部最優解,最終的全局最優解只是各個節點返回的局部最優解的一個簡單匯總。很顯然,這樣的設計思路是要充分利用各個節點的并行計算能力,并且避免大量明細數據的網絡傳輸開銷。

    用中間層隔離前后端

    上文提到過,MyFOX和Prom為數據產品的不同需求提供了數據存儲和底層查詢的解決方案,但隨之而來的問題是,各種異構的存儲模塊給前端產品的使用帶來了很大的挑戰。并且,前端產品的一個請求所需要的數據往往不可能只從一個模塊獲取。

    舉個例子,我們要在數據魔方中看昨天做熱銷的商品,首先從MyFOX中拿到一個熱銷排行榜的數據,但這里的“商品”只是一個ID,并沒有ID所對應的商品描述、圖片等數據。這個時候我們要從淘寶主站提供的接口中去獲取這些數據,然后一一對應到熱銷排行榜中,最終呈現給用戶。

    圖8 glider的技術架構
    圖8 glider的技術架構

     

    有經驗的讀者一定可以想到,從本質上來講,這就是廣義上的異構“表”之間的JOIN操作。那么,誰來負責這個事情呢?很容易想到,在存儲層與前端產品之間增加一個中間層,它負責各個異構“表”之間的數據JOIN和UNION等計算,并且隔離前端產品和后端存儲,提供統一的數據查詢服務。這個中間層就是glider(如圖8所示)。

    緩存是系統化的工程

    除了起到隔離前后端以及異構“表”之間的數據整合的作用之外,glider的另外一個不容忽視的作用便是緩存管理。上文提到過,在特定的時間段內,我們認為數據產品中的數據是只讀的,這是利用緩存來提高性能的理論基礎。

    在圖8中我們看到,glider中存在兩層緩存,分別是基于各個異構“表”(datasource)的二級緩存和整合之后基于獨立請求的一級緩存。除此之外,各個異構“表”內部可能還存在自己的緩存機制。細心的讀者一定注意到了圖3中MyFOX的緩存設計,我們沒有選擇對匯總計算后的最終結果進行緩存,而是針對每個分片進行緩存,其目的在于提高緩存的命中率,并且降低數據的冗余度。

    大量使用緩存的最大問題就是數據一致性問題。如何保證底層數據的變化在盡可能短的時間內體現給最終用戶呢?這一定是一個系統化的工程,尤其對于分層較多的系統來說。

    圖9 緩存控制體系
    圖9 緩存控制體系

     

    圖9向我們展示了數據魔方在緩存控制方面的設計思路。用戶的請求中一定是帶了緩存控制的“命令”的,這包括URL中的query string,和 HTTP頭中的“If-None-Match”信息。并且,這個緩存控制“命令”一定會經過層層傳遞,最終傳遞到底層存儲的異構“表”模塊。各異構“表” 除了返回各自的數據之外,還會返回各自的數據緩存過期時間(ttl),而glider最終輸出的過期時間是各個異構“表”過期時間的最小值。這一過期時間也一定是從底層存儲層層傳遞,最終通過HTTP頭返回給用戶瀏覽器的。

    緩存系統不得不考慮的另一個問題是緩存穿透與失效時的雪崩效應。緩存穿透是指查詢一個一定不存在的數據,由于緩存是不命中時被動寫的,并且出于容錯考慮,如果從存儲層查不到數據則不寫入緩存,這將導致這個存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。

    有很多種方法可以有效地解決緩存穿透問題,最常見的則是采用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。在數據魔方里,我們采用了一個更為簡單粗暴的方法,如果一個查詢返回的數據為空(不管是數據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。

    緩存失效時的雪崩效應對底層系統的沖擊非常可怕。遺憾的是,這個問題目前并沒有很完美的解決方案。大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線程(進程)寫,從而避免失效時大量的并發請求落到底層存儲系統上。在數據魔方中,我們設計的緩存過期機制理論上能夠將各個客戶端的數據失效時間均勻地分布在時間軸上,一定程度上能夠避免緩存同時失效帶來的雪崩效應。

    結束語

    正是基于本文所描述的架構特點,數據魔方目前已經能夠提供壓縮前80TB的數據存儲空間,數據中間層glider支持每天4000萬的查詢請求,平均響應時間在28毫秒(6月1日數據),足以滿足未來一段時間內的業務增長需求。

    盡管如此,整個系統中仍然存在很多不完善的地方。一個典型的例子莫過于各個分層之間使用短連接模式的HTTP協議進行通信。這樣的策略直接導致在流量高峰期單機的TCP連接數非常高。所以說,一個良好的架構固然能夠在很大程度上降低開發和維護的成本,但它自身一定是隨著數據量和流量的變化而不斷變化的。我相信,過不了幾年,淘寶數據產品的技術架構一定會是另外的樣子。

    posted @ 2012-12-13 10:16 小馬歌 閱讀(347) | 評論 (0)編輯 收藏
     

    Linux下GDB調試



    本文寫給主要工作在Windows操作系統下而又需要開發一些跨平臺軟件的程序員朋友,以及程序愛好者。

    GDB是一個由GNU開源組織發布的、UNIX/LINUX操作系統下的、基于命令行的、功能強大的程序調試工具。

    GDB中的命令固然很多,但我們只需掌握其中十個左右的命令,就大致可以完成日常的基本的程序調試工作。

    命令解釋示例
    file <文件名>加載被調試的可執行程序文件。
    因為一般都在被調試程序所在目錄下執行GDB,因而文本名不需要帶路徑。
    (gdb) file gdb-sample
    rRun的簡寫,運行被調試的程序。
    如果此前沒有下過斷點,則執行完整個程序;如果有斷點,則程序暫停在第一個可用斷點處。
    (gdb) r
    cContinue的簡寫,繼續執行被調試程序,直至下一個斷點或程序結束。(gdb) c
    b <行號>
    b <函數名稱>
    b *<函數名稱>
    b *<代碼地址>

    d [編號]

    b: Breakpoint的簡寫,設置斷點。兩可以使用“行號”“函數名稱”“執行地址”等方式指定斷點位置。
    其中在函數名稱前面加“*”符號表示將斷點設置在“由編譯器生成的prolog代碼處”。如果不了解匯編,可以不予理會此用法。

    d: Delete breakpoint的簡寫,刪除指定編號的某個斷點,或刪除所有斷點。斷點編號從1開始遞增。

    (gdb) b 8
    (gdb) b main
    (gdb) b *main
    (gdb) b *0x804835c

    (gdb) d

    s, ns: 執行一行源程序代碼,如果此行代碼中有函數調用,則進入該函數;
    n: 執行一行源程序代碼,此行代碼中的函數調用也一并執行。

    s 相當于其它調試器中的“Step Into (單步跟蹤進入)”;
    n 相當于其它調試器中的“Step Over (單步跟蹤)”。

    這兩個命令必須在有源代碼調試信息的情況下才可以使用(GCC編譯時使用“-g”參數)。

    (gdb) s
    (gdb) n
    si, nisi命令類似于s命令,ni命令類似于n命令。所不同的是,這兩個命令(si/ni)所針對的是匯編指令,而s/n針對的是源代碼。(gdb) si
    (gdb) ni
    p <變量名稱>Print的簡寫,顯示指定變量(臨時變量或全局變量)的值。(gdb) p i
    (gdb) p nGlobalVar
    display ...

    undisplay <編號>

    display,設置程序中斷后欲顯示的數據及其格式。
    例如,如果希望每次程序中斷后可以看到即將被執行的下一條匯編指令,可以使用命令
    “display /i $pc”
    其中 $pc 代表當前匯編指令,/i 表示以十六進行顯示。當需要關心匯編代碼時,此命令相當有用。

    undispaly,取消先前的display設置,編號從1開始遞增。

    (gdb) display /i $pc

    (gdb) undisplay 1

    iInfo的簡寫,用于顯示各類信息,詳情請查閱“help i”。(gdb) i r
    qQuit的簡寫,退出GDB調試環境。(gdb) q
    help [命令名稱]GDB幫助命令,提供對GDB名種命令的解釋說明。
    如果指定了“命令名稱”參數,則顯示該命令的詳細說明;如果沒有指定參數,則分類顯示所有GDB命令,供用戶進一步瀏覽和查詢。
    (gdb) help display

    /add************************************/

    j命令              回跳

    ret               設置返回值        (例ret 0/ret -1)

    /add************************************/

    廢話不多說,下面開始實踐。gdb-sample.c

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    #include <</font>stdio.h>

    int nGlobalVar = 0;

    int tempFunction(int a, int b)
    {
        printf("tempFunction is called, a = %d, b = %d \n", a, b);
        return (a + b);
    }

    int main()
    {
        int n;
            n = 1;
            n++;
            n--;

            nGlobalVar += 100;
            nGlobalVar -= 12;

        printf("n = %d, nGlobalVar = %d \n", n, nGlobalVar);

            n = tempFunction(1, 2);
        printf("n = %d", n);

        return 0;
    }

     

    gcc gdb-sample.c -o gdb-sample -g

    使用參數 -g 表示將源代碼信息編譯到可執行文件中。如果不使用參數 -g,會給后面的GDB調試造成不便。當然,如果我們沒有程序的源代碼,自然也無從使用 -g 參數,調試/跟蹤時也只能是匯編代碼級別的調試/跟蹤。

    下面“gdb”命令啟動GDB,將首先顯示GDB說明,不管它:

    GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
    Copyright 2003 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB. Type "show warranty" for details.
    This GDB was configured as "i386-redhat-linux-gnu".
    (gdb)

    上面最后一行“(gdb) ”為GDB內部命令引導符,等待用戶輸入GDB命令。

    下面使用“file”命令載入被調試程序 gdb-sample(這里的 gdb-sample 即前面 GCC 編譯輸出的可執行文件):

    (gdb) file gdb-sample
    Reading symbols from gdb-sample...done.

    上面最后一行提示已經加載成功。

    下面使用“r”命令執行(Run)被調試文件,因為尚未設置任何斷點,將直接執行到程序結束:

    (gdb) r
    Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample
    n = 1, nGlobalVar = 88
    tempFunction is called, a = 1, b = 2
    n = 3
    Program exited normally.

    下面使用“b”命令在 main 函數開頭設置一個斷點(Breakpoint):

    (gdb) b main
    Breakpoint 1 at 0x804835c: file gdb-sample.c, line 19.

    上面最后一行提示已經成功設置斷點,并給出了該斷點信息:在源文件 gdb-sample.c 第19行處設置斷點;這是本程序的第一個斷點(序號為1);斷點處的代碼地址為 0x804835c(此值可能僅在本次調試過程中有效)?;剡^頭去看源代碼,第19行中的代碼為“n = 1”,恰好是 main 函數中的第一個可執行語句(前面的“int n;”為變量定義語句,并非可執行語句)。

    再次使用“r”命令執行(Run)被調試程序:

    (gdb) r
    Starting program: /home/liigo/temp/gdb-sample

    Breakpoint 1, main () at gdb-sample.c:19
    19 n = 1;

    程序中斷在gdb-sample.c第19行處,即main函數是第一個可執行語句處。

    上面最后一行信息為:下一條將要執行的源代碼為“n = 1;”,它是源代碼文件gdb-sample.c中的第19行。

    下面使用“s”命令(Step)執行下一行代碼(即第19行“n = 1;”):

    (gdb) s
    20 n++;

    上面的信息表示已經執行完“n = 1;”,并顯示下一條要執行的代碼為第20行的“n++;”。

    既然已經執行了“n = 1;”,即給變量 n 賦值為 1,那我們用“p”命令(Print)看一下變量 n 的值是不是 1 :

    (gdb) p n
    $1 = 1

    果然是 1。($1大致是表示這是第一次使用“p”命令——再次執行“p n”將顯示“$2 = 1”——此信息應該沒有什么用處。)

    下面我們分別在第26行、tempFunction 函數開頭各設置一個斷點(分別使用命令“b 26”“b tempFunction”):

    (gdb) b 26
    Breakpoint 2 at 0x804837b: file gdb-sample.c, line 26.
    (gdb) b tempFunction
    Breakpoint 3 at 0x804832e: file gdb-sample.c, line 12.

    使用“c”命令繼續(Continue)執行被調試程序,程序將中斷在第二 個斷點(26行),此時全局變量 nGlobalVar 的值應該是 88;再一次執行“c”命令,程序將中斷于第三個斷點(12行,tempFunction 函數開頭處),此時tempFunction 函數的兩個參數 a、b 的值應分別是 1 和 2:

    (gdb) c
    Continuing.

    Breakpoint 2, main () at gdb-sample.c:26
    26 printf("n = %d, nGlobalVar = %d \n", n, nGlobalVar);
    (gdb) p nGlobalVar
    $2 = 88
    (gdb) c
    Continuing.
    n = 1, nGlobalVar = 88

    Breakpoint 3, tempFunction (a=1, b=2) at gdb-sample.c:12
    12 printf("tempFunction is called, a = %d, b = %d \n", a, b);
    (gdb) p a
    $3 = 1
    (gdb) p b
    $4 = 2

    上面反饋的信息一切都在我們預料之中,哈哈~~~

    再一次執行“c”命令(Continue),因為后面再也沒有其它斷點,程序將一直執行到結束:

    (gdb) c
    Continuing.
    tempFunction is called, a = 1, b = 2
    n = 3
    Program exited normally.

     

    有時候需要看到編譯器生成的匯編代碼,以進行匯編級的調試或跟蹤,又該如何操作呢?

    這就要用到display命令“display /i $pc”了(此命令前面已有詳細解釋):

    (gdb) display /i $pc
    (gdb)

    此后程序再中斷時,就可以顯示出匯編代碼了:

    (gdb) r
    Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample

    Breakpoint 1, main () at gdb-sample.c:19
    19 n = 1;
    1: x/i $pc 0x804835c : movl $0x1,0xfffffffc(?p)

    看到了匯編代碼,“n = 1;”對應的匯編代碼是“movl $0x1,0xfffffffc(?p)”。

    并且以后程序每次中斷都將顯示下一條匯編指定(“si”命令用于執行一條匯編代碼——區別于“s”執行一行C代碼):

    (gdb) si
    20 n++;
    1: x/i $pc 0x8048363 : lea 0xfffffffc(?p),?x
    (gdb) si
    0x08048366 20 n++;
    1: x/i $pc 0x8048366 : incl (?x)
    (gdb) si
    21 n--;
    1: x/i $pc 0x8048368 : lea 0xfffffffc(?p),?x
    (gdb) si
    0x0804836b 21 n--;
    1: x/i $pc 0x804836b : decl (?x)
    (gdb) si
    23 nGlobalVar += 100;
    1: x/i $pc 0x804836d : addl $0x64,0x80494fc

     

    接下來我們試一下命令“b *<函數名稱>”。

    為了更簡明,有必要先刪除目前所有斷點(使用“d”命令——Delete breakpoint):

    (gdb) d
    Delete all breakpoints? (y or n) y
    (gdb)

    當被詢問是否刪除所有斷點時,輸入“y”并按回車鍵即可。

    下面使用命令“b *main”在 main 函數的 prolog 代碼處設置斷點(prolog、epilog,分別表示編譯器在每個函數的開頭和結尾自行插入的代碼):

     

    (gdb) b *main
    Breakpoint 4 at 0x804834c: file gdb-sample.c, line 17.
    (gdb) r
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample

    Breakpoint 4, main () at gdb-sample.c:17
    17 {
    1: x/i $pc 0x804834c : push ?p
    (gdb) si
    0x0804834d 17 {
    1: x/i $pc 0x804834d : mov %esp,?p
    (gdb) si
    0x0804834f in main () at gdb-sample.c:17
    17 {
    1: x/i $pc 0x804834f : sub $0x8,%esp
    (gdb) si
    0x08048352 17 {
    1: x/i $pc 0x8048352 : and $0xfffffff0,%esp
    (gdb) si
    0x08048355 17 {
    1: x/i $pc 0x8048355 : mov $0x0,?x
    (gdb) si
    0x0804835a 17 {
    1: x/i $pc 0x804835a : sub ?x,%esp
    (gdb) si
    19 n = 1;
    1: x/i $pc 0x804835c : movl $0x1,0xfffffffc(?p)

     

    此時可以使用“i r”命令顯示寄存器中的當前值———“i r”即“Infomation Register”:

     

    (gdb) i r
    eax 0xbffff6a4 -1073744220
    ecx 0x42015554 1107383636
    edx 0x40016bc8 1073834952
    ebx 0x42130a14 1108544020
    esp 0xbffff6a0 0xbffff6a0
    ebp 0xbffff6a8 0xbffff6a8
    esi 0x40015360 1073828704
    edi 0x80483f0 134513648
    eip 0x8048366 0x8048366
    eflags 0x386 902
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x33 51

     

    當然也可以顯示任意一個指定的寄存器值:

     

    (gdb) i r eax
    eax 0xbffff6a4 -1073744220

     

    最后一個要介紹的命令是“q”,退出(Quit)GDB調試環境:

    posted @ 2012-12-12 12:24 小馬歌 閱讀(263) | 評論 (0)編輯 收藏
     
    實戰Makefile.am
    Makefile.am是一種比Makefile更高層次的規則。只需指定要生成什么目標,它由什么源文件生成,要安裝到什么目錄等構成。
    表一列出了可執行文件、靜態庫、頭文件和數據文件,四種書寫Makefile.am文件個一般格式。

    表 1Makefile.am一般格式

    對于可執行文件和靜態庫類型,如果只想編譯,不想安裝到系統中,可以用noinst_PROGRAMS代替bin_PROGRAMS,noinst_LIBRARIES代替lib_LIBRARIES。
    Makefile.am還提供了一些全局變量供所有的目標體使用:

    表 2 Makefile.am中可用的全局變量
    在Makefile.am中盡量使用相對路徑,系統預定義了兩個基本路徑:

    表 3Makefile.am中可用的路徑變量
    在上文中我們提到過安裝路徑,automake設置了默認的安裝路徑:
    1)標準安裝路徑
    默認安裝路徑為:$(prefix) = /usr/local,可以通過./configure --prefix=的方法來覆蓋。
    其它的預定義目錄還包括:bindir = $(prefix)/bin, libdir = $(prefix)/lib, datadir = $(prefix)/share, sysconfdir = $(prefix)/etc等等。
    2) 定義一個新的安裝路徑
    比如test, 可定義testdir = $(prefix)/test, 然后test_DATA =test1 test2,則test1,test2會作為數據文件安裝到$(prefix)/ /test目錄下。
    我們首先需要在工程頂層目錄下(即project/)創建一個Makefile.am來指明包含的子目錄:
    SUBDIRS=src/lib src/ModuleA/apple/shell src/ModuleA/apple/core 
    CURRENTPATH=$(shell /bin/pwd)
    INCLUDES=-I$(CURRENTPATH)/src/include -I$(CURRENTPATH)/src/ModuleA/apple/include 
    export INCLUDES
    由于每個源文件都會用到相同的頭文件,所以我們在最頂層的Makefile.am中包含了編譯源文件時所用到的頭文件,并導出,見藍色部分代碼。
    我們將lib目錄下的swap.c文件編譯成libswap.a文件,被apple/shell/apple.c文件調用,那么lib目錄下的Makefile.am如下所示:
    noinst_LIBRARIES=libswap.a
    libswap_a_SOURCES=swap.c
    INCLUDES=-I$(top_srcdir)/src/includ
    細心的讀者可能就會問:怎么表1中給出的是bin_LIBRARIES,而這里是noinst_LIBRARIES?這
    是因為如果只想編譯,而不想安裝到系統中,就用noinst_LIBRARIES代替bin_LIBRARIES,對于可執行文件就用
    noinst_PROGRAMS代替bin_PROGRAMS。對于安裝的情況,庫將會安裝到$(prefix)/lib目錄下,可執行文件將會安裝
    到${prefix}/bin。如果想安裝該庫,則Makefile.am示例如下:
    bin_LIBRARIES=libswap.a
    libswap_a_SOURCES=swap.c
    INCLUDES=-I$(top_srcdir)/src/include
    swapincludedir=$(includedir)/swap
    swapinclude_HEADERS=$(top_srcdir)/src/include/swap.h
    最后兩行的意思是將swap.h安裝到${prefix}/include /swap目錄下。
    接下來,對于可執行文件類型的情況,我們將討論如何寫Makefile.am?對于編譯apple/core目錄下的文件,我們寫成的Makefile.am如下所示:
    noinst_PROGRAMS=test
    test_SOURCES=test.c 
    test_LDADD=$(top_srcdir)/src/ModuleA/apple/shell/apple.o $(top_srcdir)/src/lib/libswap.a 
    test_LDFLAGS=-D_GNU_SOURCE
    DEFS+=-D_GNU_SOURCE
    #LIBS=-lpthread
    由于我們的test.c文件在鏈接時,需要apple.o和libswap.a文件,所以我們需要在
    test_LDADD中包含這兩個文件。對于Linux下的信號量/讀寫鎖文件進行編譯,需要在編譯選項中指明-D_GNU_SOURCE。所以在
    test_LDFLAGS中指明。而test_LDFLAGS只是鏈接時的選項,編譯時同樣需要指明該選項,所以需要DEFS來指明編譯選項,由于
    DEFS已經有初始值,所以這里用+=的形式指明。從這里可以看出,Makefile.am中的語法與Makefile的語法一致,也可以采用條件表達
    式。如果你的程序還包含其他的庫,除了用AC_CHECK_LIB宏來指明外,還可以用LIBS來指明。
    如果你只想編譯某一個文件,那么Makefile.am如何寫呢?這個文件也很簡單,寫法跟可執行文件的差不多,如下例所示:
    noinst_PROGRAMS=apple
    apple_SOURCES=apple.c
    DEFS+=-D_GNU_SOURCE
    我們這里只是欺騙automake,假裝要生成apple文件,讓它為我們生成依賴關系和執行命令。所以當你運行完automake命令后,然后修改apple/shell/下的Makefile.in文件,直接將LINK語句刪除,即:
    …….
    clean-noinstPROGRAMS:
        -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
    apple$(EXEEXT): $(apple_OBJECTS) $(apple_DEPENDENCIES) 
        @rm -f apple$(EXEEXT)
    #$(LINK) $(apple_LDFLAGS) $(apple_OBJECTS) $(apple_LDADD) $(LIBS)
                    
                    
                    

    本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u2/61254/showart_1821022.html 
    posted @ 2012-11-30 16:19 小馬歌 閱讀(2435) | 評論 (0)編輯 收藏
     

    一、 概述

    為了更好的制作configure與Makefile,我先把制作流程給寫在這里,好讓大伙都有個心理準備。這里只說流程,不做解釋。(附圖供參考)


    1、autosan命令生成configure.scan文件,這只是個模板,我們重新編輯這個文件,并把它保存為configure.in文件。

    2、aclocal命令生成aclocal.m4文件。

    3、autoheader命令生成config.h.in。前提是aclocal.m4和configure.in必須生成。

    4、autoconf命令生成configure文件(這個文件都很熟悉吧,嘎嘎)。

    5、創建并編輯Makefile.am,這個文件在根目錄與子目錄都應該有的。

    6、automake命令生成Makefile.in。

    7、./configure命令,根據Makefile.in生成Makefile文件,這個再熟悉不過了吧。

    相關閱讀: 詳解Linux下auto工具制作Makefile源碼包(工具安裝篇)http://www.linuxidc.com/Linux/2011-05/36616.htm

    二、     制作

    巧婦難做無米之炊,要想完成這次體驗,我們還得按規矩一步一步來,源文件得有。首先建個目錄Family用來放我們的東西,它下面的東西就多了。源代碼什么阿c,阿h啊,都放在src下面,src這個文件夾命名一般都是約定俗成的,我們也不破壞。當然你也可以起其他名字,別搞忘就好。

    <Family>

       |-configure.in

       |-Makefile.am 

       |-<src>

         |-wife.c

         |-daughter.c                                                                   |-main.c

         |-wife.h                                                                       |-daughter.h

         |-Makefile.am

    ※說明:

    1. configure.in 這是最重要的文檔,整個安裝過程都靠它來主導。

    2. Makefile.am automake會根據它來生成Makefile.in,再由./configure Makefile.in變成最終的Makefile,一般來說在頂級目錄和各個子目錄都應該有一個Makefile.am

    3. wife.c daughter.c main.c wife.h daughter.h 這是我們的源程序。

    不用細看,這些文件除了configure.in是用模板創建手動編輯外,其他都是手動創建并編輯的,如果你還木有,動手吧。其實這里邊有個小小的經驗,也不一定是對的,只是個人的經驗,也就是后綴名為in的文件是生成的模板。

    $ mkdir –p Family/src

    $ touch Makefile.am

    $ cd src

    $ touch wife.c daughter.c main.c wife.h daughter.h Makefile.am

    $ cd ..  進入剛創建的Family目錄

    $ autoscan 該命令產生 configure.scan 和 configure.log兩個文件,然后

    $ mv configure.scan configure.i n這樣configure.in就創建成了。

    ※源代碼內容:

    main.c: 

    #include <stdio.h>

    #include "wife.h"

    #include "daughter.h"

    #ifdef HAVE_CONFIG_H

    #include <config.h>

    #endif

    int main(void)

    {

    printf( "These are my girls\n");

    daughter_say();

    wife_say();

    return 0;

    }

    daughter.c

    #include <stdio.h>

    #include "daughter.h"

    #ifdef HAVE_CONFIG_H

    #include <config.h>

    #endif

    void daughter_say(void)

    {

    printf("My Dad ,are you call me ?\n");

    }

    daughter.h

    #ifndef _DAUGHTER_

    #define _DAUGHTER_

    void daughter_say(void);

    #endif

    wife.c

    #include <stdio.h>

    #include "wife.h"

    #ifdef HAVE_CONFIG_H

    #include <config.h>

    #endif

    void wife_say(void)

    {

    printf("My darling ,are you call me ?\n");

    }

    wife.h

    #ifndef _WIFE_

    #define _WIFE_

    void wife_say(void);

    #endif

    ※制作流程:

    第1步:編輯configure.in文件。

    上面用autoscan生成的scan后綴的文件改名而成的configure.in文件,我的用autoconf版本是2.66,貌似autoscan自動生成的模板2.61之前與之后的不大一樣,這點可以自己參照自己的版本修改,2.61以后的版本這個文件更簡單了。

    打開看看:  #                                               -*- Autoconf -*-

    # Process this file with autoconf to produce a configure script.

    AC_PREREQ([2.66])

    AC_INIT([family], [1.0], [804927399@qq.com])

    AM_INIT_AUTOMAKE(family,1.0)

    AC_CONFIG_SRCDIR([src/wife.c])

    AC_CONFIG_HEADERS([config.h])

    # Checks for programs.

    AC_PROG_CC

    # Checks for libraries.

    # Checks for header files.

    # Checks for typedefs, structures, and compiler characteristics.

    # Checks for library functions.

    AC_CONFIG_FILES([Makefile

    src/Makefile])

    AC_OUTPUT

    configure.in 未修改版,簡單對上面進行說明:

    AC_PREREQ([2.66])這個宏是用來檢測autoconf的版本的。

    AC_INIT()是個初始化宏,括號中內容分別為:要生成的軟件名稱,版本號,bug報告郵箱

    AM_INIT_AUTOMAKE(family,1.0)這個宏是新添加的,不過好像1.8的automake不用添加這個也可以,但是我沒有添加的時候在后面make的時候會有“Makefile:15: *** 遺漏分隔符 。 停止。”的問題,而加上則有警告。現在還真有點暈呼呼滴。

    AC_CONFIG_SRCDIR([src/wife.c])這個宏是用來檢測源碼目錄的有效性,srcdir就可以看出來,括號里邊的文件不一定非得是wife.c,也可以改成其他的。

    AC_CONFIG_HEADERS([config.h])這個宏用來生成標準的config.h文件。

    接下來的內容就是眾多的check了

    AC_PROG_CC這個是檢測編譯器的宏。

    我們用的版本不用AC_OUTPUT輸出了,還是比較省力的。

    第2步:aclocal命令生成aclocal.m4文件

    不知為何,我直接用命令:

    $aclocal 命令總是出錯:aclocal

    aclocal: couldn't open directory `/usr/local/share/aclocal-': 沒有那個文件或目錄。

    最后還是加上絕對路徑/usr/bin/aclocal總算是沒這個錯誤,生成aclocal.m4了。

    第3步:autoconf命令生成 configure文件

    這一步沒什么難度,輸入命令:

    $ autoconf 就ok了,autoconf可以根據configure.in和aclocal.m4生成大名鼎鼎的configure,這時候已經可以運行它了,但是會報錯,因為Makefile.in還沒出現。

    那么何為M4呢,M4的名稱取自Macro(M后面跟4個字母…)。它和C預處理器里的宏是一個概念(其實,M4和C預處理器都K&R操刀設計的?。。?,用來處理文本替換。也就是說,M4是bash里的預處理器。

    第4步:autoheader命令生成config.h.in

    $ autoheader通過autoheader命令,我們就可以得到config.h.in這個東東了。有了它,./configure才會生成config.h這個東東,所以不可大意。

    autoheader這個工具通常會從“acconfig.h”文件中復制用戶附加的符號定義,因此此處沒有附加符號定義,所以不需要創建“acconfig.h”文件。

    第5步:編輯Makefile.am文件

    我們再編譯安裝源碼包的時候都知道./configure可以生成Makefile,殊不知要生成Makefile還全仰仗Makefile.in這個老東西,而這個東西是以in結尾的 ,也是個模板,是由Makefile.am生成的,好了,知道這個來龍去脈了,就著手編寫Makefile.am,我們這里Family頂級目錄與子目錄各有一個,如果還有其他的子目錄,理論上也是需要Makefile.am的。我們這就來編輯它:

    Family/Makefile.am內容如下:

    AUTOMAKE_OPTIONS = foreign

    SUBDIRS = src

    Family/src/Makefile.am內容如下:

    AUTOMAKE_OPTIONS = foreign

    bin_PROGRAMS = family

    family_SOURCES = main.c wife.c daughter.c wife.h daughter.h

    其中的AUTOMAKE_OPTIONS為設置automake的選項。由于GNU對自己發布的軟件有嚴格的規范,比如必須附帶許可證聲明文件COPYING等,否則automake執行時會報錯。automake提供了三種軟件等級:foreign、gnu和gnits,讓用 戶選擇采用,默認等級為gnu。在本例使用foreign等級,它只檢測必須的文件,而不用檢查README啊什么的。

    bin_PROGRAMS定義要產生的執行文件名。如果要產生多個執行文件,每個文件名用空格隔開。

    family_SOURCES定義“family”這個執行程序所需要的原始文件。如果”family”這個程序是由多個原始文件所產生的,則必須把它所用到的所有原 始文件都列出來,并用空格隔開。如果源文件名字太長,可以加上”\”行連接符換行書寫。

    第6步:automake --add-missing命令生成config.h.in

    $ automake –a或者 automake -–add-missing,大功告成!

    注意運行automake命令時一定要加參數,否則不會自動生成install.sh,missing等腳本,這樣會出亂子滴。

    這樣,源碼安裝包就制作成了,用tar打包就可以了,使用的時候用三部曲./configure,make , make install安裝即可,帥吧!

    posted @ 2012-11-29 15:48 小馬歌 閱讀(553) | 評論 (0)編輯 收藏
     
    翻譯: bobning編譯和安裝ubuntu或debian下的安裝非常簡單
    # apt-get install rabbitmq-server

    默認的數據庫內容
    當第一次啟動服務,檢測數據庫是否未初始化或者被刪除,它會用下面的資源初始化一個新的數據庫:

    一個命名為 / 的虛擬宿主一個名為guest密碼也為guest的用戶,他擁有/虛擬宿主的所有權限如果你的中間件是公開訪問的,最好修改guest用戶的密碼。管理概觀rabbitmqctl 是RabbitMQ中間件的一個命令行管理工具。它通過連接一個中間件節點執行所有的動作。本地節點默認被命名為”rabbit”??梢酝ㄟ^這個命令前使 用”-n”標志明確的指定節點名稱, 例如:# rabbitmqctl -n rabbit@shortstop add_user tonyg changeit
    這個命令指示RabbitMQ中間件在rabbit@shortstop 節點創建一個tonyg/changeit的用戶。
    在一個名為”server.example.com”的主機,RabbitMQ Erlang節點的名稱通常是rabbit@server(除非RABBITMQ_NODENAM在 中間件啟動時候被設置)。hostnam -s 的輸出通常是”@”符號正確的后綴。rabbitmqctl 默認產生詳細輸出。通過”-q”標示可選擇安靜模式。rabbitmqctl -q status應用和集群管理1.停止RabbitMQ應用,關閉節點
    # rabbitmqctl stop
    2.停止RabbitMQ應用
    # rabbitmqctl stop_app
    3.啟動RabbitMQ應用
    # rabbitmqctl start_app
    4.顯示RabbitMQ中間件各種信息
    # rabbitmqctl status
    5.重置RabbitMQ節點
    # rabbitmqctl reset
    # rabbitmqctl force_reset
    從它屬于的任何集群中移除,從管理數據庫中移除所有數據,例如配置過的用戶和虛擬宿主, 刪除所有持久化的消息。
    force_reset命令和reset的區別是無條件重置節點,不管當前管理數據庫狀態以及集群的配置。如果數據庫或者集群配置發生錯誤才使用這個最后 的手段。
    注意:只有在停止RabbitMQ應用后,reset和force_reset才能成功。
    6.循環日志文件
    # rabbitmqctl rotate_logs[suffix]
    7.集群管理
    # rabbitmqctl cluster clusternode

    用戶管理
    1.添加用戶
    # rabbitmqctl add_user username password
    2.刪除用戶
    # rabbitmqctl delete_user username
    3.修改密碼
    # rabbitmqctl change_password username newpassword
    4.列出所有用戶
    # rabbitmqctl list_users

    權限控制1.創建虛擬主機
    # rabbitmqctl add_vhost vhostpath
    2.刪除虛擬主機
    # rabbitmqctl delete_vhost vhostpath
    3.列出所有虛擬主機
    # rabbitmqctl list_vhosts
    4.設置用戶權限
    # rabbitmqctl set_permissions [-p vhostpath] username regexp regexp regexp
    5.清除用戶權限
    # rabbitmqctl clear_permissions [-p vhostpath] username
    6.列出虛擬主機上的所有權限
    # rabbitmqctl list_permissions [-p vhostpath]
    7.列出用戶權限

    # rabbitmqctl list_user_permissions username

     

    例子:

    添加  rabbitmqctl add_vhost az

    rabbitmqctl set_permissions -p az guest ".*" ".*" ".*" 
    posted @ 2012-11-28 15:43 小馬歌 閱讀(4709) | 評論 (0)編輯 收藏
    僅列出標題
    共95頁: First 上一頁 30 31 32 33 34 35 36 37 38 下一頁 Last 
     
    主站蜘蛛池模板: 日本免费污片中国特一级| 特黄特色的大片观看免费视频| 中国china体内裑精亚洲日本| 亚洲熟妇AV乱码在线观看| 亚洲A∨精品一区二区三区下载| www在线观看播放免费视频日本| 97人妻精品全国免费视频| 成人在线免费看片| 国产在线98福利播放视频免费| 老司机亚洲精品影视www| 亚洲视频在线视频| 亚洲日本VA午夜在线电影| 一区二区三区免费在线观看| 人妻无码久久一区二区三区免费| 女人18毛片水真多免费播放| 色久悠悠婷婷综合在线亚洲| 亚洲综合久久成人69| 国产精品亚洲AV三区| 国产在线精品免费aaa片| 最新仑乱免费视频| 亚洲熟妇av一区二区三区漫画| 亚洲欧洲自拍拍偷综合| 菠萝菠萝蜜在线免费视频| 亚欧免费一级毛片| 永久免费bbbbbb视频| 亚洲AV无码国产精品色午友在线| 亚洲自偷自偷在线成人网站传媒| 国产男女爽爽爽免费视频| 无码日韩人妻av一区免费| 亚洲区小说区图片区QVOD| 在线aⅴ亚洲中文字幕| GOGOGO免费观看国语| 色婷婷7777免费视频在线观看| 国产日产亚洲系列最新| 亚洲中文字幕乱码AV波多JI| 久草免费福利在线| 黄网址在线永久免费观看 | 日韩精品无码免费专区网站| 97人伦色伦成人免费视频| 久久精品九九亚洲精品天堂| 噜噜综合亚洲AV中文无码|