前言

在上篇文章[ASP.NET Core中的响应压缩]中我们谈到了在ASP.NET Core服务端处理关于响应压缩的请求,服务端的主要工作就是根据Content-Encoding头信息判断采用哪种方式压缩并返回。之前在群里有人问道过,现在的网络带宽这么高了还有必要在服务端针对请求进行压缩吗?确实,如今分布式和负载均衡技术这么成熟,很多需要处理高并发大数据的场景都可以通过增加服务器节点来进行。但是,在资源受限的情况下,或者是还没必要为了某一个点去增加新的服务器节点的时候,我们还是要采用一些程序本身的常规处理手段来进行处理。笔者个人认为响应压缩的使用场景是这样的,在带宽压力比较紧张的情况,且CPU资源比较充足的情况下,使用响应压缩整体效果还是比较明显的。
    有压缩就有解压,而解压的工作就是在请求客户端处理的。比如浏览器,这是我们最常用的Http客户端,许多浏览器都是默认在我们发出请求的时候(比如我们浏览网页的时候)在Request Head中添加Content-Encoding,然后根据响应信息处理相关解压。这些都源于浏览器已经内置了关于请求压缩和解压的机制。类似的还有许多,比如常用的代理抓包工具Filder也是内置这种机制的。只不过需要手动去处理,但实现方式都是一样的。有时候我们在自己写程序的过程中也需要使用这种机制,在传统的.Net HttpWebRequest类库中,并没有这种机制,后来版本中加入了HttpClient,有自带的机制可以处理这种操作,.Net Core作为后起之秀直接将HttpClient扶正,并且在此基础上改良了HttpClientFactory,接下来我们就来探究一下在.Net Core中使用HttpClient处理响应压缩的机制。

使用方式

首先我们来看一下直接在HttpClient中如何处理响应压缩

//自定义HttpClientHandler实例
HttpClientHandler httpClientHandler = new HttpClientHandler
{AutomaticDecompression = DecompressionMethods.GZip
};
//使用传递自定义HttpClientHandler实例的构造函数
using (HttpClient client = new HttpClient(httpClientHandler))
{var response = await client.GetAsync($"http://MyDemo/Home/GetPerson?userId={userId}");
}

这个操作还是非常简单的,我们操作的并不是HttpClient的属性而是HttpClientHandler中的属性,我们在之前的文章[.NET Core HttpClient源码探究]中曾探讨过,HttpClient的本质其实就是HttpMessageHandler,而HttpClient真正使用到的是HttpMessageHandler最重要的一个子类HttpClientHandler,所有的请求操作都是通过HttpMessageHandler进行的。我们可以看到AutomaticDecompression接受的是DecompressionMethods枚举,既然是枚举就说明包含了不止一个值,接下来我们查看DecompressionMethods中的源码

[Flags]
public enum DecompressionMethods
{// 使用所有压缩解压缩算法。All = -1,// 不使用解压None = 0x0,// 使用gzip解压算法GZip = 0x1,// 使用deflate解压算法Deflate = 0x2,// 使用Brotli解压算法Brotli = 0x4
}

该枚举默认都是针对常用输出解压算法,接下来我们看一下在HttpClientFactory中如何处理响应压缩。在之前的文章[.NET Core HttpClientFactory+Consul实现服务发现]中我们曾探讨过HttpClientFactory的大致工作方式默认PrimaryHandler传递的就是HttpClientHandler实例,而且在我们注册HttpClientFactory的时候是可以通过ConfigurePrimaryHttpMessageHandler自定义PrimaryHandler的默认值,接下来我们具体代码实现

services.AddHttpClient("mydemo", c =>
{c.BaseAddress = new Uri("http://MyDemo/");
}).ConfigurePrimaryHttpMessageHandler(provider=> new HttpClientHandler
{AutomaticDecompression = DecompressionMethods.GZip
});

其实在注册HttpClientFactory的时候还可以使用自定义的HttpClient,具体的使用方式是这样的

services.AddHttpClient("mydemo", c =>
{c.BaseAddress = new Uri("http://MyDemo/");
}).ConfigureHttpClient(provider => new HttpClient(new HttpClientHandler
{AutomaticDecompression = DecompressionMethods.GZip
}));

