zoukankan      html  css  js  c++  java
  • GRPC .netcore

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

    一、定义服务

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

    // 文件名:mathservice.proto
    
    syntax = "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 %~dp0
    
    set TOOLS_PATH=C:UsersFreeman.nugetpackagesGrpc.Tools1.0.0	oolswindows_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
    
    endlocal
    timeout 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字段如果是本机测试,请一定使用本机名称或使用localhost,如果是真实环境请使用域名,因为客户端必须通过机器名(本地测试)或域名访问该服务。如果此处CN字段不使用机器名或域名,将导致以下错误:


      CN字段不使用主机名或域名时产生的错误
    • 生成服务端和客户端证书脚本generate_ssl.bat

    @echo off
    set OPENSSL_CONF=c:OpenSSL-Win64inopenssl.cfg   
    
    echo Generate CA key:
    openssl genrsa -passout pass:1111 -des3 -out ca.key 4096
    
    echo 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 4096
    
    echo 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.key
    
    echo Generate client key
    openssl genrsa -passout pass:1111 -des3 -out client.key 4096
    
    echo 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.key
    pause
    • 基于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);
        }
    }

    https://www.jianshu.com/p/f5e1c002047a

  • 相关阅读:
    我决定重新开始搞机器学习啦
    基于问句实体扩展和全局规划的答案摘要方法研究相关论文
    cjson源代码解读(三) 解析字符串、数字、数组、对象
    论文阅读:DeepWalk
    cjson源代码解读 (二)解析流程
    cjson源代码解读 (一)介绍
    DDoS攻击的几种类型
    Nmap扫描二级目录
    一次域环境的渗透
    利用enum4linux 445端口+wordpress插件任意文件上传的一次渗透
  • 原文地址:https://www.cnblogs.com/chenyishi/p/9930785.html
Copyright © 2011-2022 走看看