.Net Core gRpc调用

简介

创建gRPC

创建服务端

  1. vs2022直接搜索grpc默认下一步创建

创建控制台测试

  1. 创建控制台
  2. 引入以下dll
<PackageReference Include="Google.Protobuf" Version="3.23.4" />
<PackageReference Include="Grpc.Net.Client" Version="2.55.0" />
<PackageReference Include="Grpc.Tools" Version="2.56.2">
  1. 打开服务端.csproj文件,复制以下内容粘贴到客户端的.csproj文件中并修改GrpcServices=Client,客户端.csproj文件出现 None Update="Protos\greet.proto" 这组ItemGroup是可以删除的
<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

4.将服务端Protos文件夹及内容全部拷贝到客户端项目下
5.在客户端创建gRpcRequest.cs文件并增加下列代码,端口号填写服务端端口

using Grpc.Net.Client;
using GrpcService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static GrpcService.Greeter;

namespace gRpcConsoleTest
{
    public static class gRpcRequest
    {
        public static async Task SayHello()
        {
            using(var channel = GrpcChannel.ForAddress("https://localhost:7166")) 
            {
                GreeterClient client = new GreeterClient(channel);
                HelloReply reply = await client.SayHelloAsync(new HelloRequest() {Name = "jjjjj" });
                Console.WriteLine(reply.Message);
            }
        }
    }
}

6.客户端Program.cs文件中加入测试代码 await gRpcRequest.SayHello();
7.服务端和客户端在想要调试的位置打上断点
8.运行服务端
9.选中客户端项目右键 -> 调试 -> 启动新实例 即可两个项目全部命中断点进行调试测试

创建自定义服务

  • 服务端

    1. Protos文件夹添加 custom.proto文件并添加下列代码,重新生成项目打开项目所在文件夹,打开路径:obj\Debug\net6.0\Protos 查看CustomGrpc.cs是否存在,如果没有存在则在.csproj文件中添加:
    syntax = "proto3";
    
    option csharp_namespace = "Custom.Service";
    
    package custom;
    
    service CustomGreeter {
      rpc Plus(Number) returns (NumberResult) ;
    }
    
    message Number {
        int32 leftNumber = 1;
        int32 rightNumber = 2;
    }  
    
    message NumberResult{
        int32 result = 1;
    }
    
    1. Services文件夹下添加CustomGreeterService.cs文件,namespace 与 .protos中的csharp_namespace对应
    using Grpc.Core;
    
    namespace Custom.Service
    {
        public class CustomGreeterService : CustomGreeter.CustomGreeterBase
        {
            public override async Task<NumberResult> Plus(Number request, ServerCallContext context) => 
                await Task.FromResult<NumberResult>(new NumberResult()
                {
                    Result = request.LeftNumber + request.RightNumber,
                });
        }
    }
    

    3.在Program.cs 中注册新创建的服务,加入下列代码:
    app.MapGrpcService();

  • 客户端

    1. 将服务端的custom.proto文件拷贝到Protos文件夹内并在.csproj文件中添加(注意这里GrpcServices="Client"):
    2. 重新生成项目并检查路径: obj\Debug\net6.0\Protos 是否生成对应的CustomGrpc.cs文件
    3. 加入测试代码:
    public static async Task Plus(int leftNumber,int rightNumber)
    {
        using (var channel = GrpcChannel.ForAddress("https://localhost:7166"))
        {
            CustomGreeterClient client = new CustomGreeterClient(channel);
            NumberResult number = await client.PlusAsync(new Custom.Service.Number() { LeftNumber = leftNumber,RightNumber = rightNumber});
            Console.WriteLine(number.Result);
        }
    }
    
    1. 测试步骤与上面控制台测试一样,调试方式也一样

服务器流式处理方法

custom.proto

syntax = "proto3";

option csharp_namespace = "Custom.Service";

package custom;