HttpClient确实帮我们做了好多事情,只需要简单的配置一下就开启了针对响应压缩的处理。这更勾起了我们对HttpClient的探讨,接下来我们就通过源码的方式查看它是如何发起可响应压缩请求,并解压响应结果的。

源码探究

通过上面的使用方式我们得知,无论使用哪种形式,最终都是针对HttpClientHandler做配置操作,接下来我们查看HttpClientHandler类[点击查看源码????]中AutomaticDecompression属性的代码

public DecompressionMethods AutomaticDecompression
{get => _underlyingHandler.AutomaticDecompression;set => _underlyingHandler.AutomaticDecompression = value;
}

它本身的值操作来自_underlyingHandler这个对象,也就是说读取和设置都是在操作_underlyingHandler.AutomaticDecompression,我们查找到_underlyingHandler对象的声明位置

private readonly SocketsHttpHandler _underlyingHandler;

这里说明一下,HttpClient的实质工作类是HttpClientHandler,而HttpClientHandler真正发起请求是依靠的SocketsHttpHandler这个类,也就是说SocketsHttpHandler是最原始发起请求的类。HttpClientHandler本质还是通过SocketsHttpHandler发起的Http请求,接下来我们就查看SocketsHttpHandler类[点击查看源码????]是如何处理AutomaticDecompression这个属性的

public DecompressionMethods AutomaticDecompression
{get => _settings._automaticDecompression;set{CheckDisposedOrStarted();_settings._automaticDecompression = value;}
}

这里的_settings不再是具体的功能类,而是用于初始化或者保存SocketsHttpHandler的部分属性值的配置类

private readonly HttpConnectionSettings _settings = new HttpConnectionSettings();

这里我们不在分析SocketsHttpHandler出处理响应压缩之外的其他代码,所以具体就不再看这些了,直接查找_settings._automaticDecompression属性引用的地方,最终找到了这段代码

if (settings._automaticDecompression != DecompressionMethods.None)
{handler = new DecompressionHandler(settings._automaticDecompression, handler);
}

这里就比较清晰了,真正处理请求响应压缩相关的都是在DecompressionHandler中。正如我们之前所说的,HttpClient真正的工作方式就是一些实现自HttpMessageHandler的子类在工作,它把不同功能的实现模块都封装成了具体的Handler中。当你需要使用哪个模块的功能,直接使用对应的Handler操作类去发送处理请求即可。这种设计思路在ASP.NET Core中体现的也是淋漓尽致,ASP.NET Core采用的是构建不同终结点去处理和输出请求。通过这些我们可以得知DecompressionHandler才是今天的主题,接下来我们就来查看DecompressionHandler类的源码[点击查看源码????]就不粘贴全部源码了,我们先来看最核心的SendAsync方法,这个方法是发送请求的执行方法

internal override async ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken)
{//判断是否是GZIP压缩请求,如果是则添加请求头Accept-Encoding头为gzipif (GZipEnabled && !request.Headers.AcceptEncoding.Contains(s_gzipHeaderValue)){request.Headers.AcceptEncoding.Add(s_gzipHeaderValue);}//判断是否是Deflate压缩请求,如果是则添加请求头Accept-Encoding头为deflateif (DeflateEnabled && !request.Headers.AcceptEncoding.Contains(s_deflateHeaderValue)){request.Headers.AcceptEncoding.Add(s_deflateHeaderValue);}//判断是否是Brotli压缩请求,如果是则添加请求头Accept-Encoding头为brotliif (BrotliEnabled && !request.Headers.AcceptEncoding.Contains(s_brotliHeaderValue)){request.Headers.AcceptEncoding.Add(s_brotliHeaderValue);}//发送请求HttpResponseMessage response = await _innerHandler.SendAsync(request, async, cancellationToken).ConfigureAwait(false);Debug.Assert(response.Content != null);//获取返回的Content-Encoding输出头信息ICollection<string> contentEncodings = response.Content.Headers.ContentEncoding;if (contentEncodings.Count > 0){string? last = null;//获取最后一个值foreach (string encoding in contentEncodings){last = encoding;}//根据响应头判断服务端采用的是否为gzip压缩if (GZipEnabled && last == Gzip){//使用gzip解压算法解压返回内容,并从新赋值到response.Contentresponse.Content = new GZipDecompressedContent(response.Content);}//根据响应头判断服务端采用的是否为deflate压缩else if (DeflateEnabled && last == Deflate){//使用deflate解压算法解压返回内容,并从新赋值到response.Contentresponse.Content = new DeflateDecompressedContent(response.Content);}//根据响应头判断服务端采用的是否为brotli压缩else if (BrotliEnabled && last == Brotli){//使用brotli解压算法解压返回内容,并从新赋值到response.Contentresponse.Content = new BrotliDecompressedContent(response.Content);}}return response;
}

