Пример #1
0
        AsyncClientStreamingCall <TRequest, TResponse>(Method <TRequest, TResponse> method, string host, CallOptions options)
        {
            // No channel affinity feature for client streaming call.
            ChannelRef channelRef   = GetChannelRef();
            var        callDetails  = new CallInvocationDetails <TRequest, TResponse>(channelRef.Channel, method, host, options);
            var        originalCall = Calls.AsyncClientStreamingCall(callDetails);

            // Decrease the active streams count once async response finishes.
            var gcpResponseAsync = DecrementCountAndPropagateResult(originalCall.ResponseAsync);

            // Create a wrapper of the original AsyncClientStreamingCall.
            return(new AsyncClientStreamingCall <TRequest, TResponse>(
                       originalCall.RequestStream,
                       gcpResponseAsync,
                       originalCall.ResponseHeadersAsync,
                       () => originalCall.GetStatus(),
                       () => originalCall.GetTrailers(),
                       () => originalCall.Dispose()));

            async Task <TResponse> DecrementCountAndPropagateResult(Task <TResponse> task)
            {
                try
                {
                    return(await task.ConfigureAwait(false));
                }
                finally
                {
                    channelRef.ActiveStreamCountDecr();
                }
            }
        }
Пример #2
0
        private ChannelRef PreProcess <TRequest>(AffinityConfig affinityConfig, TRequest request)
        {
            // Gets the affinity bound key if required in the request method.
            string boundKey = null;

            if (affinityConfig != null && affinityConfig.Command == AffinityConfig.Types.Command.Bound)
            {
                boundKey = GetAffinityKeysFromProto(affinityConfig.AffinityKey, (IMessage)request).SingleOrDefault();
            }

            ChannelRef channelRef = GetChannelRef(boundKey);

            channelRef.ActiveStreamCountIncr();
            return(channelRef);
        }
Пример #3
0
 private void Bind(ChannelRef channelRef, string affinityKey)
 {
     if (!string.IsNullOrEmpty(affinityKey))
     {
         lock (thisLock)
         {
             // TODO: What should we do if the dictionary already contains this key, but for a different channel ref?
             if (!channelRefByAffinityKey.Keys.Contains(affinityKey))
             {
                 channelRefByAffinityKey.Add(affinityKey, channelRef);
             }
             channelRefByAffinityKey[affinityKey].AffinityCountIncr();
         }
     }
 }
Пример #4
0
 // Note: response may be default(TResponse) in the case of a failure. We only expect to be called from
 // protobuf-based calls anyway, so it will always be a class type, and will never be null for success cases.
 // We can therefore check for nullity rather than having a separate "success" parameter.
 private void PostProcess <TResponse>(AffinityConfig affinityConfig, ChannelRef channelRef, string boundKey, TResponse response)
 {
     channelRef.ActiveStreamCountDecr();
     // Process BIND or UNBIND if the method has affinity feature enabled, but only for successful calls.
     if (affinityConfig != null && response != null)
     {
         if (affinityConfig.Command == AffinityConfig.Types.Command.Bind)
         {
             Bind(channelRef, GetAffinityKeyFromProto(affinityConfig.AffinityKey, (IMessage)response));
         }
         else if (affinityConfig.Command == AffinityConfig.Types.Command.Unbind)
         {
             Unbind(boundKey);
         }
     }
 }
Пример #5
0
        private ChannelRef GetChannelRef(string affinityKey = null)
        {
            // TODO(fengli): Supports load reporting.
            lock (thisLock)
            {
                if (!string.IsNullOrEmpty(affinityKey))
                {
                    // Finds the gRPC channel according to the affinity key.
                    if (channelRefByAffinityKey.TryGetValue(affinityKey, out ChannelRef channelRef))
                    {
                        return(channelRef);
                    }
                    // TODO(fengli): Affinity key not found, log an error.
                }

                // TODO(fengli): Creates new gRPC channels on demand, depends on the load reporting.
                IOrderedEnumerable <ChannelRef> orderedChannelRefs =
                    channelRefs.OrderBy(channelRef => channelRef.ActiveStreamCount);
                foreach (ChannelRef channelRef in orderedChannelRefs)
                {
                    if (channelRef.ActiveStreamCount < apiConfig.ChannelPool.MaxConcurrentStreamsLowWatermark)
                    {
                        // If there's a free channel, use it.
                        return(channelRef);
                    }
                    else
                    {
                        //  If all channels are busy, break.
                        break;
                    }
                }
                int count = channelRefs.Count;
                if (count < apiConfig.ChannelPool.MaxSize)
                {
                    // Creates a new gRPC channel.
                    GrpcEnvironment.Logger.Info("Grpc.Gcp creating new channel");
                    Channel channel = new Channel(target, credentials,
                                                  options.Concat(new[] { new ChannelOption(ClientChannelId, Interlocked.Increment(ref clientChannelIdCounter)) }));
                    ChannelRef channelRef = new ChannelRef(channel, count);
                    channelRefs.Add(channelRef);
                    return(channelRef);
                }
                // If all channels are overloaded and the channel pool is full already,
                // return the channel with least active streams.
                return(orderedChannelRefs.First());
            }
        }
