前言:

gRPC默认是ProtoFirst的,即先写 proto文件,再生成代码,需要人工维护proto,生成的代码也不友好,所以出现了gRPC CodeFirst,下面来说说我们是怎么实现gRPC CodeFirst

目录:

实现和WCF一样的CodeFirst

(1). 实现gRPC CodeFirst,  简化WCF一定要抽取接口的问题

(2). 通过代码生成proto和注释,给第三方语言使用

(3). 实现gRPC DashBoard,用于Http远程调用和管理

(4). 实现gRPC scope注入的三种方式(asp.net core3.0 grpc默认是scope)

(5). 实现服务注册与发现

(6). 实现分布式日志跟踪

(7). 日志监控等等

我们是怎么实现gRPC CodeFirst-生成proto

1.怎么根据代码生成Proto,上文我们调用了GrpcMethodHelper.AutoRegisterMethod()方法,这是通过反射自动注册GrpcMethod的方法

(1).这里面调用了一个BuildMethod方法,用于生成grpc的序列化和反序列化的委托

(2).同时可以收集grpc方法和参数的信息,用于生成proto

    /// <summary>
/// 生成Grpc方法(CodeFirst方式)
/// </summary>
/// <typeparam name="TRequest"></typeparam>
/// <typeparam name="TResponse"></typeparam>
/// <param name="srv"></param>
/// <param name="methodName"></param>
/// <param name="package"></param>
/// <param name="srvName"></param>
/// <param name="mType"></param>
/// <returns></returns>
public static Method<TRequest, TResponse> BuildMethod<TRequest, TResponse>(this IGrpcService srv,
string methodName, string package = null, string srvName = null, MethodType mType = MethodType.Unary)
{
var serviceName = srvName ??
GrpcExtensionsOptions.Instance.GlobalService ??
srv.GetType().Name;
var pkg = package ?? GrpcExtensionsOptions.Instance.GlobalPackage;
if (!string.IsNullOrWhiteSpace(pkg))
{
serviceName = $"{pkg}.{serviceName}";
}
#region 为生成proto收集信息
if (!(srv is IGrpcBaseService) || GrpcExtensionsOptions.Instance.GenBaseServiceProtoEnable)
{
ProtoInfo.Methods.Add(new ProtoMethodInfo
{
ServiceName = serviceName,
MethodName = methodName,
RequestName = typeof(TRequest).Name,
ResponseName = typeof(TResponse).Name,
MethodType = mType
});
ProtoGenerator.AddProto<TRequest>(typeof(TRequest).Name);
ProtoGenerator.AddProto<TResponse>(typeof(TResponse).Name);
}
#endregion
var request = Marshallers.Create<TRequest>((arg) => ProtobufExtensions.Serialize<TRequest>(arg), data => ProtobufExtensions.Deserialize<TRequest>(data));
var response = Marshallers.Create<TResponse>((arg) => ProtobufExtensions.Serialize<TResponse>(arg), data => ProtobufExtensions.Deserialize<TResponse>(data));
return new Method<TRequest, TResponse>(mType, serviceName, methodName, request, response);
}

2.不重复造轮子,通过protobuf-net的Serializer.GetProto()来生成请求参数和返回参数的proto

(1).这里简单过滤了重复的proto,但GetProto()会把依赖的类都生成proto,这样公用类就会生成多份,需要再次过滤重复即可

(2).生成message非关键代码这里我就不列出来了,都是字符串拼接的活

    /// <summary>
/// 添加proto
/// </summary>
public static void AddProto<TEntity>(string entityName)
{
if (!ProtoMethodInfo.Protos.ContainsKey(entityName))
{
var msg = Serializer.GetProto<TEntity>(ProtoBuf.Meta.ProtoSyntax.Proto3);
ProtoMethodInfo.Protos.TryAdd(entityName, msg.FilterHead().AddMessageComment<TEntity>());
}
}

3.服务方法的proto就更简单了,直接根据方法类型拼出来即可

    /// <summary>
/// 生成grpc的service的proto内容
/// </summary>
private static string GenGrpcServiceProto(string msgProtoName, string pkgName, string srvName, List<ProtoMethodInfo> methodInfo, bool spiltProto)
{
var sb = new StringBuilder();
sb.AppendLine("syntax = \"proto3\";");
if (!string.IsNullOrWhiteSpace(GrpcExtensionsOptions.Instance.ProtoNameSpace))
{
sb.AppendLine("option csharp_namespace = \"" + GrpcExtensionsOptions.Instance.ProtoNameSpace.Trim() + "\";");
}
if (!string.IsNullOrWhiteSpace(pkgName))
{
sb.AppendLine($"package {pkgName.Trim()};");
}
if (spiltProto)
{
sb.AppendLine(string.Format("import \"{0}\";", msgProtoName));
}
sb.AppendLine(Environment.NewLine);
sb.AppendLine("service " + srvName + " {"); var template = @" rpc {0}({1}) returns({2})";
methodInfo.ForEach(q => {
var requestName = q.RequestName;
var responseName = q.ResponseName;
switch (q.MethodType)
{
case Core.MethodType.Unary:
break;
case Core.MethodType.ClientStreaming:
requestName = "stream " + requestName;
break;
case Core.MethodType.ServerStreaming:
responseName = "stream " + responseName;
break;
case Core.MethodType.DuplexStreaming:
requestName = "stream " + requestName;
responseName = "stream " + responseName;
break;
}
ProtoCommentGenerator.AddServiceComment(q,sb);
sb.AppendLine(string.Format(template, q.MethodName, requestName, responseName) + ";" + Environment.NewLine);
}); sb.AppendLine("}");
return sb.ToString();
}

4.生成 proto没有注释,第三方对接时就尴尬了,虽然命名规范,但注释还是要有的,减少沟通成本

(1).我们通过在类和方法上加入注释,然后项目里设置生成xml注释文档

(2).生成proto时通过扫描xml注释文档来给proto加入注释即可

未完,待续,欢迎评论拍砖

这些功能早在2018年就已经实现并运行在生产,感兴趣的同学可以去 github(grpc.extensions)上查看,你要的都有,欢迎提issue

我们是怎么实现gRPC CodeFirst-生成proto的更多相关文章

  1. 我们是怎么实现Grpc CodeFirst

    前言: Grpc默认是ProtoFirst的,即先写 proto文件,再生成代码,需要人工维护proto,生成的代码也不友好,所以出现了Grpc CodeFirst,下面来说说我们是怎么实现Grpc ...

  2. h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编程 vue2入坑随记(二) -- 自定义动态组件 微信上传图片

    得益于前辈的分享,做了一个h5录音的demo.效果图如下: 点击开始录音会先弹出确认框: 首次确认允许后,再次录音不需要再确认,但如果用户点击禁止,则无法录音: 点击发送 将录音内容发送到对话框中.点 ...

  3. 自动生成proto Js语句

    在与后端的WebSocket通信时,前端要带一个proto文件是一个累赘的事情.首先是明显的曝光了协议实体对象,再一个浏览器客户端很容易会缓存该文件,新的协议更新可能导致客户端不能使用,另外在cdn服 ...

  4. EF 控制code-first生成的数据库表名的单复数

    原地址:https://blog.csdn.net/winnyrain/article/details/51248410 在Code-First中,默认生成的数据库表的名称为类型的复数形式,如Mode ...

  5. EF CodeFirst生成数据库到Sqlserver中

    EF CodeFirst简单实例这篇文章介绍了如何用EF去快速生成数据库.但是这个并没有生成到sqlserver中,总觉得不爽.下面就来讲一下,如何将数据库生成到sqlserver中. 按照EF Co ...

  6. netcore codefirst生成数据库命令

    1.程序通过nuget安装包 Microsoft.EntityFrameworkCore.Design 2.生成添加脚本 add-migration InitialCreate -Context AL ...

  7. efcore mysql数据库codefirst生成

    添加引用 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.Tools Pomelo.EntityFrameworkCore.My ...

  8. NetCore + Mysql CodeFirst 生成数据库

    首先定义领域的模型类,然后配置下面的一些东西,最后执行类 1. 新建Context 继承自 DbContext public class EFProjectContext : DbContext { ...

  9. EF使用CodeFirst方式生成数据库&技巧经验

    前言 EF已经发布很久了,也有越来越多的人在使用EF.如果你已经能够非常熟练的使用EF的功能,那么就不需要看了.本文意在将自己使用EF的方式记录下来备忘,也是为了给刚刚入门的同学一些指导.看完此文,你 ...

随机推荐

  1. Thinkpad E40热键不能使用解决办法

    Thinkpad E40 0578M68笔记本电脑安装windows7 64bit和联想官网驱动后,键盘最上面一排热键中,除了静音.减小音量和增大音量之外,其余的热键均不可用,解决办法: 到联想官网下 ...

  2. iPhone8的十面埋伏

    ​ 不知不觉,iPhone已经走到了第十个年头,也正因如此,业界最普遍的预测就是:iPhone8会出现颠覆性创新,让人眼前一亮的同时,给苹果再度续命.平心而论,苹果早就青史留名,创造了大量的奇迹,科技 ...

  3. Java入门教程十(抽象类接口内部类匿名类)

    抽象类(abstract) 一个类只定义了一个为所有子类共享的一般形式,至于细节则交给每一个子类去实现,这种类没有任何具体的实例,只具有一些抽象的概念,那么这样的类称为抽象类. 在面向对象领域,抽象类 ...

  4. webpack中打包拷贝静态文件CopyWebpackPlugin插件

    copyWebpackPlugin: 作用:用于webpack打包时拷贝文件的插件包 安装:npm install copyWebpackPlugin@版本号 使用:// copy custom st ...

  5. Ribbon进行服务调用/负载均衡以及请求重试配置

    Ribbon负载均衡 经过对Eureka的认识,及Eureka集群的搭建,已经基本可以入门Eureka的使用.之前对于服务调用者我们是直接获取注册列表后通过 get(0) 的方式来获取第一个注册信息. ...

  6. python django 之 django自带的分页

    1. 例1: 基础的分页    1). vim app01/views.py        def users(request):            from django.core.pagina ...

  7. ITT Corporation之“中国战略”

    前言:众所周知,中国已经成为全世界第二大经济体,并且坐拥14亿人口的庞大市场,蕴藏着巨大的市场机遇,海外高科技企业想法获得长足的发展重视和开拓中国市场成为重中之重,诸如特斯拉,google,苹果等,近 ...

  8. Javascript学习笔记-基本概念-函数

    ECMAScript 中的函数使用function 关键字来声明,后跟一组参数以及函数体.函数的基本语法如下所示: function functionName(arg0, arg1,...,argN) ...

  9. 实用的Python(3)超简单!基于Python搭建个人“云盘”

    1 简介 当我们想要从本地向云服务器上传文件时,比较常用的有pscp等工具,但避免不了每次上传都要写若干重复的代码,而笔者最近发现的一个基于Python的工具updog,可以帮助我们在服务器上搭建类似 ...

  10. MySQL占用CPU超过百分之100解决过程

    本文转载自: https://www.93bok.com 访问网页504 Gateway Time-out,登陆服务器查看,内存正常,CPU使用率达到了400%,因为是4核,所以到了400%,几乎全部 ...