通过上面的逻辑我们可以看到GZipEnabled、DeflateEnabled、BrotliEnabled三个bool类型的变量,中三个变量决定了采用哪种请求压缩方式,主要实现方式是

internal bool GZipEnabled => (_decompressionMethods & DecompressionMethods.GZip) != 0;
internal bool DeflateEnabled => (_decompressionMethods & DecompressionMethods.Deflate) != 0;
internal bool BrotliEnabled => (_decompressionMethods & DecompressionMethods.Brotli) != 0;

主要就是根据我们配置的DecompressionMethods枚举值判断想获取哪种方式的压缩结果,解压的实现逻辑都封装在GZipDecompressedContent、DeflateDecompressedContent、BrotliDecompressedContent中,我们看一下他们的具体的代码

private sealed class GZipDecompressedContent : DecompressedContent
{public GZipDecompressedContent(HttpContent originalContent): base(originalContent){ }//使用GZipStream类对返回的流进行解压protected override Stream GetDecompressedStream(Stream originalStream) =>new GZipStream(originalStream, CompressionMode.Decompress);}private sealed class DeflateDecompressedContent : DecompressedContent{public DeflateDecompressedContent(HttpContent originalContent): base(originalContent){ }//使用DeflateStream类对返回的流进行解压protected override Stream GetDecompressedStream(Stream originalStream) =>new DeflateStream(originalStream, CompressionMode.Decompress);}private sealed class BrotliDecompressedContent : DecompressedContent{public BrotliDecompressedContent(HttpContent originalContent) :base(originalContent){ }//使用BrotliStream类对返回的流进行解压protected override Stream GetDecompressedStream(Stream originalStream) =>new BrotliStream(originalStream, CompressionMode.Decompress);}
}

其主要的工作方式就是使用对应压缩算法的解压方法得到原始信息。简单总结一下,HttpClient关于压缩相关的处理机制是,首先根据你配置的DecompressionMethods判断你想使用那种压缩算法。然后匹配到对应的压缩算法后添加Accept-Encoding请求头为你期望的压缩算法。最后根据响应结果获取Content-Encoding输出头信息,判断服务端采用的是哪种压缩算法,并采用对应的解压方法解压获取原始数据。

总结

通过本次探讨HttpClient关于响应压缩的处理我们可以了解到,HttpClient无论从设计上还是实现方式上都有非常高的灵活性和扩展性,这也是为什么到了.Net Core上官方只推荐使用HttpClient一种Http请求方式。由于使用比较简单,实现方式比较清晰,这里就不过多拗述。主要是是想告诉大家HttpClient默认可以直接处理响应压缩,而不是和之前我们使用HttpWebRequest的时候还需要手动编码的方式去实现。

????欢迎扫码关注我的公众号????

