所有代码详见
https://github.com/fodangithub/Csharp-n-Python
gRPC是一个网络通讯协议,2015年由Google提出。gRPC即g – Remote Procedure Call,RPC的官方翻译为“远程过程调用”,名字听起来很奇怪,这里就大致理解为“调用一个在远程端部署的子程序”吧。至于RPC前面的g,众多人推测g是指Google,因为是由Google主导开发的,但官方一直都没有给出明确的回答,故至今g到底是什么意义,这恐怕会在很长一段时间里成为一个谜。
不过无论怎样,名字都只是一个标记,今天我们需要看到的是它的实力,以及如何在它的帮助下快速实现不同语言之间类似于源生代码般的互相调用。
其实现的基本思路是通过Python架设本地服务器,留好端口,C#通过网络通讯协议HTTP或者HTTPS访问该端口,实现“远程”调用。也就是说,Python将运行一个服务器,C#程序运行客户端,然后客户端和服务器均在同一电脑上,虽然是“远程”调用,但实际上并不会存在所谓网络延迟。
我们使用gRPC的时候需要预先定义好服务器和客户端需要交换什么样的“数据”、有哪些“调用方式”,类似于在OOP(面向对象编程)中的class和function。用gRPC的好处是,该协议是“Language Neutral”,也就是说对于任何语言来说,这种“数据”、“调用方式”的编写都一样,写完之后可以发布到任意语言平台的服务器/客户端中。gRPC中的协议定义文件为.proto文件。
本文介绍的例子中实现完整的代码需要:
Python部分:grpc 库、grpc-tools
C#部分:(NuGet包)Grpc.net.Client、Google.Protobuf、Grpc.Tools
本文通过实现“一个简单的两个整数相的python代码,使用C#客户端传入参数调用这个python代码,获取返回值并打印”这样的例子来展示如何通过gRPC来实现C#调用python代码。同时应该注意到,由于gRPC可以适配任意语言组合(前提是gRPC支持,目前官方支持的语言有C#/C++/Dart/Go/Java/Kotlin/Node.js/Objective-C/php/Python/Ruby),所以该方法同样适用于python调用Ruby、C++调用Java等任意组合(服务器端/客户端组合)。
所有内容均在Windows操作系统下实现。如果在Linux下,我相信看完之后你会发现某些操作比windows下更容易实现;而如果是在MacOS下,gRPC服务器端的配置需要某些特别的权限开放,详情请查看网上针对MacOS写的gRPC教程,这里限于篇幅就不展开了。
流程大致如下:
1、gRPC proto 文件编写
1) 交换数据类型定义
2)调用方式定义
2、构架Python服务器端
3、构架C#客户端
由于第2、3步都是与对应语言相关性十分强的内容,故在此仅给出必要代码及简略解释,详情还需参考对应语言(此处为Python和C#)的资料。
—— gRPC proto文件编写 ——
通过观察该例子的需求——“加法程序”,我们可以很容易设计该例子中,作为服务器端的Python程序需要哪些输入值及输出值,在这里,我们需要实现的是:
-
两个整型输入值
-
一个整型返回值
-
一个“两数相加”的方法
这里直接给出proto文件的写法,具体释义详见Github源码注释。
message AddTwoNumberRequest {
int32 firstNum = 1;
int32 secondNum = 2;
}
message AddTwoNumberReply {
int32 resultVal = 1;
}
service AddTwoNumberService {
rpc AddTwo (AddTwoNumberRequest) returns (AddTwoNumberReply);
}
在完成proto文件之后,我们先来构筑服务器端所需的python代码。
—— 构架Python服务器端 ——
创建一个空文件夹,将我们的proto文件先复制进去,这里的proto文件我们命名为“addTwoNumbers.proto”。
第一步我们需要将proto文件翻译成python可以识别的.py文件,这步骤之后,我们的python代码就可以直接import这个翻译后的文件,将我们定义的gRPC协议内容当作原生python类来使用了。值得一提的是,翻译这部分工作并不需要我们来做,而是有现成的工具可以实现,针对于其他编程语言也是如此,故此说proto文件或者gRPC协议是“language neutral”的,因为所有的翻译工具都会有官方支持,无需开发人员自己翻译。
在预先已经装好grpc-tools的前提下(pip install grpcio-tools),在命令行cmd或者Windows PowerShell中进入我们创建的文件夹,输入
python.exe -m grpc_tools.protoc addTwoNumbers.proto –python_out=. –grpc_python_out=. –proto_path=.
然后我们可以发现在文件夹中多了两个py文件
这两个py文件本质上与我们之前在proto文件中定义了相同的内容,也就是proto文件内容的python翻译版本。此时,我们就可以将这两个文件import进我们构架python服务器时所用的py文件了。
新建一个py文件,命名为“server.py”,输入以下内容:(当然,在import grpc这行可以生效之前,你需要安装好grpc库,pip install grpcio)
from concurrent import futures
import logging
import grpc
import addTwoNumbers_pb2
import addTwoNumbers_pb2_grpc
class TwoNumberAdder(addTwoNumbers_pb2_grpc.AddTwoNumberServiceServicer):
def AddTwo(self, request, context):
return addTwoNumbers_pb2.AddTwoNumberReply(resultVal = request.firstNum + request.secondNum)
logging.basicConfig()
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) # A
addTwoNumbers_pb2_grpc.add_AddTwoNumberServiceServicer_to_server(TwoNumberAdder(), server) # B
server.add_insecure_port(‘[::]:34567’) # C
server.start()
print(‘Server started…’)
server.wait_for_termination()
简单的来说,上面的代码干了下面几件事情:
-
建立了一个支持grpc服务的服务器
-
将我们的“两个数相加”的服务添加到我们的服务器中(实现proto协议接口类中定义的接口——建立一个Class并实现“AddTwo”方法)
-
对某端口监听并启动该服务器
此时,python端配置已经结束,服务器已经蓄势待发准备好了接受客户端的请求,接下来我们需要配置C#客户端
—— 构架C#客户端 ——
启动Visual Studio,创建一个C#的Console Application,可以是.Net Core或者.Net Framework。
创建成功之后,在“工具”-“NuGet包管理”菜单栏中选择“管理项目中的NuGet包”(中文翻译可能有出入,请查看具体图片)
安装前文所述的Grpc.net.Client、Google.Protobuf、Grpc.Tools三个包,保证在已安装标签下可以看到这三个NuGet包:
安装完成后,在项目文件下新建一个文件夹,起名Protos
打开这个文件夹,把前面编写好的proto文件复制进去
然后,右键单击项目文件,选择编辑项目文件:
在原有代码基础上,插入如下内容
此时,当我们回到Program.cs中开始编写代码时,我们应该可以看到VS已经可以帮我们自动完成“proto到C#代码库”这一步,并且可以有代码完成提示了:
这里的namespace名字与我们之前在proto里定义csharp_namespace的option有关。如果到这一步时,VS并没有成功实现代码自动完成,这有可能说明C#的配置存在问题,可以重新检查一下前面的步骤、重新安装NuGet包,或者其他途径重新配置一下C#项目。
接下来,完成Program.cs,输入给python端的两个被相加的数为320和456:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using addTwoNumbersService;
using Grpc.Net.Client;
namespace gRPC_Client_Example
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine(“Hello World, We are about to explore gRPC!”); AppContext.SetSwitch(“System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport”, true);
HttpClientHandler insecureHCH = new HttpClientHandler();
insecureHCH.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
GrpcChannel grpcChannel = GrpcChannel.ForAddress( “http://localhost:34567”, new GrpcChannelOptions { HttpHandler = insecureHCH });
AddTwoNumberService.AddTwoNumberServiceClient client = new AddTwoNumberService.AddTwoNumberServiceClient(grpcChannel);
AddTwoNumberRequest req = new AddTwoNumberRequest { FirstNum = 320, SecondNum = 456 };
AddTwoNumberReply reply = await client.AddTwoAsync(req);
Console.WriteLine($”320 + 456 = {reply.ResultVal}”);
}
}
}
p.s. 代码排版真的好难,有看不懂的还请直接移步Github源码,IDE自带高亮排版)
需要注意到几个点是:
下面就是见证奇迹的时刻。
最终实现 < Put everything together >
在python服务器端文件夹下,运行server.py。
在C#客户端,直接运行项目:
好了,今天的介绍就到这里,更多细节例如为什么Python服务器端配置下使用server.add_insecure_port ,C#客户端配置下有DangerousAcceptAnyServerCertificateValidator之类的内容由于关联到HTTP和HTTPS的内容,就留到下期在介绍了。
所有代码详见
https://github.com/fodangithub/Csharp-n-Python
为了方便大家交流技术和互通行业资讯,
请添加我们“转自:非解构-公众号”微信,
加入相关讨论交流群。