使用beetle簡單地實現高效的http基礎服務

來源:互聯網
上載者:User

之前的章節裡已經講述了Beetle對不同應用協議的擴充和處理,在這章會講解Beetle實現一個比較通用的應用協議HTTP擴充.組件對於HTTP協 議的擴充也是一件非常簡單的事情,同時也能得到組件出色的效能和穩定性所支援.不過在實現之前必須對HTTP協議組成部分有個大概的瞭解.HTTP協議主 要由兩大部分組件分別是訊息頭和訊息體,訊息頭是必須的有於描述擷取相關內容和附帶的一些屬性如:GET /images/logo.gif HTTP/1.1,通過斷行符號分行符號來標記訊息頭結束.對於訊息休則是可選的如果存在訊息體必須在訊息頭裡標識Content-Length.對於HTTP 更詳細的內容可以查看http://zh.wikipedia.org/zh/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE

接下就詳細講述Beetle實現HTTP服務的過程,具體內容如下:

  • 制訂對應的HTTP對象訊息結構
  • 制訂HTTP協議分析器
  • 實現一個HTTP服務並在瀏覽中訪問
  • 效能測試
  • 總結
制訂HTTP對象訊息

既然HTTP協議由兩大部分組成,那就可以根據這制訂相應的協議對象

  • 訊息頭
        //http頭描述        public class HttpHeader : HttpMessage        {            public HttpHeader()            {                Headers = new Hashtable(8);            }            //訊息頭常量定義,詳細參考http://en.wikipedia.org/wiki/List_of_HTTP_header_fields            #region headers            public const string HTTP_ContentLength = "Content-Length";            public const string HTTP_Request_Accept = "Accept";            public const string HTTP_Request_AcceptCharset = "Accept-Charset";            public const string HTTP_Requst_AcceptEncoding = "Accept-Encoding";            #endregion            //擷取http頭索引值表            public Hashtable Headers            {                get;                set;            }            //擷取設定方法資訊如GET /images/logo.gif HTTP/1.1或返回資訊            public string MethodInfo { get; set; }            //擷取或設定訊息休長度            public int ContentLength            {                get                {                    object value = Headers[HTTP_ContentLength];                    if (value != null)                        return int.Parse(value.ToString().Trim());                    return 0;                }                set                {                    Headers[HTTP_ContentLength] = value.ToString();                }            }            public string this[string key]            {                get                {                    return (string)Headers[key];                }                set                {                    Headers[key] = value;                }            }        }
  • 訊息體
        //訊息體資料區塊        public class HttpDataSegment : HttpMessage        {            public HttpDataSegment()            {                Data = HttpPackage.BufferPools.Pop();                Data.SetInfo(0, 0);            }            //當前塊是否尾部            public bool Eof            {                get;                set;            }            //擷取資料區塊內容            public ByteArraySegment Data            {                get;                set;            }            //釋放資料區塊對應的buffer            protected override void OnDispose()            {                base.OnDispose();                if (Data != null)                {                    HttpPackage.BufferPools.Push(Data);                    Data = null;                }            }        }

由於訊息體有可能比較大,如果是幾百MB的情況也不太可能用一個Buffer來處理,所以訊息設計由多個資料區塊組件.

  • 訊息適器
        //訊息適配器用於對象寫入流轉換        public class HttpAdater : IMessage        {            public HttpMessage Message            {                get;                set;            }            //從流載入資料,由於直接有協議分析器來處理了所以不需要實現相關方法            public void Load(BufferReader reader)            {                throw new NotImplementedException();            }            //把http協議對象寫入網路流            public void Save(BufferWriter writer)            {                if (Message is HttpHeader)                {                    OnSaveHeader(writer, Message as HttpHeader);                }                else if (Message is HttpDataSegment)                {                    OnSaveDataSegment(writer, Message as HttpDataSegment);                }                else                {                }                Message.Dispose();            }            //寫入訊息頭資訊            private void OnSaveHeader(BufferWriter writer, HttpHeader header)            {                writer.WriteString(header.MethodInfo + "\r\n");                foreach (object key in header.Headers.Keys)                {                    writer.WriteString(string.Format("{0}: {1}\r\n", key, header.Headers[key]));                }                writer.WriteString("\r\n");            }            //寫入訊息體資訊            private void OnSaveDataSegment(BufferWriter writer, HttpDataSegment segment)            {                writer.Write(segment.Data.Array, segment.Data.Offset, segment.Data.Count);            }        }
制訂HTTP協議分析器

組件對協議的支援並不需要修改組件核心代碼,都是通過擴充的方式實現.只需要繼承Package實現相關方法即可.

        /// <summary>        /// 網路資料匯入方法        /// </summary>        /// <param name="data">接收資料流</param>        /// <param name="start">開始索引</param>        /// <param name="count">總長度</param>        public override void Import(byte[] data, int start, int count)        {            int index = 0;            if (mHeader == null)            {                //從池中擷取頭載入記憶體塊                if (mHeaderData == null)                {                    mHeaderData = BufferPools.Pop();                    //初始化塊內容                    mHeaderData.SetInfo(0, 0);                }                //查詢http頭結束標記                index = ByteIndexOf(data, EofData, start, count);                if (index >= 0)                {                    //把http頭資料流複製到當前分析緩衝區中                    Buffer.BlockCopy(data, start, mHeaderData.Array, mHeaderData.Offset, index + 1);                    mHeaderData.Count += index + 1;                    start = index + 1;                    count = count - index + 1;                    //分析頭資訊                    OnCreateHeader();                    MessageArgs.Message = mHeader;                    //觸發訊息接收事件                    OnReceiveMessage(MessageArgs);                }            }            //如果存在接收內容            if (ContentLength > 0)            {                //建立一個資料區塊訊息                HttpDataSegment hds = new HttpDataSegment();                //把資料流複製到相應的資料區塊中                Buffer.BlockCopy(data, start, hds.Data.Array, 0, count);                hds.Data.SetInfo(0, count);                ContentLength -= count;                //如果擷取資料流完整後設定為結束塊                if (ContentLength == 0)                    hds.Eof = true;                MessageArgs.Message = hds;                //觸發訊息接收事件                OnReceiveMessage(MessageArgs);            }            //清除當前接收請求內容            if (mHeader !=null && ContentLength == 0)            {                DisposeHeaderData();                mHeader = null;            }        }

