dotnet core高吞吐Http api服務元件FastHttpApi

smark發表於2018-09-17

簡介

是dotNet core下基於Beetlex實現的一個高度精簡化和高吞吐的HTTP API服務開源元件,它並沒有完全實現HTTP SERVER的所有功能,而是隻實現了在APP和WEB中提供資料服務最常用兩個指令GET/SET,滿足在應用實現JSON,PROTOBUF和MSGPACK等基於HTTP的資料互動功能,雖然是一個精簡版本但針對SSL這方面的安全性還是支援。有犧牲就必然有收穫,FastHttpApi作出這麼大的精簡必然在效能上有所收穫取,經測試FastHttpApi在GET/POST這些資料互動的場景下效能和吞吐能力是Asp.net core整合的Kestrel的要優勝許多。

https://github.com/IKende/FastHttpApi

使用便利性

FastHttpApi雖然在HTTP方面作了大量的精簡,但並沒有為此增加了它使用的複雜度。FastHttpApi具備asp.net core webapi的便利性;應用人員只需要制定和webapi一樣的方法即可,在使用過程中和寫普通邏輯方法沒有多大的區別。

定義一個控制器

控制器用來定義具體相應URL處理的方法,只需要在類上定義Controller屬性即可把類中的Public方法提供給Http訪問;方法引數來源於QueryString,當引數標記為BodyParameter的時候引數來源於Http Body.

   [Controller]
    public class ControllerTest
    {
        //  /hello?name=
        public string Hello(string name)
        {
            return DateTime.Now + " hello " + name;
        }
        // /add?a=&b=
        public string Add(int a, int b)
        {
            return string.Format("{0}+{1}={2}", a, b, a + b);
        }
        // /post?name=
        public object Post(string name, [BodyParameter] UserInfo data)
        {
            return data;
        }
        // /listcustomer?count
        public IList<Customer> ListCustomer(int count)
        {
            return Customer.List(count);
        }
        // /listemployee?count
        public IList<Employee> ListEmployee(int count)
        {
            return Employee.List(count);
        }
        // post /AddEmployee 
        public Employee AddEmployee([BodyParameter]Employee item)
        {
            return item;
        }
    }
Filter定義

Filter是Controller處理方法的攔載器,通Filter可以對所有方法進行統一攔載處理,如許可權日誌等。

    [Controller]
    [NotFoundFilter]
    public class ControllerTest
    {
        //  /hello?name=
        [SkipFilter(typeof(NotFoundFilter))]
        [CustomFilter]
        public string Hello(string name)
        {
            return DateTime.Now + " hello " + name;
        }
        // /add?a=&b=
        public string Add(int a, int b)
        {
            return string.Format("{0}+{1}={2}", a, b, a + b);
        }
    }
    public class GlobalFilter : FilterAttribute
    {
        public override void Execute(ActionContext context)
        {
            Console.WriteLine(DateTime.Now + " globalFilter execting...");
            context.Execute();
            Console.WriteLine(DateTime.Now + " globalFilter executed");
        }
    }

    public class NotFoundFilter : FilterAttribute
    {
        public override void Execute(ActionContext context)
        {
            Console.WriteLine(DateTime.Now + " NotFoundFilter execting...");
            context.Response.NotFound();
            Console.WriteLine(DateTime.Now + " NotFoundFilter executed");
        }
    }

    public class CustomFilter : FilterAttribute
    {
        public override void Execute(ActionContext context)
        {
            Console.WriteLine(DateTime.Now + " CustomFilter execting...");
            context.Execute();
            Console.WriteLine(DateTime.Now + " CustomFilter executed");
        }
    }
啟動服務
        static void Main(string[] args)
        {
            mApiServer = new BeetleX.FastHttpApi.HttpApiServer();
            mApiServer.Register(typeof(Program).Assembly);
            mApiServer.ServerConfig.BodySerializer = new BeetleX.FastHttpApi.JsonBodySerializer();
            mApiServer.Open();
            Console.Read();
        }

