如今的应用部署逐渐向微服务化发展,导致一个完整的事务往往会跨越很多的应用或服务,出于分布式链路跟踪的需要,我们往往将从上游服务获得的跟踪请求报头无脑地向下游服务进行转发。本文介绍的这个名为HeaderForwarder的组件可以帮助我们完成针对指定HTTP请求报头的自动转发。本篇文章分为上下两篇,上篇通过三个例子介绍HeaderForwarder的应用场景,下篇则介绍该组件的设计与实现。

目录

一、自动转发指定的请求报头

二、添加任意需要转发的请求报头

三、在非ASP.NET Core应用中使用

一、自动转发指定的请求报头

假设整个分布式调用链路由如下图所示的三个应用构成。请求由控制台应用App1通过HttpClient向WebApp1(localhost:5000),该请求携带foo和bar两个需要被转发的跟踪报头。ASP.NET Core应用WebApp1在通过HttpClient调用WebApp2时,我们的组件会自动实现这对这两个请求报头的转发。

如下所示的是作为下游应用的WebApp2的定义。如代码片段所示,为了验证指定的跟踪报头是否在WebApp1中被我们的组件成功转发,我们将接收到的所有请求报头拼接成一个字符串作为响应内容。

public class Program
{
public static void Main()
{
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(web => web.Configure(app=>app.Run(Process)))
.Build()
.Run(); static Task Process(HttpContext httpContext)
{
var headerString = string.Join(";", httpContext.Request.Headers.Select(it => $"{it.Key}={it.Value}"));
return httpContext.Response.WriteAsync(headerString);
}
}
}

WebApp1的所有代码定义如下。HeaderForwarder组件通过调用IHostBuilder的扩展方法UseHeaderForwarder进行注册,在调用该方法的时候我们指定了需要转发的请求报头名称(foo和bar)。在接收到请求之后,WebApp1会利用HttpClient调用WebApp2,并将得到结果作为相应的内容。

public class Program
{
public static void Main()
{
Host.CreateDefaultBuilder()
.UseHeaderForwarder(forwarder=>forwarder.AddHeaderNames("foo", "bar"))
.ConfigureWebHostDefaults(web => web
.ConfigureServices(svcs=>svcs.AddHttpClient())
.Configure(app => app.Run(Process)))
.Build()
.Run(); static async Task Process(HttpContext httpContext)
{
var httpClient = httpContext.RequestServices.GetRequiredService<IHttpClientFactory>().CreateClient();
var headerString = await httpClient.GetStringAsync("http://localhost:6000");
await httpContext.Response.WriteAsync(headerString);
}
}
}

作为上游应用的App具有如下所示的定义。它直接利用HttpClient向WebApp1发送了一个请求,该请求携带了foo和bar这两个需要WebApp1转发的报头。如果WebApp1完成了针对这两个请求报头的转发,那么得到的响应内容将包含这两个报头的值,我们将这一验证逻辑体现在两个调试断言中。

class Program
{
static async Task Main(string[] args)
{
var httpClient = new HttpClient();
var request = new HttpRequestMessage
{
RequestUri = new Uri("http://localhost:5000"),
Method = HttpMethod.Get
};
request.Headers.Add("foo", "123");
request.Headers.Add("bar", "456");
var response = await httpClient.SendAsync(request);
var headers = (await response.Content.ReadAsStringAsync()).Split(";");
Debug.Assert(headers.Contains("foo=123"));
Debug.Assert(headers.Contains("bar=456"));

}
}

二、添加任意需要转发的请求报头

上面我们演示了HeaderForwarder组件自动提取指定的报头并自动转发的功能,实际上该组件还可以帮助我们将任意的报头添加到由HttpClient发出的请求消息中。假设WebApp1除了自动转发的foo和bar报头之外,还需要额外添加一个baz报头,我们可以对程序作如下的修改。

public class Program
{
public static void Main()
{
Host.CreateDefaultBuilder()
.UseHeaderForwarder(forwarder => forwarder.AddHeaderNames("foo", "bar"))
.ConfigureWebHostDefaults(web => web
.ConfigureServices(svcs => svcs.AddHttpClient())
.Configure(app => app.Run(Process)))
.Build()
.Run(); static async Task Process(HttpContext httpContext)
{
using (new HttpInvocationContextScope())
{
HttpInvocationContext.Current.AddOutgoingHeader("baz", "789");
var httpClient = httpContext.RequestServices.GetRequiredService<IHttpClientFactory>().CreateClient();
var headerString = await httpClient.GetStringAsync("http://localhost:6000");
await httpContext.Response.WriteAsync(headerString);
}
}
}
}

