博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用beetle简单地实现高效的http基础服务
阅读量:5991 次
发布时间:2019-06-20

本文共 10160 字,大约阅读时间需要 33 分钟。

之前的章节里已经讲述了Beetle对不同应用协议的扩展和处理,在这章会讲解Beetle实现一个比较通用的应用协议HTTP扩展.组件对于HTTP协 议的扩展也是一件非常简单的事情,同时也能得到组件出色的性能和稳定性所支持.不过在实现之前必须对HTTP协议组成部分有个大概的了解.HTTP协议主 要由两大部分组件分别是消息头和消息体,消息头是必须的有于描述获取相关内容和附带的一些属性如:GET /images/logo.gif HTTP/1.1,通过回车换行符来标记消息头结束.对于消息休则是可选的如果存在消息体必须在消息头里标识Content-Length.对于HTTP 更详细的内容可以查看

接下就详细讲述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实现相关方法即可.

///         /// 网络数据导入方法        ///         /// 接收数据流        /// 开始索引        /// 总长度        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
{ 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%左右的优化空间.

转载地址:http://jgvlx.baihongyu.com/

你可能感兴趣的文章
HDFS架构原理
查看>>
用js实现返回上一页
查看>>
Docker创建本地仓库
查看>>
成为Java GC专家(3)—如何优化Java垃圾回收机制
查看>>
awk操作应用
查看>>
ASP.NET 使用js插件出现上传较大文件失败的解决方法(ajaxfileupload.js第一弹)
查看>>
菜鸟也能搞定C++内存泄漏
查看>>
CentOS系统中常用查看日志命令
查看>>
线程和进程的通信方式以及区别
查看>>
mongoDB 入门指南、示例
查看>>
CentOS 7.0设置IP地址、网关DNS
查看>>
Spring生态顶级项目说明
查看>>
lamp搭建discuz缺少依赖 mysqli_connect()
查看>>
MySQL 设置cmd命令行登陆
查看>>
Solr 多核(MultiCore)配置
查看>>
[BZOJ 3170] [TJOI 2013] 松鼠聚会
查看>>
ARM 的异常处理
查看>>
字符串 值、输出效果、转义
查看>>
python类型
查看>>
第八周-学习进度条
查看>>