一、序言
二、程序集源码、Demo下载
三、安装
Nuget安装RRQMSocket.RPC
即可,具体步骤详看链接博客。
四、创建RPC
在之前【RRQMSocket.RPC】C# 创建多协议、多方式、多语言、多平台RPC框架中,已经提到创建服务方法,但是由于没有解析器
,所以无法对服务进行下一步设置,那么这节就让我们来详细创建服务吧。
4.1 创建服务
在服务器项目
中新建一个类,继承于ServerProvider
类(或实现IServerProvider
),然后在该类中写公共方法,并用RRQMRPC
属性标签标记,如果方法有重载
,需要重新指定函数键
,方法可定义同步执行
,也可以定义异步执行
。
public class MyRpcServer : ServerProvider
{
[RRQMRPC]
public string TestOne(int id)//同步服务
{
return $"若汝棋茗,id={id}";
}
[RRQMRPC("TestOne_Name")]//在重载服务时需要重新设定服务唯一键
public string TestOne(int id, string name)
{
return $"若汝棋茗,Name={name},id={id}";
}
[RRQMRPC]
public void TestOut(out int id)
{
id = 10;
}
[RRQMRPC]
public void Testref(ref int id)
{
id +=1;
}
[RRQMRPC(Async = true)]
public Task<string> AsyncTestOne(int id)//异步服务,尽量不要用Async结尾,不然生成的异步代码方法将出现两个Async
{
return Task.Run(() =>
{
return $"若汝棋茗,id={id}";
});
}
}
然后,创建TcpRpcParser解析器。在解析器中,设置类似TcpService
。
private static IRPCParser CreateRRQMTcpParser(int port)
{
TcpRpcParser tcpRPCParser = new TcpRpcParser();
//声明配置
var config = new TcpRpcParserConfig();
//继承TcpService配置
config.ListenIPHosts = new IPHost[] {new IPHost(port) };//同时监听两个地址
//继承TokenService配置
config.VerifyToken = "123RPC";//连接验证令箭,可实现多租户模式
config.VerifyTimeout = 3 * 1000;//验证3秒超时
//继承TcpRpcParser配置,以实现RPC交互
config.ProxyToken = "RPC";//代理令箭,当客户端获取代理文件,或服务时需验证令箭
//载入配置
tcpRPCParser.Setup(config);
//启动服务
tcpRPCParser.Start();
Console.WriteLine($"TCP解析器添加完成,端口号:{port},VerifyToken={tcpRPCParser.VerifyToken},ProxyToken={tcpRPCParser.ProxyToken}");
return tcpRPCParser;
}
最后添加解析器(添加时需要以键、值方式添加,方便后续查找),然后注册服务
即可。
服务器创建完成。
static void Main(string[] args)
{
//实例化RPCService
RPCService rpcService = new RPCService();
//添加解析器,解析器根据传输协议,序列化方式的不同,调用RPC服务
rpcService.AddRPCParser("tcpRpcParser", CreateRRQMTcpParser(7794));
//注册服务
rpcService.RegisterServer<MyRpcServer>();
//注册当前程序集的所有服务
//rpcService.RegisterAllServer();
//分享代理,代理文件可通过RRQMTool获取。
rpcService.ShareProxy(new IPHost(8848));
Console.WriteLine("RPC服务已启动");
}
五、发现、调用RPC服务
实例化TcpRpcClient,然后依托于内部方法,完成服务的发现与调用。
- 调用Connect,连接到服务器
- 调用DiscoveryService,发现服务
- 调用Invoke,传入服务名称、参数,完成调用
static void Main(string[] args)
{
TcpRpcClient client = new TcpRpcClient();
var config = new TcpRpcClientConfig();
config.RemoteIPHost = new IPHost("127.0.0.1:7794");
client.Setup(config);
try
{
//1.先连接
client.Connect("123RPC");
Console.WriteLine("连接成功");
//2.然后发现服务
MethodItem[] methodItems = client.DiscoveryService("RPC");
Console.WriteLine("服务发现成功");
foreach (var item in methodItems)
{
Console.WriteLine($"服务{item.ServerName}中的‘{item.Method}’可以调用");
}
Console.WriteLine("按任意键调用TestOne");
Console.ReadKey();
//3.调用
string returnString = client.Invoke<string>("TestOne", InvokeOption.WaitInvoke, 10);
Console.WriteLine($"调用成功,结果={returnString}");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
六、调用痛点
RPC的创建与调用其实是一个比较简单的过程,但是要想更加智能化、便捷化的使用,还有很长的一段路要走。
在上述示例中不难发现,RPC的调用实际上是一个非常麻烦且难以维护的过程。麻烦的是调用者必须准确的知晓服务方法名称
,且要传入类型结构相同的数据类型,尤其是后者,麻烦点甚多。而维护,也是疲不胜烦,每当服务端增加、修改、删除某个服务时,客户端也必须同步修改,这给产品更新,增加了不小的难度。
所以,我们想要的RPC绝不能仅仅是这样的…破玩意。
七 、获取代理文件
八、使用
在引入代理代码(dll或者.cs源码)后,即可以使用代理的类(MyRpcServer
)。
使用代理的优点如下:
- 维护简单,客户端只需要重新运行工具即可获得新代理。
- 数据结构生成,假如RPC方法的参数是复杂类型,则会直接生成相同结构的数据。
- 方法说明,客户独很好的得知该方法的注释。
8.1 基础操作
- 调用Connect,连接到服务器
- 调用DiscoveryService,发现服务
- 实例化服务代理,传入IRpcClient实例
- 调用代理实例对应方法
static void Main(string[] args)
{
TcpRpcClient client = new TcpRpcClient();
var config = new TcpRpcClientConfig();
config.RemoteIPHost = new IPHost("127.0.0.1:7794");
client.Setup(config);
try
{
//1.先连接
client.Connect("123RPC");
Console.WriteLine("连接成功");
//2.然后发现服务
MethodItem[] methodItems = client.DiscoveryService("RPC");
Console.WriteLine("服务发现成功");
foreach (var item in methodItems)
{
Console.WriteLine($"服务{item.ServerName}中的‘{item.Method}’可以调用");
}
Console.WriteLine("按任意键调用TestOne");
Console.ReadKey();
//3.实例化服务代理,传入IRpcClient
MyRpcServer myRpcServer = new MyRpcServer(client);
//4.通过MyRpcServer代理类直接调用
string returnString = myRpcServer.TestOne(10);
Console.WriteLine($"调用成功,结果={returnString}");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
8.2 常规用法
实际上,我们在使用RPC时,会再封装一下Client的连接动作
,而且还会拦截异常
,然后记录日志。
- 实例化RpcHandler(也能设置为静态方法)
- 调用代理实例对应方法
所以,建议使用示例代码如下:
public class RpcHandler
{
private TcpRpcClient client;
private IMyRpcServer myRpcServer;
public TcpRpcClient Client => client;
public string IPHost { get => "127.0.0.1:7794"; }
/// <summary>
/// 检验client是否在线
/// 不在线,则重新初始化。
/// </summary>
/// <returns></returns>
public bool CheckOnline()
{
if (string.IsNullOrEmpty(IPHost))
{
return false;
}
try
{
if (client == null)
{
client = new TcpRpcClient();
myRpcServer = new MyRpcServer(client);
TcpRpcClientConfig config = new TcpRpcClientConfig();
config.RemoteIPHost = new IPHost(IPHost);
config.VerifyToken = "FileServer";
client.Setup(config);
try
{
client.Connect();
client.DiscoveryService("FileServerRPC");
}
catch
{
return false;
}
}
if (!client.Online)
{
client.Dispose();
client = null;
myRpcServer = null;
return CheckOnline();
}
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 此处是示例调用
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public string TestOne(int id)
{
try
{
if (CheckOnline())
{
return myRpcServer.TestOne(id);
}
else
{
return null;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);//模拟日志记录
return null;
}
}
}
九、高级调用配置
简单的调用,上面已经演示完成,但是,还不足以应对在实际使用中的所有情况,所以可通过高级配置,进行个性化设置。