service CustomGreeter {
  rpc SelfIncreaseServer(IntArrayModel) returns (stream BathTheCatResp); //服务端流
}

message BathTheCatResp{
    string message = 1;
}

message IntArrayModel{
    repeated int32 number = 1;
}

CustomGreeterService.cs

using Grpc.Core;

namespace Custom.Service
{
    public class CustomGreeterService : CustomGreeter.CustomGreeterBase
    {
        public override async Task SelfIncreaseServer(IntArrayModel request, IServerStreamWriter<BathTheCatResp> responseStream, ServerCallContext context)
        {
            foreach (var item in request.Number)
            {
                Console.WriteLine($"客户端传入参数: {item}");
                await responseStream.WriteAsync(new BathTheCatResp() { Message = item.ToString()});
                await Task.Delay(1000);
            }
        }
    }
}

gRpcRequest.cs

using Custom.Service;
using Grpc.Core;
using Grpc.Net.Client;
using GrpcService;
using static Custom.Service.CustomGreeter;

namespace gRpcConsoleTest
{
    public static class gRpcRequest
    {
        public static async Task SelfIncreaseServe()
        {
            using (var channel = GrpcChannel.ForAddress("https://localhost:7166"))
            {
                CustomGreeterClient client = new CustomGreeterClient(channel);
                IntArrayModel intArray = new IntArrayModel();
                for (int i = 0; i < 10; i++)
                {
                    intArray.Number.Add(i);   
                }

                var batch = client.SelfIncreaseServer(intArray);

                Task batchTask = Task.Run(async ()=>
                {
                    await foreach (var item in batch.ResponseStream.ReadAllAsync())
                    {
                        Console.WriteLine($"服务端相应数据: {item.Message}");
                    }
                });

                await batchTask;
            }
        }
    }
}

客户端流式处理方法

custom.proto

syntax = "proto3";

option csharp_namespace = "Custom.Service";

package custom;

service CustomGreeter {
  rpc SelfIncreaseClient(stream BathTheCatReq) returns (IntArrayModel); //客户端流
}

message BathTheCatResp{
    string message = 1;
}

message IntArrayModel{
    repeated int32 number = 1;
}

CustomGreeterService.cs

using Grpc.Core;

namespace Custom.Service
{
    public class CustomGreeterService : CustomGreeter.CustomGreeterBase
    {
        public override async Task<IntArrayModel> SelfIncreaseClient(IAsyncStreamReader<BathTheCatReq> requestStream, ServerCallContext context)
        {
            IntArrayModel result = new IntArrayModel();
            while (await requestStream.MoveNext())
            {
                var message = requestStream.Current;  
                Console.WriteLine($"客户端流传入消息: {message}");
                result.Number.Add(message.Id + 1);
            }
            return result;
        }
    }
}

gRpcRequest.cs

using Custom.Service;
using Grpc.Core;
using Grpc.Net.Client;
using GrpcService;
using static Custom.Service.CustomGreeter;

namespace gRpcConsoleTest
{
    public static class gRpcRequest
    {
        public static async Task SelfIncreaseClient()
        {
            using (var channel = GrpcChannel.ForAddress("https://localhost:7166"))
            {
                CustomGreeterClient client = new CustomGreeterClient(channel);
                var batch = client.SelfIncreaseClient();
                for (int i = 0; i < 10; i++)
                {
                    await batch.RequestStream.WriteAsync(new BathTheCatReq() { Id = i });
                    await Task.Delay(1000);
                }
                await batch.RequestStream.CompleteAsync();

                foreach (var item in batch.ResponseAsync.Result.Number)
                {
                    Console.WriteLine($"响应数据: {item}");
                }
            }
        }
    }
}

双向流式处理方法

custom.proto

syntax = "proto3";

option csharp_namespace = "Custom.Service";

package custom;

service CustomGreeter {
  rpc SelfIncreaseDouble(stream BathTheCatReq) returns (stream BathTheCatResp);//双端流
}

message BathTheCatReq{
    int32 id = 1;
}

message BathTheCatResp{
    string message = 1;
}

CustomGreeterService.cs

using Grpc.Core;

namespace Custom.Service
{
    public class CustomGreeterService : CustomGreeter.CustomGreeterBase
    {
        public override async Task SelfIncreaseDouble(IAsyncStreamReader<BathTheCatReq> requestStream, IServerStreamWriter<BathTheCatResp> responseStream, ServerCallContext context)
        {
            while (await requestStream.MoveNext())
            {
                var message = requestStream.Current.Id;
                Console.WriteLine($"客户端流传入消息: {message}");
                await responseStream.WriteAsync(new BathTheCatResp() { Message=(message+1).ToString()});
            }
        }
    }
}

gRpcRequest.cs

using Custom.Service;
using Grpc.Core;
using Grpc.Net.Client;
using GrpcService;
using static Custom.Service.CustomGreeter;
using static GrpcService.Greeter;

namespace gRpcConsoleTest
{
    public static class gRpcRequest
    {
        public static async Task SelfIncreaseDouble()
        {
            using (var channel = GrpcChannel.ForAddress("https://localhost:7166"))
            {
                CustomGreeterClient client = new CustomGreeterClient(channel);
                var batch = client.SelfIncreaseDouble();

                Task batchTask = Task.Run(async () =>
                {
                    await foreach (var item in batch.ResponseStream.ReadAllAsync())
                    {
                        Console.WriteLine($"服务端相应数据: {item.Message}");
                    }
                });


                for (int i = 0; i < 10; i++)
                {
                    await batch.RequestStream.WriteAsync(new BathTheCatReq() { Id = i });
                    await Task.Delay(1000);
                }

                await batchTask;
            }
        }
    }
}

.Net Core 调用gRpc

项目引用

<PackageReference Include="Google.Protobuf" Version="3.24.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.55.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.55.0" />
<PackageReference Include="Grpc.Tools" Version="2.56.2">

<ItemGroup>
  <Protobuf Include="Protos\custom.proto" GrpcServices="Client" />
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

Program.cs

//CustomGreeterClient grpc连接类
builder.Services.AddGrpcClient<CustomGreeterClient>(options =>
{
    options.Address = new Uri("https://localhost:7166"); //grpc 服务地址
});

gRpcController.cs

using Custom.Service;
using Microsoft.AspNetCore.Mvc;
using static Custom.Service.CustomGreeter;

namespace gRpcWebAPI.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class gRpcController : ControllerBase
    {
        CustomGreeterClient _client; //使用构造函数注入
        public gRpcController(CustomGreeterClient client)
        {
            _client = client;
        }

        [HttpGet]
        public async Task<IActionResult> Plus(int leftNumber, int rightNumber)
        {
            NumberResult number = await _client.PlusAsync(new Custom.Service.Number() { LeftNumber = leftNumber, RightNumber = rightNumber });
            return new JsonResult(number);
        }
    }
}

支持Aop

  1. 服务端, 客户端 都需要继承 Interceptor
  2. 重新需要实现Aop的方法,如服务端: UnaryServerHandler,客户端: AsyncUnaryCall 针对一元调用的Aop
  3. 我这里使用的是NLog写的日志,NLog可以使用可以翻阅我先前的博客
    .Net Core NLog+oracel

服务端 Program.cs

builder.Services.AddGrpc(options =>
{
    options.Interceptors.Add<LogInterceptor>();
});

服务端 LogInterceptor.cs

using Grpc.Core;
using Grpc.Core.Interceptors;

namespace GrpcService.Interceptors
{
    public class LogInterceptor : Interceptor
    {
        ILogger<LogInterceptor> _logger;
        public LogInterceptor(ILogger<LogInterceptor> logger)
        {
            _logger = logger;
        }
        public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
        {
            _logger.LogInformation("===========UnaryServerHandler==========");
            return continuation(request, context);
        }
    }
}

