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

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

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

    內容不斷更新,目前包括協議中握手和數據幀的分析

     

    1.1 背景

    1.2 協議概覽

    協議包含兩部分:握手,數據傳輸。

    客戶端的握手如下:
    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://example.com
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13

    服務端的握手如下:
    HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat
    客戶端和服務端都發送了握手,并且成功,數據傳輸即可開始。
     
    1.3 發起握手
    發起握手是為了兼容基于HTTP的服務端程序,這樣一個端口可以同時處理HTTP客戶端和WebSocket客戶端
    因此WebSocket客戶端握手是一個HTTP Upgrade請求:
    GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
    握手中的域的順序是任意的。
     
    5 數據幀
    5.1 概述
    WebScoket協議中,數據以幀序列的形式傳輸。
    考慮到數據安全性,客戶端向服務器傳輸的數據幀必須進行掩碼處理。服務器若接收到未經過掩碼處理的數據幀,則必須主動關閉連接。
    服務器向客戶端傳輸的數據幀一定不能進行掩碼處理。客戶端若接收到經過掩碼處理的數據幀,則必須主動關閉連接。
    針對上情況,發現錯誤的一方可向對方發送close幀(狀態碼是1002,表示協議錯誤),以關閉連接。
    5.2 幀協議
    WebSocket數據幀結構如下圖所示:
          0                   1                   2                   3       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1      +-+-+-+-+-------+-+-------------+-------------------------------+      |F|R|R|R| opcode|M| Payload len |    Extended payload length    |      |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |      |N|V|V|V|       |S|             |   (if payload len==126/127)   |      | |1|2|3|       |K|             |                               |      +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +      |     Extended payload length continued, if payload len == 127  |      + - - - - - - - - - - - - - - - +-------------------------------+      |                               |Masking-key, if MASK set to 1  |      +-------------------------------+-------------------------------+      | Masking-key (continued)       |          Payload Data         |      +-------------------------------- - - - - - - - - - - - - - - - +      :                     Payload Data continued ...                :      + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +      |                     Payload Data continued ...                |      +---------------------------------------------------------------+
     
    FIN:1位
    表示這是消息的最后一幀(結束幀),一個消息由一個或多個數據幀構成。若消息由一幀構成,起始幀即結束幀。
     
    RSV1,RSV2,RSV3:各1位
    MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
    這里我翻譯不好,大致意思是如果未定義擴展,各位是0;如果定義了擴展,即為非0值。如果接收的幀此處非0,擴展中卻沒有該值的定義,那么關閉連接。
     
    OPCODE:4位
    解釋PayloadData,如果接收到未知的opcode,接收端必須關閉連接。
    0x0表示附加數據幀
    0x1表示文本數據幀
    0x2表示二進制數據幀
    0x3-7暫時無定義,為以后的非控制幀保留
    0x8表示連接關閉
    0x9表示ping
    0xA表示pong
    0xB-F暫時無定義,為以后的控制幀保留
     
    MASK:1位
    用于標識PayloadData是否經過掩碼處理。如果是1,Masking-key域的數據即是掩碼密鑰,用于解碼PayloadData。客戶端發出的數據幀需要進行掩碼處理,所以此位是1。
     
    Payload length:7位,7+16位,7+64位
    PayloadData的長度(以字節為單位)。
    如果其值在0-125,則是payload的真實長度。
    如果值是126,則后面2個字節形成的16位無符號整型數的值是payload的真實長度。注意,網絡字節序,需要轉換。
    如果值是127,則后面8個字節形成的64位無符號整型數的值是payload的真實長度。注意,網絡字節序,需要轉換。
    長度表示遵循一個原則,用最少的字節表示長度(我理解是盡量減少不必要的傳輸)。舉例說,payload真實長度是124,在0-125之間,必須用前7位表示;不允許長度1是126或127,然后長度2是124,這樣違反原則。
    Payload長度是ExtensionData長度與ApplicationData長度之和。ExtensionData長度可能是0,這種情況下,Payload長度即是ApplicationData長度。
     
     
    WebSocket協議規定數據通過幀序列傳輸。
    客戶端必須對其發送到服務器的所有幀進行掩碼處理。
    服務器一旦收到無掩碼幀,將關閉連接。服務器可能發送一個狀態碼是1002(表示協議錯誤)的Close幀。
    而服務器發送客戶端的數據幀不做掩碼處理,一旦客戶端發現經過掩碼處理的幀,將關閉連接。客戶端可能使用狀態碼1002。
     

    消息分片

    分片目的是發送長度未知的消息。如果不分片發送,即一幀,就需要緩存整個消息,計算其長度,構建frame并發送;使用分片的話,可使用一個大小合適的buffer,用消息內容填充buffer,填滿即發送出去。

    分片規則:

    1.一個未分片的消息只有一幀(FIN為1,opcode非0)

    2.一個分片的消息由起始幀(FIN為0,opcode非0),若干(0個或多個)幀(FIN為0,opcode為0),結束幀(FIN為1,opcode為0)。

    3.控制幀可以出現在分片消息中間,但控制幀本身不允許分片。

    4.分片消息必須按次序逐幀發送。

    5.如果未協商擴展的情況下,兩個分片消息的幀之間不允許交錯。

    6.能夠處理存在于分片消息幀之間的控制幀

    7.發送端為非控制消息構建長度任意的分片

    8.client和server兼容接收分片消息與非分片消息

    9.控制幀不允許分片,中間媒介不允許改變分片結構(即為控制幀分片)

    10.如果使用保留位,中間媒介不知道其值表示的含義,那么中間媒介不允許改變消息的分片結構

    11.如果協商擴展,中間媒介不知道,那么中間媒介不允許改變消息的分片結構,同樣地,如果中間媒介不了解一個連接的握手信息,也不允許改變該連接的消息的分片結構

    12.由于上述規則,一個消息的所有分片是同一數據類型(由第一個分片的opcode定義)的數據。因為控制幀不允許分片,所以一個消息的所有分片的數據類型是文本、二進制、opcode保留類型中的一種。

    需要注意的是,如果控制幀不允許夾雜在一個消息的分片之間,延遲會較大,比如說當前正在傳輸一個較大的消息,此時的ping必須等待消息傳輸完成,才能發送出去,會導致較大的延遲。為了避免類似問題,需要允許控制幀夾雜在消息分片之間。

    控制幀

     

     

    根據官方文檔整理,官方文檔參考http://datatracker.ietf.org/doc/rfc6455/?include_text=1

    posted @ 2014-04-19 15:16 小馬歌 閱讀(434) | 評論 (0)編輯 收藏
     
    今天@julyclyde 在微博上問我websocket的細節。但是這個用70個字是無法說清楚的,所以就整理在這里吧。恰好我最近要重構年前寫的websocket的代碼。
    眾所周知,HTTP是一種基于消息(message)的請求(request )/應答(response)協議。當我們在網頁中點擊一條鏈接(或者提交一個表單)的時候,瀏覽器給服務器發一個request message,然后服務器算啊算,答復一條response message。主動發起TCP連接的是client,接受TCP連接的是server。HTTP消息只有兩種:request和response。client只能發送request message,server只能發送response message。一問一答,因此按HTTP協議本身的設計,服務器不能主動的把消息推給客戶端。而像微博、網頁聊天、網頁游戲等都需要服務器主動給客戶端推東西,現在只能用long polling等方式模擬,很不方便。
     
    OK,來看看internet的另一邊,網絡游戲是怎么工作的?
    我之前在一個游戲公司工作。我們做游戲的時候,普遍采用的模式是雙向、異步消息模式。
    首先通信的最基本單元是message。(這點和HTTP一樣)
    其次,是雙向的。client和server都可以給對方發消息(這點和HTTP不一樣)
    最后,消息是異步的。我給服務器發一條消息出去,然后可能有一條答復,也可能有多條答復,也可能根本沒有答復。無論如何,調用完send方法我就不管了,我不會傻乎乎的在這里等答復。服務器和客戶端都會有一個線程專門負責read,以及一個大大的switch… case,根據message id做相應的action。
    while( msg=myconnection.readMessage()){
    switch(msg.id()){
    case LOGIN: do_login(); break;
    case TALK: do_talk(); break;
    }
    }
    Websocket就是把這樣一種模式,搬入到HTTP/WEB的框架內。它主要解決兩個問題:
    從服務器給客戶端主動推東西。
    HTTP協議傳輸效率低下的問題。這一點在web service中尤為突出。每個請求和應答都得帶上很長的http header!
    websocket協議在RFC 6455中定義,這個RFC在上個月(2011年12月)才終于定稿、提交。所以目前沒有任何一個瀏覽器是能完全符合這個RFC的最終版的。Google是websocket協議的主力支持者,目前主流的瀏覽器中,對websocket支持最好的就是chrome。chrome目前的最新版本是16,支持的是RFC 6455的draft 13,http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-13 。IE9則是完全不支持websocket。而server端,只有jetty和Node.js對websocket的支持比較好。
     
    Websocket協議可以分為兩個階段,一個是握手階段,一個是數據傳輸階段。
    在建立TCP連接之后,首先是websocket層的握手。這階段很簡單,client給server發一個http request,server給client一個http response。這個階段,所有數據傳輸都是基于文本的,和現有的HTTP/1.1協議保持兼容。
    這是一個請求的例子:
    Connection:Upgrade
    Host:echo.websocket.org
    Origin:http://websocket.org
    Sec-WebSocket-Key:ov0xgaSDKDbFH7uZ1o+nSw==
    Sec-WebSocket-Version:13
    Upgrade:websocket
     
    (其中Host和Origin不是必須的)
    Connection是HTTP/1.1中常用的一個header,以前經常填的是keepalive或close。這里填的是Upgrade。在設計HTTP/1.1的時候,委員們就想到一個問題,假如以后出HTTP 2.0了,那么現有的這套東西怎么辦呢?所以HTTP協議中就預定義了一個header叫Upgrade。如果客戶端發來的請求中有這個,那么意思就是說,我支持某某協議,并且我更偏向于用這個協議,你看你是不是也支持?你要是支持,咱們就換新協議吧!
    然后就是websocket協議中所定義的兩個特殊的header,Sec-WebSocket-Key和Sec-WebSocket-Version。
    其中Sec-WebSocket-Key是客戶端生的一串隨機數,然后base64之后填進去的。Sec-WebSocket-Version是指協議的版本號。這里的13,代表draft 13。下面給出,我年前寫的發送握手請求的JAVA代碼:
    // 生一個隨機字符串,作為Sec-WebSocket-Key
    ?View Code JAVA
            byte[] nonce = new byte[16];        rand.nextBytes(nonce);        BASE64Encoder encode = new BASE64Encoder();        String chan = encode.encode(nonce);         HttpRequest request = new BasicHttpRequest("GET", "/someurl");        request.addHeader("Host", host);        request.addHeader("Upgrade", "websocket");        request.addHeader("Connection", "Upgrade");        request.addHeader("Sec-WebSocket-Key", chan); // 生的隨機串         request.addHeader("Sec-WebSocket-Version", "13");        HttpResponse response;        try {            conn.sendRequestHeader(request);            conn.flush();            request.toString();            response = conn.receiveResponseHeader();        } catch (HttpException ex) {            throw new RuntimeException("handshake fail", ex);        }
     
    服務器在收到握手請求之后需要做相應的答復。消息的例子如下:
    HTTP/1.1 101 Switching Protocols
    Connection:Upgrade
    Date:Sun, 29 Jan 2012 18:05:49 GMT
    Sec-WebSocket-Accept:7vI97qQ5QRxq6lD6E5RRX36mOBc=
    Server:jetty
    Upgrade:websocket
    (其中Date、Server都不是必須的)
    第一行是HTTP的Status-Line。注意,這里的Status Code是101。很少見吧!Sec-WebSocket-Accept字段是一個根據client發來的Sec-WebSocket-Key得到的計算結果。
    算法為:
    把客戶端發來的key作為字符串,與” 258EAFA5-E914-47DA-95CA-C5AB0DC85B11″這個字符串連接起來,然后做sha1 Hash,將計算結果base64編碼。注意,用來和” 258EAFA5-E914-47DA-95CA-C5AB0DC85B11″做連接操作的字符串,是base64編碼后的。也就是說,客戶端發來什么樣就是什么樣,不要試圖去做base64解碼。
    示例代碼如下:
    ?View Code CPP
        std::string value=request.get("Sec-WebSocket-Key");    value+="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";    unsigned char hash[20];    sha1::calc(value.c_str(),value.length(),hash);    std::string res=base64_encode(hash,sizeof(hash));    std::ostringstream oss;    oss<<"HTTP/1.1 101 Switching Protocols\r\n"        "Upgrade: websocket\r\n"        "Connection: Upgrade\r\n"        "Sec-WebSocket-Accept: "<<res<<"\r\n";                   connection.send(oss.str());
    握手成功后,進入數據流階段。這個階段就和http協議沒什么關系了。是在TCP流的基礎上,把數據分成frame而已。首先,websocket的一個message,可以被分成多個frame。從邏輯上來看,frame的格式如下
    isFinal
    opcode
    isMasked
    Data length
    Mask key
    Data(可以是文本,也可以是二進制)
     
    isFinal:
    每個frame的第一個字節的最高位,代表這個frame是不是該message的最后一個frame。1代表是最后一個,0代表后面還有。
    opcode:
    指明這個frame的類型。目前定義了這么幾類continuation、text 、binary 、connection close、ping、pong。對于應用而言,最關心的就是,這個message是binary的呢,還是text的?因為html5中,對于這兩種message的接口有細微不一樣。
    isMasked:
    客戶端發給服務器的消息,要求加擾之后發送。加擾的方式是:客戶端生一個32位整數(mask key),然后把data和這32位整數做異或。
    mask key:
    前面已經說過了,就是用來做異或的隨機數。
    Data:
    這才是我們真正要傳輸的數據啊!!
    發送frame時加擾的代碼如下:
            java.util.Random rand ;
            ByteBuffer buffer;
            byte[] dataToSend;
            …
            
            byte[] mask = new byte[4];
            rand.nextBytes(mask);
            buffer.put(mask);
            int oldpos = buffer.position();        
            buffer.put(data);
            int newpos = buffer.position();
            // 按位異或
            for (int i = oldpos; i != newpos; ++i) {
                int maskIndex = (i – oldpos) % mask.length;
                buffer.put(i, (byte) (buffer.get(i) ^ (byte) mask[maskIndex]));
            }
    下面討論一下這個協議的某些設計:
    為什么要做這個異或操作呢?
    說來話長。首先從Connection:Upgrade這個header講起。本來它是留給TLS用的。就是,假如我從80端口去連接一個服務器,然后通過發送Connection:Upgrade,仿照上面所說的流程,把http協議”升級”成https協議。但是實際上根本沒人這么用。你要用https,你就去連接443。我80端口根本不處理https。由于這個header只是出現在rfc中,并未實際使用,于是大多數cache server看不懂這個header。這樣的結果是,cache server可能以為后面的傳輸數據依然是普通的http協議,然后按照原來的規則做cache。那么,如果這個client和server都已經被黑客很好的操控,他就可以往這個cache server上投毒。比如,從client發送一個websocket frame,但是偽裝成普通的http GET請求,指向一個JS文件。但是這個GET請求的目的地未必是之前那個websocket server,可能是另外一臺web server。然后他再去操控這個web server,做好配合,給一個看起來像http response的答復(實際是websocket frame),里面放的是被修改過的js文件。然后cache server就會把這個js文件錯誤的緩存下來,以后發給其他人。
    首先,client是誰?是瀏覽器。它在一個不很安全的環境中,很容易受到XSS或者流氓插件的攻擊。假如我們的頁面遭到了xss,使得攻擊者可以利用JS從受害者的頁面上發送任意字符串給服務器,如果沒有這個異或操作,那么他就可以控制什么樣的二進制數據出現在信道上,從而實現上述攻擊。但是我還是覺得有點問題。proxy server一般都會對目的地做嚴格的限制,比如,sina的squid肯定不會幫new.163.com做cache。那么既然你已經控制了一個web server,為什么不讓js直接這么做呢?那篇paper的名字叫《Talking to Yourself for Fun and Pro?t》,有空我繼續看。貌似是中國人寫的。
    還有,為什么要把message分成frame呢? 因為HTTP協議有chunk功能,可以讓服務器一邊生數據,一邊發。而websocket協議也考慮到了這點。如果沒有framing功能,那么我必須知道整個message的長度之后,才能開始發送message的data。
    posted @ 2014-04-19 15:00 小馬歌 閱讀(412) | 評論 (0)編輯 收藏
     

    C/C++並沒有提供內建的int轉string函數,這裡提供幾個方式達到這個需求。

    1.若用C語言,且想將int轉char *,可用sprintf(),sprintf()可用類似printf()參數轉型。

     1/* 
     2(C) OOMusou 2007 http://oomusou.cnblogs.com
     3
     4Filename    : int2str_sprintf.cpp
     5Compiler    : Visual C++ 8.0 / ANSI C
     6Description : Demo the how to convert int to const char *
     7Release     : 01/06/2007 1.0
     8*/

     9#include "stdio.h"
    10
    11void int2str(int , char *);
    12
    13int main() {
    14  int i = 123;
    15  char s[64];
    16  int2str(i, s);
    17  puts(s);
    18}

    19
    20void int2str(int i, char *s) {
    21  sprintf(s,"%d",i);
    22}


    2.若用C語言,還有另外一個寫法,使用_itoa(),Microsoft將這個function擴充成好幾個版本,可參考MSDN Library。

     1/* 
     2(C) OOMusou 2007 http://oomusou.cnblogs.com
     3
     4Filename    : int2str_itoa.cpp
     5Compiler    : Visual C++ 8.0 / ANSI C
     6Description : Demo the how to convert int to const char *
     7Release     : 01/06/2007 1.0
     8*/

     9#include "stdio.h"  // puts()
    10#include "stdlib.h" // _itoa()
    11
    12void int2str(int , char *);
    13
    14int main() {
    15  int i = 123;
    16  char s[64];
    17  int2str(i, s);
    18  puts(s);
    19}

    20
    21void int2str(int i, char *s) {
    22  _itoa(i, s, 10);
    23}


    3.若用C++,stringstream是個很好用的東西,stringstream無論是<<或>>,都會自動轉型,要做各型別間的轉換,stringstream是個很好的媒介。

     1/* 
     2(C) OOMusou 2007 http://oomusou.cnblogs.com
     3
     4Filename    : int2str_sstream.cpp
     5Compiler    : Visual C++ 8.0 / ISO C++
     6Description : Demo the how to convert int to string
     7Release     : 01/06/2007 1.0
     8*/

     9
    10#include <iostream>
    11#include <string>
    12#include <sstream>
    13
    14using namespace std;
    15
    16string int2str(int &);
    17
    18int main(void{
    19  int i = 123;
    20  string s;
    21  s = int2str(i);
    22
    23  cout << s << endl;
    24}

    25
    26string int2str(int &i) {
    27  string s;
    28  stringstream ss(s);
    29  ss << i;
    30
    31  return ss.str();
    32}


    4.若用C++,據稱boost有更好的方法,不過我還沒有裝boost,所以無從測試

    posted @ 2014-04-11 11:50 小馬歌 閱讀(435) | 評論 (0)編輯 收藏
     

    在網絡程序的開發中,免不了會涉及到服務器與客戶端之間的協議交互,由于客戶端與服務器端的平臺的差異性(有可能是windows,android,linux等等),以及網絡字節序等問題,通信包一般會做序列化與反序列化的處理,也就是通常說的打包解包工作。google的protobuf是一個很棒的東西,它不僅提供了多平臺的支持,而且直接支持從配置文件生成代碼。但是這么強大的功能,意味著它的代碼量以及編譯生成的庫文件等等都不會小,如果相對于手機游戲一兩M的安裝包來說,這個顯然有點太重量級了(ps:查了一下protobuf2.4.1的jar的包大小有400k左右),而且在手機游戲客戶端與服務器的交互過程中,很多時候基本的打包解包功能就足夠了。

    今天經同事推薦,查閱了一下msgpack相關的資料。msgpack提供快速的打包解包功能,官網上給出的圖號稱比protobuf快4倍,可以說相當高效了。最大的好處在與msgpack支持多語言,服務器端可以用C++,android客戶端可以用java,能滿足實際需求。

    在實際安裝msgpack的過程中,碰到了一點小問題,因為開發機器是32位機器,i686的,所以安裝完后跑一個簡單的sample時,c++編譯報錯,錯誤的表現為鏈接時報錯:undefined reference to `__sync_sub_and_fetch_4',后來參考了下面的博客,在configure時指定CFLAGS="-march=i686"解決,注意要make clean先。

    msgpack官網地址:http://msgpack.org/

    安裝過程中參考的博客地址:http://blog.csdn.net/xiarendeniao/article/details/6801338



    ====================================================================================

    ====================================================================================

    補上近期簡單的測試結果

    測試機器,cpu 4核 Dual-Core AMD Opteron(tm) Processor 2212,單核2GHz,1024KB cache, 內存4G。


    1. 簡單包測試

    1. struct ProtoHead  
    2. {  
    3.     uint16_t uCmd;      // 命令字  
    4.     uint16_t uBodyLen;  // 包體長度(打包之后的字符串長度)  
    5.     uint32_t uSeq;      //消息的序列號,唯一標識一個請求  
    6.     uint32_t uCrcVal;           // 對包體的crc32校驗值(如果校驗不正確,服務器會斷開連接)  
    7. };  
    8.   
    9. struct ProtoBody  
    10. {  
    11.     int num;  
    12.     std::string str;  
    13.     std::vector<uint64_t> uinlst;  
    14.     MSGPACK_DEFINE(num, str, uinlst);  
    15. };  

    測試中省略了包頭本地字節序與網絡字節序之間的轉化,只有包體做msgpack打包處理.

    vector數組中元素數量為16個,每次循環做一次打包與解包,并驗證前后數據是否一致,得到的測試結果如下:

    總耗時(s)

    循環次數

    平均每次耗時(ms)

    0.004691

    100

    0.04691

    0.044219

    1000

    0.044219

    0.435725

    10000

    0.043573

    4.473818

    100000

    0.044738

    總結:基本每次耗時0.045ms左右,每秒可以打包解包22k次,速度很理想。


    2. 復雜包測試(vector嵌套)

    1. struct test_node  
    2. {  
    3.     std::string str;  
    4.     std::vector<uint32_t> idlst;  
    5.   
    6.     test_node()  
    7.     {  
    8.         str = "it's a test node";  
    9.   
    10.         for (int i = 0; i++; i < 10)  
    11.         {  
    12.             idlst.push_back(i);  
    13.         }  
    14.     }  
    15.   
    16.     bool operator == (const test_node& node) const  
    17.     {  
    18.         if (node.str != str)  
    19.         {  
    20.             return false;  
    21.         }  
    22.   
    23.         if (node.idlst.size() != idlst.size())  
    24.         {  
    25.             return false;  
    26.         }  
    27.   
    28.         for (int i = 0; i < idlst.size(); i++)  
    29.         {  
    30.             if (idlst[i] != node.idlst[i])  
    31.             {  
    32.                 return false;  
    33.             }  
    34.         }  
    35.         return true;  
    36.     }  
    37.   
    38.     MSGPACK_DEFINE(str, idlst);  
    39. };  
    40.   
    41. struct ProtoBody  
    42. {  
    43.     int num;  
    44.     std::string str;  
    45.     std::vector<uint64_t> uinlst;  
    46.     std::vector<test_node> nodelst;  
    47.   
    48.     MSGPACK_DEFINE(num, str, uinlst, nodelst);  
    49. };  
    每個nodelst中插入16個node,每個node中的idlst插入16個id,同1中的測試方法,得到測試結果如下:

    總耗時(s)

    循環次數

    平均每次耗時(ms)

    0.025401

    100

    0.25401

    0.248396

    1000

    0.248396

    2.533385

    10000

    0.253339

    25.823562

    100000

    0.258236

    基本上每次打包解包一次要耗時0.25ms,每秒估算可以做4k次打包解包,速度還是不錯的。


    3. 加上crc校驗

    如果每個循環中,打包過程加上crc的計算,解包過程中加上crc校驗,得到測試結果如下:

    總耗時(s)

    循環次數

    平均每次耗時(ms)

    0.025900

    100

    0.25900

    0.260424

    1000

    0.260424

    2.649585

    10000

    0.264959

    26.855452

    100000

    0.268555

    基本上每次打包解包耗時0.26ms左右,與沒有crc差別不大;

    posted @ 2014-04-10 13:43 小馬歌 閱讀(568) | 評論 (0)編輯 收藏
     

    MessagePack(以下簡稱MsgPack)一個基于二進制高效的對象序列化類庫,可用于跨語言通信。它可以像JSON那樣,在許多種語言之間交換結構對象;但是它比JSON更快速也更輕巧。支持Python、Ruby、Java、C/C++等眾多語言。比Google Protocol Buffers還要快4倍。

    代碼:
    > require ‘msgpack’
    > msg = [1,2,3].to_msgpack  #=> “\x93\x01\x02\x03″
    > MessagePack.unpack(msg)   #=> [1,2,3]

    以上摘自oschina介紹。

    msgpack官方主頁:http://msgpack.org/

    github主頁:https://github.com/msgpack/msgpack

    因我只使用C++版本,故只下載了CPP部分,大家請按需下載。

    源碼安裝msgpack

    打開終端下載msgpac 4 cpp最新版本0.5.7

    wget http://msgpack.org/releases/cpp/msgpack-0.5.7.tar.gz

    解壓

    tar zxvf msgpack-0.5.7.tar.gz

    進入解壓后的文件夾中進行安裝

    cd msgpack-0.5.7 ./configure make sudo make install

    當然了,你也可以使用git和svn直接抓取源代碼進行編譯,不過需要安裝版本控制工具。

    自動安裝msgpack
    apt-get install libmsgpack-dev

    (安裝過程中會將頭文件拷貝到 /usr/local/include/ 庫文件拷貝到/usr/local/lib/)

    安裝好了,我們直接使用用它看看效果。

    直接包含msgpack.hpp即可使用。

    simple using
    #include <msgpack.hpp> #include <vector> #include <string> #include <iostream>  int main() { 	std::vector<std::string> _vecString; 	_vecString.push_back("Hello"); 	_vecString.push_back("world");  	// pack 	msgpack::sbuffer _sbuffer; 	msgpack::pack(_sbuffer, _vecString); 	std::cout << _sbuffer.data() << std::endl;  	// unpack 	msgpack::unpacked msg; 	msgpack::unpack(&msg, _sbuffer.data(), _sbuffer.size()); 	msgpack::object obj = msg.get(); 	std::cout << obj << std::endl;  	// convert 	std::vector<std::string> _vecRString; 	obj.convert(&_vecRString);  	// print 	for(size_t i = 0; i < _vecRString.size(); ++i) 	{ 		std::cout << _vecRString[i] << std::endl; 	}      return 0; }

    結果就不貼了,大家自己運行下便知。

    using stream
    #include <msgpack.hpp> #include <vector> #include <string> #include <iostream>  int main() { 	// msgpack stream  	// use msgpack::packer to pack multiple objects. 	msgpack::sbuffer buffer_; 	msgpack::packer pack_(&buffer_); 	pack_.pack(std::string("this is 1st string")); 	pack_.pack(std::string("this is 2nd string")); 	pack_.pack(std::string("this is 3th string"));  	// use msgpack::unpacker to unpack multiple objects. 	msgpack::unpacker unpack_; 	unpack_.reserve_buffer(buffer_.size()); 	memcpy(unpack_.buffer(), buffer_.data(), buffer_.size()); 	unpack_.buffer_consumed(buffer_.size());  	msgpack::unpacked result_; 	while (unpack_.next(&result_)) 	{ 		std::cout << result_.get() << std::endl; 	}  	return 0; }

    使用sbuffer stream序列化多個對象。

    如何序列化自定義數據結構

    msgpack支持序列化/反序列化自定義數據結構,只需要簡單的使用MSGPACK_DEFINE宏即可。

    ##include <msgpack.hpp> #include <vector> #include <string>  class my_class { private: 	std::string my_string; 	std::vector vec_int; 	std::vector vec_string; public: 	MSGPACK_DEFINE(my_string, vec_int, vec_string); };  int main() { 	std::vector<my_class> my_class_vec;  	// add some data  	msgpack::sbuffer buffer; 	msgpack::pack(buffer, my_class_vec);  	msgpack::unpacked msg; 	msgpack::unpack(&msg, buffer.data(), buffer.size());  	msgpack::object obj = msg.get(); 	std::vector<my_class> my_class_vec_r; 	obj.convert(&my_class_vec_r);  	return 0; }

    這樣我們就可以在網絡通訊等地方可以使用msgpack來序列化我們的數據結構,完全可以做到安全高效,并且可以在接收方使用別的語言來處理結構做邏輯。完全是 多種語言-多種語言,現在支持的語言如下:

    Ruby Perl Python C/C++ Java PHP JS OC C# Lua Scala D Haskell Erlang Ocaml Smallalk GO LabVIEW

    完全夠我們使用了,當然了,如果沒有你要的語言,建議看源代碼模仿一個。

    關于性能測試結果可以查看:linux使用msgpack及測試 

    posted @ 2014-04-10 13:42 小馬歌 閱讀(740) | 評論 (0)編輯 收藏
     
    功能特點:
    可以導出導入任意大小的數據庫。FaisunSQL 采用分卷導出的方式,將數據庫分為多個部份多次導出,因此理論上無論多大的數據庫,它都可以勝任。 
    導出的文件本身可以在PHP環境下執行,因此不需要借助其他工具(也不再需要 FaisunSQL程序)。導出的文件為完整的 PHP 文件,直接在服務器中執行即可,使用方便。 
    雖然為多頁導出和導入,但其過程會自動運行,且執行速度較快,成功率高。 
    程序編寫時考慮了程序的可整合性,因此只要略加修改即可整合到其他程序的后臺。 
    導出方式、每個數據文件的大小和數據表等都可以進行設置,個性化強。 
    程序對數據進行了一定的壓縮,減少了備份文件的空間占用。 
    對導出的程序進行了加密,安全性高。 
    導出和導入時基本上按照默認的配置即可,使用方便快捷。 
    posted @ 2014-04-01 15:50 小馬歌 閱讀(236) | 評論 (0)編輯 收藏
     

    說起項目構建工具,Linux 用戶最熟悉的恐怕就是 Autotools,它將編譯安裝這個步驟大大簡化。但對于項目作者來說,想要使用 Autotools 生成有效的配置文件著實需要下一番功夫,用現在流行的話來說就是用戶體驗不夠友好。對 Unix shell 的依賴,也使得 Autotools 天生對于跨平臺支持不佳。

    后來我從大貓同學那里聽說了 CMake,CMake 使用 C++ 編寫,原生支持跨平臺,不需要像 Autotools 那樣寫一堆的配置文件,只需一個 CMakeLists.txt 文件即可。簡潔的使用方式,強大的功能使得我立馬對 CMake 情有獨鐘。在后來的使用過程中,雖然會遇到一些因為使用習慣帶來的小困擾,但我對于 CMake 還是基本滿意的。直到我發現了 GYP。

    GYP(Generate Your Projects)是由 Chromium 團隊開發的跨平臺自動化項目構建工具,Chromium 便是通過 GYP 進行項目構建管理。為什么我要選擇 GYP,而放棄 CMake 呢?功能上 GYP 和 CMake 很是相似,在我看來,它們的最大區別在于配置文件的編寫方式和其中蘊含的思想。

    編寫 CMake 配置文件相比 Autotools 來說已經簡化很多,一個最簡單的配置文件只需要寫上源文件及生成類型(可執行文件、靜態庫、動態庫等)即可。對分支語句和循環語句的支持也使得 CMake 更加靈活。但是,CMake 最大的問題也是在這個配置文件,請看下面這個示例文件:

    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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 
    cmake_minimum_required(VERSION 2.8) project(VP8 CXX)  add_definitions(-Wall) cmake_policy(SET CMP0015 NEW) include_directories("include") link_directories("lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "../lib") set(VP8SRC VP8Encoder.cpp VP8Decoder.cpp)  if(X86)     set(CMAKE_SYSTEM_NAME Darwin)     set(CMAKE_SYSTEM_PROCESSOR i386)     set(CMAKE_OSX_ARCHITECTURES "i386")      add_library(vp8 STATIC ${VP8SRC}) elseif(IPHONE)     if(SIMULATOR)         set(PLATFORM "iPhoneSimulator")         set(PROCESSOR i386)         set(ARCH "i386")     else()         set(PLATFORM "iPhoneOS")         set(PROCESSOR arm)         set(ARCH "armv7")     endif()      set(SDKVER "4.0")     set(DEVROOT "/Developer/Platforms/${PLATFORM}.platform/Developer")     set(SDKROOT "${DEVROOT}/SDKs/${PLATFORM}${SDKVER}.sdk")     set(CMAKE_OSX_SYSROOT "${SDKROOT}")     set(CMAKE_SYSTEM_NAME Generic)     set(CMAKE_SYSTEM_PROCESSOR ${PROCESSOR})     set(CMAKE_CXX_COMPILER "${DEVROOT}/usr/bin/g++")     set(CMAKE_OSX_ARCHITECTURES ${ARCH})      include_directories(SYSTEM "${SDKROOT}/usr/include")     link_directories(SYSTEM "${SDKROOT}/usr/lib")      add_definitions(-D_PHONE)     add_library(vp8-armv7-darwin STATIC ${VP8SRC}) endif() 

    你能一眼看出這個配置文件干了什么嗎?其實這個配置文件想要產生的目標(target)只有一個,就是通過${VP8SRC} 編譯生成的靜態庫,但因為加上了條件判斷,及各種平臺相關配置,使得這個配置文件看起來很是復雜。在我看來,編寫 CMake 配置文件是一種線性思維,對于同一個目標的配置可能會零散分布在各個地方。而 GYP 則相當不同,GYP 的配置文件更多地強調模塊化、結構化。看看下面這個示例文件:

    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 29 30 31 32 33 34 35 36 37 38 39 40 41 
    {   'targets': [     {       'target_name': 'foo',       'type': '<(library)',       'dependencies': [         'bar',       ],       'defines': [         'DEFINE_FOO',         'DEFINE_A_VALUE=value',       ],       'include_dirs': [         '..',       ],       'sources': [         'file1.cc',         'file2.cc',       ],       'conditions': [         ['OS=="linux"', {           'defines': [             'LINUX_DEFINE',           ],           'include_dirs': [             'include/linux',           ],         }],         ['OS=="win"', {           'defines': [             'WINDOWS_SPECIFIC_DEFINE',           ],         }, { # OS != "win",           'defines': [             'NON_WINDOWS_DEFINE',           ],         }]       ],     }   ], } 

    我們可以立馬看出上面這個配置文件的輸出目標只有一個,也就是 foo,它是一個庫文件(至于是靜態的還是動態的這需要在生成項目時指定),它依賴的目標、宏定義、包含的頭文件路徑、源文件是什么,以及根據不同平臺設定的不同配置等。這種定義配置文件的方式相比 CMake 來說,讓我覺得更加舒服,也更加清晰,特別是當一個輸出目標的配置越來越多時,使用 CMake 來管理可能會愈加混亂。

    配置文件的編寫方式是我區分 GYP 和 CMake 之間最大的不同點,當然 GYP 也有一些小細節值得注意,比如支持跨平臺項目工程文件輸出,Windows 平臺默認是 Visual Studio,Linux 平臺默認是 Makefile,Mac 平臺默認是 Xcode,這個功能 CMake 也同樣支持,只是缺少了 Xcode。Chromium 團隊成員也撰文詳細比較了 GYP 和 CMake 之間的優缺點,在開發 GYP 之前,他們也曾試圖轉到 SCons(這個我沒用過,有經驗的同學可以比較一下),但是失敗了,于是 GYP 就誕生了。

    當然 GYP 也不是沒有缺點,相反,我覺得它的「缺點」一大堆:

    • 文檔不夠完整,項目不夠正式,某些地方還保留著 Chromium 的影子,看起來像是還沒有完全獨立出來。
    • 大量的括號嵌套,很容易讓人看暈,有過 Lisp 使用經驗的同學可以對號入座。對于有括號恐懼癥,或者不使用現代編輯器的同學基本可以繞行。
    • 為了支持跨平臺,有時不得不加入某些特定平臺的配置信息,比如只適用于 Visual Studio 的 RuntimeLibrary配置,這不利于跨平臺配置文件的編寫,也無形中增加了編寫復雜度。
    • 不支持 make clean,唯一的方法就是將輸出目錄整個刪除或者手動刪除其中的某些文件。

    如果你已經打算嘗試 GYP,那一定記得在生成項目工程文件時加上 --depth 參數,譬如:

    $ gyp --depth=. foo.gyp 

    這也是一個從 Chromium 項目遺留下來的歷史問題。

    也許你根本用不上跨平臺特性,但是 GYP 依然值得嘗試。我編寫了一份 GYP 配置文件的模板,有興趣的同學可以參考。GYP 和 CMake 分別代表了兩種迥異的「風格」,至于孰優孰劣,還得仁者見仁,智者見智。

    posted @ 2014-03-18 14:36 小馬歌 閱讀(378) | 評論 (0)編輯 收藏
     

     

    最近需要獲取別人網站上的音樂數據。用了file_get_contents函數,但是總是會遇到獲取失敗的問題,盡管按照手冊中的 例子設置了超時,可多數時候不會奏效:


    $config['context'] = stream_context_create(array(‘http’ => array(‘method’ => “GET”,
       ’timeout’ => 5//這個超時時間不穩定,經常不奏效
       )
      ));

    這時候,看一下服務器的連接池,會發現一堆類似的錯誤,讓我頭疼萬分:
    file_get_contents(http://***): failed to open stream…

    現在改用了curl庫,寫了一個函數替換:
    function curl_file_get_contents($durl){
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $durl);
      curl_setopt($ch, CURLOPT_TIMEOUT, 5);
      curl_setopt($ch, CURLOPT_USERAGENT, _USERAGENT_);
      curl_setopt($ch, CURLOPT_REFERER,_REFERER_);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      $r = curl_exec($ch);
      curl_close($ch);
       return $r;
    }

    如此,除了真正的網絡問題外,沒再出現任何問題。
    這是別人做過的關于curl和file_get_contents的測試:
    file_get_contents抓取google.com需用秒數:

     

    2.31319094

    2.30374217
    2.21512604
    3.30553889
    2.30124092

    curl使用的時間:

     

    0.68719101

    0.64675593
    0.64326
    0.81983113
    0.63956594

    差距很大?呵呵,從我使用的經驗來說,這兩個工具不只是速度有差異,穩定性也相差很大。

    建議對網絡數據抓取穩定性要求比較高的朋友使用上面的 curl_file_get_contents函數,不但穩定速度快,還能假冒瀏覽器欺騙目標地址哦!

     

     

     

    看到的其他文章收藏于此===============================

    php fsockopen

    方法1: 用file_get_contents 以get方式獲取內容
    <?php
    $url='http://www.domain.com/';
    $html = file_get_contents($url);
    echo $html;
    ?>

    方法2: 用fopen打開url, 以get方式獲取內容
    <?php
    $fp = fopen($url, 'r');
    stream_get_meta_data($fp);
    while(!feof($fp)) {
    $result .= fgets($fp, 1024);
    }
    echo "url body: $result";
    fclose($fp);
    ?>



    方法3:用file_get_contents函數,以post方式獲取url
    <?php
    $data = array ('foo' => 'bar');
    $data = http_build_query($data);

    $opts = array (
    'http' => array (
    'method' => 'POST',
    'header'=> "Content-type: application/x-www-form-urlencoded\r\n" .
    "Content-Length: " . strlen($data) . "\r\n",
    'content' => $data
    )
    );

    $context = stream_context_create($opts);
    $html = file_get_contents('http://localhost/e/admin/test.html', false, $context);

    echo $html;
    ?>


    方法4:用fsockopen函數打開url,以get方式獲取完整的數據,包括header和body

    <?php
    function get_url ($url,$cookie=false)
    {
    $url = parse_url($url);
    $query = $url[path]."?".$url[query];
    echo "Query:".$query;
    $fp = fsockopen( $url[host], $url[port]?$url[port]:80 , $errno, $errstr, 30);
    if (!$fp) {
    return false;
    } else {
    $request = "GET $query HTTP/1.1\r\n";
    $request .= "Host: $url[host]\r\n";
    $request .= "Connection: Close\r\n";
    if($cookie) $request.="Cookie:   $cookie\n";
    $request.="\r\n";
    fwrite($fp,$request);
    while()) {
    $result .= @fgets($fp, 1024);
    }
    fclose($fp);
    return $result;
    }
    }
    //獲取url的html部分,去掉header
    function GetUrlHTML($url,$cookie=false)
    {
    $rowdata = get_url($url,$cookie);
    if($rowdata)
    {
    $body= stristr($rowdata,"\r\n\r\n");
    $body=substr($body,4,strlen($body));
    return $body;
    }

        return false;
    }
    ?>



    方法5:用fsockopen函數打開url,以POST方式獲取完整的數據,包括header和body

    <?php
    function HTTP_Post($URL,$data,$cookie, $referrer="")
    {

        // parsing the given URL
    $URL_Info=parse_url($URL);

        // Building referrer
    if($referrer=="") // if not given use this script as referrer
    $referrer="111";

        // making string from $data
    foreach($data as $key=>$value)
    $values[]="$key=".urlencode($value);
    $data_string=implode("&",$values);

        // Find out which port is needed - if not given use standard (=80)
    if(!isset($URL_Info["port"]))
    $URL_Info["port"]=80;

        // building POST-request:
    $request.="POST ".$URL_Info["path"]." HTTP/1.1\n";
    $request.="Host: ".$URL_Info["host"]."\n";
    $request.="Referer: $referer\n";
    $request.="Content-type: application/x-www-form-urlencoded\n";
    $request.="Content-length: ".strlen($data_string)."\n";
    $request.="Connection: close\n";

        $request.="Cookie:   $cookie\n";

        $request.="\n";
    $request.=$data_string."\n";

        $fp = fsockopen($URL_Info["host"],$URL_Info["port"]);
    fputs($fp, $request);
    while(!feof($fp)) {
    $result .= fgets($fp, 1024);
    }
    fclose($fp);

        return $result;
    }

    ?>


    方法6:使用curl庫,使用curl庫之前,可能需要查看一下php.ini是否已經打開了curl擴展

    <?php
    $ch = curl_init();
    $timeout = 5;
    curl_setopt ($ch, CURLOPT_URL, 'http://www.domain.com/');
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    $file_contents = curl_exec($ch);
    curl_close($ch);

    echo $file_contents;
    ?>

     

     

     

     

    php中 curl, fsockopen ,file_get_contents 三個函數 都可以實現采集模擬發言 。 三者有什么區別,或者講究么  

    趙永斌:
    有些時候用file_get_contents()調用外部文件,容易超時報錯。換成curl后就可以.具體原因不清楚
    curl 效率比file_get_contents()和fsockopen()高一些,原因是CURL會自動對DNS信息進行緩存(亮點啊 有我待親測)

    范佳鵬:
    file_get_contents curl fsockopen
    在當前所請求環境下選擇性操作,沒有一概而論:
    具我們公司開發KBI應用來看:
    剛開始采用:file_get_contents
    后來采用:fsockopen
    最后到至今采用:curl

    (遠程)我個人理解到的表述如下(不對請指出,不到位請補充)
    file_get_contents 需要php.ini里開啟allow_url_fopen,請求http時,使用的是http_fopen_wrapper,不會keeplive.curl是可以的。
    file_get_contents()單個執行效率高,返回沒有頭的信息。
    這個是讀取一般文件的時候并沒有什么問題,但是在讀取遠程問題的時候就會出現問題。
    如果是要打一個持續連接,多次請求多個頁面。那么file_get_contents和fopen就會出問題。
    取得的內容也可能會不對。所以做一些類似采集工作的時候,肯定就有問題了。
    sock較底層,配置麻煩,不易操作。 返回完整信息。

    潘少寧-騰訊:

    file_get_contents 雖然可以獲得某URL的內容,但不能post  get啊。
    curl 則可以post和get啊。還可以獲得head信息
    而socket則更底層。可以設置基于UDP或是TCP協議去交互
    file_get_contents 和 curl 能干的,socket都能干。
    socket能干的,curl 就不一定能干了
    file_get_contents  更多的時候 只是去拉取數據。效率比較高  也比較簡單。
    趙的情況這個我也遇到過,我通過CURL設置host 就OK了。  這和網絡環境有關系

    posted @ 2014-02-14 16:44 小馬歌 閱讀(1313) | 評論 (0)編輯 收藏
     

    原文:http://blog.rushcj.com/2010/08/21/try-thrift/

    Thrift是一個非常棒的工具,是Facebook的開源項目,目前的開發非常的活躍,由Apache管理,所以用的是Apache Software License,這非常重要,因為可以放心的對其修改并用到自己的項目中。

    談到修改Thrift,這非常重要。因為我覺得如果要嚴肅的使用Thrift,不可避免的要深入了解它,并幾乎都要修改Thrift的代碼。一個通信框架,它不可能幫你做到所有的事情,也不可能在不了解的情況下就貿然的使用。

    1.Thrift 的Java Server/Client有個較為嚴重的bug(https://issues.apache.org/jira/browse/THRIFT-601 ),隨機向thrift  sever的監聽端口發些數據,可能會導致Server OutOfMemory,細細看看代碼,這個bug有點土。

    2.Thrift Client線程不安全,多線程下使用可能導致Server和客戶端程序崩潰。Client的每次調用遠程方法其實是有多次Socket寫操作,因此每個線程中使用的Client要保證獨立,如果多個線程混用同一個Client(其實是用同一個Socket),可能會導致傳輸的字節順序混亂,使得Server OutOfMemory(參考1)

    3.Thrift定義數據結構時,盡量避免用map, 或者set。在cpp下, map被對應為std::map(rb tree)和std::set,thrift生成的類不會重載”<”,因此需要手動修改生成類,否則link沒法通過。較為麻煩。

    4.如果Client端基于效率考慮,要緩存Socket,需要重新實現其TTransport類,以支持 Socket緩存池。當然,這個實現其實跟thrift沒多大關系,算是2次開發。但一般都要這么做的吧?

    5.如果Client基于效率考慮,緩存了Socket,那么thrift Server端的模式選擇就較為重要了。如果使用同步的TThreadPoolServer,那么無可避免的,客戶端緩存1個Socket,Server端就會有一個線程一直處于Server狀態,等待peek這個Socket上的數據。這個線程就不能用于其它請求了。所以,及時清理Client端的Socket及控制Socket池的大小是非常必要的。

    6.聽同事說CPP Thrift Server的Epoll NonBlocking模式有效率問題。其實,并發要求不高的Server用LT模式的EPoll其實很方便的,當然,這個要自己給Thrfit Server做patch了,不過也不麻煩。開發起來也是很方便的。我想給我們的Server加個EPOLLONESHOT的同步EPoll實現。

    7.CPP下的 TThreadPoolServer和TThreadServer由一個有趣的問題,如果有客戶端維護長連接,那么對這個Server實例做析構的時候會堵塞(前面說過了,在peek中…)。

    8.用valgrind看,thrift cpp似乎有一些內存問題。沒細看。

    9.無論是Java,還是CPP,Server端都無法通過合法的方式獲取Client的ip, port。可以通過編寫ThriftServerEventHandler可以處理這件事情。如果想要獲取Client ip, port的話,可以看看這個東西。

    posted @ 2014-02-14 16:42 小馬歌 閱讀(1587) | 評論 (0)編輯 收藏
     

    nginx upstream keepalive connections

     

    Nginx從 1.1.4 開始,實現了對后端機器的長連接支持,這是一個激動人心的改進,這意味著 Nginx 與后端機器的通信效率更高,后端機器的負擔更低。

     

    例如,對一個沒有長連接支持的后端機器,會出現大量TIME_WAIT 狀態的連接,使用以下命令驗證之:

    netstat -n | grep TIME_WAIT

     

    經過查閱官方文檔,其目前已經實現了http, fastcgi, memcache 協議的長連接支持。而之前的版本中僅支持memcache 協議。

     

    1. 啟用到 memcache 服務器的長連接 
    在upstream 配置段中增加 keepalive N 指令即可: 
    upstream memcached_backend {

        server 127.0.0.1:11211;

        server 10.0.0.2:11211;

         keepalive 32;

    }

     

    server {

        ...

        location /memcached/ {

            set $memcached_key $uri;

            memcached_pass memcached_backend;

        }

    }

     

     

    2.  啟用fastcgi 長連接支持 
    除了需要在upstream 中配置 keepalive N 外,還需要在 location 中增加 fastcgi_keep_conn on;

    upstream fastcgi_backend {

        server 127.0.0.1:9000;

         keepalive 8;

    }

     

    server {

        ...

        location /fastcgi/ {

            fastcgi_pass fastcgi_backend;

             fastcgi_keep_conn on;

            ...

        }

    }

     

    3.  啟用對后端機器HTTP 長連接支持

    upstream http_backend {

        server 127.0.0.1:8080;

        keepalive 16;

    }

     

    server {

        ...

        location /http/ {

            proxy_pass http://http_backend;

            proxy_http_version 1.1;

            proxy_set_header Connection "";

            ...

        }

    }

     

    注意:需要設置nginx 代理請求的 http 協議版本號為 1.1,  以及清除掉 Connection 請求header,  官方文檔描述:

    For HTTP, the proxy_http_version directive should be set to “ 1.1 ”  and the  “Connection ”  header field should be cleared .

     

    The connections parameter should be set low enough to allow upstream servers to process additional new incoming connections as well. 

     

    即是說:keepalive N 指令中 , N 的值應該盡可能設置小一些,以便后端機器可以同時接受新的連接。

     

    在我負責的生產環境中,前端是nginx,  靜態文件緩存使用 varnish,  使用長連接之后, varnish機器的連接數從 8000 多下降至 200 多,負載值也有明顯降低。

     

    但是針對fastcgi,  即后端機器是 php-fpm 服務時,在 nginx 日志中出現以下錯誤:

     upstream sent unsupported FastCGI protocol version: 0 while reading upstream 

     

    廣泛搜集,目前還未解決之。如果您遇到同樣的問題并解決之,請一定聯系筆者信箱zhangxugg@163.com,  甚是感謝。

    posted @ 2014-02-14 15:35 小馬歌 閱讀(893) | 評論 (0)編輯 收藏
    僅列出標題
    共95頁: First 上一頁 21 22 23 24 25 26 27 28 29 下一頁 Last 
     
    主站蜘蛛池模板: 国产l精品国产亚洲区在线观看| 天天综合亚洲色在线精品| 免费一级毛片在播放视频| 51精品视频免费国产专区| 成人一级免费视频| 国产亚洲玖玖玖在线观看| 亚洲国产精品人久久| 国产美女亚洲精品久久久综合| 免费看大黄高清网站视频在线| 四虎在线成人免费网站| a级成人毛片免费视频高清| 春意影院午夜爽爽爽免费| 亚洲国产精品无码久久九九大片| 亚洲美女视频免费| 亚洲天堂男人天堂| 亚洲乱码无码永久不卡在线 | 亚洲av永久无码精品表情包| 亚洲国产成人精品女人久久久| 免费毛片在线视频| 美女裸身网站免费看免费网站| 99久久免费看国产精品| 免费无码av片在线观看| 黄色短视频免费看| caoporn国产精品免费| 黄色一级毛片免费| 国产午夜亚洲精品不卡电影| 亚洲AV成人片无码网站| 亚洲精品V天堂中文字幕| 亚洲人av高清无码| 国产人成亚洲第一网站在线播放| 亚洲乱人伦精品图片| 亚洲一本之道高清乱码| 亚洲国产日韩女人aaaaaa毛片在线| 亚洲视频免费在线看| 亚洲视频一区二区在线观看| 亚洲精品日韩专区silk| 亚洲国产精品成人精品软件| 亚洲av日韩av无码av| 亚洲欧洲日韩国产一区二区三区| 自拍偷区亚洲国内自拍| 国产精品亚洲а∨无码播放麻豆|