zoukankan      html  css  js  c++  java
  • 在.NET Core中批量注入Grpc服务

      GRPC 是谷歌发布的一个开源、高性能、通用RPC服务,尽管大部分 RPC 框架都使用 TCP 协议,但其实 UDP 也可以,而 gRPC 干脆就用了 HTTP2。还有就是它具有跨平台、跨语言 等特性,这里就不再说明RPC是啥。

      在写项目当中,grp服务过多会非常头疼,那么我们分析一下如果解决这个问题。我们都知道在grpc注入到.NET Core 中使用的方法是 MapGrpcService 方法,是一个泛型方法。

        [NullableAttribute(0)]
        [NullableContextAttribute(1)]
        public static class GrpcEndpointRouteBuilderExtensions
        {
            public static GrpcServiceEndpointConventionBuilder MapGrpcService<TService>(this IEndpointRouteBuilder builder) where TService : class;
        }

    那我们就可以通过反射调用这个方法来进行服务批量注册,看方法的样子我们只需要将我们的服务对应 TService 以及将我们的 endpointBuilder 传入即可,我们看下源码是不是就像我所说的那样?

        public static class GrpcEndpointRouteBuilderExtensions
        {
            public static GrpcServiceEndpointConventionBuilder MapGrpcService<TService>(this IEndpointRouteBuilder builder) where TService : class
            {
                if (builder == null)
                {
                    throw new ArgumentNullException(nameof(builder));
                }
    
                ValidateServicesRegistered(builder.ServiceProvider);
    
                var serviceRouteBuilder = builder.ServiceProvider.GetRequiredService<ServiceRouteBuilder<TService>>();
                var endpointConventionBuilders = serviceRouteBuilder.Build(builder);
    
                return new GrpcServiceEndpointConventionBuilder(endpointConventionBuilders);
            }
    
            private static void ValidateServicesRegistered(IServiceProvider serviceProvider)
            {
                var marker = serviceProvider.GetService(typeof(GrpcMarkerService));
                if (marker == null)
                {
                    throw new InvalidOperationException("Unable to find the required services. Please add all the required services by calling " +
                        "'IServiceCollection.AddGrpc' inside the call to 'ConfigureServices(...)' in the application startup code.");
                }
            }
        }

      ok,看样子没什么问题就像我刚才所说的那样做。现在我们准备一个proto以及一个Service.这个就在网上找个吧..首先定义一个proto,它是grpc中的协议,也就是每个消费者遵循的。

    syntax = "proto3";
    option csharp_namespace = "Grpc.Server";
    package Greet;
    service Greeter {
      rpc SayHello (HelloRequest) returns (HelloReply);
    }
    message HelloRequest {
      string name = 1;
      enum Laguage{
          en_us =0 ;
          zh_cn =1 ;
      }
      Laguage LaguageEnum = 2;
    }
    message HelloReply {
      string message = 1;
      int32 num = 2;
      int32 adsa =3;
    }

    随后定义Service,当然非常简单, Greeter.GreeterBase 是重新生成项目根据proto来生成的。

    public class GreeterService : Greeter.GreeterBase
        {
            public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
            {
                var greeting = string.Empty;
                switch (request.LaguageEnum)
                {
                    case HelloRequest.Types.Laguage.EnUs:
                        greeting = "Hello";
                        break;
                    case HelloRequest.Types.Laguage.ZhCn:
                        greeting = "你好";
                        break;
                }
                return Task.FromResult(new HelloReply
                {
                    Message = $"{greeting} {request.Name}",
                    Num = new Random().Next()
                });
            }
        }

    此时我们需要自定义一个中间件,来批量注入grpc服务,其中我们获取了类型为 GrpcEndpointRouteBuilderExtensions ,并获取了它的方法,随后传入了他的TService,最后通过Invoke转入了我们的终点对象。

    public static class GrpcServiceExtension
        {
            public static void Add_Grpc_Services(IEndpointRouteBuilder builder)
            {
                Assembly assembly = Assembly.GetExecutingAssembly(); 
                foreach (var item in ServicesHelper.GetGrpcServices("Grpc.Server"))
                {
                    Type mytype = assembly.GetType(item.Value + "."+item.Key);
                    var method = typeof(GrpcEndpointRouteBuilderExtensions).GetMethod("MapGrpcService").MakeGenericMethod(mytype);
                    method.Invoke(null, new[] { builder }); 
                };
            }
            public static void useMyGrpcServices(this IApplicationBuilder app)
            {
                app.UseEndpoints(endpoints =>
                {
                    Add_Grpc_Services(endpoints);
                });
            }
        }

    在 ServicesHelper 中通过反射找到程序集当中的所有文件然后判断并返回。

     public static class ServicesHelper
        {
            public static Dictionary<string,string> GetGrpcServices(string assemblyName)
            {
                if (!string.IsNullOrEmpty(assemblyName))
                {
                    Assembly assembly = Assembly.Load(assemblyName);
                    List<Type> ts = assembly.GetTypes().ToList();
    
                    var result = new Dictionary<string, string>();
                    foreach (var item in ts.Where(u=>u.Namespace == "Grpc.Server.Services"))
                    {
                        result.Add(item.Name,item.Namespace);
                    }
                    return result;
                }
                return new Dictionary<string, string>();
            }
    }

    这样子我们就注入了所有命名空间为Grpc.Server.Services的服务,但这样好像无法达到某些控制,我们应当如何处理呢,我建议携程Attribute的形式,创建一个Flag.

    public class GrpcServiceAttribute : Attribute
    {
            public bool IsStart { get; set; }
    }

    将要在注入的服务商添加该标识,例如这样。

     [GrpcService]
        public class GreeterService : Greeter.GreeterBase
        {...}    

    随后根据反射出来的值找到 AttributeType 的名称进行判断即可。

     public static Dictionary<string,string> GetGrpcServices(string assemblyName)
            {
                if (!string.IsNullOrEmpty(assemblyName))
                {
                    Assembly assembly = Assembly.Load(assemblyName);
                    List<Type> ts = assembly.GetTypes().ToList();
    
                    var result = new Dictionary<string, string>();
                    foreach (var item in ts.Where(u=>u.CustomAttributes.Any(a=>a.AttributeType.Name == "GrpcServiceAttribute")))
                    {
                        result.Add(item.Name,item.Namespace);
                    }
                    return result;
                }
                return new Dictionary<string, string>();
            }

    随后我们的批量注入在Starup.cs中添加一行代码即可。

    app.useMyGrpcServices();

    启动项目试一试效果:

    示例代码:传送门

  • 相关阅读:
    高中时的口头禅
    我想跟你说
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
  • 原文地址:https://www.cnblogs.com/ZaraNet/p/12167517.html
Copyright © 2011-2022 走看看