客户端 Program.cs

builder.Services.AddGrpcClient<CustomGreeterClient>(options =>
{
    options.Address = new Uri("https://localhost:7166"); //服务端地址
}).AddInterceptor<LogInterceptor>();

客户端 LogInterceptor.cs

using Grpc.Core;
using Grpc.Core.Interceptors;
namespace GrpcService.Interceptors
{
    public class LogInterceptor : Interceptor
    {
        ILogger<LogInterceptor> _logger;
        public LogInterceptor(ILogger<LogInterceptor> logger)
        {
            _logger = logger;
        }
        public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
        {
            _logger.LogInformation("===========AsyncUnaryCall===========");
            return continuation(request, context);
        }
    }
}

jwt+gRPC验证

  1. 准备单独的一个网站发布jwt Token (授权中心)
  2. 然后在gRPC项目中jwt鉴权 和 获取到角色信息之后授权
  3. webapi测试gRPC调用, 先在授权中心获取token然后在 ConfigureChannel 方法中设置gRPC全局的jwt token
  4. 返回401: jwt鉴权不通过
  5. 返回403: jwt授权不通过

准备Jwt Token发布中心

Program.cs

//读取Jwt配置
builder.Services.Configure<JwtConfig>(builder.Configuration.GetSection("JwtTokenOptions"));

JwtConfig.cs

namespace AuthenorizationCenter.Tools.Model
{
    public class JwtConfig
    {
        public string? Audience { get; set; }
        public string? Issuer { get; set; }
        public string? SecurityKey { get; set; }
        public int ExpiresMinutes { get; set; }
    }
}

AuthenorizationController.cs

using AuthenorizationCenter.Tools;
using AuthenorizationCenter.Tools.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace AuthenorizationCenter.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthenorizationController : ControllerBase
    {

        JwtConfig _jwtconfig;
        public AuthenorizationController(IOptions<JwtConfig> jwtconfig) 
        {
            _jwtconfig = jwtconfig.Value;
        }

        [HttpGet]
        public async Task<string> GetToken(string userName, string passWord)
        {
            string token = JwtHeleper.GetToken(new()
            {
                UserName = userName,
                Extended1 = "无信息",
                Role = new List<RoleInfo>()
                {
                    new RoleInfo() { Id = "1",Role="系统管理员"} ,
                    new RoleInfo() { Id = "2",Role="用户管理员"} ,
                }
            }, new()
            {
                Audience = _jwtconfig.Audience,
                Issuer = _jwtconfig.Issuer,
                SecurityKey = _jwtconfig.SecurityKey,
                ExpiresMinutes = 5,
            });

            await Task.CompletedTask;
            return token;
        }
    }
}

JwtHeleper.cs

using AuthenorizationCenter.Tools.Model;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace AuthenorizationCenter.Tools
{
    public class JwtHeleper
    {
        public static string GetToken(UserInfo user, JwtConfig jwtConfig)
        {
            List<Claim> claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.UserName ?? ""),
                new Claim("Extended1", user.Extended1 ?? ""),
                new Claim("Extended2", user.Extended2 ?? ""),
                new Claim("Extended3", user.Extended3 ?? ""),
                new Claim("Extended4", user.Extended4 ?? ""),
                new Claim("Extended5", user.Extended5 ?? ""),
            };
            if (user.Role is not null)
            {
                foreach (var item in user.Role)
                {
                    claims.Add(new Claim(item.Id.ToString(), item.Role));
                }
            }
            if (jwtConfig.SecurityKey == null)
            {
                throw new Exception("JwtConfig.SecurityKey 不能为空");
            }
            SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey));
            SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            JwtSecurityToken token = new JwtSecurityToken(
                issuer: jwtConfig.Issuer,
                audience: jwtConfig.Audience,
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(jwtConfig.ExpiresMinutes),
                signingCredentials: creds
            );
            string resultToken = new JwtSecurityTokenHandler().WriteToken(token);
            return resultToken;
        }
    }
}

RoleInfo.cs

namespace AuthenorizationCenter.Tools.Model
{
    public class RoleInfo
    {
        public string Id { get; set; }
        public string Role { get; set; }
    }
}

UserInfo.cs

namespace AuthenorizationCenter.Tools.Model
{
    public class UserInfo
    {
        public string? UserName { get; set; }
        public List<RoleInfo>? Role { get; set; }
        public string? Extended1 { get; set; }
        public string? Extended2 { get; set; }
        public string? Extended3 { get; set; }
        public string? Extended4 { get; set; }
        public string? Extended5 { get; set; }
    }
}

appsetting.json

{
  "JwtTokenOptions": {
    "Issuer": "https://localhost:7117",
    "Audience": "https://localhost:7117",
    "SecurityKey": "kq4DY5N1eFJhscOkI7Zp4Nd0WNy9d9AEsN6Yjgdv9OxLyol66tzGBKT_7vwolN7GZ8EDwqJBwccjDJfb81ws5s3sbbP5wUzQ3-PcTSsD-Rueiu2rsOUZwg_NR3RBCwmtouV-832YV2trCjNTawLB1z0LMukWGFNaAJVZ8WdQcrYn6a0ko5oVhZqaHBgsCLEGiqPtoFsiCcrJTz1IvXHk9_cDSr2hwEmSl18GlkOtgCHFH8aidYth3aQHRHuClTi6Y9mYRJtqqK-FNQYq4ZP23DSGZGFejJFTnM9YMpppuTMLklhSGySwX8rfjZ_0L5ac18nHaykTaiC2fvH00W42qQ"
  }
}

gRPC准备

Program.cs

JwtConfig jwtConfig = new JwtConfig();
builder.Configuration.Bind("JwtTokenOptions", jwtConfig);

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true,
        ValidIssuer = jwtConfig.Issuer, //发行人
        ValidateAudience = true,
        ValidAudience = jwtConfig.Audience,//订阅人
        ValidateIssuerSigningKey = true,
        //对称加密密钥
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey!)),
        ValidateLifetime = true, //验证失效时间
        ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值
        RequireExpirationTime = true,
        AudienceValidator = (audiences, securityToken, validationParameters) =>
        {
            return true;
        },
        LifetimeValidator = (notBefore, expires, securityToken, validationParameters) =>
        {
            return true;
        }
    };
});

builder.Services.AddTransient<IUserServices, UserServices>();
builder.Services.AddTransient<IAuthorizationHandler, JwtAuthorization>();
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("JwtPolicy", policy =>
    {
        //jwt 授权
        policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
        //这里为自定义授权指定一下类
        .AddRequirements(new UserRoleRequirement(JwtBearerDefaults.AuthenticationScheme));
    });
});

CustomGreeterService.cs

using Grpc.Core;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;

namespace Custom.Service
{
    public class CustomGreeterService : CustomGreeter.CustomGreeterBase
    {
        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,Policy = "JwtPolicy")]
        public override async Task<NumberResult> Plus(Number request, ServerCallContext context) => 
            await Task.FromResult<NumberResult>(new NumberResult()
            {
                Result = request.LeftNumber + request.RightNumber,
            });
    }
}

JwtAuthorization.cs

using Cnpc.Com.Ioc.IBll;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

namespace GrpcService.Authorization
{
    public class UserRoleRequirement : IAuthorizationRequirement
    {
        public string AuthenticateScheme;
        public UserRoleRequirement(string authenticateScheme)
        {
            AuthenticateScheme = authenticateScheme;
        }
    }
    public class JwtAuthorization : AuthorizationHandler<UserRoleRequirement>
    {
        IUserServices userSercices;
        public JwtAuthorization(IUserServices userSercices)
        {
            this.userSercices = userSercices;
        }
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserRoleRequirement requirement)
        {
            string? userName = context.User.FindFirst(it => it.Type == ClaimTypes.Name)?.Value;
            if (userSercices.IsAdmin(userName!))
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }
            return Task.CompletedTask;
        }
    }
}

