欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

如何避免 HttpClient 丟失請求頭:通過 HttpRequestMessage 解決并優化

freeflydom
2024年11月7日 8:47 本文熱度 470

在使用 HttpClient 發起 HTTP 請求時,可能會遇到請求頭丟失的問題,尤其是像 Accept-Language 這樣的請求頭丟失。這個問題可能會導致請求的內容錯誤,甚至影響整個系統的穩定性和功能。本文將深入分析這一問題的根源,并介紹如何通過 HttpRequestMessage 來解決這一問題。

1. 問題的背景:HttpClient的設計與共享機制

HttpClient 是 .NET 中用于發送 HTTP 請求的核心類,它是一個設計為可復用的類,其目的是為了提高性能,減少在高并發情況下頻繁創建和銷毀 HTTP 連接的開銷。HttpClient 的復用能夠利用操作系統底層的連接池機制,避免了每次請求都要建立新連接的性能損失。

但是,HttpClient 復用的機制也可能導致一些問題,尤其是在多線程并發請求時。例如,如果我們在共享的 HttpClient 實例上頻繁地修改請求頭,可能會導致這些修改在不同的請求之間意外地“傳遞”或丟失。

2. 常見問題:丟失請求頭

假設我們有如下的代碼,其中我們希望在每次請求時設置 Accept-Language 頭:

using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace ConsoleApp9
{
    internal class Program
    {
        private static readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            NullValueHandling = NullValueHandling.Ignore
        };
        private static readonly HttpClient httpClient = new HttpClient(); // 復用HttpClient實例
        private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(100); // 限制并發請求數量為100
        static async Task Main(string[] args)
        {
            List<Task> tasks = new List<Task>();
            int taskNoCounter = 1; // 用于跟蹤 taskno
            // 只使用一個HttpClient對象(全局共享)
            for (int i = 0; i < 50; i++)
            {
                tasks.Add(Task.Run(async () =>
                {
                    // 等待信號量,控制最大并發數
                    await semaphore.WaitAsync();
                    try
                    {
                        var postData = new
                        {
                            taskno = taskNoCounter++,
                            content = "等待翻譯的內容"
                        };
                        var json = JsonConvert.SerializeObject(postData, serializerSettings);
                        var reqdata = new StringContent(json, Encoding.UTF8, "application/json");
                        // 設置請求頭語言
                        httpClient.DefaultRequestHeaders.Add("Accept-Language", "en-US");                      
                        // 發送請求
                        var result = await httpClient.PostAsync("http://localhost:5000/translate", reqdata);
                        // 讀取并反序列化 JSON 數據
                        var content = await result.Content.ReadAsStringAsync();
                        var jsonResponse = JsonConvert.DeserializeObject<Response>(content);
                        var response = jsonResponse.Data.Content;
                       
                        // 反序列化后,直接輸出解碼后的文本
                        Console.WriteLine($"結果為:{response}");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"請求失敗: {ex.Message}");
                    }
                    finally
                    {
                        // 釋放信號量
                        semaphore.Release();
                    }
                }));
            }
            await Task.WhenAll(tasks);
        }
    }
    // 定義與響應結構匹配的類
    public class Response
    {
        public int Code { get; set; }
        public ResponseData Data { get; set; }
        public string Msg { get; set; }
    }
    public class ResponseData
    {
        public string Content { get; set; }
        public string Lang { get; set; }
        public int Taskno { get; set; }
    }
}


接收代碼如下:

from flask import Flask, request, jsonify
from google.cloud import translate_v2 as translate
app = Flask(__name__)
# 初始化 Google Cloud Translate 客戶端
translator = translate.Client()
@app.route('/translate', methods=['POST'])
def translate_text():
    try:
        # 從請求中獲取 JSON 數據
        data = request.get_json()
        # 獲取請求的文本內容
        text = data.get('content')
        taskno = data.get('taskno', 1)
        # 獲取請求頭中的 Accept-Language 信息,默認為 'zh-CN'
        accept_language = request.headers.get('Accept-Language', 'zh-CN')
        # 調用 Google Translate API 進行翻譯
        result = translator.translate(text, target_language=accept_language)
        # 構造響應數據
        response_data = {
            "code": 200,
            "msg": "OK",
            "data": {
                "taskno": taskno,
                "content": result['translatedText'],
                "lang": accept_language
            }
        }
        # 返回 JSON 響應
        return jsonify(response_data), 200
    except Exception as e:
        return jsonify({"code": 500, "msg": str(e)}), 500
if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=5000)

 

Accept-Language 請求頭是通過 httpClient.DefaultRequestHeaders.Add("Accept-Language", language) 來設置的。這是一個常見的做法,目的是為每個請求指定特定的語言。然而,在實際應用中,尤其是當 HttpClient 被復用并發發送多個請求時,這種方法可能會引發請求頭丟失或錯誤的情況。