通過實現Import方法來處理協議資料分析,對於http頭的拆分可以通過下載完整代碼查看.

實現一個HTTP服務並在瀏覽中訪問

組件提供基礎的服務物件,只需要在繼承指寫對應的協議分析器即可,用起來也非常簡單方便.

    class Program:ServerBase<Beetle.Packages.HttpPackage>    {        static void Main(string[] args)        {            //初如化組件            TcpUtils.Setup("beetle");            Program server = new Program();            server.Listens = 1000;            //在所有IP的8088連接埠上監聽服務            server.Open(8088);            Console.WriteLine("Beetle Http Server start@8088");            System.Threading.Thread.Sleep(-1);        }        protected override void OnConnected(object sender, ChannelEventArgs e)        {            base.OnConnected(sender, e);            e.Channel.EnabledSendCompeletedEvent = true;            e.Channel.SendMessageCompleted = (o, i) => {                HttpPackage.HttpAdater adapter = (HttpPackage.HttpAdater)i.Messages[i.Messages.Count - 1];                //訊息發送完成後判斷是否關閉對應的串連                if (adapter.Message is HttpPackage.HttpHeader)                {                    if (((HttpPackage.HttpHeader)adapter.Message).ContentLength == 0 && e.Channel != null)                        e.Channel.Dispose();                }                else if (adapter.Message is HttpPackage.HttpDataSegment)                {                    if (((HttpPackage.HttpDataSegment)adapter.Message).Eof && e.Channel != null)                        e.Channel.Dispose();                }            };                   }        //錯誤處理事件,可以擷取協議分析和邏輯處理環節中出現的異常        protected override void OnError(object sender, ChannelErrorEventArgs e)        {            base.OnError(sender, e);           Console.WriteLine("{0} error:{1}", e.Channel.EndPoint, e.Exception.Message);            Console.WriteLine(e.Exception.StackTrace);        }        //串連釋放過程        protected override void OnDisposed(object sender, ChannelDisposedEventArgs e)        {            base.OnDisposed(sender, e);        }        //訊息接收處理事件        protected override void OnMessageReceive(PacketRecieveMessagerArgs e)        {            base.OnMessageReceive(e);            if (e.Message is HttpPackage.HttpHeader)            {                OnRequestHeader(e.Channel, (HttpPackage.HttpHeader)e.Message);            }            else if (e.Message is HttpPackage.HttpDataSegment)            {                OnRequestSegment(e.Channel, (HttpPackage.HttpDataSegment)e.Message);            }        }        //得到要求標頭資訊處理過程        private void OnRequestHeader(TcpChannel channel, Beetle.Packages.HttpPackage.HttpHeader header)        {            //Console.WriteLine(header.MethodInfo);            //foreach (object key in header.Headers.Keys)            //{            //    Console.WriteLine("{0}:\t{1}", key, header[(string)key]);            //}            HttpPackage.HttpHeader response = new HttpPackage.HttpHeader();            HttpPackage.HttpDataSegment responsedata = new HttpPackage.HttpDataSegment();            responsedata.Data.Encoding(Resource1.BEETLE_HTTP_TEST, channel.Coding);            responsedata.Eof = true;            response.ContentLength = responsedata.Data.Count;            response.MethodInfo = "HTTP/1.1 200 OK";            response["Cache-Control"] = "private";            response["Connection"] = "Close";            response["Content-Type"] = "text/html; charset=utf-8";            response["Server"] = "Beetle Http Server";            //發送應答頭            channel.Send(response);            //發送應答資料            channel.Send(responsedata);        }        private void OnRequestSegment(TcpChannel channel, Beetle.Packages.HttpPackage.HttpDataSegment segment)        {        }    }

以上一個HTTP服務已經實現了,由於測試用所以當接收請求後並沒有分析對應的請求資訊,直接測試內容.通過瀏覽器查詢如下:

效能測試

為作一個服務型的應用需要關注的是其效能和穩定性,下面通過AB工具對這個服務進行壓力測試,並和IIS7擷取一個靜態頁進行對比,測試內容是分別請求100W次

Beetle結果

IIS結果

總結

Beetle 可以很靈活地實現不同的應用協議支援,而你在擴充的過程只需要關心協議和邏輯上的工作,對於效能和穩定性Beetle可以給你做保障.由於Beetle是 純c#實現,所以也可以說明.net的socket 處理能力還是很不錯的,由於Beetle並不是針對http這種短串連應用設計,所以在這應用上並沒有真正發揮出.net socket在這方面的能力.總的來說應該還有10%-20%左右的最佳化空間.

下載代碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.