github源码下载地址:https://github.com/yangwohenmai/OpenSource_Learn/tree/master/%E8%BF%9C%E7%A8%8B%E5%AF%B9%E8%B1%A1%E8%B0%83%E7%94%A8Thrift%E6%A1%86%E6%9E%B6%E5%AD%A6%E4%B9%A0
RPC(Remote Procedure Call)远程过程调用,说白了就是客户段远程调用服务端的功能。可以用在微服务架构中。
.Net Core 下的 RPC(远程方法调用)框架有 gRPC、Thrift 等,都支持主流的编程语言。
RPC 虽然效率略高,但是耦合性强,如果兼容性处理不好的话,一旦服务器端接口升级,客户端就要更新,即使是增加一个参数,而 rest 则比较灵活。
最佳实践:对内一些性能要求高的场合用 RPC,对内其他场合以及对外用 Rest。比如 web 服务器和视频转码服务器之间通讯可以用 restful 就够了,转账接口用 RPC 性能会更高一些。
但这个框架也有缺陷,就是一旦其调用的远程服务运行负荷较重,数据返回的速度就会很慢。
把thrift-***.exe解压到磁盘,改名为thrift.exe(用起来方便一些)
首先新建一个txt文本文件,改后缀为thrift。这这时候就得到一个名字为XXX.thrift的文本文件,这个文件中相当于定义了一个客户端和服务端通信的中间件的信息,或者说定义了一个接口的结构。
在这个文件里定义了客户端要调用服务端的所有方法,以及所有通信中要传递的对象。thrift.exe会根据这个文件生成一个RPC项目。
定义单个变量可以用required 和optional 两种类型,required关键字定义的参数是对象中必须定义的参数,optional关键字是对象中可选的参数。
struct 关键字可以定义通讯中需要用的对象,struct User { }就是定义一个User对象的结构,里面写的是对象内部的成员变量。
service 关键字定义的是服务端中可供调用的方法,定义的方法可以有返回值回传给客户端。这里定义成service UserService{ },后面thrift就会生成一个叫UserService的接口类,让你去实现接口中定义的方法。
enum 关键字则是定义枚举类
还有一些细节的定义int, bool, string之类的参数,可以参考文档:https://blog.csdn.net/u011642663/article/details/56015576
以下是我定义的程序的thrift接口文件示例,我的文件名字叫UserService.thrift
:
namespace csharp ThriftTest1.Contract
service UserService{
SaveResult Save(1:User user) //定义了一个返回值为SaveResult的方法函数
User Get(1:i32 id) //定义了一个返回值为User类型的方法函数
list GetAll() //定义了一个返回值为list类型的方法函数
}
//定义了一个SaveResult 枚举类
enum SaveResult {
SUCCESS = 0,
FAILED = 1,
}
//定义了一个User对象
struct User {
1: required i64 Id;//对象中的成员:Id,longint类型
2: required string Name;//对象中的成员:Name,string 类型
3: required i32 Age;//对象中的成员:Age,int类型
4: optional bool IsVIP;//对象中的成员:Age,bool 类型
5: optional string Remark;
}
解压之前下载thrift文件,加压后有一个thrift.exe文件,把thrift.exe文件和刚定义的接口文件XXX.thrift放在同一个文件夹下,启动cmd,在thrift.exe所在文件夹下执行命令
cmd -> thrift.exe -gen csharp UserService.thrift
thrift.exe会根据thrift语法生成相应的C#代码,创建一个类库项目 ThriftTest1.Contract,作为客户端和服务器之间的共用协议,把上一步生成的代码cs文件拷贝到你创建的项目。
在项目中用nuget安装apache-thrift-netcore插件,nuget指令如下:
Install-Package apache-thrift-netcore
此时中间通信层就完成了
在项目中创建一个服务端server项目和一个客户端client项目。两个项目都要引用ThriftTest1.Contract这个项目。
在server项目中创建一个类,这个类继承UserService.Iface这个接口文件,对接口中的方法进行实现。
client项目可以直接在main方法中调用ThriftTest1.Contract的方法。调用之前要定义好发送请求的ip和端口。
server项目中定义好监听的接口即可。
server端方法如下,创建一个Server类,这个类要实现UserService.Iface接口
using RuPeng.ThriftTest1.Contract;
using System;
using System.Collections.Generic;
using System.Text;
namespace ThriftTest1.Server
{
public class UserServiceImpl : UserService.Iface//继承UserService.Iface
{
public User Get(int id)
{
User u = new User();
u.Id = id;
u.Name = "用户" + id;
u.Age = 6;
return u;
}
///
/// 客户端获取数据回传,实现接口方法
///
///
public List GetAll()
{
List list = new List();
list.Add(new User { Id = 1, Name = "yzk", Age = 18, Remark = "hello" });
list.Add(new User { Id = 2, Name = "rupeng", Age = 6 });
return list;
}
///
/// 客户端执行一个操作,返回一个值,看是否成功,实现接口方法
///
///
///
public SaveResult Save(User user)
{
if (user.Name == "ERROR")
return SaveResult.FAILED;
Console.WriteLine($"保存用户,{user.Id},用户名,{user.Name},用户年龄,{user.Age}");
return SaveResult.SUCCESS;
}
}
}
在server端的main函数中定义好要监听的端口8801
using System;
using Thrift.Server;
using Thrift.Transport;
namespace ThriftTest1.Server
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
TServerTransport transport = new TServerSocket(8801);//监听8801端口
var processor = new RuPeng.ThriftTest1.Contract.UserService.Processor(new UserServiceImpl());//设置实现类
TServer server = new TThreadPoolServer(processor, transport);
server.Serve();
}
}
}
在client项目中,定义要发送的IP地址和对应接口8801,调用方法如下
using RuPeng.ThriftTest1.Contract;
using System;
using System.Collections.Generic;
using Thrift.Protocol;
using Thrift.Transport;
namespace ThriftTest1.Client
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
using (TTransport transport = new TSocket("localhost", 8801))
using (TProtocol protocol = new TBinaryProtocol(transport))
using (var clientUser = new UserService.Client(protocol))
{
transport.Open();
User u = clientUser.Get(1);
Console.WriteLine($"{u.Id},{u.Name},{u.Age}");
List list = clientUser.GetAll();
Console.WriteLine($"{list[0].Id},{list[0].Name},{list[0].Age},{list[0].Remark}");
Console.WriteLine($"{list[1].Id},{list[1].Name},{list[1].Age},{list[1].Remark}");
Console.WriteLine("Finish");
SaveResult rs1 = clientUser.Save(new User(112233, "name11try", 112233));
Console.WriteLine("执行结果" + rs1);
SaveResult rs2 = clientUser.Save(new User(332211, "name11tryagain", 332211));
Console.WriteLine("执行结果" + rs2);
SaveResult r3 = clientUser.Save(new User(332211, "ERROR", 332211));
Console.WriteLine("执行结果" + r3);
Console.ReadLine();
}
}
}
}
完成上述部分,客户端就可以和server端通过8801端口实现远程通信了。
源码在文章开端的github地址下载。