客户端调用

Program.cs

builder.Services.AddGrpcClient<CustomGreeterClient>(options =>
{
    options.Address = new Uri("https://localhost:7166");
}).AddInterceptor<LogInterceptor>().ConfigureChannel(async config =>
{
    //所有调用自动添加 Authorization
    CallCredentials credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
    {
        string token = await HttpClientHelper.HttpGetAsync("https://localhost:7117/api/Authenorization?userName=admin&passWord=666");
        metadata.Add("Authorization", $"Bearer {token}");
    });
    config.Credentials = ChannelCredentials.Create(new SslCredentials(), credentials);
});

HttpClientHelper.cs

using Newtonsoft.Json;
using System.Text;

namespace gRpcWebAPI.Utility
{
    public static class HttpClientHelper
    {
        public static async Task<string> HttpGetAsync(string url, string contentType = "application/json", Dictionary<string, string> headers = null)
        {
            using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
            {
                if (contentType != null)
                    client.DefaultRequestHeaders.Add("ContentType", contentType);
                if (headers != null)
                {
                    foreach (var header in headers)
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
                HttpResponseMessage response = await client.GetAsync(url);
                return await response.Content.ReadAsStringAsync();
            }
        }
    }
}

gRpcController.cs

using Custom.Service;
using Grpc.Core;
using gRpcWebAPI.Utility;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.Security.Cryptography.X509Certificates;
using static Custom.Service.CustomGreeter;

namespace gRpcWebAPI.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class gRpcController : ControllerBase
    {
        CustomGreeterClient _client;
        public gRpcController(CustomGreeterClient client)
        {
            _client = client;
        }

        [HttpGet]
        public async Task<IActionResult> AsyncPlus(int leftNumber, int rightNumber)
        {
            try
            {
                //string token = await HttpClientHelper.HttpGetAsync("https://localhost:7117/api/Authenorization?userName=admin&passWord=666");
                //Metadata jwtCode = new Metadata { { "Authorization", $"Bearer {token}" } };
                NumberResult number = await _client.PlusAsync(new Custom.Service.Number() { LeftNumber = leftNumber, RightNumber = rightNumber });
                return new JsonResult(number);
            }
            catch (Exception ex)
            {
                return new JsonResult(ex.Message);
            }
        }

        [HttpGet]
        public IActionResult Plus(int leftNumber, int rightNumber)
        {
            try
            {
                string token = HttpClientHelper.HttpGet("https://localhost:7117/api/Authenorization?userName=admin&passWord=666");

                Metadata jwtCode = new Metadata { { "Authorization",$"Bearer {token}"} };
                NumberResult number = _client.Plus(new Custom.Service.Number() { LeftNumber = leftNumber, RightNumber = rightNumber },headers: jwtCode);
                return new JsonResult(number);
            }
            catch (Exception ex)
            {
                return new JsonResult(ex.Message);
            }
        }
    }
}

Rpc 与 Restful 区别

  • RPC是以一种调用本地方法的思路来调用远程方法,通过各种RPC框架隐藏调用远程方法的细节,让用户以为调用的就是本地方法。RPC隐藏了底层网络通信的复杂度,让我们更专注于业务逻辑的开发。

  • REST通过HTTP实现,把用户的需求抽象成对资源的操作,用户必须通过HTTP协议的GET、HEAD、POST、PUT、DELETE、TRACE、OPTIONS七种基本操作去和服务器交互。

  • RPC通常是服务器和服务器之间的通信,比如和中间件的通信,MQ、分布式缓存、分布式数据库等等。

  • 而REST通常是面向客户端的(一般是浏览器),他们的使用场景也是不一样的。

posted @ 2023-08-09 21:02  qfccc  阅读(252)  评论(0编辑  收藏  举报