測試結果:每20個請求就會有一個接收拿不到語言,會使用默認的zh-CN,這條請求就不會翻譯。在上面的代碼中,

3. 為什么會丟失請求頭?

丟失請求頭的問題通常出現在以下兩種情況:

  • 并發請求之間共享 HttpClient 實例:當多個線程或任務共享同一個 HttpClient 實例時,它們可能會修改 DefaultRequestHeaders,導致請求頭在不同請求之間互相干擾。例如,如果一個請求修改了 Accept-Language,它會影響到后續所有的請求,而不是每個請求都獨立使用自己的請求頭。

  • 頭部緩存問題HttpClient 實例可能會緩存頭部信息。如果請求頭未正確設置,緩存可能會導致丟失之前設置的頭部。

在這種情況下,丟失請求頭或請求頭不一致的現象就會發生,從而影響請求的正確性和響應的準確性。

4. 解決方案:使用 HttpRequestMessage

為了解決這個問題,我們可以使用 HttpRequestMessage 來替代直接修改 HttpClient.DefaultRequestHeadersHttpRequestMessage 允許我們為每個請求獨立地設置請求頭,從而避免了多個請求之間共享頭部的風險。

以下是改進后的代碼:

using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace ConsoleApp9
{
    internal class Program
    {
        private static readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            NullValueHandling = NullValueHandling.Ignore
        };
        private static readonly HttpClient httpClient = new HttpClient(); // 復用HttpClient實例
        private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(100); // 限制并發請求數量為100
        static async Task Main(string[] args)
        {
            List<Task> tasks = new List<Task>();
            int taskNoCounter = 1; // 用于跟蹤 taskno
            // 只使用一個HttpClient對象(全局共享)
            for (int i = 0; i < 50; i++)
            {
                tasks.Add(Task.Run(async () =>
                {
                    // 等待信號量,控制最大并發數
                    await semaphore.WaitAsync();
                    try
                    {
                        var postData = new
                        {
                            taskno = taskNoCounter++,
                            content = "等待翻譯的內容"
                        };
                        var json = JsonConvert.SerializeObject(postData, serializerSettings);
                        var reqdata = new StringContent(json, Encoding.UTF8, "application/json");
                        // 使用HttpRequestMessage確保每個請求都可以單獨設置頭
                        var requestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost:5000/translate")
                        {
                            Content = reqdata
                        };
                        // 設置請求頭
                        requestMessage.Headers.Add("Accept-Language", "en-US");
                        // 發起POST請求
                        var result = await httpClient.SendAsync(requestMessage);
                        // 讀取并反序列化 JSON 數據
                        var content = await result.Content.ReadAsStringAsync();
                        var jsonResponse = JsonConvert.DeserializeObject<Response>(content);
                        var response = jsonResponse.Data.Content;
                     
                        // 反序列化后,直接輸出解碼后的文本
                        Console.WriteLine($"結果為:{response}");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"請求失敗: {ex.Message}");
                    }
                    finally
                    {
                        // 釋放信號量
                        semaphore.Release();
                    }
                }));
            }
            await Task.WhenAll(tasks);
        }
    }
    // 定義與響應結構匹配的類
    public class Response
    {
        public int Code { get; set; }
        public ResponseData Data { get; set; }
        public string Msg { get; set; }
    }
    public class ResponseData
    {
        public string Content { get; set; }
        public string Lang { get; set; }
        public int Taskno { get; set; }
    }
}
?

  

5. 解析解決方案:為何 HttpRequestMessage 更加可靠

  • 獨立請求頭HttpRequestMessage 是一個每個請求都可以獨立設置頭部的類,它允許我們為每個 HTTP 請求單獨配置請求頭,而不會被其他請求所干擾。通過這種方式,我們可以確保每個請求都使用準確的請求頭。

  • 高并發控制:當 HttpClient 實例被多個請求共享時,HttpRequestMessage 確保每個請求都能夠獨立處理頭部。即使在高并發環境下,每個請求的頭部設置都是獨立的,不會相互影響。

  • 請求靈活性HttpRequestMessage 不僅可以設置請求頭,還可以設置請求方法、請求體、請求的 URI 等,這使得它比直接使用 DefaultRequestHeaders 更加靈活和可控。

6. 小結:優化 HttpClient 請求頭管理

總結來說,當使用 HttpClient 時,若多個請求共用一個實例,直接修改 DefaultRequestHeaders 會導致請求頭丟失或不一致的問題。通過使用 HttpRequestMessage 來管理每個請求的頭部,可以避免這個問題,確保請求頭的獨立性和一致性。

  • 使用 HttpRequestMessage 來獨立設置請求頭,是確保請求頭正確性的最佳實踐。

  • 復用 HttpClient 實例是提升性能的好方法,但要注意并發請求時請求頭可能會丟失或錯誤,HttpRequestMessage 是解決這一問題的有效工具。

轉自https://www.cnblogs.com/morec/p/18529308


該文章在 2024/11/7 8:47:54 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved