您的位置:首页 > 其它

.NET Core 使用 grpc 实现微服务

2016-11-03 07:30 681 查看
GRPC 是Google发布的一个开源、高性能、通用RPC(Remote Procedure Call)框架。提供跨语言、跨平台支持。以下以一个.NET Core Console项目演示如何使用GRPC框架。

一、定义服务

通过proto定义一个数学计算服务,其中包括两个服务方法(Add, Multipy)以及4个请求响应对象(AddRequest, AddReply, MultiplyRequest, MultiplyReply)。

// 文件名:mathservice.protosyntax = "proto3";
option java_multiple_files = false;
option java_package = "MathServices";
option java_outer_classname = "MathServicesProto";
option objc_class_prefix = "MathServices";
package MathServices;// 数学运算服务service MathService
{  
    rpc Add (AddRequest) returns (AddReply) {}  rpc Multiply (MultiplyRequest)
   returns (MultiplyReply) {}
}
message AddRequest {  double First = 1;  double Second = 2;
}
message AddReply {  double Sum = 1;
}
message MultiplyRequest {  double First = 1;  double Second = 2;
}
message MultiplyReply {  double Result = 1;
}

二、将服务编译成存根(stub)

通过以下批处理命令
generate_protos.bat
将服务定义生成多种语言和平台版本的客户端和服务端存根。

@rem 生成客户端和服务器端存根

setlocal

@rem 进入当前目录
cd /d %~dp0set TOOLS_PATH=C:\Users\Freeman\.nuget\packages\Grpc.Tools\1.0.0\tools\windows_x86 %TOOLS_PATH%\protoc.exe ^--proto_path protos ^--cpp_out=Interfaces/cpp ^--csharp_out=Interfaces/csharp ^--java_out=Interfaces/java ^--js_out=Interfaces/javascript ^--grpc_out=Interfaces/csharp ^--plugin=protoc-gen-grpc=%TOOLS_PATH%\grpc_csharp_plugin.exe ^protos/mathservice.proto endlocaltimeout 5

针对CSHARP语言,protoc.exe编译器生成了如下图几个类,其中左边4个类用于构造请求和响应对象,MathService类用于下一步构造服务和消费服务。



CSHARP STUBS

三、实现并运行服务

通过上一步的编译,自动生成了MathService类,下面通过该类构造并启动grpc服务。

通过继承基类实现服务接口

   /// <summary>
   /// 实现RPC服务端接口。
   /// </summary>
   public class MathServiceImpl : MathService.MathServiceBase
   {
       public override Task<AddReply> Add(AddRequest request, ServerCallContext context)
       {        
            return Task.FromResult(new AddReply { Sum = request.First + request.Second });
       }

       public override Task<MultiplyReply> Multiply(MultiplyRequest request, ServerCallContext context)
       {        
             return Task.FromResult(new MultiplyReply { Result = request.First * request.Second });
       }
   }

启动服务

const string ip = "0.0.0.0";const int port = 50051;
Server server = new Server();
server.Ports.Add(new ServerPort(ip, port, ServerCredentials.Insecure));
server.Services.Add(MathService.BindService(new MathServiceImpl()));
server.Start();
server.Ports.ToList().ForEach(a => Console.WriteLine($"Server listening on port {a.Port}..."));
Console.ReadLine();

四、客户端调用服务

客户端通过创建一个Channel和一个服务客户端来使用服务。

var channel = new Channel($"{"127.0.0.1"}:{port}", SslCredentials.Insecure);
var client = new MathService.MathServiceClient(channel);
var random = new Random();while (true)
{    var first = random.NextDouble();    
      var second = random.NextDouble();  
     var reply = client.Add(new AddRequest { First = first, Second = second });  
     Console.WriteLine($"RPC call Add service: {first:F4} + {second:F4} = {reply.Sum:F4}");    Thread.Sleep(500);
}




RPC调用

