edisonchou https://mp.weixin.qq.com/s/-XZXqXawR-NxJMPCeiNsmg

.NET Core微服务之服务间的调用方式(REST and RPC)

Edison Zhou  edisonchou  前天

Tip: 此篇已加入.NET Core微服务基础系列文章索引

一、REST or RPC ?

1.1 REST & RPC

  微服务之间的接口调用通常包含两个部分,序列化和通信协议。常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等;通信比较流行的是http、soap、websockect,RPC通常基于TCP实现,常用框架例如dubbo,netty、mina、thrift。

  REST:严格意义上说接口很规范,操作对象即为资源,对资源的四种操作(post、get、put、delete),并且参数都放在URL上,但是不严格的说Http+json、Http+xml,常见的http api都可以称为Rest接口。

  RPC:即我们常说的远程过程调用,就是像调用本地方法一样调用远程方法,通信协议大多采用二进制方式。

1.2 HTTP vs 高性能二进制协议

  HTTP相对更规范,更标准,更通用,无论哪种语言都支持HTTP协议。如果你是对外开放API,例如开放平台,外部的编程语言多种多样,你无法拒绝对每种语言的支持,相应的,如果采用HTTP,无疑在你实现SDK之前,支持了所有语言,所以,现在开源中间件,基本最先支持的几个协议都包含RESTful。

  RPC协议性能要高的多,例如Protobuf、Thrift、Kyro等,(如果算上序列化)吞吐量大概能达到http的二倍。响应时间也更为出色。千万不要小看这点性能损耗,公认的,微服务做的比较好的,例如,netflix、阿里,曾经都传出过为了提升性能而合并服务。如果是交付型的项目,性能更为重要,因为你卖给客户往往靠的就是性能上微弱的优势。

  所以,最佳实践一般是对外REST,对内RPC,但是追求极致的性能会消耗很多额外的成本,所以一般情况下对内一般也REST,但对于个别性能要求较高的接口使用RPC。

二、案例结构

  这里假设有两个服务,一个ClinetService和一个PaymentService,其中PaymentService有两部分,一部分是基于REST风格的WebApi部分,它主要是负责一些对性能没有要求的查询服务,另一部分是基于TCP的RPC Server,它主要是负责一些对性能要求高的服务,比如支付和支出等涉及到钱的接口。假设User在消费ClientService时需要调用PaymentService根据客户账户获取Payment History(走REST)以及进行交易事务操作(走RPC)。

三、REST调用

3.1 一个好用的REST Client : WebApiClient

  使用过Java Feign Client的人都知道,一个好的声明式REST客户端可以帮我们省不少力。在.NET下,园子里的大大老九就写了一款类似于Feign Client的REST Client:WebApiClient。WebApiClient是开源在github上的一个httpClient客户端库,内部基于HttpClient开发,是一个只需要定义C#接口(interface),并打上相关特性,即可异步调用http-api的框架 ,支持.net framework4.5+、netcoreapp2.0和netstandard2.0。它的GitHub地址是:https://github.com/dotnetcore/WebApiClient

  如何安装?

NuGet>Install-Package WebApiClient-JIT  

3.2 使用实例:走API Gateway

  Step1.定义HTTP接口

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    [HttpHost("http://yourgateway:5000")]    public interface IPaymentWebApi: IHttpApi
   {        // GET api/paymentservice/history/edisonzhou        // Return 原始string内容
[HttpGet("/api/paymentservice/history/{account}")]
       ITask<IList<string>> GetPaymentHistoryByAccountAsync(string account);
   }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  这里需要注意的是,由于我们要走API网关,所以这里定义的HttpHost地址是一个假的,后面具体调用时会覆盖掉,当然你也可以直接把地址写在这里,不过我更倾向于写到配置文件中,然后把这里的HttpHost设置注释掉。

  Step2.在Controller中即可异步调用:

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    [Route("api/[controller]")]    public class PaymentController : Controller
   {        private readonly string gatewayUrl;public PaymentController(IConfiguration _configuration)
       {
           gatewayUrl = _configuration["Gateway:Uri"];
       }        [HttpGet("{account}")]        public async Task<IList<string>> Get(string account)
       {            using (var client = HttpApiClient.Create<IPaymentWebApi>(gatewayUrl))
           {                var historyList = await client.GetPaymentHistoryByAccountAsync(account);                // other business logic code here                // ......
return historyList;
           }
       }
  }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  当然你也可以在Service启动时注入一个单例的IPaymentServiceWebApi实例,然后直接在各个Controller中直接使用,这样更加类似于Feign Client的用法:

  (1)StartUp类注入

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    public void ConfigureServices(IServiceCollection services)
   {        // IoC - WebApiClient
services.AddSingleton(HttpApiClient.Create<IPaymentServiceWebApi>(Configuration["PaymentService:Url"]));    }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  (2)Controller中直接使用

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    [HttpPost]    public async Task<string> Post([FromBody]ModelType model, [FromServices]IPaymentServiceWebApi restClient)
   {
       ......        var result = await restClient.Save(model);
       ......
   }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  这里PaymentService的实现很简单,就是返回了一个String集合:

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    // GET api/history/{account}
[HttpGet("{account}")]    public IList<string> Get(string account)
   {        // some database logic        // ......
IList<string> historyList = new List<string>
       {            "2018-06-10,10000RMB,Chengdu",            "2018-06-11,11000RMB,Chengdu",            "2018-06-12,12000RMB,Beijing",            "2018-06-13,10030RMB,Chengdu",            "2018-06-20,10400RMB,HongKong"
       };        return historyList;
   }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  最终调用结果如下:

  