.Net Core HttpClient处理响应压缩相关推荐

  1. ASP.NET Core中的响应压缩

    介绍 响应压缩技术是目前Web开发领域中比较常用的技术,在带宽资源受限的情况下,使用压缩技术是提升带宽负载的首选方案.我们熟悉的Web服务器,比如IIS.Tomcat.Nginx.Apache等都可以 ...

  2. asp.net core 系列之Performance的 Response compression(响应压缩)

    本文,帮助了解响应压缩的一些知识及用法(大部分翻译于官网,英文水平有限,不准确之处,欢迎指正). 什么是响应压缩?响应压缩简单的说就是为了减少网络带宽,而把返回的响应压缩,使之体积缩小,从而加快响应的 ...

  3. [小技巧]ASP.NET Core中如何预压缩静态文件

    原文地址:Pre-compressed static files with ASP.NET Core 作者:Gunnar Peipman 译者:Lamond Lu 译文:https://www.cnb ...

  4. 在ASP.NET Core中使用brotli压缩

    Brotli是一种全新的数据格式,可以提供比Zopfli高20-26%的压缩比.据谷歌研究,Brotli压缩速度同zlib的Deflate实现大致相同,而在Canterbury语料库上的压缩密度比LZ ...

  5. .net core HttpClient 使用之消息管道解析(二)

    一.前言 前面分享了 .net core HttpClient 使用之掉坑解析(一),今天来分享自定义消息处理HttpMessageHandler和PrimaryHttpMessageHandler ...

  6. .net core httpclient An error occurred while sending the request

    .net core HttpClient 报错 An error occurred while sending the request ,The response ended prematurely. ...

  7. .NET Core HttpClient请求异常思考

    [导读]上一篇我们讨论了针对项目上异常信息的具体分析而给出对应解决方案,本篇仅是我个人对相关异常信息了解过后的进一步学习和思考,希望对后续遇到此异常信息的同学们给予思路扩展 下面我们结合如下两个异常信 ...

  8. .NET Core HttpClient请求异常分析

    [导读]最近项目上每天间断性捕获到HttpClient请求异常,感觉有点奇怪,于是乎观察了两三天,通过日志以及对接方沟通确认等等,查看对应版本源码,尝试添加部分配置发布后,观察十几小时暂无异常情况出现 ...

  9. .NET Core HttpClient源码探究

    前言 在之前的文章我们介绍过HttpClient相关的服务发现,确实HttpClient是目前.NET Core进行Http网络编程的的主要手段.在之前的介绍中也看到了,我们使用了一个很重要的抽象Ht ...

最新文章

  1. 百度地图设置div样式宽高为百分比不显示地图
  2. 为维护视图创建事物码
  3. 在ECS实例的centos系统中安装Hadoop
  4. 计算机主板4针风扇插座,电脑的散热风扇2针、3针、4针接口有什么不同吗?
  5. C# 连接SQLServer数据库及登录验证知识
  6. anjularjs ajax 调用,AngularJS AJAX调用的服务(AngularJS Ajax Call in Service
  7. 神舟笔记本bios_海尔雷神(蓝天)神舟战神游戏本风扇狂转掉电大写灯狂闪维修实例...
  8. 错误:在keystone中无法找到默认角色user_Kubernetes RBAC角色权限控制
  9. CentOS6.4之Linux软件包管理
  10. 记录最近业务中出现的两个问题
  11. Android【报错】Description Resource Path Location Type Call requires API level 9 (current min is 8):
  12. poj 1151 hdu 1542 Atlantis 线段树扫描线(详细讲解)
  13. 终于有人做了一款新时代的搜索引擎
  14. 阶段3 2.Spring_09.JdbcTemplate的基本使用_1 今日课程内容介绍
  15. LaTeX 绘制思维导图
  16. mount的挂载远程服务器文件夹
  17. 双11之战:被激化的酒类电商出击,看1919新打法
  18. NOI的1.9.8白细胞计数
  19. wtc6508bsi,键释放引发的问题分析及解决
  20. 双臂魔方机器人的学习

热门文章

  1. [k8s]metricbeat的kubernetes模块kube-metric模块
  2. pyinstaller---将py文件打包成exe
  3. sql查询结果集根据指定条件排序的方法
  4. Google:推荐几款好用的Chrome浏览器插件
  5. google +按钮_如何禁用或改善Google的Google+集成
  6. 军哥华为HCNP(科目H12-221)真题解析课程:1-30题
  7. Mysql身份认证漏洞及利用(CVE-2012-2122) 补充测试用例
  8. 如何部署同一个Spring boot web 应用到不同的环境
  9. NATS服务器部署及测试
  10. 与众不同 制作会唱歌的WinRAR - imsoft.cnblogs