如上面的代码片段所示,我们将针对HttpClient的调用置于HttpInvocationContextScope对象构建的上下文范围中。在调用HttpClient发送请求之前,我们通过Current静态属性得到当前的HttpInvocationContext上下文,并通过调用其AddOutgoingHeader方法设置待转发的baz报头。为了验证WebApp1针对baz报头的转发,我们将App的程序进行如下的改写。

class Program
{
static async Task Main(string[] args)
{
var httpClient = new HttpClient();
var request = new HttpRequestMessage
{
RequestUri = new Uri("http://localhost:5000"),
Method = HttpMethod.Get
};
request.Headers.Add("foo", "123");
request.Headers.Add("bar", "456");
var response = await httpClient.SendAsync(request);
var headers = (await response.Content.ReadAsStringAsync()).Split(";");
Debug.Assert(headers.Contains("foo=123"));
Debug.Assert(headers.Contains("bar=456"));
Debug.Assert(headers.Contains("baz=789"));

}
}

如果涉及到多个HTTP调用都需要对相同的报头进行转发,上面介绍的这种基于HttpInvocationContextScope/HttpInvocationContext的编程模式会变得很方便。

using (new HttpInvocationContextScope())
{
HttpInvocationContext.Current
.AddOutgoingHeader("foo", "123")
.AddOutgoingHeader("bar", "456")
.AddOutgoingHeader("baz", "789");
var result1 = await httpClient.GetStringAsync("http://www.foo.com/");
var result2 = await httpClient.GetStringAsync("http://www.bar.com/");
var result3 = await httpClient.GetStringAsync("http://www.baz.com/");
}

三、在非ASP.NET Core应用中使用

在ASP.NET Core应用中,HeaderForwarder是通过调用IHostBuilder的扩展方法UseHeaderForwarder进行注册的,如果在控制台应用又该如何使用。其实很简单,HeaderForwarder针对请求(通过HttpClient发送)报头的添加是通过该注册提供的一个HttpClientObserver对象提供的,它实现了IObserver<DiagnosticListener>接口,我们只需要对该对象进行注册就可以了。

class Program
{
static async Task Main()
{
var httpClientObserver = new ServiceCollection()
.AddHeaderForwarder()
.BuildServiceProvider()
.GetRequiredService<HttpClientObserver>();
DiagnosticListener.AllListeners.Subscribe(httpClientObserver); using (new HttpInvocationContextScope())
{
HttpInvocationContext.Current
.AddOutgoingHeader("foo", "123")
.AddOutgoingHeader("bar", "456");
var headers = (await new HttpClient().GetStringAsync("http://locahost:5000")).Split(";");
Debug.Assert(headers.Contains("foo=123"));
Debug.Assert(headers.Contains("bar=456"));
Debug.Assert(headers.Contains("baz=789"));
}
}
}

如上面的代码片段所示,我们调用扩展方法AddHeaderForwarder将HeaderForwarder相关的服务注册到创建的ServiceCollection对象上,并利用构建的IServiceProvider对象得到我们需要的HttpClientObserver对象,并将其添加到DiagnosticListener.AllListeners属性的IObservable<DiagnosticListener>列表中。有了HttpClientObserver的加持,设置请求报头的方式就可以通过上述的编程模式了。

如何实现Http请求报头的自动转发[应用篇]
如何实现Http请求报头的自动转发[设计篇]

