溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

關(guān)于大小型項目如何最大限度提高WebAPi性能

發(fā)布時間:2020-07-20 16:18:41 來源:網(wǎng)絡(luò) 閱讀:1317 作者:xsster 欄目:建站服務(wù)器

性能提升一:JSON序列化器(Jil)

在.NET里面默認的序列化器是JavaScriptSrializer,都懂的,性能實在是差,后來出現(xiàn)了Json.NET,以至于在目前創(chuàng)建項目時默認用的序列化器是Json.NET,它被.NET開發(fā)者所廣泛使用,它的強大和性能毋庸置疑,以至于現(xiàn)在Json.NET版本已經(jīng)更新到9.0版本,但是在大型項目中一旦數(shù)據(jù)量巨大時,此時用Json.NET來序列化數(shù)據(jù)會略慢,這時我們就可以嘗試用Jil,它里面的APi也足夠我們用,我們講述幾個常用的APi并一起對比Json.NET來看看:

序列化對比

在Json.NET中是這樣序列化的

JsonConvert.SerializeObject(obj)

而在Jil中序列化數(shù)據(jù)是這樣的

JSON.Serialize(obj)

此時對于Jil序列化數(shù)據(jù)返回的字符串形式有兩種

(1)直接接收

 object obj = new { Foo = 123, Bar = "abc" }; string s = Jil.JSON.Serialize(obj)

(2)傳遞給StringWriter來接收

var obj = new { Foo = 123, Bar = "abc" };var t = new StringWriter();
JSON.SerializeDynamic(obj, t);

上述說到對于數(shù)據(jù)量巨大時用Jil其效率高于Json.NET,下來我們來驗證序列化10000條數(shù)據(jù)

序列化類:

    public class Person
    {        public int Id { get; set; }        public string Name { get; set; }
    }

測試數(shù)據(jù):

