<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      記錄core中GRPC長連接導致負載均衡不均衡問題一:查看源碼,看創建過程

      一 問題描述:

      由來:公司有個功能需要被大量請求,并且中間涉及到多個不同的語言組成(c++/java/c#等),就決定使用grpc來做rpc服務。我是做c#的當然使用grpc for c# 來處理。這里涉及到一個問題,這個底層服務耗費性能,并且只是在一定時間內被大量請求,所以運維啟用監視,當單個容器使用過多時候,便增加新pod,然后通過k8s自己的負載均衡進行協調。大體流程:

        注:1.pod1,pod2是grpcserver

               2.pod會根據容器檢測自動啟用新容器

               3.api層只負責轉發和記錄當前請求內容,不做io處理,所以只需要啟用一個站點,便可以支撐所有請求,(即使真的太多,那么重啟一個服務也是毫無難度的,類似于nginx)

      二:問題產生

       從理論來看這種屬于最簡單的流式調用,沒有任何問題。一個pod處理不過來,那么便多啟用幾個pod處理增加速度。一切都很好。但是由于k8s負載均衡只是做個中轉,然后因為grpc的http2.0長連接導致只要連接一個pod,那么在使用當前grpcclient的情況下,一直指向一個pod無法釋放,導致在巔峰期,出現一個pod累成狗,其他pod看熱鬧的情況發生。

       

       最終導致pod1一直重啟。然后連接到pod2,然后pod2掛掉,持續如此

      三:問題原因

          由于grpc使用的http2.0長連接(注意與http1.0的長連接,即連接復用one by one方式不一樣),是多個請求可同時在一個連接上并行執行

      通過tcpdump抓包可以看出來,44026與6001之間多次連接傳輸數據,并且即使6001沒有回傳數據,44026也會傳輸新請求給6001.這就是http2.0的連接并行

       

       四:解決問題的方式:

      1.最簡單的方式是在api層與k8s之間通過一個nginx來處理長連接,但是此處將grpc的長連接強制改為短連接了,此方法pass掉

      2.將k8s連接到pod中的方式改為通過k8s自己的負載均衡處理。但是使用的是阿x云服務器,阿x云不提供當前方案,自己的運維也不愿意在線上折騰,所以此方案pass

      3.最傻的解決方式,將api層做負載,每次啟動2個pod,一個api的pod一個grpc的pod,然后在api層做負載,讓api每次使用同一個長連接。大概如下

       

       如圖所示,就知道這種方案有多傻是多傻。但是由于線上緊急,所以使用了此種方式進行處理

      5.產生新的問題

         由于只有c#出現這個問題,所以有些人啊,一直在那里說c#垃圾,c#不如java,我那個氣啊。這不行,不爭口香爭口氣。剛好乘著過年好好捋一捋grpc的代碼,看到底啥情況,那么上github看源碼

      https://github.com/grpc/grpc-dotnet.git,上面就是官方提供的grpc的連接,一步步來看,首先看我們注入的解決方式

      1.先看創建過程AddGrpcClient<TClient>

      public static void AddGrpcClient<TClient>(this IServiceCollection services, Action<GrpcClientFactoryOptions> configureClient) where TClient : class
              {
                  var name = TypeNameHelper.GetTypeDisplayName(typeof(TClient), fullName: false);
                  services.Configure(name, configureClient);
                  services.TryAddSingleton<GrpcClientFactory, DefaultGrpcClientFactory>();
                  services.TryAddSingleton<GrpcCallInvokerFactory>();
                  services.TryAddSingleton<DefaultClientActivator<TClient>>();
                  services.TryAddSingleton(new GrpcClientMappingRegistry());
                  Action<IServiceProvider, HttpClient> configureTypedClient = (s, httpClient) =>
                  {
                      var os = s.GetRequiredService<IOptionsMonitor<GrpcClientFactoryOptions>>();
                      var clientOptions = os.Get(name);
                      httpClient.BaseAddress = clientOptions.Address;
                      httpClient.Timeout = Timeout.InfiniteTimeSpan;
                  };
                  services
                      .AddHttpClient(name, configureTypedClient)
                      .ConfigurePrimaryHttpMessageHandler(() =>
                      {
                          var handler = new HttpClientHandler();
                          return handler;
                      });
                  services.AddTransient<TClient>(s =>
                  {
                      var clientFactory = s.GetRequiredService<GrpcClientFactory>();
                      return clientFactory.CreateClient<TClient>(name);
                  });
              }
      View Code

      整理出最核心代碼,可以發現,生成GrpcClient過程中還是基于HttpClient.這些是注入過程,其中看到一個關鍵的注入方式

      Services.AddTransient<TClient>(s =>
                  {
                      var clientFactory = s.GetRequiredService<GrpcClientFactory>();
                      return clientFactory.CreateClient<TClient>(builder.Name);
                  });

      可以看見,當我注入Greet.GreetClient時候,在ioc獲取的時候 是基于Transient來獲取的

      2.在看看創建GrpcClient過程,通過上面的注入方式獲取GrpcClientFactory來獲取Client:

      services.TryAddSingleton<GrpcClientFactory, DefaultGrpcClientFactory>();

      再來看看DefaultGrpcClientFactory的CreateClient

              public override TClient CreateClient<TClient>(string name) where TClient : class
              {
                  var defaultClientActivator = _serviceProvider.GetService<DefaultClientActivator<TClient>>();
                  var clientFactoryOptions = _clientFactoryOptionsMonitor.Get(name);
                  var httpClient = _httpClientFactory.CreateClient(name);
                  var callInvoker = _callInvokerFactory.CreateCallInvoker(httpClient, name, clientFactoryOptions);
      
                  if (clientFactoryOptions.Creator != null)
                  {
                      var c = clientFactoryOptions.Creator(callInvoker);
                      if (c is TClient client)
                      {
                          return client;
                      }
                  }
                  else
                  {
                      return defaultClientActivator.CreateClient(callInvoker);
                  }
              }
      View Code

      有個DefaultClientActivator<TClient>用來生成TClient

      private readonly static Func<ObjectFactory> _createActivator = () => ActivatorUtilities.CreateFactory(typeof(TClient), new Type[] { typeof(CallInvoker), });

        var activator = LazyInitializer.EnsureInitialized(ref _activator,ref _initialized,ref _lock,_createActivator);

      這個方法查看了注釋,是用來通過Create a delegate that will instantiate a type with constructor arguments provided directly and/or from an System.IServiceProvider.

      也就是通過注入IServiceProvider創建一個基于CallInvoker對象生成的Client,但是這點也是我比較奇怪的地方。都已經提供了創建對象的arguments了,為什么還需要通過IServiceProvider來獲取注入的參數,暫時沒有去看這方面的源碼,我就不去猜想這種實現的差異,反正這里目的是創建一個Client,在看看Callinvoke的實現方式

                  var clientFactoryOptions = _clientFactoryOptionsMonitor.Get(name);
                  var httpClient = _httpClientFactory.CreateClient(name);
                  var callInvoker = _callInvokerFactory.CreateCallInvoker(httpClient, name, clientFactoryOptions);

      這里面的代碼都比較熟悉,通過IOption注入的GrpcClientFactoryOptions,注入的HttpClient,最后關鍵點的是CallInvoke

                  var channelOptions = new GrpcChannelOptions();
                  channelOptions.HttpClient = httpClient;
                  channelOptions.LoggerFactory = _loggerFactory;
      
                  if (clientFactoryOptions.ChannelOptionsActions.Count > 0)
                  {
                      foreach (var applyOptions in clientFactoryOptions.ChannelOptionsActions)
                      {
                          applyOptions(channelOptions);
                      }
                  }
      
                  var address = clientFactoryOptions.Address ?? httpClient.BaseAddress;
                  var channel = GrpcChannel.ForAddress(address, channelOptions);
      
                  var httpClientCallInvoker = channel.CreateCallInvoker();

      可以很清晰的看出來,是通過GrpcChannel.ForAddress(address, channelOptions);來解析所有的參數,只不過有個CallInvoke來當做解析點,在與官方提供的Grpc點比較

              var handler = new HttpClientHandler();
              handler.ClientCertificates.Add(cert);
              var httpClient = new HttpClient(handler);
      
              var channel = GrpcChannel.ForAddress("https://localhost:5001/", new GrpcChannelOptions
              {
                  HttpClient = httpClient
              });
      
              var grpc = new Greeter.GreeterClient(channel);
              var response = await grpc.SayHelloAsync(new HelloRequest { Name = "Bob" });

      也就是換個方式來實現new Client的步驟。這就是所有的Grpc生成的源碼

      posted @ 2021-03-04 09:20  Best_Hong  閱讀(1426)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲成人av综合一区| 人妻少妇偷人作爱av| 91亚洲国产三上悠亚在线播放| 国产第一页浮力影院入口| 精品国产av无码一区二区三区 | 天堂中文8资源在线8| 亚洲人成网站在线在线观看| 亚洲第一国产综合| 亚洲国产成人无码av在线播放| 亚洲高清最新AV网站| 蜜桃视频无码区在线观看 | 亚洲精品久久久久久无码色欲四季| 国产AV无码专区亚洲AV紧身裤 | 阿瓦提县| 久久一区二区三区黄色片| 日本中文字幕一区二区三| 国产精品免费中文字幕| 激情综合五月网| 国产一级特黄性生活大片| 亚洲精品天堂在线观看| 亚洲中文字幕在线二页| 石原莉奈日韩一区二区三区| 丰满少妇内射一区| 国产大学生粉嫩无套流白浆| 国产毛片精品av一区二区| 亚洲一区中文字幕第十页| 亚洲一区二区三区在线播放无码| 亚洲国产在一区二区三区| 午夜福利yw在线观看2020| 亚洲国产美女精品久久久| 国产精品任我爽爆在线播放6080| 国产99精品成人午夜在线| 大地资源网第二页免费观看| 亚洲国产精品成人av网| 性一交一乱一伦一| 永川市| 色呦呦九九七七国产精品| 亚洲色婷婷综合开心网| 丰满多毛的大隂户视频| 亚洲女同性同志熟女| 精品亚洲精品日韩精品|