httpcline转发_如何实现Http请求报头的自动转发[应用篇]
如今的應(yīng)用部署逐漸向微服務(wù)化發(fā)展,導(dǎo)致一個完整的事務(wù)往往會跨越很多的應(yīng)用或服務(wù),出于分布式鏈路跟蹤的需要,我們往往將從上游服務(wù)獲得的跟蹤請求報頭無腦地向下游服務(wù)進(jìn)行轉(zhuǎn)發(fā)。本文介紹的這個名為HeaderForwarder的組件可以幫助我們完成針對指定HTTP請求報頭的自動轉(zhuǎn)發(fā)。本篇文章分為上下兩篇,上篇通過三個例子介紹HeaderForwarder的應(yīng)用場景,下篇則介紹該組件的設(shè)計與實(shí)現(xiàn)。[源代碼從這里下載]目錄
一、自動轉(zhuǎn)發(fā)指定的請求報頭
二、添加任意需要轉(zhuǎn)發(fā)的請求報頭
三、在非ASP.NET Core應(yīng)用中使用
一、自動轉(zhuǎn)發(fā)指定的請求報頭
假設(shè)整個分布式調(diào)用鏈路由如下圖所示的三個應(yīng)用構(gòu)成。請求由控制臺應(yīng)用App1通過HttpClient向WebApp1(localhost:5000),該請求攜帶foo和bar兩個需要被轉(zhuǎn)發(fā)的跟蹤報頭。ASP.NET Core應(yīng)用WebApp1在通過HttpClient調(diào)用WebApp2時,我們的組件會自動實(shí)現(xiàn)這對這兩個請求報頭的轉(zhuǎn)發(fā)。
如下所示的是作為下游應(yīng)用的WebApp2的定義。如代碼片段所示,為了驗(yàn)證指定的跟蹤報頭是否在WebApp1中被我們的組件成功轉(zhuǎn)發(fā),我們將接收到的所有請求報頭拼接成一個字符串作為響應(yīng)內(nèi)容。
public classProgram
{public static voidMain()
{
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(web=> web.Configure(app=>app.Run(Process)))
.Build()
.Run();staticTask Process(HttpContext httpContext)
{var headerString = string.Join(";", httpContext.Request.Headers.Select(it => $"{it.Key}={it.Value}"));returnhttpContext.Response.WriteAsync(headerString);
}
}
}
WebApp1的所有代碼定義如下。HeaderForwarder組件通過調(diào)用IHostBuilder的擴(kuò)展方法UseHeaderForwarder進(jìn)行注冊,在調(diào)用該方法的時候我們指定了需要轉(zhuǎn)發(fā)的請求報頭名稱(foo和bar)。在接收到請求之后,WebApp1會利用HttpClient調(diào)用WebApp2,并將得到結(jié)果作為相應(yīng)的內(nèi)容。
public classProgram
{public static voidMain()
{
Host.CreateDefaultBuilder().UseHeaderForwarder(forwarder=>forwarder.AddHeaderNames("foo", "bar")).ConfigureWebHostDefaults(web=>web
.ConfigureServices(svcs=>svcs.AddHttpClient())
.Configure(app=>app.Run(Process)))
.Build()
.Run();static asyncTask Process(HttpContext httpContext)
{var httpClient = httpContext.RequestServices.GetRequiredService().CreateClient();var headerString = await httpClient.GetStringAsync("http://localhost:6000");awaithttpContext.Response.WriteAsync(headerString);
}
}
}
作為上游應(yīng)用的App具有如下所示的定義。它直接利用HttpClient向WebApp1發(fā)送了一個請求,該請求攜帶了foo和bar這兩個需要WebApp1轉(zhuǎn)發(fā)的報頭。如果WebApp1完成了針對這兩個請求報頭的轉(zhuǎn)發(fā),那么得到的響應(yīng)內(nèi)容將包含這兩個報頭的值,我們將這一驗(yàn)證邏輯體現(xiàn)在兩個調(diào)試斷言中。
classProgram
{static async Task Main(string[] args)
{var httpClient = newHttpClient();var request = newHttpRequestMessage
{
RequestUri= new Uri("http://localhost:5000"),
Method=HttpMethod.Get
};
request.Headers.Add("foo", "123");
request.Headers.Add("bar", "456");var response = awaithttpClient.SendAsync(request);var headers = (await response.Content.ReadAsStringAsync()).Split(";");Debug.Assert(headers.Contains("foo=123"));
Debug.Assert(headers.Contains("bar=456"));}
}
二、添加任意需要轉(zhuǎn)發(fā)的請求報頭
上面我們演示了HeaderForwarder組件自動提取指定的報頭并自動轉(zhuǎn)發(fā)的功能,實(shí)際上該組件還可以幫助我們將任意的報頭添加到由HttpClient發(fā)出的請求消息中。假設(shè)WebApp1除了自動轉(zhuǎn)發(fā)的foo和bar報頭之外,還需要額外添加一個baz報頭,我們可以對程序作如下的修改。
public classProgram
{public static voidMain()
{
Host.CreateDefaultBuilder()
.UseHeaderForwarder(forwarder=> forwarder.AddHeaderNames("foo", "bar"))
.ConfigureWebHostDefaults(web=>web
.ConfigureServices(svcs=>svcs.AddHttpClient())
.Configure(app=>app.Run(Process)))
.Build()
.Run();static asyncTask Process(HttpContext httpContext)
{using (newHttpInvocationContextScope())
{
HttpInvocationContext.Current.AddOutgoingHeader("baz", "789");var httpClient = httpContext.RequestServices.GetRequiredService().CreateClient();var headerString = await httpClient.GetStringAsync("http://localhost:6000");awaithttpContext.Response.WriteAsync(headerString);
}
}
}
}
如上面的代碼片段所示,我們將針對HttpClient的調(diào)用置于HttpInvocationContextScope對象構(gòu)建的上下文范圍中。在調(diào)用HttpClient發(fā)送請求之前,我們通過Current靜態(tài)屬性得到當(dāng)前的HttpInvocationContext上下文,并通過調(diào)用其AddOutgoingHeader方法設(shè)置待轉(zhuǎn)發(fā)的baz報頭。為了驗(yàn)證WebApp1針對baz報頭的轉(zhuǎn)發(fā),我們將App的程序進(jìn)行如下的改寫。
classProgram
{static async Task Main(string[] args)
{var httpClient = newHttpClient();var request = newHttpRequestMessage
{
RequestUri= new Uri("http://localhost:5000"),
Method=HttpMethod.Get
};
request.Headers.Add("foo", "123");
request.Headers.Add("bar", "456");var response = awaithttpClient.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調(diào)用都需要對相同的報頭進(jìn)行轉(zhuǎn)發(fā),上面介紹的這種基于HttpInvocationContextScope/HttpInvocationContext的編程模式會變得很方便。
using (newHttpInvocationContextScope())
{
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應(yīng)用中使用
在ASP.NET Core應(yīng)用中,HeaderForwarder是通過調(diào)用IHostBuilder的擴(kuò)展方法UseHeaderForwarder進(jìn)行注冊的,如果在控制臺應(yīng)用又該如何使用。其實(shí)很簡單,HeaderForwarder針對請求(通過HttpClient發(fā)送)報頭的添加是通過該注冊提供的一個HttpClientObserver對象提供的,它實(shí)現(xiàn)了IObserver接口,我們只需要對該對象進(jìn)行注冊就可以了。
classProgram
{static asyncTask Main()
{var httpClientObserver = newServiceCollection()
.AddHeaderForwarder()
.BuildServiceProvider()
.GetRequiredService();
DiagnosticListener.AllListeners.Subscribe(httpClientObserver);using (newHttpInvocationContextScope())
{
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"));
}
}
}
如上面的代碼片段所示,我們調(diào)用擴(kuò)展方法AddHeaderForwarder將HeaderForwarder相關(guān)的服務(wù)注冊到創(chuàng)建的ServiceCollection對象上,并利用構(gòu)建的IServiceProvider對象得到我們需要的HttpClientObserver對象,并將其添加到DiagnosticListener.AllListeners屬性的IObservable列表中。有了HttpClientObserver的加持,設(shè)置請求報頭的方式就可以通過上述的編程模式了。
總結(jié)
以上是生活随笔為你收集整理的httpcline转发_如何实现Http请求报头的自动转发[应用篇]的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: beanshell断言_jmeter学习
- 下一篇: 阶乘的计算java_java中四种阶乘的