如何实现Http请求报头的自动转发[应用篇]的更多相关文章

  1. 被「李笑来老师」拉黑之「JavaScript微博自动转发的脚本」

    故事的背景如下图,李笑来 老师于10月19日在 知乎Live 开设 一小时建立终生受用的阅读操作系统 的讲座,他老人家看到大家伙报名踊跃,便在微博上发起了一个 猜数量赢取iPhone7 的活动. 因为 ...

  2. Response ServletContext 中文乱码 Request 编码 请求行 共享数据 转发重定向

    Day35  Response 1.1.1 ServletContext概念 u 项目的管理者(上下文对象),服务器启动时,会为每一个项目创建一个对应的ServletContext对象. 1.1.2  ...

  3. 技术揭秘“QQ空间”自动转发不良信息

    大家经常会看到QQ空间自动转发一些附带链接的不良信息,即便我们的QQ密码并没有被盗取.最近通过对一个QQ空间自动转发链接进行分析,发现该自动转发机制通过利用腾讯网站存在漏洞的页面,精心构造出利用代码获 ...

  4. http请求报头

    客户请求的处理:Http请求报头 创建高效servlet的关键之一,就是要了解如何操纵超文本传输协议(HypeText TransferProtocol, HTTP). HTTP请求报头不同于前一章的 ...

  5. HTTP报头:通用报头,请求报头,响应报头和实体报头

    缓存控制优先级从高到低分别是Pragma -> Cache-Control -> Expires 报头 每一个报头都是由 [名称 + ":" + 空格 + 值 + ] ...

  6. 四种为HttpClient添加默认请求报头的解决方案

    HttpClient在Web调用中具有广泛的应用,而为它添加默认请求头是我们经常遇到的需求,本文介绍4种为HttpClient添加默认请求头的方式. 第一种方式 直接在创建的HttpClient对象的 ...

  7. C#实现通过HttpWebRequest发送POST请求实现网站自动登陆

    C#实现通过HttpWebRequest发送POST请求实现网站自动登陆   怎样通过HttpWebRequest 发送 POST 请求到一个网页服务器?例如编写个程序实现自动用户登录,自动提交表单数 ...

  8. 用Tasker实现收到Android手机短信自动转发到邮箱

    发送短信到邮箱的原理与 <用Tasker实现收到Android手机短信自动转发到邮箱>有些类似.  发送短信到邮箱是利用Ifttt这个服务将短信转发到邮箱中.Ifttt服务的可扩展性很强, ...

  9. Exchange 2019中启用自动转发到外部域

    今天遇到一个用户反映自动转发的邮件规则没有生效.检查了一下,邮件规则配置没有问题.用户邮箱也能正常收到邮件,但是就是没有转发出去.仔细检查邮件规则,转发的收件人是外部邮箱.Exchange出于安全考虑 ...

随机推荐

  1. javascript in IE

    前提:一个页面导入若干个js文件 问题: 1.如果其中一个文件出问题可能会导致下面的文件导入失败,如果导入很多外部js库文件,导致错误不好排查,可以调整好js的加载顺序,以免影响页面功能 2.IE获取 ...

  2. MeteoInfoLab脚本示例:数据投影-FLEXPART

    FLEXPART是一个类似HYSPLIT的扩散模式,它输出的netcdf文件参照了WRF,可惜全局属性没有写全,比如只有一个投影名称(例如Lambert),没有相关的投影参数:中央经度,标准纬度等等. ...

  3. 协同开发功能——Github团队协作

    最近需要写一个HoloLens开发的简明介绍,其中要测试几个demo.用到github以团队协作,像下面是简单的事件记录. 一.创建项目 1. 2.项目设置 名称 描述description Init ...

  4. 扫描仪扫描文件处理-Photoshop批处理内存不足问题解决

    一次批处理上千张扫描图片,如果提示内存不足,这时候需要修改操作系统虚拟内存.最小值:512(512M),最大值建议51200(50G). 同时设置让Photoshop使用100%. 

  5. Learning Attention-based Embeddings for Relation Prediction in Knowledge Graphs

    这篇论文试图将GAT应用于KG任务中,但是问题是知识图谱中实体与实体之间关系并不相同,因此结构信息不再是简单的节点与节点之间的相邻关系.这里进行了一些小的trick进行改进,即在将实体特征拼接在一起的 ...

  6. pychartdir模块安装

    python模块pychartdir导入问题 在迁移别人写好的脚本时,发现pychartdir没有导入,脚本执行报错.以下是报错内容: [modps@LGJF-ZYC5-MMSC-WEB02 ~]$ ...

  7. 基于SSH框架的人力资源管理系统设计与实现

    - - ->关注博主公众号[C you again],获取更多IT资源(IT技术文章,毕业设计.课程设计系统源码,经典游戏源码,HTML网页模板,PPT.简历模板,!!还可以投稿赚钱!!,点击查 ...

  8. Spring框架中获取连接池常用的四种方式

    1:DBCP数据源 DBCP类包位于 /lib/jakarta-commons/commons-dbcp.jar,DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池,所 ...

  9. F2. Same Sum Blocks (Hard) 解析(思維、前綴和、貪心)

    Codeforce 1141 F2. Same Sum Blocks (Hard) 解析(思維.前綴和.貪心) 今天我們來看看CF1141F2(Hard) 題目連結 題目 給你一個數列\(a\),要你 ...

  10. Docker学习笔记之--.Net Core项目容器连接mssql容器(环境:centos7)

    前一节演示在docker中安装mssql,地址:Docker学习笔记之--安装mssql(Sql Server)并使用Navicat连接测试(环境:centos7) 本节演示 .Net Core项目容 ...