如今的应用部署逐渐向微服务化发展,导致一个完整的事务往往会跨越很多的应用或服务,出于分布式链路跟踪的需要,我们往往将从上游服务获得的跟踪请求报头无脑地向下游服务进行转发。本文介绍的这个名为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. 多测师讲解html _段落标签002_高级讲师肖sir

    <html> <head> <meta charset="UTF-8"> <title>段落标签</title> < ...

  2. MeteoInfoLab脚本示例:Streamline流线图

    绘制Stramline流线图的函数是streamline,需要两个变量(U/V分量或者风向/风速).脚本程序: f = addfile('D:/Temp/GrADS/model.ctl') u = f ...

  3. C语言的污垢,一个能污染内存的神秘操作!神级坑位再现~

    本文目的是为了更好的理解指针和内存管理 背景 我们定义一个变量A,修改另外一个一个变量B,导致A的值被修改,我们称它为内存污染. 案例 如下程序,正常的预期输出应该是:97 98 256 ,但正确的结 ...

  4. 【C语言/C++程序员编程】一小时做出来的数字雨(一颗开花的树)!

    相信大家看过许许多多的关于计算机黑客.骇客.人工智能.AI方面的电影,每当黑客入侵某个五角大楼,某个网站时,都会出现这样一副画面: 入侵 或者这样的: 数字雨 然后就轻而易举的成功入侵夺取管理员权限了 ...

  5. 并查集算法Union-Find的思想、实现以及应用

    并查集算法,也叫Union-Find算法,主要用于解决图论中的动态连通性问题. Union-Find算法类 这里直接给出并查集算法类UnionFind.class,如下: /** * Union-Fi ...

  6. Go语言基础知识01-用Go打个招呼

    每一种编程语言,从读一本好书开始 每一种编程语言,也从Helloworld开始 1. 环境准备 1.1 安装golang 在Ubuntu下,直接输入命令可以安装最新版本: $ sudo apt-get ...

  7. linux(centos8):安装jmeter5.3

    一,jmeter的用途: Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试, 它最初被设计用于Web应用测试,但后来扩展到其他测试领域.  Apach ...

  8. apache自带的ab测试失败请求原因

    只要出现 Failed requests 就会多出现一行要求失败的各原因的数据统计,分别有 Connect, Length, 与 Exception 三种,分别代表的意义为:Connect       ...

  9. 带你了解 MySQL Binlog 不为人知的秘密

    MySQL 的 Binlog 日志是一种二进制格式的日志,Binlog 记录所有的 DDL 和 DML 语句(除了数据查询语句SELECT.SHOW等),以 Event 的形式记录,同时记录语句执行时 ...

  10. Linux文件元数据和节点表结构

    文件元数据 一块硬盘的分区可以认为有两部分组成,保存元数据的成为节点表,用来保存属性等. 元数据中有个小指针,指向数据存放的实际空间. 元数据(Metadata) 又称中介数据.中继数据,为描述数据的 ...