Пример #6
0
        private Tuple <ChannelRef, string> PreProcess <TRequest>(AffinityConfig affinityConfig, TRequest request)
        {
            // Gets the affinity bound key if required in the request method.
            string boundKey = null;

            if (affinityConfig != null)
            {
                if (affinityConfig.Command == AffinityConfig.Types.Command.Bound ||
                    affinityConfig.Command == AffinityConfig.Types.Command.Unbind)
                {
                    boundKey = GetAffinityKeyFromProto(affinityConfig.AffinityKey, (IMessage)request);
                }
            }
            ChannelRef channelRef = GetChannelRef(boundKey);

            channelRef.ActiveStreamCountIncr();
            return(new Tuple <ChannelRef, string>(channelRef, boundKey));
        }
Пример #7
0
        BlockingUnaryCall <TRequest, TResponse>(Method <TRequest, TResponse> method, string host, CallOptions options, TRequest request)
        {
            affinityByMethod.TryGetValue(method.FullName, out AffinityConfig affinityConfig);

            ChannelRef channelRef = PreProcess(affinityConfig, request);

            var       callDetails = new CallInvocationDetails <TRequest, TResponse>(channelRef.Channel, method, host, options);
            TResponse response    = default(TResponse);

            try
            {
                response = Calls.BlockingUnaryCall <TRequest, TResponse>(callDetails, request);
                return(response);
            }
            finally
            {
                PostProcess(affinityConfig, channelRef, request, response);
            }
        }
Пример #8
0
        AsyncDuplexStreamingCall <TRequest, TResponse>(Method <TRequest, TResponse> method, string host, CallOptions options)
        {
            // No channel affinity feature for duplex streaming call.
            ChannelRef channelRef   = GetChannelRef();
            var        callDetails  = new CallInvocationDetails <TRequest, TResponse>(channelRef.Channel, method, host, options);
            var        originalCall = Calls.AsyncDuplexStreamingCall(callDetails);

            // Decrease the active streams count once the streaming response finishes its final batch.
            var gcpResponseStream = new GcpClientResponseStream <TRequest, TResponse>(
                originalCall.ResponseStream,
                (resp) => channelRef.ActiveStreamCountDecr());

            // Create a wrapper of the original AsyncDuplexStreamingCall.
            return(new AsyncDuplexStreamingCall <TRequest, TResponse>(
                       originalCall.RequestStream,
                       gcpResponseStream,
                       originalCall.ResponseHeadersAsync,
                       () => originalCall.GetStatus(),
                       () => originalCall.GetTrailers(),
                       () => originalCall.Dispose()));
        }
Пример #9
0
        AsyncServerStreamingCall <TRequest, TResponse>(Method <TRequest, TResponse> method, string host, CallOptions options, TRequest request)
        {
            affinityByMethod.TryGetValue(method.FullName, out AffinityConfig affinityConfig);

            ChannelRef channelRef = PreProcess(affinityConfig, request);

            var callDetails  = new CallInvocationDetails <TRequest, TResponse>(channelRef.Channel, method, host, options);
            var originalCall = Calls.AsyncServerStreamingCall(callDetails, request);

            // Executes affinity postprocess once the streaming response finishes its final batch.
            var gcpResponseStream = new GcpClientResponseStream <TRequest, TResponse>(
                originalCall.ResponseStream,
                (resp) => PostProcess(affinityConfig, channelRef, request, resp));

            // Create a wrapper of the original AsyncServerStreamingCall.
            return(new AsyncServerStreamingCall <TResponse>(
                       gcpResponseStream,
                       originalCall.ResponseHeadersAsync,
                       () => originalCall.GetStatus(),
                       () => originalCall.GetTrailers(),
                       () => originalCall.Dispose()));
        }
Пример #10
0
        AsyncUnaryCall <TRequest, TResponse>(Method <TRequest, TResponse> method, string host, CallOptions options, TRequest request)
        {
            affinityByMethod.TryGetValue(method.FullName, out AffinityConfig affinityConfig);

            Tuple <ChannelRef, string> tupleResult = PreProcess(affinityConfig, request);
            ChannelRef channelRef = tupleResult.Item1;
            string     boundKey   = tupleResult.Item2;

            var callDetails  = new CallInvocationDetails <TRequest, TResponse>(channelRef.Channel, method, host, options);
            var originalCall = Calls.AsyncUnaryCall(callDetails, request);

            // Executes affinity postprocess once the async response finishes.
            var gcpResponseAsync = PostProcessPropagateResult(originalCall.ResponseAsync);

            // Create a wrapper of the original AsyncUnaryCall.
            return(new AsyncUnaryCall <TResponse>(
                       gcpResponseAsync,
                       originalCall.ResponseHeadersAsync,
                       () => originalCall.GetStatus(),
                       () => originalCall.GetTrailers(),
                       () => originalCall.Dispose()));

            async Task <TResponse> PostProcessPropagateResult(Task <TResponse> task)
            {
                TResponse response = default(TResponse);

                try
                {
                    response = await task.ConfigureAwait(false);

                    return(response);
                }
                finally
                {
                    PostProcess(affinityConfig, channelRef, boundKey, response);
                }
            }
        }