五、使用SSL实现加密通讯

grpc默认实现了基于证书的SSL加密通讯,使用中需要注意以下事项。

在Windows上开发请安装 OpenSSL对应版本并将openssl.exe所在路径添加到环境变量中。

通过以下样例脚本生成通讯中所需要的服务端和客户端证书,其中需要特别注意的是,Generate server signing request:中的CN=KEKYK字段如果是本机测试,请一定使用本机名称,如果是真实环境请使用域名,因为客户端必须通过机器名(本地测试)或域名访问该服务。如果此处CN字段不使用机器名或域名,将导致以下错误:



CN字段不使用主机名或域名时产生的错误

生成服务端和客户端证书脚本generate_ssl.bat

@echo offset OPENSSL_CONF=c:\OpenSSL-Win64\bin\openssl.cfg   echo Generate CA key: openssl genrsa -passout pass:1111 -des3 -out ca.key 4096echo Generate CA certificate: openssl req -passin pass:1111 -new -x509 -days 365 -key ca.key -out ca.crt -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=MyRootCA"echo Generate server key: openssl genrsa -passout pass:1111 -des3 -out server.key 4096echo Generate server signing request: openssl req -passin pass:1111 -new -key server.key -out server.csr -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=kekyk"echo Self-sign server certificate: openssl x509 -req -passin pass:1111 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt echo Remove passphrase from server key: openssl rsa -passin pass:1111 -in server.key -out server.keyecho Generate client keyopenssl genrsa -passout pass:1111 -des3 -out client.key 4096echo Generate client signing request: openssl req -passin pass:1111 -new -key client.key -out client.csr -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=client"echo Self-sign client certificate: openssl x509 -passin pass:1111 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt echo Remove passphrase from client key: openssl rsa -passin pass:1111 -in client.key -out client.keypause

基于SSL的服务端启动如下,创建服务的时候请使用主机名(开发环境)或域名(生产环境),不要使用IP地址。
public static void RpcServerSsl()
{  
   var cacert = File.ReadAllText(CombinePath("ca.crt"));
  var servercert = File.ReadAllText(CombinePath("server.crt"));  
  
   var serverkey = File.ReadAllText(CombinePath("server.key"));
 var keypair = new KeyCertificatePair(servercert, serverkey);
 
 var sslCredentials = new SslServerCredentials(new List<KeyCertificatePair>()
 
    { keypair }, cacert, false);  
   
  var server = new Server
 {
     Services = { MathService.BindService(new MathServiceImpl()) },
     Ports = { new ServerPort("KEKYK", sslPort, sslCredentials) }
 };
 server.Start();
 server.Ports.ToList().ForEach(a => Console.WriteLine($"Server (SSL) listening on port {a.Port}..."));
 Console.ReadLine();
}


基于SSL的客户端使用如下,注意测试环境中使用主机名,生产环境中使用域名来,不要使用任何形式的IP地址。
public static void RpcClientSsl(){  
  var cacert = File.ReadAllText(CombinePath("ca.crt"));
  var clientcert = File.ReadAllText(CombinePath("client.crt"));
 
 var clientkey = File.ReadAllText(CombinePath("client.key"));  
 
  var ssl = new SslCredentials(cacert, new KeyCertificatePair(clientcert, clientkey));
  var channel = new Channel("KEKYK", sslPort, ssl);  
 
  var client = new MathService.MathServiceClient(channel);  
  var random = new Random();  while (true)
 {      
        var first = random.NextDouble();  
      var second = random.NextDouble();      
      
        var reply = client.AddAsync(new AddRequest { First = first, Second = second },
          new CallOptions()).ResponseAsync.Result;
     Console.WriteLine($"RPC call Add service: {first:F4} + {second:F4} = {reply.Sum:F4}");
     Thread.Sleep(1000);
 }
}


原文地址:http://www.jianshu.com/p/f5e1c002047a

.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