首页 科技内容详情
源{yuan}码剖析Grpc阻挡器(C#版本「ben」)

源{yuan}码剖析Grpc阻挡器(C#版本「ben」)

分类:科技

网址:

反馈错误: 联络客服

点击直达

a55555彩票网www.a55555.net)是澳洲幸运5彩票官方网站,开放澳洲幸运5彩票会员开户、澳洲幸运5彩票代理开户、澳洲幸运5彩票线上投注、澳洲幸运5实时开奖等服务的平台。

前言

实在Grpc阻挡器是我以前研究过,然则我看网上相关C#版本的源码剖析相对少一点,以是笔者借这篇文章给人人分享下Grpc阻挡器的实现,空话不多说,直接开讲(Grpc的源码看着很利便,包自动都能还原乐成。.Net源码就硬生啃。。。弄了半天没还原乐成)。
ps:

  • 本篇文章主要是解说源码,并不举行举例Demo,以是读者只管先写一个小Demo,看看天生的代码,然后随同着看文章。
  • 若是没有用过Grpc的读者,可以先写个小Demo,可以看官网点击这里,主要是查看下通过Proto文件天生的代码的花样。
  • 这篇文章解说划分从客户端和服务端两部剖析说(实现有点纷歧样),篇幅缘故原由只解说一元挪用的示例,其他形式的挪用实在是类似的。

Client端

Interceptor和CallInvoker抽象类

public abstract class Interceptor
{
    //一元挪用同步阻挡器
    public virtual TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
        where TRequest : class
        where TResponse : class
    {
        return continuation(request, context);
    }
    //一元挪用异步阻挡器
    public virtual AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
        where TRequest : class
        where TResponse : class
    {
        return continuation(request, context);
    }
}
public abstract class CallInvoker
{
    //一元挪用同步阻挡器
    public abstract TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
        where TRequest : class
        where TResponse : class;
    //一元挪用异步阻挡器
    public abstract AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
        where TRequest : class
        where TResponse : class;
}

首先我们要明白这两个抽象类划分是干什么的,上述代码解说:

  • Interceptor我们知道,在实现自界说的阻挡器时,需要继续这个类,并对某些方式举行自界说的实现,而continuation就是挪用下一个阻挡器。
  • 实在CallInvoker实在就是客户端组织的工具,主要用于挪用远程服务,通过你自己实现的Demo可以看到,先确立Channel,然后通过Channe确立默认的CallInvoker,而在确立Client通过proto天生的文件里可以看到对应的重载组织函数。

添加阻挡器

public static class CallInvokerExtensions
{
    //增添一个阻挡器
    public static CallInvoker Intercept(this CallInvoker invoker, Interceptor interceptor)
    {
        return new InterceptingCallInvoker(invoker, interceptor);
    }
    //增添一组阻挡器
    public static CallInvoker Intercept(this CallInvoker invoker, params Interceptor[] interceptors)
    {
        //检查是否为Null
        GrpcPreconditions.CheckNotNull(invoker, nameof(invoker));
        GrpcPreconditions.CheckNotNull(interceptors, nameof(interceptors));
        //反转聚集,组织工具
        foreach (var interceptor in interceptors.Reverse())
        {
            invoker = Intercept(invoker, interceptor);
        }

        return invoker;
    }
    //篇幅缘故原由,这种方式这里不举行解说,人人可以自己翻下源码看下,主要作用就是增添用户自界说的分外报文值,类似Http请求中的Header
    public static CallInvoker Intercept(this CallInvoker invoker, Func<Metadata, Metadata> interceptor)
    {
        return new InterceptingCallInvoker(invoker, new MetadataInterceptor(interceptor));
    }
}

上述代码总结:

  • 添加一个阻挡器,则直接确立一个InterceptingCallInvoker工具返回,而它肯定继续CallInvoker。
  • 添加一组阻挡器,则将聚集反转,然后组织Invoker。
  • 而在客户端proto天生的代码中可以看到,方式的挪用是通过CallInvoker工具挪用的,读者可以看一下你自己天生的代码。

InterceptingCallInvoker类

internal class InterceptingCallInvoker : CallInvoker
{
    //下一个invoker工具
    readonly CallInvoker invoker;
    //当前的阻挡器
    readonly Interceptor interceptor;

    public InterceptingCallInvoker(CallInvoker invoker, Interceptor interceptor)
    {
        this.invoker = GrpcPreconditions.CheckNotNull(invoker, nameof(invoker));
        this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
    }
    //一元同步挪用
    public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
    {
        return interceptor.BlockingUnaryCall(
            request,
            new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
            //当前请求参数和上下文,挪用下一个BlockingUnaryCall
            (req, ctx) => invoker.BlockingUnaryCall(ctx.Method, ctx.Host, ctx.Options, req));
    }
    //一元异步挪用
    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
    {
        return interceptor.AsyncUnaryCall(
            request,
            new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
            //当前请求参数和上下文,挪用下一个BlockingUnaryCall
            (req, ctx) => invoker.AsyncUnaryCall(ctx.Method, ctx.Host, ctx.Options, req));
    }
}
//默认的CallInvoker,也就是不加任何阻挡器时刻的实现
public class DefaultCallInvoker : CallInvoker
{
    readonly Channel channel;

    public DefaultCallInvoker(Channel channel)
    {
        this.channel = GrpcPreconditions.CheckNotNull(channel);
    }
    //一元同步挪用
    public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
    {
        var call = CreateCall(method, host, options);
        return Calls.BlockingUnaryCall(call, request);
    }
    //一元异步挪用
    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
    {
        var call = CreateCall(method, host, options);
        return Calls.AsyncUnaryCall(call, request);
    }
}

上述代码总结:

欧博allbet网址

欢迎进入欧博allbet网址(www.aLLbetgame.us),欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

  • 构建InterceptingCallInvoker工具时,会保留当前阻挡器工具和下一个invoker工具,以利便挪用。
  • 当前阻挡器工具的在挪用方式时,第三个参数是委托,而这个委托就是Interceptor对应方式内里的continuation参数,客户端通过它来挪用下一个阻挡器。
  • 而DefaultInvoker内里实在是内部挪用远程服务,也就是默认实现,而这个是在通过Channel来组织Client的时刻组织出来的。

Client总结

  • 贯串上面的代码可以看出,不管是挪用单个添加阻挡器,或者链式添加单个阻挡器,又或者是添加一组阻挡器,最终一定返回CallInvoker工具,而CallInvoker工具是在proto天生的代码中可以看到,在挪用对应方式时是由CallInvoker工具挪用的。
  • 关于构建InterceptingCallInvoker ,实在可以和设计模式中的装饰着模式关联下,刚最先只构建了默认的DefaultInvoke(这个内里实在是构建毗邻,挪用server端),然后在这基础上添加其他差其余阻挡器功效,返回最终的CallInvoker工具。
  • 需要注重的是,当链式添加单个阻挡器时,好比Intercept(a).Intercept(b).Intercept(c),那么最终执行的顺序是c(continuation前)->b(continuation前)->a->b(continuation后)->c(continuation后)。若是一次添加一组阻挡器Intercept(a,b,c),那么最终执行的顺序是:a(continuation前)->b(continuation前)->c->b(continuation后)->a(continuation后)。

Server端

Interceptor抽象类和ServerServiceDefinition类

public abstract class Interceptor
{
    //服务端一元挪用阻挡器
    public virtual Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
        where TRequest : class
        where TResponse : class
    {
        return continuation(request, context);
    }
}
public class ServerServiceDefinition
{
    //方式列表,也就是服务端写的那些方式
    readonly IReadOnlyList<Action<ServiceBinderBase>> addMethodActions;
    
    internal ServerServiceDefinition(List<Action<ServiceBinderBase>> addMethodActions)
    {
        this.addMethodActions = addMethodActions.AsReadOnly();
    }
    //给方式绑定服务,也就是绑定阻挡器,一会的源码会提到
    internal void BindService(ServiceBinderBase serviceBinder)
    {
        //给每个方式都绑定一下阻挡器
        foreach (var addMethodAction in addMethodActions)
        {
            addMethodAction(serviceBinder);
        }
    }
    //确立Builder,可以在proto文件中天生的代码看到,会有挪用这个方式
    public static Builder CreateBuilder()
    {
        return new Builder();
    }
    
    public class Builder
    {
        //检测是否有同名方式,这是不被允许的
        readonly Dictionary<string, object> duplicateDetector = new Dictionary<string, object>();
        //服务端方式聚集
        readonly List<Action<ServiceBinderBase>> addMethodActions = new List<Action<ServiceBinderBase>>();

        public Builder()
        {
        }
        //可以看到在proto天生的代码中,有挪用AddMethod,将方式添加到聚集中
        public Builder AddMethod<TRequest, TResponse>(
            Method<TRequest, TResponse> method,
            UnaryServerMethod<TRequest, TResponse> handler)
                where TRequest : class
                where TResponse : class
        {
            duplicateDetector.Add(method.FullName, null);
            addMethodActions.Add((serviceBinder) => serviceBinder.AddMethod(method, handler));
            return this;
        }

        //这中央省略了除一元挪用的其他挪用,有兴趣的可以自己翻下源码
        
        //初始化build,将上面的方式列表添加到其中
        public ServerServiceDefinition Build()
        {
            return new ServerServiceDefinition(addMethodActions);
        }
    }
}

上述代码总结:

  • 对应每个service,都市维护一个方式的聚集,然后把用户界说的方式添加到聚集中(在proto天生的代码中可以看到)。
  • 在给每个方式添加阻挡器时(固然现在看不出来,下面会说),会给每个方式都加上,也就是说,它们之间是互不影响的。

添加阻挡器

public static class ServerServiceDefinitionExtensions
{
    //单个添加阻挡器
    public static ServerServiceDefinition Intercept(this ServerServiceDefinition serverServiceDefinition, Interceptor interceptor)
    {
        GrpcPreconditions.CheckNotNull(serverServiceDefinition, nameof(serverServiceDefinition));
        GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
        //组织新的ServiceBinder
        var binder = new InterceptingServiceBinder(interceptor);
        //将阻挡器绑定到每个方式上
        serverServiceDefinition.BindService(binder);
        //天生并返回新的service
        return binder.GetInterceptedServerServiceDefinition();
    }

    //添加一组阻挡器
    public static ServerServiceDefinition Intercept(this ServerServiceDefinition serverServiceDefinition, params Interceptor[] interceptors)
    {
        GrpcPreconditions.CheckNotNull(serverServiceDefinition, nameof(serverServiceDefinition));
        GrpcPreconditions.CheckNotNull(interceptors, nameof(interceptors));

        foreach (var interceptor in interceptors.Reverse())
        {    
            serverServiceDefinition = Intercept(serverServiceDefinition, interceptor);
        }

        return serverServiceDefinition;
    }

    //只保留了一元挪用的代码
    private class InterceptingServiceBinder : ServiceBinderBase
    {
        //确立一个空的Builder
        readonly ServerServiceDefinition.Builder builder = ServerServiceDefinition.CreateBuilder();
        //当前阻挡器
        readonly Interceptor interceptor;

        public InterceptingServiceBinder(Interceptor interceptor)
        {
            this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
        }
        //组织新的Builder
        internal ServerServiceDefinition GetInterceptedServerServiceDefinition()
        {
            return builder.Build();
        }
        //添加一元挪用的方式,而这个就是你自界说的阻挡器
        public override void AddMethod<TRequest, TResponse>(
            Method<TRequest, TResponse> method,
            UnaryServerMethod<TRequest, TResponse> handler)
        {
            builder.AddMethod(method, (request, context) => interceptor.UnaryServerHandler(request, context, handler));
        }
        //这里省略了一部门代码。。。
    }
}

实在到这里,咱们再串联上个小部门的代码,应该就能看出一些眉目,上述代码总结:

  • 这里链式添加或者单次添加一组,它和客户端阻挡器挪用顺序实在是一致的。
  • 我们连系现在上面Server端的所有代码,可以也许看出,当我们不添加任何阻挡器时,ServerServiceDefinition工具内里的方式聚集列表仅仅包罗用户界说的方式委托聚集。然而当我们添加阻挡器时,它代码的执行顺序则是,构建InterceptingServiceBinder->挪用BindService方式,原来的委托聚集最先执行,组织新的委托,而挪用的AddMethod则是InterceptingServiceBinder工具内里的AddMethod,handler则是我们写的阻挡器内里的continuation,用于转达。
  • 最终我们就会获得一个ServerServiceDefinition工具。固然,上述我们只看到了组织工具,而这个工具在那里挪用的呢?我们继续往下看。

DefaultServiceBinder类

internal static class ServerServiceDefinitionExtensions
{
    //在写服务端的时刻,我们需要绑定服务,而在绑定服务的时刻需要先挪用静态BindService方式(可以在proto天生的代码中看到这个方式),然后添加Services时,内部会挪用GetCallHandlers方式。
    internal static ReadOnlyDictionary<string, IServerCallHandler> GetCallHandlers(this ServerServiceDefinition serviceDefinition)
    {    
        //构建默认的ServiceBinder,内里实在是执行组织的最终handler
        var binder = new DefaultServiceBinder();
        //挪用BindService方式,将执行聚集委托
        serviceDefinition.BindService(binder);
        //返回聚集列表
        return binder.GetCallHandlers();
    }

    private class DefaultServiceBinder : ServiceBinderBase
    {
        readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>();

        internal ReadOnlyDictionary<string, IServerCallHandler> GetCallHandlers()
        {
            return new ReadOnlyDictionary<string, IServerCallHandler>(this.callHandlers);
        }

        public override void AddMethod<TRequest, TResponse>(
            Method<TRequest, TResponse> method,
            UnaryServerMethod<TRequest, TResponse> handler)
        {
            //每个方式名称对应的一个handler
            callHandlers.Add(method.FullName, ServerCalls.UnaryCall(method, handler));
        }
    }
}

上述代码总结:

  • 在组织出ServerServiceDefinition工具时,用户再将工具绑定到grpc的Servers时,最先执行GetCallHandlers方式,把它又重新构建一遍。
  • grpc默认的会组织一个聚集,key是方式全名,value则是IServerCallHandler,现实上每次请求进来会检索方式名,然后执行IServerCallHandler内部的HandleCall方式(这个是在源码内里可以看到)。
  • ServerCalls.UnaryCall想领会的可以看下源码,实质上内部就是执行handler,而这个handler就是用户构建的最终ServerServiceDefinition。

Server总结

  • 通过上面我们可以看出,其大致思绪可Client端实现很相像,只不外最终返回的是ServerServiceDefinition工具,而这个工具从刚最先默认handler(用户重写的Server端方式),到添加阻挡器时在上面的封装,而这个阻挡器又通过InterceptingServiceBinder类将其添加进去,它们都继续了ServiceBinderBase,通过组织最终的Builder工具来返回最终的ServerServiceDefinition。
  • 最终的ServerServiceDefinition在我们写的服务端Demo中可以看到,它被添加到Servers中,而在这时刻挪用GetCallHandlers天生最终的以方式名为key,handler为value的聚集。
  • 当有请求进来时,我们只需要凭证方式名找到对应的handler,然后把参数转达进去,再执行handler就可以把阻挡器和自己界说的方式所有走一遍,这些有兴趣的可以参考下源码。

总结

关于Grpc的阻挡器,信托你看完之后会有一定的收获,这里我再分外说一些其他的关于阅读Grpc源码时的小tips:

  • 默认情形下,服务启动时,只有4个后台线程去消费请求(和盘算机的CPU数目有关),然则请求的执行默认是通过添加线程池义务来执行的,固然也可以设置不通过线程池执行,直接执行时要注重防止壅闭。
  • 默认情形下,Grpc支持统一时间同时处置8000个请求(也和盘算机的CPU数目有关),若是有更多的请求应该就被壅闭了。这个数目是可以开发职员去调治的。

以上就是笔者对Grpc阻挡器的明白,本篇文章也主要是希望给读者提供源码阅读思绪,可能会有误差,还请谈论指正。

皇冠管理端

www.x2w00.com)实时更新发布最新最快最有效的皇冠管理端网址,包括皇冠管理端手机网址,皇冠管理端备用网址,皇冠管理端最新网址,皇冠管理端足球网址,皇冠管理端网址大全。

  • usdt承兑商合作(www.usdt8.vip) @回复Ta

    2021-10-09 00:05:16 

    着实就在最近两个月之内,ofo公司CEO戴威的动作一再。先是在员工大会上,戴威向员工准许,ofo并不会倒闭。一周之后,戴威又公布站内信,宣布了组织架构调整和升级的新闻。这些调整转达的新闻只有一个,那就是ofo尚有一线生气。然而,这些调整并非做给外界看的,反而是做给公司内部员工看的。想象力不错

    • 皇冠网址(www.huangguan.us) @回复Ta

      2021-10-13 11:33:52 

      皇冠足球appwww.huangguan.us)是一个开放皇冠即时比分、代理最新登录线路、会员最新登录线路、皇冠代理APP下载、皇冠会员APP下载、皇冠线路APP下载、皇冠电脑版下载、皇冠手机版下载的皇冠新现金网平台。皇冠官网平台上登录线路最新、新2皇冠网址更新最快,皇冠体育APP开放皇冠会员注册、皇冠代理开户等业务。

      人有多少啊

发布评论