一、REST & RPC
微服务之间的接口调用通常包含两个部分,序列化和通信协议。
常见的序列化协议包括json、xml、hession(二进制序列化 + http协议)、protobuf、text、bytes等;
通信比较流行的是http、soap、websockect、TCP,RPC通常基于TCP实现,常用框架例如Dubbo,Netty、Spring Cloud、Thrift、gRpc。
附:TCP、HTTP协议的区别:https://www.jianshu.com/p/f4db4eb065bd
REST:
严格意义上说接口很规范,操作对象即为资源,对资源的四种操作(post、get、put、delete),并且参数都放在URL上,但是不严格的说Http+json、Http+xml,常见的http api都可以称为Rest接口。采用 Http 进行通讯,优点是开放、标准、简单、兼容性升级容易。
RPC:
即我们常说的远程过程调用,就是像调用本地方法一样调用远程方法,通信协议大多采用二进制方式。RPC 虽然效率略高,但是耦合性强,如果兼容性处理不好的话,一旦服务器端接口升级,客户端就要更新,即使是增加一个参数,而 rest 则比较灵活。
最佳实践:
对内一些性能要求高的场景用 RPC,对内其他场景以及对外用 Rest。比如付款接口用 RPC 性能会更高一些。
二、REST实例
在Controller中调用实例:
[Route("api/[controller]")] [ApiController] public class AreaController : ControllerBase { private readonly IBasAreService _AreaService; public AreaController(IBasAreService AreaService) { _AreaService = AreaService; } // GET api/values /// <summary> /// 取所有的省份 /// </summary> /// <returns></returns> [HttpGet] public async Task<IEnumerable<BasAreaDto>> GetAreasAsync() { return await _AreaService.GetAreaByParentCode("-1"); } // GET api/area/H [Route("{fcode}")] [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] public async Task<ActionResult<IEnumerable<BasAreaDto>>> GetAreaByParentCode(string fcode) { if (string.IsNullOrEmpty(fcode)) { return BadRequest(); } var item = await _AreaService.GetAreaByParentCode(fcode); if (item != null) { return item; } return NotFound(); }
……
三、RPC实例
Thrift 【θrɪft】 是apache的,可以构建28种语言包括C#,可以无缝结合、高效服务。也可以使用gRpc,不过从网上的性能对比来看,Thrift性能要优于gRpc 2倍以上。对比参考文章:https://yq.aliyun.com/articles/268867
3.1 Thrift的实例
1)下载Thrigt,地址:http://thrift.apache.org/download
根据网上说的,解压之后修改名为thrift.exe,因为这样方便点,后面要敲命令行的。
2)编写一个xxx.thrift文件,这是他的中间语言
namespace csharp Blake.Test.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; }
执行命令行操作,如果发现录入命令时机器上无法操作,我重新下载一个就好了。
thrift.exe -gen csharp TestRpcService.thrift
3)创建三个项目,一个类库,一个client控制台,一个Service控制台。为了图省事,大家别见笑。
a)服务端的实现代码
public class PaymentServiceImpl : PaymentService.Iface { public TrxnResult Save(TrxnRecord trxn) { Console.WriteLine("Log : TrxnName:{0}, TrxnAmount:{1}, Remark:{2}", trxn.TrxnName, trxn.TrxnAmount, trxn.Remark); return TrxnResult.SUCCESS; } }
class Program { private const int port = 8885; static void Main(string[] args) { Console.WriteLine("[Welcome] PaymentService RPC Server is lanuched..."); TServerTransport transport = new TServerSocket(port); var processor = new PaymentService.Processor(new PaymentServiceImpl()); TServer server = new TThreadedServer(processor, transport); // lanuch server.Serve(); } }
b)客户端代码
static void Main(string[] args) { TestMethod(); Console.ReadLine(); } private static void TestMethod() { using (TTransport transport = new TSocket("localhost", 8885)) { using (TProtocol protocol = new TBinaryProtocol(transport)) { using (var serviceClient = new PaymentService.Client(protocol)) { transport.Open(); TrxnRecord record = new TrxnRecord { TrxnId = 123123123, TrxnName = "Blake", TrxnAmount = 12, TrxnType = "", Remark = "已付款成功!" }; var result = serviceClient.Save(record); Console.WriteLine($"结果为: {result}"); } } } }
c)执行结果
因为我用的net core3.1,一开始应用包除了问题,后来搜到apache-thrift-netcore。
四、总结
该文章主要是介绍一个RPC和REST的区别和应用场景,同时简单的介绍一下Thrift的使用。这样就可以从代码看出REST的简单好多,没有依赖。