[{"data":1,"prerenderedAt":1989},["ShallowReactive",2],{"post-\u002Fblog\u002Fload-balancing-long-lived-connections-in-kubernetes":3},{"id":4,"title":5,"body":6,"book":1978,"date":1979,"description":1980,"extension":1981,"meta":1982,"navigation":609,"path":1983,"seo":1984,"stem":1985,"tags":1986,"__hash__":1988},"blog\u002Fblog\u002Fload-balancing-long-lived-connections-in-kubernetes.md","Load Balancing Long Lived Connections in Kubernetes",{"type":7,"value":8,"toc":1964},"minimark",[9,13,18,21,24,32,35,38,43,56,60,63,66,69,81,84,88,92,95,98,120,240,243,246,249,253,256,263,266,269,272,363,366,377,380,384,387,393,396,402,405,408,411,415,500,503,507,510,514,1960],[10,11,12],"p",{},"TL;DR: Kubernetes doesn't load balance long lived connections, and some Pods might receive more requests than others. Consider client side load balancing or a proxy if you're using HTTP\u002F2, gRPC, long lived HttpClient or other long lived database connection.",[14,15,17],"h2",{"id":16},"service-clusterip-nodeport-in-k8s","Service (ClusterIP, NodePort) in k8s",[10,19,20],{},"Kubernetes Services don't exist.",[10,22,23],{},"There's no process for listening to the IP address and port of the Service. Even the IP address can't be found anywhere.",[10,25,26,27,31],{},"You can check that this is the case by accessing any node in your Kubernetes cluster and executing ",[28,29,30],"code",{},"netstat -ntlp",".",[10,33,34],{},"Kube proxy reads the list of IP addresses for all Services and writes rules in every node.",[10,36,37],{},"The rules are meant to say, \" If you see this Service IP address, rewrite the request and pick one of the Pods as the destination.\" The IP Address is only a placeholder.",[39,40,42],"h3",{"id":41},"by-default-kubernetes-uses-iptables-to-implement-services-iptables-traditionally-does-not-implement-load-balancing-or-round-robin-strategy-but-kubernetes-implements-an-interesting-rule-into-their-iptable-for-example-with-3-pods-under-this-service-ip-they-will-implement-the-rule","By default, Kubernetes uses iptables to implement Services. iptables traditionally does not implement load balancing or round robin strategy, but Kubernetes implements an interesting rule into their iptable. For example, with 3 pods under this service IP, they will implement the rule:",[44,45,46,50,53],"ol",{},[47,48,49],"li",{},"With a likelihood of 33%, select Pod 1 as the destination. Otherwise, proceed to the following rule.",[47,51,52],{},"With a probability of 50%, choose Pod 2 as the destination. Otherwise, proceed to the following rule.",[47,54,55],{},"Select Pod 3 as the destination (no probability).",[14,57,59],{"id":58},"long-lived-connections-dont-get-load-balanced","Long lived connections don't get load balanced",[10,61,62],{},"What happened if the clients keep sending requests to the same Service? They will be sent to the same pods",[10,64,65],{},"You can reproduce this with a small test service that sends repeated requests through a single long lived client.",[10,67,68],{},"Why is the traffic not distributed?",[70,71,72,75,78],"ul",{},[47,73,74],{},"A single TCP connection is open, and the iptables rule was invoked the first time.",[47,76,77],{},"One of the three Pods was selected as the destination.",[47,79,80],{},"Since all subsequent requests are channelled through the same TCP connection, iptables doesn't invoke anymore.",[10,82,83],{},"So now you achieve better throughput and latency but completely lost the ability to scale your backend services.",[14,85,87],{"id":86},"solutions","Solutions",[39,89,91],{"id":90},"_1-client-side-load-balancing","1. Client side load balancing",[10,93,94],{},"This is the most common solution for larger systems.",[10,96,97],{},"The client side code that executes the load balancing should follow the logic below:",[44,99,100,103,106,109,112],{},[47,101,102],{},"Retrieve a list of endpoints from the Service.",[47,104,105],{},"For each of them, open a connection and keep it open.",[47,107,108],{},"Pick one of the open connections When you need to make a request.",[47,110,111],{},"At regular intervals, refresh the list of endpoints and remove or add new connections.",[47,113,114,115],{},"Refresh the list of endpoints when you have host error",[44,116,117],{},[47,118,119],{},"In .NET, you can detect host error with this (you can't use http status code for this error)",[121,122,127],"pre",{"className":123,"code":124,"language":125,"meta":126,"style":126},"language-csharp shiki shiki-themes one-light one-dark-pro","catch (HttpRequestException ex) when \n    (ex.InnerException is SocketException socketEx && \n     (socketEx.ErrorCode == 11001 || socketEx.ErrorCode == 11004))\n{\n    Console.WriteLine(\"IP Resolution Failed\");\n}\n","csharp","",[28,128,129,153,179,205,211,234],{"__ignoreMap":126},[130,131,134,138,142,146,150],"span",{"class":132,"line":133},"line",1,[130,135,137],{"class":136},"sLKXg","catch",[130,139,141],{"class":140},"s5ixo"," (",[130,143,145],{"class":144},"sC09Y","HttpRequestException",[130,147,149],{"class":148},"sz0mV"," ex",[130,151,152],{"class":140},") when \n",[130,154,156,159,162,164,167,170,173,176],{"class":132,"line":155},2,[130,157,158],{"class":140},"    (",[130,160,161],{"class":144},"ex",[130,163,31],{"class":140},[130,165,166],{"class":144},"InnerException",[130,168,169],{"class":148}," is",[130,171,172],{"class":144}," SocketException",[130,174,175],{"class":148}," socketEx",[130,177,178],{"class":140}," && \n",[130,180,182,185,188,190,193,196,198,200,202],{"class":132,"line":181},3,[130,183,184],{"class":140},"     (",[130,186,187],{"class":144},"socketEx",[130,189,31],{"class":140},[130,191,192],{"class":144},"ErrorCode",[130,194,195],{"class":140}," == 11001 || ",[130,197,187],{"class":144},[130,199,31],{"class":140},[130,201,192],{"class":144},[130,203,204],{"class":140}," == 11004))\n",[130,206,208],{"class":132,"line":207},4,[130,209,210],{"class":140},"{\n",[130,212,214,218,220,224,227,231],{"class":132,"line":213},5,[130,215,217],{"class":216},"s7GmK","    Console",[130,219,31],{"class":140},[130,221,223],{"class":222},"sAdtL","WriteLine",[130,225,226],{"class":140},"(",[130,228,230],{"class":229},"sDhpE","\"IP Resolution Failed\"",[130,232,233],{"class":140},");\n",[130,235,237],{"class":132,"line":236},6,[130,238,239],{"class":140},"}\n",[10,241,242],{},"Or you can do something more complicated with a dedicated client side load balancer.",[10,244,245],{},"You can check sample code at the end.",[10,247,248],{},"Cons: Complicated",[39,250,252],{"id":251},"_2-service-mesh-to-the-rescue","2. Service Mesh to the rescue",[10,254,255],{},"A service mesh decouples the communication between services from the application layer to the infrastructure layer. The abstraction at the infrastructure level happens by proxying the traffic between services. Many companies use Service Mesh",[10,257,258],{},[259,260],"img",{"alt":261,"src":262},"image-20241206-073028.png","\u002Fblog\u002Fload-balancing-long-lived-connections-in-kubernetes\u002Fimage_20241206_073028.png",[10,264,265],{},"One of the key benefit of this is the proxy will do the load balancing for you.",[10,267,268],{},"There are 2 big players in the market: istio vs linkerd",[10,270,271],{},"You can check the CNCF landscape or service mesh project documentation for more options.",[121,273,277],{"className":274,"code":275,"language":276,"meta":126,"style":126},"language-yaml shiki shiki-themes one-light one-dark-pro","apiVersion: networking.istio.io\u002Fv1alpha3\nkind: DestinationRule\nmetadata:\n  name: my-service-destination\nspec:\n  host: my-service\n  trafficPolicy:\n    loadBalancer:\n      simple: ROUND_ROBIN\n","yaml",[28,278,279,291,301,309,319,326,336,344,352],{"__ignoreMap":126},[130,280,281,285,288],{"class":132,"line":133},[130,282,284],{"class":283},"sJa8x","apiVersion",[130,286,287],{"class":140},": ",[130,289,290],{"class":229},"networking.istio.io\u002Fv1alpha3\n",[130,292,293,296,298],{"class":132,"line":155},[130,294,295],{"class":283},"kind",[130,297,287],{"class":140},[130,299,300],{"class":229},"DestinationRule\n",[130,302,303,306],{"class":132,"line":181},[130,304,305],{"class":283},"metadata",[130,307,308],{"class":140},":\n",[130,310,311,314,316],{"class":132,"line":207},[130,312,313],{"class":283},"  name",[130,315,287],{"class":140},[130,317,318],{"class":229},"my-service-destination\n",[130,320,321,324],{"class":132,"line":213},[130,322,323],{"class":283},"spec",[130,325,308],{"class":140},[130,327,328,331,333],{"class":132,"line":236},[130,329,330],{"class":283},"  host",[130,332,287],{"class":140},[130,334,335],{"class":229},"my-service\n",[130,337,339,342],{"class":132,"line":338},7,[130,340,341],{"class":283},"  trafficPolicy",[130,343,308],{"class":140},[130,345,347,350],{"class":132,"line":346},8,[130,348,349],{"class":283},"    loadBalancer",[130,351,308],{"class":140},[130,353,355,358,360],{"class":132,"line":354},9,[130,356,357],{"class":283},"      simple",[130,359,287],{"class":140},[130,361,362],{"class":229},"ROUND_ROBIN\n",[10,364,365],{},"Cons:",[70,367,368,371,374],{},[47,369,370],{},"You have an extra container in every pod",[47,372,373],{},"Costs more to operate",[47,375,376],{},"Better to be maintained by a dedicated IT team",[10,378,379],{},"Service Mesh basically handles client side load balancing for you.",[39,381,383],{"id":382},"_3-scaled-down-number-of-pods","3. Scaled down number of pods",[10,385,386],{},"If you have more clients than servers, there should be limited issue. The servers will be more utilized",[10,388,389],{},[259,390],{"alt":391,"src":392},"image-20241206-073629.png","\u002Fblog\u002Fload-balancing-long-lived-connections-in-kubernetes\u002Fimage_20241206_073629.png",[10,394,395],{},"The opposite scenario is the troublesome one",[10,397,398],{},[259,399],{"alt":400,"src":401},"image-20241206-073740.png","\u002Fblog\u002Fload-balancing-long-lived-connections-in-kubernetes\u002Fimage_20241206_073740.png",[10,403,404],{},"This is why Horizontal Scaling will not help unless you can load balance long lived connection properly.",[10,406,407],{},"So obviously 1 of the solution is to scale down, potentially to even 1 pod, and do Vertical Scaling if needed",[10,409,410],{},"Cons: what are the cons of fewer pods, or 1 pod, with vertical scaling?",[39,412,414],{"id":413},"_4-shorter-long-lived-clients","4. Shorter long lived clients",[121,416,418],{"className":123,"code":417,"language":125,"meta":126,"style":126},"services.AddHttpClient\u003CTClient, TImplementation>()\n  .ConfigurePrimaryHttpMessageHandler(_ => new SocketsHttpHandler\n  {\n      PooledConnectionLifetime = TimeSpan.FromMinutes(60)\n  });\n",[28,419,420,445,464,469,495],{"__ignoreMap":126},[130,421,422,425,427,430,433,436,439,442],{"class":132,"line":133},[130,423,424],{"class":216},"services",[130,426,31],{"class":140},[130,428,429],{"class":222},"AddHttpClient",[130,431,432],{"class":140},"\u003C",[130,434,435],{"class":144},"TClient",[130,437,438],{"class":140},", ",[130,440,441],{"class":144},"TImplementation",[130,443,444],{"class":140},">()\n",[130,446,447,450,453,455,458,461],{"class":132,"line":155},[130,448,449],{"class":140},"  .",[130,451,452],{"class":222},"ConfigurePrimaryHttpMessageHandler",[130,454,226],{"class":140},[130,456,457],{"class":216},"_",[130,459,460],{"class":140}," => new ",[130,462,463],{"class":144},"SocketsHttpHandler\n",[130,465,466],{"class":132,"line":181},[130,467,468],{"class":140},"  {\n",[130,470,471,474,478,481,483,486,488,492],{"class":132,"line":207},[130,472,473],{"class":148},"      PooledConnectionLifetime",[130,475,477],{"class":476},"sknuh"," =",[130,479,480],{"class":216}," TimeSpan",[130,482,31],{"class":140},[130,484,485],{"class":222},"FromMinutes",[130,487,226],{"class":140},[130,489,491],{"class":490},"sAGMh","60",[130,493,494],{"class":140},")\n",[130,496,497],{"class":132,"line":213},[130,498,499],{"class":140},"  });\n",[10,501,502],{},"We can make the connection lifetime shorter. You cannot make it too short or else there is no point of having a long lived client",[39,504,506],{"id":505},"_5-changing-architecture","5. Changing architecture",[10,508,509],{},"This is not possible most of the time, but if you use Event driven architecture, load balancing task is handled by Kafka\u002FRabbitMQ or whatever library you use",[39,511,513],{"id":512},"code-for-number-1","Code for number 1",[121,515,517],{"className":123,"code":516,"language":125,"meta":126,"style":126},"public class RoundRobinK8sClientHandler : HttpClientHandler\n{\n    private ConcurrentDictionary\u003Cstring, HttpClient> _persistentClients;\n    private ReaderWriterLockSlim _clientsLock = new ReaderWriterLockSlim();\n    private int _currentIndex = 0;\n\n    private readonly string _namespace;\n    private readonly string _serviceName;\n    private readonly Kubernetes _k8sClient = new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig());\n\n    public RoundRobinK8sClientHandler(string namespace, string serviceName)\n    {\n        _namespace = namespace;\n        _serviceName = serviceName;\n        _persistentClients = new ConcurrentDictionary\u003Cstring, HttpClient>();\n        RefreshClient();\n    }\n\n    private async Task\u003CList\u003CPodInfo>> GetPodsByServiceWithIpAsync()\n    {\n        var service = await _k8sClient.ReadNamespacedServiceAsync(_serviceName, _namespace);\n        var podList = await _k8sClient.ListNamespacedPodAsync(\n            _namespace, \n            labelSelector: string.Join(\",\", \n                service.Spec.Selector.Select(kvp => $\"{kvp.Key}={kvp.Value}\"))\n        );\n\n        return podList.Items.Select(pod => pod.Status.PodIP).ToList();\n    }\n\n    private void RefreshClient()\n    {\n        _clientsLock.EnterWriteLock();\n        try\n        {\n            var endpoints = GetPodsByServiceWithIpAsync().GetAwaiter().GetResult();\n\n            var newClients = new ConcurrentDictionary\u003Cstring, HttpClient>();\n            foreach (var endpoint in endpoints)\n            {\n                if (!_persistentClients.TryGetValue(endpoint, out var existingClient))\n                {\n                    existingClient = new HttpClient(new HttpClientHandler())\n                    {\n                        BaseAddress = new Uri(endpoint)\n                    };\n                }\n                newClients[endpoint] = existingClient;\n            }\n\n            _persistentClients = newClients;\n        }\n        finally \n        {\n            _clientsLock.ExitWriteLock();\n        }\n    }\n\n    protected override async Task\u003CHttpResponseMessage> SendAsync(\n        HttpRequestMessage request, \n        CancellationToken cancellationToken)\n    {\n        _clientsLock.EnterReadLock();\n        try\n        {\n            if (_persistentClients.Count == 0)\n            {\n                RefreshClient();\n            }\n\n            var endpoints = _persistentClients.Keys.ToList();\n            if (endpoints.Count == 0)\n            {\n                throw new InvalidOperationException(\"No endpoints available\");\n            }\n\n            \u002F\u002F Round-robin endpoint selection\n            string selectedEndpoint = endpoints[\n                Interlocked.Increment(ref _currentIndex) % endpoints.Count];\n\n            var client = _persistentClients[selectedEndpoint];\n\n            \u002F\u002F Clone the request for the specific endpoint\n            var newRequest = new HttpRequestMessage(request.Method, request.RequestUri);\n            newRequest.Content = request.Content;\n            foreach (var header in request.Headers)\n            {\n                newRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);\n            }\n\n            return await client.SendAsync(newRequest, cancellationToken);\n        }\n        catch (HttpRequestException ex) when \n            (ex.InnerException is SocketException socketEx && \n              (socketEx.ErrorCode == 11001 || socketEx.ErrorCode == 11004))\n        {\n            RefreshClient();\n        }\n        finally \n        {\n            _clientsLock.ExitReadLock();\n        }\n    }\n}\n\n\nservices.AddHttpClient(\"MyClient\")\n    .ConfigurePrimaryHttpMessageHandler(sp => new RoundRobinK8sClientHandler(\"app-namespace\", \"sample-service\"));\n",[28,518,519,536,540,567,588,605,611,626,639,671,676,700,706,718,730,754,762,768,773,803,808,842,864,873,895,961,967,972,1016,1021,1026,1039,1044,1057,1063,1069,1096,1101,1125,1146,1152,1188,1194,1215,1221,1240,1246,1252,1272,1278,1283,1295,1301,1310,1315,1328,1333,1338,1343,1368,1379,1390,1395,1407,1412,1417,1439,1444,1452,1457,1462,1485,1505,1510,1528,1533,1538,1545,1561,1594,1599,1618,1623,1629,1665,1686,1709,1714,1748,1753,1758,1785,1790,1804,1824,1846,1851,1859,1864,1871,1876,1888,1893,1898,1903,1908,1913,1929],{"__ignoreMap":126},[130,520,521,524,527,530,533],{"class":132,"line":133},[130,522,523],{"class":136},"public",[130,525,526],{"class":136}," class",[130,528,529],{"class":144}," RoundRobinK8sClientHandler",[130,531,532],{"class":140}," : ",[130,534,535],{"class":144},"HttpClientHandler\n",[130,537,538],{"class":132,"line":155},[130,539,210],{"class":140},[130,541,542,545,548,550,553,555,558,561,564],{"class":132,"line":181},[130,543,544],{"class":136},"    private",[130,546,547],{"class":144}," ConcurrentDictionary",[130,549,432],{"class":140},[130,551,552],{"class":136},"string",[130,554,438],{"class":140},[130,556,557],{"class":144},"HttpClient",[130,559,560],{"class":140},"> ",[130,562,563],{"class":283},"_persistentClients",[130,565,566],{"class":140},";\n",[130,568,569,571,574,577,579,582,585],{"class":132,"line":207},[130,570,544],{"class":136},[130,572,573],{"class":144}," ReaderWriterLockSlim",[130,575,576],{"class":283}," _clientsLock",[130,578,477],{"class":476},[130,580,581],{"class":140}," new ",[130,583,584],{"class":144},"ReaderWriterLockSlim",[130,586,587],{"class":140},"();\n",[130,589,590,592,595,598,600,603],{"class":132,"line":213},[130,591,544],{"class":136},[130,593,594],{"class":136}," int",[130,596,597],{"class":283}," _currentIndex",[130,599,477],{"class":476},[130,601,602],{"class":490}," 0",[130,604,566],{"class":140},[130,606,607],{"class":132,"line":236},[130,608,610],{"emptyLinePlaceholder":609},true,"\n",[130,612,613,615,618,621,624],{"class":132,"line":338},[130,614,544],{"class":136},[130,616,617],{"class":136}," readonly",[130,619,620],{"class":136}," string",[130,622,623],{"class":283}," _namespace",[130,625,566],{"class":140},[130,627,628,630,632,634,637],{"class":132,"line":346},[130,629,544],{"class":136},[130,631,617],{"class":136},[130,633,620],{"class":136},[130,635,636],{"class":283}," _serviceName",[130,638,566],{"class":140},[130,640,641,643,645,648,651,653,655,658,660,663,665,668],{"class":132,"line":354},[130,642,544],{"class":136},[130,644,617],{"class":136},[130,646,647],{"class":144}," Kubernetes",[130,649,650],{"class":283}," _k8sClient",[130,652,477],{"class":476},[130,654,581],{"class":140},[130,656,657],{"class":144},"Kubernetes",[130,659,226],{"class":140},[130,661,662],{"class":216},"KubernetesClientConfiguration",[130,664,31],{"class":140},[130,666,667],{"class":222},"BuildDefaultConfig",[130,669,670],{"class":140},"());\n",[130,672,674],{"class":132,"line":673},10,[130,675,610],{"emptyLinePlaceholder":609},[130,677,679,682,684,686,688,691,693,695,698],{"class":132,"line":678},11,[130,680,681],{"class":136},"    public",[130,683,529],{"class":222},[130,685,226],{"class":140},[130,687,552],{"class":136},[130,689,690],{"class":216}," namespace",[130,692,438],{"class":140},[130,694,552],{"class":136},[130,696,697],{"class":216}," serviceName",[130,699,494],{"class":140},[130,701,703],{"class":132,"line":702},12,[130,704,705],{"class":140},"    {\n",[130,707,709,712,714,716],{"class":132,"line":708},13,[130,710,711],{"class":148},"        _namespace",[130,713,477],{"class":476},[130,715,690],{"class":148},[130,717,566],{"class":140},[130,719,721,724,726,728],{"class":132,"line":720},14,[130,722,723],{"class":148},"        _serviceName",[130,725,477],{"class":476},[130,727,697],{"class":148},[130,729,566],{"class":140},[130,731,733,736,738,740,743,745,747,749,751],{"class":132,"line":732},15,[130,734,735],{"class":148},"        _persistentClients",[130,737,477],{"class":476},[130,739,581],{"class":140},[130,741,742],{"class":144},"ConcurrentDictionary",[130,744,432],{"class":140},[130,746,552],{"class":136},[130,748,438],{"class":140},[130,750,557],{"class":144},[130,752,753],{"class":140},">();\n",[130,755,757,760],{"class":132,"line":756},16,[130,758,759],{"class":222},"        RefreshClient",[130,761,587],{"class":140},[130,763,765],{"class":132,"line":764},17,[130,766,767],{"class":140},"    }\n",[130,769,771],{"class":132,"line":770},18,[130,772,610],{"emptyLinePlaceholder":609},[130,774,776,778,781,784,786,789,791,794,797,800],{"class":132,"line":775},19,[130,777,544],{"class":136},[130,779,780],{"class":136}," async",[130,782,783],{"class":144}," Task",[130,785,432],{"class":140},[130,787,788],{"class":144},"List",[130,790,432],{"class":140},[130,792,793],{"class":144},"PodInfo",[130,795,796],{"class":140},">> ",[130,798,799],{"class":222},"GetPodsByServiceWithIpAsync",[130,801,802],{"class":140},"()\n",[130,804,806],{"class":132,"line":805},20,[130,807,705],{"class":140},[130,809,811,814,817,819,822,825,827,830,832,835,837,840],{"class":132,"line":810},21,[130,812,813],{"class":136},"        var",[130,815,816],{"class":148}," service",[130,818,477],{"class":476},[130,820,821],{"class":140}," await ",[130,823,824],{"class":216},"_k8sClient",[130,826,31],{"class":140},[130,828,829],{"class":222},"ReadNamespacedServiceAsync",[130,831,226],{"class":140},[130,833,834],{"class":148},"_serviceName",[130,836,438],{"class":140},[130,838,839],{"class":148},"_namespace",[130,841,233],{"class":140},[130,843,845,847,850,852,854,856,858,861],{"class":132,"line":844},22,[130,846,813],{"class":136},[130,848,849],{"class":148}," podList",[130,851,477],{"class":476},[130,853,821],{"class":140},[130,855,824],{"class":216},[130,857,31],{"class":140},[130,859,860],{"class":222},"ListNamespacedPodAsync",[130,862,863],{"class":140},"(\n",[130,865,867,870],{"class":132,"line":866},23,[130,868,869],{"class":148},"            _namespace",[130,871,872],{"class":140},", \n",[130,874,876,879,881,883,885,888,890,893],{"class":132,"line":875},24,[130,877,878],{"class":216},"            labelSelector",[130,880,287],{"class":140},[130,882,552],{"class":136},[130,884,31],{"class":140},[130,886,887],{"class":222},"Join",[130,889,226],{"class":140},[130,891,892],{"class":229},"\",\"",[130,894,872],{"class":140},[130,896,898,901,903,906,908,911,913,916,918,921,924,927,931,933,935,938,941,944,946,948,950,953,955,958],{"class":132,"line":897},25,[130,899,900],{"class":216},"                service",[130,902,31],{"class":140},[130,904,905],{"class":216},"Spec",[130,907,31],{"class":140},[130,909,910],{"class":216},"Selector",[130,912,31],{"class":140},[130,914,915],{"class":222},"Select",[130,917,226],{"class":140},[130,919,920],{"class":216},"kvp",[130,922,923],{"class":140}," => ",[130,925,926],{"class":229},"$\"",[130,928,930],{"class":929},"sMj0N","{",[130,932,920],{"class":216},[130,934,31],{"class":929},[130,936,937],{"class":216},"Key",[130,939,940],{"class":929},"}",[130,942,943],{"class":229},"=",[130,945,930],{"class":929},[130,947,920],{"class":216},[130,949,31],{"class":929},[130,951,952],{"class":216},"Value",[130,954,940],{"class":929},[130,956,957],{"class":229},"\"",[130,959,960],{"class":140},"))\n",[130,962,964],{"class":132,"line":963},26,[130,965,966],{"class":140},"        );\n",[130,968,970],{"class":132,"line":969},27,[130,971,610],{"emptyLinePlaceholder":609},[130,973,975,978,980,982,985,987,989,991,994,996,998,1000,1003,1005,1008,1011,1014],{"class":132,"line":974},28,[130,976,977],{"class":136},"        return",[130,979,849],{"class":216},[130,981,31],{"class":140},[130,983,984],{"class":216},"Items",[130,986,31],{"class":140},[130,988,915],{"class":222},[130,990,226],{"class":140},[130,992,993],{"class":216},"pod",[130,995,923],{"class":140},[130,997,993],{"class":216},[130,999,31],{"class":140},[130,1001,1002],{"class":216},"Status",[130,1004,31],{"class":140},[130,1006,1007],{"class":216},"PodIP",[130,1009,1010],{"class":140},").",[130,1012,1013],{"class":222},"ToList",[130,1015,587],{"class":140},[130,1017,1019],{"class":132,"line":1018},29,[130,1020,767],{"class":140},[130,1022,1024],{"class":132,"line":1023},30,[130,1025,610],{"emptyLinePlaceholder":609},[130,1027,1029,1031,1034,1037],{"class":132,"line":1028},31,[130,1030,544],{"class":136},[130,1032,1033],{"class":136}," void",[130,1035,1036],{"class":222}," RefreshClient",[130,1038,802],{"class":140},[130,1040,1042],{"class":132,"line":1041},32,[130,1043,705],{"class":140},[130,1045,1047,1050,1052,1055],{"class":132,"line":1046},33,[130,1048,1049],{"class":216},"        _clientsLock",[130,1051,31],{"class":140},[130,1053,1054],{"class":222},"EnterWriteLock",[130,1056,587],{"class":140},[130,1058,1060],{"class":132,"line":1059},34,[130,1061,1062],{"class":136},"        try\n",[130,1064,1066],{"class":132,"line":1065},35,[130,1067,1068],{"class":140},"        {\n",[130,1070,1072,1075,1078,1080,1083,1086,1089,1091,1094],{"class":132,"line":1071},36,[130,1073,1074],{"class":136},"            var",[130,1076,1077],{"class":148}," endpoints",[130,1079,477],{"class":476},[130,1081,1082],{"class":222}," GetPodsByServiceWithIpAsync",[130,1084,1085],{"class":140},"().",[130,1087,1088],{"class":222},"GetAwaiter",[130,1090,1085],{"class":140},[130,1092,1093],{"class":222},"GetResult",[130,1095,587],{"class":140},[130,1097,1099],{"class":132,"line":1098},37,[130,1100,610],{"emptyLinePlaceholder":609},[130,1102,1104,1106,1109,1111,1113,1115,1117,1119,1121,1123],{"class":132,"line":1103},38,[130,1105,1074],{"class":136},[130,1107,1108],{"class":148}," newClients",[130,1110,477],{"class":476},[130,1112,581],{"class":140},[130,1114,742],{"class":144},[130,1116,432],{"class":140},[130,1118,552],{"class":136},[130,1120,438],{"class":140},[130,1122,557],{"class":144},[130,1124,753],{"class":140},[130,1126,1128,1131,1133,1136,1139,1142,1144],{"class":132,"line":1127},39,[130,1129,1130],{"class":136},"            foreach",[130,1132,141],{"class":140},[130,1134,1135],{"class":136},"var",[130,1137,1138],{"class":148}," endpoint",[130,1140,1141],{"class":136}," in",[130,1143,1077],{"class":148},[130,1145,494],{"class":140},[130,1147,1149],{"class":132,"line":1148},40,[130,1150,1151],{"class":140},"            {\n",[130,1153,1155,1158,1160,1163,1165,1167,1170,1172,1175,1177,1180,1183,1186],{"class":132,"line":1154},41,[130,1156,1157],{"class":136},"                if",[130,1159,141],{"class":140},[130,1161,1162],{"class":476},"!",[130,1164,563],{"class":216},[130,1166,31],{"class":140},[130,1168,1169],{"class":222},"TryGetValue",[130,1171,226],{"class":140},[130,1173,1174],{"class":148},"endpoint",[130,1176,438],{"class":140},[130,1178,1179],{"class":136},"out",[130,1181,1182],{"class":136}," var",[130,1184,1185],{"class":148}," existingClient",[130,1187,960],{"class":140},[130,1189,1191],{"class":132,"line":1190},42,[130,1192,1193],{"class":140},"                {\n",[130,1195,1197,1200,1202,1204,1206,1209,1212],{"class":132,"line":1196},43,[130,1198,1199],{"class":148},"                    existingClient",[130,1201,477],{"class":476},[130,1203,581],{"class":140},[130,1205,557],{"class":144},[130,1207,1208],{"class":140},"(new ",[130,1210,1211],{"class":144},"HttpClientHandler",[130,1213,1214],{"class":140},"())\n",[130,1216,1218],{"class":132,"line":1217},44,[130,1219,1220],{"class":140},"                    {\n",[130,1222,1224,1227,1229,1231,1234,1236,1238],{"class":132,"line":1223},45,[130,1225,1226],{"class":148},"                        BaseAddress",[130,1228,477],{"class":476},[130,1230,581],{"class":140},[130,1232,1233],{"class":144},"Uri",[130,1235,226],{"class":140},[130,1237,1174],{"class":148},[130,1239,494],{"class":140},[130,1241,1243],{"class":132,"line":1242},46,[130,1244,1245],{"class":140},"                    };\n",[130,1247,1249],{"class":132,"line":1248},47,[130,1250,1251],{"class":140},"                }\n",[130,1253,1255,1258,1261,1263,1266,1268,1270],{"class":132,"line":1254},48,[130,1256,1257],{"class":216},"                newClients",[130,1259,1260],{"class":140},"[",[130,1262,1174],{"class":148},[130,1264,1265],{"class":140},"] ",[130,1267,943],{"class":476},[130,1269,1185],{"class":148},[130,1271,566],{"class":140},[130,1273,1275],{"class":132,"line":1274},49,[130,1276,1277],{"class":140},"            }\n",[130,1279,1281],{"class":132,"line":1280},50,[130,1282,610],{"emptyLinePlaceholder":609},[130,1284,1286,1289,1291,1293],{"class":132,"line":1285},51,[130,1287,1288],{"class":148},"            _persistentClients",[130,1290,477],{"class":476},[130,1292,1108],{"class":148},[130,1294,566],{"class":140},[130,1296,1298],{"class":132,"line":1297},52,[130,1299,1300],{"class":140},"        }\n",[130,1302,1304,1307],{"class":132,"line":1303},53,[130,1305,1306],{"class":136},"        finally",[130,1308,1309],{"class":140}," \n",[130,1311,1313],{"class":132,"line":1312},54,[130,1314,1068],{"class":140},[130,1316,1318,1321,1323,1326],{"class":132,"line":1317},55,[130,1319,1320],{"class":216},"            _clientsLock",[130,1322,31],{"class":140},[130,1324,1325],{"class":222},"ExitWriteLock",[130,1327,587],{"class":140},[130,1329,1331],{"class":132,"line":1330},56,[130,1332,1300],{"class":140},[130,1334,1336],{"class":132,"line":1335},57,[130,1337,767],{"class":140},[130,1339,1341],{"class":132,"line":1340},58,[130,1342,610],{"emptyLinePlaceholder":609},[130,1344,1346,1349,1352,1354,1356,1358,1361,1363,1366],{"class":132,"line":1345},59,[130,1347,1348],{"class":136},"    protected",[130,1350,1351],{"class":136}," override",[130,1353,780],{"class":136},[130,1355,783],{"class":144},[130,1357,432],{"class":140},[130,1359,1360],{"class":144},"HttpResponseMessage",[130,1362,560],{"class":140},[130,1364,1365],{"class":222},"SendAsync",[130,1367,863],{"class":140},[130,1369,1371,1374,1377],{"class":132,"line":1370},60,[130,1372,1373],{"class":144},"        HttpRequestMessage",[130,1375,1376],{"class":216}," request",[130,1378,872],{"class":140},[130,1380,1382,1385,1388],{"class":132,"line":1381},61,[130,1383,1384],{"class":144},"        CancellationToken",[130,1386,1387],{"class":216}," cancellationToken",[130,1389,494],{"class":140},[130,1391,1393],{"class":132,"line":1392},62,[130,1394,705],{"class":140},[130,1396,1398,1400,1402,1405],{"class":132,"line":1397},63,[130,1399,1049],{"class":216},[130,1401,31],{"class":140},[130,1403,1404],{"class":222},"EnterReadLock",[130,1406,587],{"class":140},[130,1408,1410],{"class":132,"line":1409},64,[130,1411,1062],{"class":136},[130,1413,1415],{"class":132,"line":1414},65,[130,1416,1068],{"class":140},[130,1418,1420,1423,1425,1427,1429,1432,1435,1437],{"class":132,"line":1419},66,[130,1421,1422],{"class":136},"            if",[130,1424,141],{"class":140},[130,1426,563],{"class":216},[130,1428,31],{"class":140},[130,1430,1431],{"class":216},"Count",[130,1433,1434],{"class":476}," ==",[130,1436,602],{"class":490},[130,1438,494],{"class":140},[130,1440,1442],{"class":132,"line":1441},67,[130,1443,1151],{"class":140},[130,1445,1447,1450],{"class":132,"line":1446},68,[130,1448,1449],{"class":222},"                RefreshClient",[130,1451,587],{"class":140},[130,1453,1455],{"class":132,"line":1454},69,[130,1456,1277],{"class":140},[130,1458,1460],{"class":132,"line":1459},70,[130,1461,610],{"emptyLinePlaceholder":609},[130,1463,1465,1467,1469,1471,1474,1476,1479,1481,1483],{"class":132,"line":1464},71,[130,1466,1074],{"class":136},[130,1468,1077],{"class":148},[130,1470,477],{"class":476},[130,1472,1473],{"class":216}," _persistentClients",[130,1475,31],{"class":140},[130,1477,1478],{"class":216},"Keys",[130,1480,31],{"class":140},[130,1482,1013],{"class":222},[130,1484,587],{"class":140},[130,1486,1488,1490,1492,1495,1497,1499,1501,1503],{"class":132,"line":1487},72,[130,1489,1422],{"class":136},[130,1491,141],{"class":140},[130,1493,1494],{"class":216},"endpoints",[130,1496,31],{"class":140},[130,1498,1431],{"class":216},[130,1500,1434],{"class":476},[130,1502,602],{"class":490},[130,1504,494],{"class":140},[130,1506,1508],{"class":132,"line":1507},73,[130,1509,1151],{"class":140},[130,1511,1513,1516,1518,1521,1523,1526],{"class":132,"line":1512},74,[130,1514,1515],{"class":136},"                throw",[130,1517,581],{"class":140},[130,1519,1520],{"class":144},"InvalidOperationException",[130,1522,226],{"class":140},[130,1524,1525],{"class":229},"\"No endpoints available\"",[130,1527,233],{"class":140},[130,1529,1531],{"class":132,"line":1530},75,[130,1532,1277],{"class":140},[130,1534,1536],{"class":132,"line":1535},76,[130,1537,610],{"emptyLinePlaceholder":609},[130,1539,1541],{"class":132,"line":1540},77,[130,1542,1544],{"class":1543},"sW2Sy","            \u002F\u002F Round-robin endpoint selection\n",[130,1546,1548,1551,1554,1556,1558],{"class":132,"line":1547},78,[130,1549,1550],{"class":136},"            string",[130,1552,1553],{"class":148}," selectedEndpoint",[130,1555,477],{"class":476},[130,1557,1077],{"class":216},[130,1559,1560],{"class":140},"[\n",[130,1562,1564,1567,1569,1572,1574,1577,1579,1582,1585,1587,1589,1591],{"class":132,"line":1563},79,[130,1565,1566],{"class":216},"                Interlocked",[130,1568,31],{"class":140},[130,1570,1571],{"class":222},"Increment",[130,1573,226],{"class":140},[130,1575,1576],{"class":136},"ref",[130,1578,597],{"class":148},[130,1580,1581],{"class":140},") ",[130,1583,1584],{"class":476},"%",[130,1586,1077],{"class":216},[130,1588,31],{"class":140},[130,1590,1431],{"class":216},[130,1592,1593],{"class":140},"];\n",[130,1595,1597],{"class":132,"line":1596},80,[130,1598,610],{"emptyLinePlaceholder":609},[130,1600,1602,1604,1607,1609,1611,1613,1616],{"class":132,"line":1601},81,[130,1603,1074],{"class":136},[130,1605,1606],{"class":148}," client",[130,1608,477],{"class":476},[130,1610,1473],{"class":216},[130,1612,1260],{"class":140},[130,1614,1615],{"class":148},"selectedEndpoint",[130,1617,1593],{"class":140},[130,1619,1621],{"class":132,"line":1620},82,[130,1622,610],{"emptyLinePlaceholder":609},[130,1624,1626],{"class":132,"line":1625},83,[130,1627,1628],{"class":1543},"            \u002F\u002F Clone the request for the specific endpoint\n",[130,1630,1632,1634,1637,1639,1641,1644,1646,1649,1651,1654,1656,1658,1660,1663],{"class":132,"line":1631},84,[130,1633,1074],{"class":136},[130,1635,1636],{"class":148}," newRequest",[130,1638,477],{"class":476},[130,1640,581],{"class":140},[130,1642,1643],{"class":144},"HttpRequestMessage",[130,1645,226],{"class":140},[130,1647,1648],{"class":216},"request",[130,1650,31],{"class":140},[130,1652,1653],{"class":216},"Method",[130,1655,438],{"class":140},[130,1657,1648],{"class":216},[130,1659,31],{"class":140},[130,1661,1662],{"class":216},"RequestUri",[130,1664,233],{"class":140},[130,1666,1668,1671,1673,1676,1678,1680,1682,1684],{"class":132,"line":1667},85,[130,1669,1670],{"class":216},"            newRequest",[130,1672,31],{"class":140},[130,1674,1675],{"class":216},"Content",[130,1677,477],{"class":476},[130,1679,1376],{"class":216},[130,1681,31],{"class":140},[130,1683,1675],{"class":216},[130,1685,566],{"class":140},[130,1687,1689,1691,1693,1695,1698,1700,1702,1704,1707],{"class":132,"line":1688},86,[130,1690,1130],{"class":136},[130,1692,141],{"class":140},[130,1694,1135],{"class":136},[130,1696,1697],{"class":148}," header",[130,1699,1141],{"class":136},[130,1701,1376],{"class":216},[130,1703,31],{"class":140},[130,1705,1706],{"class":216},"Headers",[130,1708,494],{"class":140},[130,1710,1712],{"class":132,"line":1711},87,[130,1713,1151],{"class":140},[130,1715,1717,1720,1722,1724,1726,1729,1731,1734,1736,1738,1740,1742,1744,1746],{"class":132,"line":1716},88,[130,1718,1719],{"class":216},"                newRequest",[130,1721,31],{"class":140},[130,1723,1706],{"class":216},[130,1725,31],{"class":140},[130,1727,1728],{"class":222},"TryAddWithoutValidation",[130,1730,226],{"class":140},[130,1732,1733],{"class":216},"header",[130,1735,31],{"class":140},[130,1737,937],{"class":216},[130,1739,438],{"class":140},[130,1741,1733],{"class":216},[130,1743,31],{"class":140},[130,1745,952],{"class":216},[130,1747,233],{"class":140},[130,1749,1751],{"class":132,"line":1750},89,[130,1752,1277],{"class":140},[130,1754,1756],{"class":132,"line":1755},90,[130,1757,610],{"emptyLinePlaceholder":609},[130,1759,1761,1764,1766,1769,1771,1773,1775,1778,1780,1783],{"class":132,"line":1760},91,[130,1762,1763],{"class":136},"            return",[130,1765,821],{"class":140},[130,1767,1768],{"class":216},"client",[130,1770,31],{"class":140},[130,1772,1365],{"class":222},[130,1774,226],{"class":140},[130,1776,1777],{"class":148},"newRequest",[130,1779,438],{"class":140},[130,1781,1782],{"class":148},"cancellationToken",[130,1784,233],{"class":140},[130,1786,1788],{"class":132,"line":1787},92,[130,1789,1300],{"class":140},[130,1791,1793,1796,1798,1800,1802],{"class":132,"line":1792},93,[130,1794,1795],{"class":136},"        catch",[130,1797,141],{"class":140},[130,1799,145],{"class":144},[130,1801,149],{"class":148},[130,1803,152],{"class":140},[130,1805,1807,1810,1812,1814,1816,1818,1820,1822],{"class":132,"line":1806},94,[130,1808,1809],{"class":140},"            (",[130,1811,161],{"class":144},[130,1813,31],{"class":140},[130,1815,166],{"class":144},[130,1817,169],{"class":148},[130,1819,172],{"class":144},[130,1821,175],{"class":148},[130,1823,178],{"class":140},[130,1825,1827,1830,1832,1834,1836,1838,1840,1842,1844],{"class":132,"line":1826},95,[130,1828,1829],{"class":140},"              (",[130,1831,187],{"class":144},[130,1833,31],{"class":140},[130,1835,192],{"class":144},[130,1837,195],{"class":140},[130,1839,187],{"class":144},[130,1841,31],{"class":140},[130,1843,192],{"class":144},[130,1845,204],{"class":140},[130,1847,1849],{"class":132,"line":1848},96,[130,1850,1068],{"class":140},[130,1852,1854,1857],{"class":132,"line":1853},97,[130,1855,1856],{"class":222},"            RefreshClient",[130,1858,587],{"class":140},[130,1860,1862],{"class":132,"line":1861},98,[130,1863,1300],{"class":140},[130,1865,1867,1869],{"class":132,"line":1866},99,[130,1868,1306],{"class":136},[130,1870,1309],{"class":140},[130,1872,1874],{"class":132,"line":1873},100,[130,1875,1068],{"class":140},[130,1877,1879,1881,1883,1886],{"class":132,"line":1878},101,[130,1880,1320],{"class":216},[130,1882,31],{"class":140},[130,1884,1885],{"class":222},"ExitReadLock",[130,1887,587],{"class":140},[130,1889,1891],{"class":132,"line":1890},102,[130,1892,1300],{"class":140},[130,1894,1896],{"class":132,"line":1895},103,[130,1897,767],{"class":140},[130,1899,1901],{"class":132,"line":1900},104,[130,1902,239],{"class":140},[130,1904,1906],{"class":132,"line":1905},105,[130,1907,610],{"emptyLinePlaceholder":609},[130,1909,1911],{"class":132,"line":1910},106,[130,1912,610],{"emptyLinePlaceholder":609},[130,1914,1916,1918,1920,1922,1924,1927],{"class":132,"line":1915},107,[130,1917,424],{"class":216},[130,1919,31],{"class":140},[130,1921,429],{"class":222},[130,1923,226],{"class":140},[130,1925,1926],{"class":229},"\"MyClient\"",[130,1928,494],{"class":140},[130,1930,1932,1935,1937,1939,1942,1944,1947,1949,1952,1954,1957],{"class":132,"line":1931},108,[130,1933,1934],{"class":140},"    .",[130,1936,452],{"class":222},[130,1938,226],{"class":140},[130,1940,1941],{"class":216},"sp",[130,1943,460],{"class":140},[130,1945,1946],{"class":144},"RoundRobinK8sClientHandler",[130,1948,226],{"class":140},[130,1950,1951],{"class":229},"\"app-namespace\"",[130,1953,438],{"class":140},[130,1955,1956],{"class":229},"\"sample-service\"",[130,1958,1959],{"class":140},"));\n",[1961,1962,1963],"style",{},"html pre.shiki code .sLKXg, html code.shiki .sLKXg{--shiki-default:#A626A4;--shiki-dark:#C678DD}html pre.shiki code .s5ixo, html code.shiki .s5ixo{--shiki-default:#383A42;--shiki-dark:#ABB2BF}html pre.shiki code .sC09Y, html code.shiki .sC09Y{--shiki-default:#C18401;--shiki-dark:#E5C07B}html pre.shiki code .sz0mV, html code.shiki .sz0mV{--shiki-default:#383A42;--shiki-dark:#E06C75}html pre.shiki code .s7GmK, html code.shiki .s7GmK{--shiki-default:#383A42;--shiki-dark:#E5C07B}html pre.shiki code .sAdtL, html code.shiki .sAdtL{--shiki-default:#4078F2;--shiki-dark:#61AFEF}html pre.shiki code .sDhpE, html code.shiki .sDhpE{--shiki-default:#50A14F;--shiki-dark:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJa8x, html code.shiki .sJa8x{--shiki-default:#E45649;--shiki-dark:#E06C75}html pre.shiki code .sknuh, html code.shiki .sknuh{--shiki-default:#383A42;--shiki-dark:#56B6C2}html pre.shiki code .sAGMh, html code.shiki .sAGMh{--shiki-default:#986801;--shiki-dark:#D19A66}html pre.shiki code .sMj0N, html code.shiki .sMj0N{--shiki-default:#50A14F;--shiki-dark:#ABB2BF}html pre.shiki code .sW2Sy, html code.shiki .sW2Sy{--shiki-default:#A0A1A7;--shiki-default-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic}",{"title":126,"searchDepth":155,"depth":155,"links":1965},[1966,1969,1970],{"id":16,"depth":155,"text":17,"children":1967},[1968],{"id":41,"depth":181,"text":42},{"id":58,"depth":155,"text":59},{"id":86,"depth":155,"text":87,"children":1971},[1972,1973,1974,1975,1976,1977],{"id":90,"depth":181,"text":91},{"id":251,"depth":181,"text":252},{"id":382,"depth":181,"text":383},{"id":413,"depth":181,"text":414},{"id":505,"depth":181,"text":506},{"id":512,"depth":181,"text":513},null,"2024-12-09","How long lived connections interact with Kubernetes load balancing.","md",{},"\u002Fblog\u002Fload-balancing-long-lived-connections-in-kubernetes",{"title":5,"description":1980},"blog\u002Fload-balancing-long-lived-connections-in-kubernetes",[1987],"tech","NtCHtc8ODlX24UAKr_NdHFZb6vKZcXrCt_pY07tZOL4",1778998257695]