3.3 使用实例:直接访问具体服务

  在服务众多,且单个服务就部署了多个实例的情况下,我们可以通过API网关进行中转,但是当部分场景我们不需要通过API网关进行中转的时候,比如:性能要求较高,负载压力较小单个实例足够等,我们可以直接与要通信的服务进行联接,也就不用从API网关绕一圈。

  Step1.改一下HTTP接口:

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    [HttpHost("http://paymentservice:8880")]    public interface IPaymentDirectWebApi: IHttpApi
   {        // GET api/paymentservice/history/edisonzhou        // Return 原始string内容
[HttpGet("/api/history/{account}")]
       ITask<IList<string>> GetPaymentHistoryByAccountAsync(string account);
   }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  同理,这里的HttpHost也是后面需要被覆盖的,原因是我们将其配置到了配置文件中。

  Step2.改一下调用代码:

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    [Route("api/[controller]")]    public class PaymentController : Controller
   {        private readonly string gatewayUrl;        private readonly string paymentServiceUrl;        public PaymentController(IConfiguration _configuration)
       {
           gatewayUrl = _configuration["Gateway:Uri"];
           paymentServiceUrl = _configuration["PaymentService:Uri"];
       }        [HttpGet("{account}")]        public async Task<IList<string>> Get(string account)
       {            #region v2 directly call PaymentService using (var client = HttpApiClient.Create<IPaymentDirectWebApi>(paymentServiceUrl))
           {                var historyList = await client.GetPaymentHistoryByAccountAsync(account);                // other business logic code here                // ......
return historyList;
           }            #endregion
       }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  最终调用结果如下:

  

四、RPC调用

4.1 Thrift简介

  

  Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。

  当然,还有gRPC也可以选择,不过从网上的性能测试来看,Thrift性能应该优于gRPC 2倍以上,但是gRPC的文档方面要比Thrift友好很多。

4.2 Thrift的使用

  (1)下载Thrift (这里选择Windows版)

  

  下载完成后解压,这里我将其改名为thrift.exe(去掉了版本号),一会在命令行敲起来更方便一点。

  (2)编写一个PaymentService.thrift,这是一个IDL中间语言

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

namespace csharp Manulife.DNC.MSAD.Contracts

service PaymentService {
   TrxnResult Save(1:TrxnRecord trxn)
}enum TrxnResult {
   SUCCESS = 0,
   FAILED = 1,
}struct TrxnRecord {
   1: required i64 TrxnId;
   2: required string TrxnName;
   3: required i32 TrxnAmount;
   4: required string TrxnType;
   5: optional string Remark;
}

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  (3)根据thrift语法规则生成C#代码

cmd>thrift.exe -gen csharp PaymentService.thrift

  

  (4)创建一个Contracts类库项目,将生成的C#代码放进去

  

4.3 增加RPC Server

  (1)新增一个控制台项目,作为我们的Payment Service RPC Server,并引用Contracts类库项目

  

  (2)引入thrift-netcore包:

NuGet>Install-Package apache-thrift-netcore

  (3)加入一个新增的PaymentService实现类

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    public class PaymentServiceImpl : Manulife.DNC.MSAD.Contracts.PaymentService.Iface
   {        public TrxnResult Save(TrxnRecord trxn)
       {            // some business logic here            //Thread.Sleep(1000 * 1);
Console.WriteLine("Log : TrxnName:{0}, TrxnAmount:{1}, Remark:{2}", trxn.TrxnName, trxn.TrxnAmount, trxn.Remark);            return TrxnResult.SUCCESS;
       }
   }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  这里输出日志仅仅是为了测试。

  (4)编写启动RPC Server的主程序

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    public class Program
   {        private const int port = 8885;        public static void Main(string[] args)
       {
           Console.WriteLine("[Welcome] PaymentService RPC Server is lanuched...");
           TServerTransport transport = new TServerSocket(port);            var processor = new Manulife.DNC.MSAD.Contracts.PaymentService.Processor(new PaymentServiceImpl());
           TServer server = new TThreadedServer(processor, transport);            // lanuch            server.Serve();
       }
   }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  (5)如果是多个服务实现的话,也可以如下这样启动:

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    public static void Main(string[] args)
   {
       Console.WriteLine("[Welcome] PaymentService RPC Server is lanuched...");
       TServerTransport transport = new TServerSocket(port);        var processor1 = new Manulife.DNC.MSAD.Contracts.PaymentService.Processor(new PaymentServiceImpl());        var processor2 = new Manulife.DNC.MSAD.Contracts.PayoutService.Processor(new PayoutServiceImpl());        var processorMulti = new Thrift.Protocol.TMultiplexedProcessor();
       processorMulti.RegisterProcessor("Service1", processor1);
       processorMulti.RegisterProcessor("Service2", processor2);
       TServer server = new TThreadedServer(processorMulti, transport);        // lanuch        server.Serve();
   }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

4.4 调用RPC

  在ClientService中也引入apache-thrift-netcore包,然后在调用的地方修改如下:

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

    [HttpPost]    public string Post([FromBody]TrxnRecordDTO trxnRecordDto)
   {        // RPC - use Thrift
using (TTransport transport = new TSocket(
           configuration["PaymentService:RpcIP"],
           Convert.ToInt32(configuration["PaymentService:RpcPort"])))
       {            using (TProtocol protocol = new TBinaryProtocol(transport))
           {                using (var serviceClient = new PaymentService.Client(protocol))
               {
                   transport.Open();
                   TrxnRecord record = new TrxnRecord
                   {
                       TrxnId = GenerateTrxnId(),
                       TrxnName = trxnRecordDto.TrxnName,
                       TrxnAmount = trxnRecordDto.TrxnAmount,
                       TrxnType = trxnRecordDto.TrxnType,
                       Remark = trxnRecordDto.Remark
                   };                    var result = serviceClient.Save(record);                    return Convert.ToInt32(result) == 0 ? "Trxn Success" : "Trxn Failed";
               }
           }
       }
   }    private long GenerateTrxnId()
   {        return 10000001;
   }

aaarticlea/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="" data-ratio="1" data-src="https://mmbiz.qpic.cn/mmbiz_png/fCpd1cf8iacYj7ick27pRuJPKmHDpDV57ugruP6xUicj1ZwIc3bbDj0TppXpjV3HGSSL4tibmXxZ2L9uib0jFl1TNxw/640?wx_fmt=gif" data-type="gif" data-w="20" />

  最终测试结果如下:

  

五、小结

  本篇简单的介绍了下微服务架构下服务之间调用的两种常用方式:REST与RPC,另外前面介绍的基于消息队列的发布/订阅模式也是服务通信的方式之一。本篇基于WebApiClient这个开源库介绍了如何进行声明式的REST调用,以及Thrift这个RPC框架介绍了如何进行RPC的通信,最后通过一个小例子来结尾。最后,服务调用的最佳实践一般是对外REST,对内RPC,但是追求极致的性能会消耗很多额外的成本,所以一般情况下对内一般也REST,但对于个别性能要求较高的接口使用RPC。

参考资料

远方的行者,《微服务 RPC和REST》

杨中科,《.NET Core微服务课程:Thrift高效通讯》

醉眼识朦胧,《Thrift入门初探--thrift安装及java入门实例》

focus-lei,《.net core下使用Thrift》

宝哥在路上,《Thrift性能测试与分析》

【序列化和通信协议】

微服务之间的接口调用通常包含两个部分,序列化和通信协议。常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等;通信比较流行的是http、soap、websockect,RPC通常基于TCP实现,常用框架例如dubbo,netty、mina、thrift。

REST RPC HTTP vs 高性能二进制协议 序列化和通信协议的更多相关文章

  1. Netty实现高性能RPC服务器优化篇之消息序列化

    在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...

  2. 开源!一款功能强大的高性能二进制序列化器Bssom.Net

    好久没更新博客了,我开源了一款高性能的二进制序列化器Bssom.Net和新颖的二进制协议Bssom,欢迎大家Star,欢迎参与项目贡献! Net开源技术交流群 976304396,禁止水,只能讨论技术 ...

  3. 轻量级通信引擎StriveEngine —— C/S通信demo(2) —— 使用二进制协议 (附源码)

    在网络上,交互的双方基于TCP或UDP进行通信,通信协议的格式通常分为两类:文本消息.二进制消息. 文本协议相对简单,通常使用一个特殊的标记符作为一个消息的结束. 二进制协议,通常是由消息头(Head ...

  4. [.net 面向对象程序设计进阶] (9) 序列化(Serialization) (一) 二进制流序列化

    [.net 面向对象程序设计进阶]  (9)  序列化(Serialization) (一) 二进制流序列化 本节导读: 在.NET编程中,经常面向对象处理完以后要转换成另一种格式传输或存储,这种将对 ...

  5. HTTP与私有二进制协议之间的区别

    简单的文本协议.二进制协议 写网络程序躲不过协议,协议其实就是定义了消息的格式,以及消息是如何交换的.协议可简单可复杂,复杂精密如TCP协议,简单奔放如HTTP的协议.这里将我所接触到的协议稍微总结一 ...

  6. C#轻量级通通讯组件StriveEngine —— C/S通信开源demo(2) —— 使用二进制协议 (附源码)

    前段时间,有几个研究ESFramework通信框架的朋友对我说,ESFramework有点庞大,对于他们目前的项目来说有点“杀鸡用牛刀”的意思,因为他们的项目不需要文件传送.不需要P2P.不存在好友关 ...

  7. 使用Netty实现通用二进制协议的高效数据传输

    Netty是一个高性能的NIO通信框架,提供异步的.事件驱动的网络编程模型.使用Netty可以方便用户开发各种常用协议的网络程序.例如:TCP.UDP.HTTP等等. Netty的最新版本是3.2.7 ...

  8. 二进制协议gob及msgpack介绍

    本文主要介绍二进制协议gob及msgpack的基本使用. 最近在写一个gin框架的session服务时遇到了一个问题,Go语言中的json包在序列化空接口存放的数字类型(整型.浮点型等)都序列化成fl ...

  9. 二进制协议gob和msgpack介绍

    二进制协议gob和msgpack介绍 本文主要介绍二进制协议gob及msgpack的基本使用. 最近在写一个gin框架的session服务时遇到了一个问题,Go语言中的json包在序列化空接口存放的数 ...

随机推荐

  1. 如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2'

    如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2' 大家在做真机调试的时候,或许会遇到这样的问题,那如何 ...

  2. 如果您无法使用Docker的存储库来安装Docker CE

    如果您无法使用Docker的存储库来安装Docker CE,则可以下载.deb适用于您的发行版的 文件并手动安装.每次要升级Docker CE时都需要下载新文件. 转到https://download ...

  3. BZOJ 1879 [Sdoi2009]Bill的挑战 ——状压DP

    本来打算好好写写SDOI的DP题目,但是忒难了, 太难了,就写的这三道题仿佛是可做的. 生在弱省真是兴奋. 这题目直接状压,f[i][j]表示匹配到i,状态集合为j的方案数,然后递推即可. #incl ...

  4. 启动uwsgi报错error while loading shared libraries: libpcre.so.1:

    启动uwsgi时候报错: [root@ richie]# /usr/bin/uwsgi --ini /usr/local/nginx/conf/uwsgi.ini /usr/bin/uwsgi: er ...

  5. Merge into语句用法及其效率问题

    Merge into语句用法及其效率问题 /*Merge into 详细介绍MERGE语句用来合并UPDATE和INSERT语句.通过MERGE语句,根据一张表或子查询的连接条件对另外一张表进行查询, ...

  6. 家用电脑架服务器提供web

    要搞一个可以对外的web服务,需要服务器,域名.这些都需要money,但有时,我们只是想自己可以在外面访问,或是提供给朋友看自己的网站有多牛.这时使用家用电脑配置一个可以提供web的服务器,就显得很必 ...

  7. Redis集群模式配置

    redis集群部署安装: https://blog.csdn.net/huwh_/article/details/79242625 https://www.cnblogs.com/mafly/p/re ...

  8. 用GDB远程调试android native程序

    上次写了几个native程序,想着如何调试,经过一阵子搜索和测试,终于完成了.有几个关键点: 1 gdb和gdbserver 因为这两个需要配套,建议使用同一个ndk下面的gdb和gdbserver ...

  9. js-判断当前页面是否在微信浏览器中打开

    方案一:推荐 var ua = navigator.userAgent.toLowerCase(); var isWinxin = ua.indexOf('micromessenger') != -1 ...

  10. AbstractQueuedSynchronizer 队列同步器源码分析

    AbstractQueuedSynchronizer 队列同步器(AQS) 队列同步器 (AQS), 是用来构建锁或其他同步组件的基础框架,它通过使用 int 变量表示同步状态,通过内置的 FIFO ...