制定HTTP Body轉換器

轉換器是元件最常用的自定義功能,通過它可以實現不同種類的資料格式,如json,protobuf和msgpack等。以下是一個json轉換器的實現

 public class JsonBodySerializer : IBodySerializer
    {
        public JsonBodySerializer()
        {
            ContentType = "application/json";
        }
        public string ContentType { get; set; }
        public object GetInnerError(Exception e, HttpResponse response, bool outputStackTrace)
        {
            return new ErrorResult { url = response.Request.Url, code = 500, error = e.Message, stackTrace = outputStackTrace? e.StackTrace:null };
        }
        public object GetNotSupport(HttpResponse response)
        {
            return new ErrorResult { url = response.Request.Url, code = 403, error = response.Request.Method + " method type not support" };
        }
        public object GetNotFoundData(HttpResponse response)
        {
            return new ErrorResult { url = response.Request.Url, code = 404 };
        }
        public class ErrorResult
        {
            public string url { get; set; }
            public int code { get; set; }
            public string error { get; set; }
            public string stackTrace { get; set; }
        }
        public virtual int Serialize(PipeStream stream, object data)
        {
            int length = stream.CacheLength;
            string value = Newtonsoft.Json.JsonConvert.SerializeObject(data);
            stream.Write(value);
            return stream.CacheLength - length;
        }
        public virtual bool TryDeserialize(PipeStream stream, int length, Type type, out object data)
        {
            data = null;
            if (stream.Length >= length)
            {
                string value = stream.ReadString(length);
                if (type != null)
                {
                    data = Newtonsoft.Json.JsonConvert.DeserializeObject(value,type);
                }
                else
                {
                    data = Newtonsoft.Json.JsonConvert.DeserializeObject(value);
                }
                return true;
            }
            return false;
        }
    }

效能對比測試

由於dotnet core下面沒有其他簡化的http api元件,只能拿Kestrel asp.net core來作對比,雖然對asp.net core不公平,但這樣的對比測也只是為了體現簡化後的效能回報;測試伺服器是阿里雲的4核虛擬機器,8G記憶體,測試工具是AB,測試功能主要是針對GET/POST的json資料處理。由於Kestrel asp.net core預設不支援AB的Keep-Alive選項,所以測試結果就並沒有針對asp.net core的Keep-Alive測試

Kestrel asp.net core程式碼
        // GET api/values/5
        [HttpGet("{id}")]
        public ActionResult Get(int id)
        {
            return new JsonResult(Employee.List(id));
        }
        // POST api/values
        [HttpPost]
        public ActionResult Post([FromBody] Employee value)
        {
            return new JsonResult(value);
        }
FastHttpApi 程式碼
        // /listemployee?count
        public IList<Employee> ListEmployee(int count)
        {
            return Employee.List(count);
        }
        // post /AddEmployee 
        public Employee AddEmployee([BodyParameter]Employee item)
        {
            return item;
        }
Kestrel asp.net core GET測試結果

FastHttpApi GET測試結果

FastHttpApi GET測試結果開啟Keep-Alive

Kestrel asp.net core POST測試結果

FastHttpApi POST測試結果

FastHttpApi POST測試結果開啟Keep-Alive

針對Kestrel的對比測試

對比一下兩者在accept connection上的效能差異,開啟了兩個AB例項同時進行壓測,結果是FastHttpApi在處理併發數快高於Kestrel快一倍的情況,CPU使用率只有Kestrel的一半。

Kestrel程式碼
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.Run(context =>
            {
                byte[] data = System.Text.Encoding.UTF8.GetBytes(DateTime.Now.ToString());
                return context.Response.Body.WriteAsync(data, 0, data.Length);
            });
        }
FastHttpApi程式碼
        //  /hello?name=
        public string Hello(string name)
        {
            return DateTime.Now + " hello " + name;
        }
Kestrel測試結果

FastHttpApi測試結果

相關文章