關(guān)于大小型項目如何最大限度提高WebAPi性能

             list =  List<Person> ( i = ; i < ; i++ stop =  jil = stop1 =  json =

關(guān)于大小型項目如何最大限度提高WebAPi性能

Jil序列化封裝:

關(guān)于大小型項目如何最大限度提高WebAPi性能

        private static string SerializeList(List<Person> list)
        {            using (var output = new StringWriter())
            {
                JSON.Serialize(
                    list,
                    output
                );                return output.ToString();
            }
        }

關(guān)于大小型項目如何最大限度提高WebAPi性能

我們來看看測試用例:

關(guān)于大小型項目如何最大限度提高WebAPi性能此時利用Json.NET序列化數(shù)據(jù)明顯優(yōu)于Jil,但序列化數(shù)據(jù)為10萬條數(shù),Jil所耗時間會接近于Json.NET,當數(shù)據(jù)高于100萬條數(shù)時這個時候就可以看出明顯的效果,如下:

關(guān)于大小型項目如何最大限度提高WebAPi性能

此時Jil序列化數(shù)據(jù)不到1秒,而利用Json.NET則需要足足接近3秒。

測試用例更新:

當將代碼進行如下修改時,少量數(shù)據(jù)也是優(yōu)于Json.NET,數(shù)據(jù)量越大性能越明顯,感謝園友【calvinK】提醒:

關(guān)于大小型項目如何最大限度提高WebAPi性能

            var list = new List<int>();            for (int i = 0; i < 10000; i++)
            {
                list.Add(i);
            }            var stop = new Stopwatch();
            stop.Start();            for (var i = 0; i < 1000; i++)
            {                var jil = SerializeList(list);

            }

            Console.WriteLine(stop.ElapsedMilliseconds);
            stop.Stop();            var stop1 = new Stopwatch();
            stop1.Start();            for (var i = 0; i < 1000; i++)
            {                var json = JsonConvert.SerializeObject(list);

            }
            Console.WriteLine(stop1.ElapsedMilliseconds);
            stop1.Stop();

關(guān)于大小型項目如何最大限度提高WebAPi性能

結(jié)果如下:

關(guān)于大小型項目如何最大限度提高WebAPi性能

 關(guān)于Jil的序列化還有一種則是利用JSON.SerializeDynamic來序列化那些在編譯時期無法預(yù)測的類型。 至于反序列化也是和其序列化一一對應(yīng)。

下面我們繼續(xù)來看看Jil的其他特性。若在視圖上渲染那些我們需要的數(shù)據(jù),而對于實體中不必要用到的字段我們就需要進行過濾,此時我們用到Jil中的忽略屬性。

 [JilDirective(Ignore = true)]

我們來看看:

關(guān)于大小型項目如何最大限度提高WebAPi性能

    public class Person
    {
        [JilDirective(Ignore = true)]        public int Id { get; set; }        public int Name { get; set; }
    }

關(guān)于大小型項目如何最大限度提高WebAPi性能

            var jil = SerializeList(new Person() { Id = 1, Name = 123 } );
            Console.WriteLine(jil);

關(guān)于大小型項目如何最大限度提高WebAPi性能

另外在Jil中最重要的屬性則是Options,該屬性用來配置返回的日期格式以及其他配置,若未用其屬性默認利用Json.NET返回如【\/Date(143546676)\/】,我們來看下:

var jil = SerializeList(new Person() { Id = 1, Name = "123", Time = DateTime.Now });

進行如下設(shè)置:

               JSON.Serialize(
                    p,
                    output,                    new Options(dateFormat: DateTimeFormat.ISO8601)
                );

關(guān)于大小型項目如何最大限度提高WebAPi性能

有關(guān)序列化繼承類時我們同樣需要進行如下設(shè)置,否則無法進行序列化

new Options(dateFormat: DateTimeFormat.ISO8601, includeInherited: true)

Jil的性能絕對優(yōu)于Json.NET,Jil一直在追求序列化的速度所以在更多可用的APi可能少于Json.NET或者說沒有Json.NET靈活,但是足以滿足我們的要求。 

性能提升二:壓縮(Compress) 

壓縮方式(1) 【IIS設(shè)置】

啟動IIS動態(tài)內(nèi)容壓縮

關(guān)于大小型項目如何最大限度提高WebAPi性能

壓縮方式(2)【DotNetZip】 

利用現(xiàn)成的輪子,下載程序包【DotNetZip】即可,此時我們則需要在執(zhí)行方法完畢后來進行內(nèi)容的壓縮即可,所以我們需要重寫【 ActionFilterAttribute 】過濾器,在此基礎(chǔ)上進行我們的壓縮操作。如下:

關(guān)于大小型項目如何最大限度提高WebAPi性能

    public class DeflateCompressionAttribute : ActionFilterAttribute
    {        public override void OnActionExecuted(HttpActionExecutedContext actionContext)
        {            var content = actionContext.Response.Content;            var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;            var compressContent = bytes == null ? new byte[0] : CompressionHelper.DeflateByte(bytes);
            actionContext.Response.Content = new ByteArrayContent(compressContent);
            actionContext.Response.Content.Headers.Remove("Content-Type");            if (string.Equals(actionContext.Request.Headers.AcceptEncoding.First().Value, "deflate"))
                actionContext.Response.Content.Headers.Add("Content-encoding", "deflate");            else
                actionContext.Response.Content.Headers.Add("Content-encoding", "gzip");
            actionContext.Response.Content.Headers.Add("Content-Type", "application/json;charset=utf-8");            base.OnActionExecuted(actionContext);
        }

    }

關(guān)于大小型項目如何最大限度提高WebAPi性能

利用DotNetZip進行快速壓縮:

關(guān)于大小型項目如何最大限度提高WebAPi性能

    public class CompressionHelper
    {        public static byte[] DeflateByte(byte[] str)
        {            if (str == null)
            {                return null;

            }            using (var output = new MemoryStream())
            {                using (var compressor = new Ionic.Zlib.GZipStream(

                output, Ionic.Zlib.CompressionMode.Compress,

                Ionic.Zlib.CompressionLevel.BestSpeed))
                {

                    compressor.Write(str, 0, str.Length);

                }                return output.ToArray();

            }

        }

    }

關(guān)于大小型項目如何最大限度提高WebAPi性能

我們來對比看一下未進行內(nèi)容壓縮前后結(jié)果響應(yīng)的時間以及內(nèi)容長度,給出如下測試類:

關(guān)于大小型項目如何最大限度提高WebAPi性能

  Task<IHttpActionResult><, > dict =  Dictionary<, ><Employee> li =  List<Employee> Employee { Id = , Name = , Email =  Employee { Id = , Name = , Email =  Employee { Id = , Name = , Email =  Employee { Id = , Name = ,Email =

關(guān)于大小型項目如何最大限度提高WebAPi性能

結(jié)果運行錯誤:

關(guān)于大小型項目如何最大限度提高WebAPi性能

這里應(yīng)該是序列化出現(xiàn)問題,在有些瀏覽器返回的XML數(shù)據(jù),我用的是搜狗瀏覽器,之前學(xué)習(xí)WebAPi時其返回的就是XML數(shù)據(jù),我們試著將其返回為Json數(shù)據(jù)看看。

關(guān)于大小型項目如何最大限度提高WebAPi性能

            var formatters = config.Formatters.Where(formatter =>
                 formatter.SupportedMediaTypes.Where(media =>
                 media.MediaType.ToString() == "application/xml" || media.MediaType.ToString() == "text/html").Count() > 0) //找到請求頭信息中的介質(zhì)類型                 .ToList();            foreach (var match in formatters)
            {
                config.Formatters.Remove(match);
            }

關(guān)于大小型項目如何最大限度提高WebAPi性能

我們未將其壓縮后響應(yīng)的長度如下所示:

關(guān)于大小型項目如何最大限度提高WebAPi性能

壓縮后結(jié)果明顯得到提升

關(guān)于大小型項目如何最大限度提高WebAPi性能

接下來我們自定義用.NET內(nèi)置的壓縮模式來實現(xiàn)看看

壓縮方式(3)【自定義實現(xiàn)】

既然響應(yīng)的內(nèi)容是通過HttpContent,我們則需要在重寫過濾器ActionFilterAttribute的基礎(chǔ)上來實現(xiàn)重寫HttpContent,最終根據(jù)獲取到瀏覽器支持的壓縮格式對數(shù)據(jù)進行壓縮并寫入到響應(yīng)流中即可。

關(guān)于大小型項目如何最大限度提高WebAPi性能

    public class CompressContent : HttpContent
    {        private readonly string _encodingType;        private readonly HttpContent _originalContent;        public CompressContent(HttpContent content, string encodingType = "gzip")
        {
            _originalContent = content;
            _encodingType = encodingType.ToLowerInvariant();
            Headers.ContentEncoding.Add(encodingType);
        }        protected override bool TryComputeLength(out long length)
        {
            length = -1;            return false;
        }        protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            Stream compressStream = null;            switch (_encodingType)
            {                case "gzip":
                    compressStream = new GZipStream(stream, CompressionMode.Compress, true);                    break;                case "deflate":
                    compressStream = new DeflateStream(stream, CompressionMode.Compress, true);                    break;                default:
                    compressStream = stream;                    break;
            }            return _originalContent.CopyToAsync(compressStream).ContinueWith(tsk =>
            {                if (compressStream != null)
                {
                    compressStream.Dispose();
                }
            });
        }
    }

關(guān)于大小型項目如何最大限度提高WebAPi性能

重寫過濾器特性

關(guān)于大小型項目如何最大限度提高WebAPi性能

    public class CompressContentAttribute : ActionFilterAttribute
    {        public override void OnActionExecuted(HttpActionExecutedContext context)
        {            var acceptedEncoding = context.Response.RequestMessage.Headers.AcceptEncoding.First().Value;            if (!acceptedEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase)            && !acceptedEncoding.Equals("deflate", StringComparison.InvariantCultureIgnoreCase))
            {                return;
            }
            context.Response.Content = new CompressContent(context.Response.Content, acceptedEncoding);
        }  

    }

關(guān)于大小型項目如何最大限度提高WebAPi性能

關(guān)于其響應(yīng)結(jié)果對比則不再敘述,和上述利用DotNetZip結(jié)果一致。 

當寫壓縮內(nèi)容時,我發(fā)現(xiàn)一個問題,產(chǎn)生了疑問, context.Response.Content.Headers 和 context.Response.Headers 為何響應(yīng)中有兩個頭Headers呢?,沒有去細究這個問題,大概說說個人想法。

context.Response.Content.Headers和context.Response.Headers有什么不同呢?

我們看看context.Response.Headers中的定義,其摘要如下:

關(guān)于大小型項目如何最大限度提高WebAPi性能

        // 摘要: 
        //     Gets a value that indicates if the HTTP response was successful.        //
        // 返回結(jié)果: 
        //     Returns System.Boolean.A value that indicates if the HTTP response was successful.        //     true if System.Net.Http.HttpResponseMessage.StatusCode was in the range 200-299;        //     otherwise false.

關(guān)于大小型項目如何最大限度提高WebAPi性能

而context.Response.Content.Headers中的定義,其摘要如下:

關(guān)于大小型項目如何最大限度提高WebAPi性能

        // 摘要: 
        //     Gets the HTTP content headers as defined in RFC 2616.        //
        // 返回結(jié)果: 
        //     Returns System.Net.Http.Headers.HttpContentHeaders.The content headers as        //     defined in RFC 2616.

關(guān)于大小型項目如何最大限度提高WebAPi性能

對于Content.Headers中的Headers的定義是基于RFC 2616即Http規(guī)范,想必這么做的目的是將Http規(guī)范隔離開來,我們能夠方便我們實現(xiàn)自定義代碼或者設(shè)置有關(guān)響應(yīng)頭信息最終直接寫入到Http的響應(yīng)流中。我們更多的是操作Content.Headers所以將其區(qū)別開來,或許是出于此目的吧,有知道的園友可以給出合理的解釋,這里只是我的個人揣測。

性能提升三:緩存(Cache:粒度比較大)

緩存大概是談的最多的話題,當然也有大量的緩存組件供我們使用,這里只是就比較大的粒度來談?wù)撨@個問題,對于一些小的項目還是有一點作用,大的則另當別論。

當我們進行請求可以查看響應(yīng)頭中會有這樣一個字段【Cache-Control】,如果我們未做任何處理當然則是其值為【no-cache】。在任何時期都不會進行緩存,都會重新進行請求數(shù)據(jù)。這個屬性里面對應(yīng)的值還有private/public、must-revalidate,當我們未指定max-age的值時且設(shè)置值為private、no-cache、must-revalidate此時的請求都會去服務(wù)器獲取數(shù)據(jù)。這里我們首先了解下關(guān)于Http協(xié)議的基本知識。

【1】若設(shè)置為private,則其不能共享緩存意思則是不會在本地緩存頁面即對于代理服務(wù)器而言不會復(fù)制一份,而如果對于用戶而言其緩存更加是私有的,只是對于個人而言,用戶之間的緩存相互獨立,互不共享。若為public則說明每個用戶都可以共享這一塊緩存。對于這二者打個比方對于博客園的推送的新聞是公開的,則可以設(shè)置為public共享緩存,充分利用緩存。

【2】max-age則是緩存的過期時間,在某一段時間內(nèi)不會去重新請求從服務(wù)器獲取數(shù)據(jù),直接在本地瀏覽器緩存中獲取。

【3】must-revalidate從字面意思來看則是必須重新驗證,也就是對于過期的數(shù)據(jù)進行重新獲取新的數(shù)據(jù),那么它到底什么時候用呢?歸根結(jié)底一句話:must-revalidate主要與max-age有關(guān),當設(shè)置了max-age時,同時也設(shè)置了must-revalidate,等緩存過期后,此時must-revalidate則會告訴服務(wù)器來獲取最新的數(shù)據(jù)。也就是說當設(shè)置max-age = 0,must-revalidate = true時可以說是與no-cache = true等同。

下面我們來進行緩存控制:

關(guān)于大小型項目如何最大限度提高WebAPi性能

    public class CacheFilterAttribute : ActionFilterAttribute
    {        public int CacheTimeDuration { get; set; }        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            actionExecutedContext.Response.Headers.CacheControl = new CacheControlHeaderValue
            {
                MaxAge = TimeSpan.FromSeconds(CacheTimeDuration),
                MustRevalidate = true,
                Public = true
            };
        }
    }

關(guān)于大小型項目如何最大限度提高WebAPi性能

添加緩存過濾特性:

關(guān)于大小型項目如何最大限度提高WebAPi性能

        [HttpGet]
        [CompressContent]        [CacheFilter(CacheTimeDuration = 100)]        public async Task<IHttpActionResult> GetZipData()
        {            var sw = new Stopwatch();
            sw.Start();
            Dictionary<object, object> dict = new Dictionary<object, object>();
            List<Employee> li = new List<Employee>();
            li.Add(new Employee { Id = "2", Name = "xpy0928", Email = "a@gmail.com" });
            li.Add(new Employee { Id = "3", Name = "tom", Email = "b@mail.com" });
            li.Add(new Employee { Id = "4", Name = "jim", Email = "c@mail.com" });
            li.Add(new Employee { Id = "5", Name = "tony", Email = "d@mail.com" });

            sw.Stop();

            dict.Add("Details", li);
            dict.Add("Time", sw.Elapsed.Milliseconds);            return Ok(dict);

        }

關(guān)于大小型項目如何最大限度提高WebAPi性能

結(jié)果如下:

關(guān)于大小型項目如何最大限度提高WebAPi性能

性能提升四:async/await(異步方法)

當在大型項目中會出現(xiàn)并發(fā)現(xiàn)象,常見的情況例如注冊,此時有若干個用戶同時在注冊時,則會導(dǎo)致當前請求阻塞并且頁面一直無響應(yīng)最終導(dǎo)致服務(wù)器崩潰,為了解決這樣的問題我們需要用到異步方法,讓多個請求過來時,線程池分配足夠的線程來處理多個請求,提高線程池的利用率 !如下:

         public async Task<IHttpActionResult> Register(Employee model)
         {            var result = await UserManager.CreateAsync(model);            return Ok(result);
         }


向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI