入门干货之Grpc的.Net 封装-MagicOnion

0x01、Grpc

1、介绍

Google主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf作为序列化工具,支持多种语言。在.NET Core “大更新” 之前,也就是目前来说还算是个很不错的选择。

2、吐槽

a、有很多性能比较的文章拿Grpc开涮.

b、搭建困难,恶心,复杂,反胃,有点吃不消,吃吗丁啉不一定好使,砸键盘也解决不了问题。

3、搭建流程

a、引用

都能搜索到,搜索不到就打全。

b、编写跨语言服务文件(.proto),内容如下

syntax = "proto3";

package gRPCDemo;

service gRPC {

rpc SayHello (HelloRequest) returns (HelloReply) {}

}

message HelloRequest {

string name = 1;

}

message HelloReply {

string message = 1;

}

c、这时开始用到了tools, 敲命令:

首先你的路径能识别下面两个exe,如果不能,就搜索这两个exe,我当时就没找到,用everthing搜索,然后复制粘贴。

命令:protoc.exe -I GrpcConsole --csharp_out GrpcConsole GrpcConsole\helloworld.proto --grpc_out GrpcConsole --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe

对,你找文章都这一长串,demo也是,运行时候各种报错,神特么烦,也不跟你细说选项作用,注意事项。

这里就详细说一下:

①、-I指定一个或者多个目录,用来搜索.proto文件的。所以上面那行的GrpcConsole\helloworld.proto 已经可以换成helloworld.proto了,因为-I已经指定了。注意:如果不指定,那就是当前目录,没毛病。

②、--csharp_out生成C#代码,当然了还能cpp_out、java_out、javanano_out、js_out、objc_out、php_out、python_out、ruby_out这时候你就应该知道,这玩意就是支持多语言的,才用的,生成一些文件,然后给各个语言平台调用。参数1是输出路径,参数2是proto的文件名或者路径。

③、--grpc_out到这里可能有人会懵逼,咋回事?C#不是有一个自己的输出目录么?怎么又一个输出?

csharp_out是输出类似于咱们平时写的实体类,接口,定义之类的。生成的文件叫,额,就叫xxx.cs吧.

grpc_out是跟服务相关,创建,调用,绑定,实现相关。生成的玩意叫xxxGrpc.cs。 对比上个选项生成的文件名,大概能了解个十之八九吧。

④、--plugin=protoc-gen-grpc=grpc_csharp_plugin.exe这个就是csharp的插件,python有python的,java有java的。你不指定它,你毛都生成不了。

d、开发

请允许我做被悲伤、绝望地砸键盘动作,你忙活了那么久,现在刚刚进入开发阶段,具体的,自己个去看官方demo. 毕竟本文重点并不是这货。

0x02、MagicOnion

1、介绍

首先这里声明一点,技术无国界。

MagicOnion 是一位来自日本的 CTO (目前就职于Grani游戏公司), UniRx、MsgPack序列化CSharp实现的作者,连续...反正好多年的MVP(从2011年开始),叫Yoshifumi Kawai ?(咋这么像‘卡哇伊’??)。作品集:https://github.com/neuecc。

MagicOnion是对Grpc的封装,并且采用了MessagePack序列化技术,提高性能,随后支持了SwaggerUI。

MessagePack (msgPack)是很早之前的一个序列化技术,各文章媒体介绍说比json快10倍,之前关注过,json这两年的势头太猛了。 MessagePack-CSharp 去年(2017年)开始浮出水面的,实测比json.net序列化快很多很多,不是一个数量级,这里我就不谈protobuf和mesasgepack的差别了,因为我可能写了假代码(他的readme说MessagePack比protobuf效率要高,我测protobuf序列化性能比他要高)。

Swagger, 必应一大把。

MIT协议,写着放心,用着舒心。

2、吐槽

两个月没更新了,Github上有灰。

3、普通C/S框架搭建流程

a、Nuget上搜MagicOnion,下载安装。

b、非常非常重要的接口,你不得不写的接口,这个接口服务端和客户端都要定义。

1  //不管你在哪里写,反正服务端和客户端都要写,对!要么引用,要么写一模一样的。2  //你能看懂的那都是你写的。3  //你看不懂的都是它库里自带的,比如IService,UnaryResult。4  publicinterfaceIHello : IService5  {6    UnaryResult Hello(stringname);7  }8  publicinterfaceIWorld : IService9  {10    UnaryResult World(stringname);11  }121516  //不管你在哪写,反正服务端需要实现,客户端远程调用接口的时候走的就是这方法17publicclassHelloServices : ServiceBase, IHello18{19publicUnaryResult Hello(stringname)20{21returnnewUnaryResult($"hello");22}23}24publicclassWorldServices : ServiceBase, IWorld25{26publicUnaryResult World(stringname)27{28returnnewUnaryResult($"This is 's world!");29}30}

c、编写客户端和服务端,简单到我不好意思贴代码,贴图吧

各位老铁,这代码能看懂吧?没毛病。流传输去官网下看Readme的例子,也挺容易。

4、集成在Web中,做成接口。

a、引用

MagicOnion.HttpGateway (其中包含了MagicOnion、Asp.net core 中间件扩展、Json.net、MagicOnion.HttpGetway)

Swashbuckle.AspNetCore

b、有了第3个C/S的基础,那么你就可以继续干了。写你的接口,并且配上注释,因为Swagger要用!

ReturnResult是我自己定义的返回值,跟这些库无关,你随便写。

c、写你的代码,实体类,要支持MessagePack序列化,打个标签。

d、其他的工作(都随你便)

publicclassTest :ServiceBase, ITest {publicUnaryResult GetStudent(intsid) {//GetStudentBySidFromDatabase(sid)//GetModelStudent student; student.Name ="Test_小明"; student.Sid = sid;//FillResultReturnResult result =newReturnResult() { Data = student, Status =};//ReturnreturnUnaryResult(result); } }

这是是实现你定义的接口,你想返回啥都行,读数据库,读缓存都随便你,我就是简单写个例子。

e、然后就是添加服务,你这个rpc是作为服务集成在web中的。

publicvoidConfigureServices(IServiceCollection services) {//这代码跟咱们之前定义服务的那个代码一个样子varservice = MagicOnionEngine.BuildServerServiceDefinition(newMagicOnionOptions(true) { MagicOnionLogger =newMagicOnionLogToGrpcLogger() });varserver =newServer { Services = { service }, Ports = {newServerPort("localhost",8800, ServerCredentials.Insecure) } };//这里开始不同,你要把注释生成到xml里给swagger,这里是swagger的用法,看不懂去学swaggerservices.AddSwaggerGen(c => {varfilePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath,"Swagger.xml"); c.IncludeXmlComments(filePath); }); server.Start();//这里添加服务services.Add(newServiceDescriptor(typeof(MagicOnionServiceDefinition), service)); services.AddMvc(); }

f、配置,各种配置。

publicvoidConfigure(IApplicationBuilder app, IHostingEnvironment env) {//获取添加了的服务varmagicOnion = app.ApplicationServices.GetService();//使用MagicOnion的Swagger扩展,就是让你的rpc接口也能在swagger页面上显示//下面这些东西你可能乍一看就懵逼,但你看到页面的时候就会发现,一个萝卜一个坑。//注意:swagger原生用法属性都是大写的,这里是小写。app.UseMagicOnionSwagger(magicOnion.MethodHandlers,newSwaggerOptions("MagicOnion.Server","Swagger Integration Test","/") { Info =newInfo() { title ="MGrpc", version ="v1", description ="This is the API-Interface for MGrpc", termsOfService ="By NMS", contact =newContact { name ="LanX", email ="2765968624@qq.com"} },//使用Swagger生成的xml,就是你接口的注释XmlDocumentPath = PlatformServices.Default.Application.ApplicationBasePath +"Swagger.xml"});//要想让rpc成为该web服务的接口,流量和协议被统一到你写的这个web项目中来,那么就要用个方法链接你和rpc//这个web项目承接你的请求,然后web去调用rpc获取结果,再返回给你。//因此需要下面这句话app.UseMagicOnionHttpGateway(magicOnion.MethodHandlers,newChannel("localhost:8800", ChannelCredentials.Insecure));

if(env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }//以下是swagger的用法,不赘述app.UseSwagger(c => { c.RouteTemplate ="swagger//swagger.json"; }); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json","API-v1"); c.ShowJsonEditor(); c.ShowRequestHeaders(); }); app.UseSwagger(); app.UseMvc(); }

5、成果:

0x03、窥视你的通信过程

1、抓包查看它们的通信过程,装Wireshark, 但是它不抓本地包,只抓网卡的,本地环回地址实际上是走的是操作系统内部,跟网卡无关,因此需要借助一个工具,Npcap,下载页面https://nmap.org/npcap/#download,文章中间有下载的地方。某些人会留意到神器Nmap,吊得一比。装完之后你的Wireshark会多一个接口,本地接口,然后你就侦听吧,过滤语句:http || tcp.port == 8800,复制粘贴就是干。

效果:

交互过程以及三次握爪

发送数据,可靠传输

0x04、广告时间

0x05、结束

感谢各位老铁能坚持看完,没有彩蛋,拜拜~

原文地址:https://www.cnblogs.com/nmslanx/articles/8242105.html

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180110B02HJC00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券