跳到主要内容
版本:1.3.9

创建rpc服务

一、说明

RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。

过程是什么? 过程就是业务处理、计算任务,更直白的说,就是程序,就是想调用本地方法一样调用远程的过程。

TouchRpc支持服务器与客户端互相调用,也支持客户端之间相互调用。

二、定义服务

  1. 被调用端中新建一个类名为MyRpcServer
  2. 继承于RpcServer类、或实现IRpcServer。亦或者将服务器声明为瞬时生命的服务,继承TransientRpcServer、或ITransientRpcServer。
  3. 在该类中写公共方法,并用TouchRpc属性标签标记。
public class MyRpcServer : RpcServer
{
[Description("登录")]//服务描述,在生成代理时,会变成注释。
[TouchRpc("Login")]//服务注册的函数键,此处为显式指定。默认不传参的时候,为该函数类全名+方法名的全小写。
public bool Login(string account,string password)
{
if (account=="123"&&password=="abc")
{
return true;
}

return false;
}
}
public class MyRpcServer : TransientRpcServer
{
[Description("登录")]//服务描述,在生成代理时,会变成注释。
[TouchRpc("Login")]//服务注册的函数键,此处为显式指定。默认不传参的时候,为该函数类全名+方法名的全小写。
public bool Login(string account,string password)
{
if (account=="123"&&password=="abc")
{
return true;
}

return false;
}
}
提示

瞬时生命的服务,最大的特点就是,每个请求,都会创建一个新的服务类对象。然后可以通过this.CallContext直接访问当前的调用上下文。

三、启动Rpc服务器

以下仅示例基于Tcp协议TouchRpc。其他协议的服务器请看创建TouchRpc服务器

var service =new  TcpTouchRpcService();
TouchSocketConfig config= new TouchSocketConfig()//配置
.SetListenIPHosts(new IPHost[] { new IPHost(7789) })
.ConfigureRpcStore(a=>
{
a.RegisterServer<MyRpcServer>();//注册服务
})
.SetVerifyToken("TouchRpc");

service.Setup(config)
.Start();

service.Logger.Info($"{service.GetType().Name}已启动");

四、调用Rpc

4.1 直接调用

直接调用,则是不使用任何代理,使用字符串参数直接Call Rpc,使用比较简单。

下列以TcpTouchRpcClient为例,其他客户端一模一样。

TcpTouchRpcClient client = new TcpTouchRpcClient();
client.Setup(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7789")
.SetVerifyToken("TouchRpc"));
client.Connect();

//直接调用时,第一个参数为调用键
//第二个参数为调用配置参数,可设置调用超时时间,取消调用等功能。示例中使用的预设,实际上可以自行new InvokeOption();
//后续参数为调用参数。
//泛型为返回值类型。
bool result = client.Invoke<bool>("Login", InvokeOption.WaitInvoke, 123, "abc");

4.2、代理调用

代理调用的便捷在于,客户端不用再知道哪些服务可调,也不用再纠结调用的参数类型正不正确,因为这些,代理工具都会替你做好。

详细步骤:

  1. 生成代理文件
  2. 将生成的cs文件添加到调用端一起编译。
备注

以上示例,会生成下列代理代码。

【生成的代理】

using TouchSocket.Rpc;
using System.Threading.Tasks;
namespace RpcProxy
{
public interface IMyRpcServer : IRemoteServer
{
///<summary>
///登录
///</summary>
/// <exception cref="System.TimeoutException">调用超时</exception>
/// <exception cref="TouchSocket.Rpc.RpcInvokeException">Rpc调用异常</exception>
/// <exception cref="System.Exception">其他异常</exception>
System.Boolean Login(System.String account, System.String password, IInvokeOption invokeOption = default);
///<summary>
///登录
///</summary>
/// <exception cref="System.TimeoutException">调用超时</exception>
/// <exception cref="TouchSocket.Rpc.RpcInvokeException">Rpc调用异常</exception>
/// <exception cref="System.Exception">其他异常</exception>
Task<System.Boolean> LoginAsync(System.String account, System.String password, IInvokeOption invokeOption = default);

}
public class MyRpcServer : IMyRpcServer
{
public MyRpcServer(IRpcClient client)
{
this.Client = client;
}
public IRpcClient Client { get; private set; }
///<summary>
///登录
///</summary>
/// <exception cref="System.TimeoutException">调用超时</exception>
/// <exception cref="TouchSocket.Rpc.RpcInvokeException">Rpc调用异常</exception>
/// <exception cref="System.Exception">其他异常</exception>
public System.Boolean Login(System.String account, System.String password, IInvokeOption invokeOption = default)
{
if (Client == null)
{
throw new RpcException("IRpcClient为空,请先初始化或者进行赋值");
}
if (Client.TryCanInvoke?.Invoke(Client) == false)
{
throw new RpcException("Rpc无法执行。");
}
object[] parameters = new object[] { account, password };
System.Boolean returnData = Client.Invoke<System.Boolean>("Login", invokeOption, parameters);
return returnData;
}
///<summary>
///登录
///</summary>
public Task<System.Boolean> LoginAsync(System.String account, System.String password, IInvokeOption invokeOption = default)
{
if (Client == null)
{
throw new RpcException("IRpcClient为空,请先初始化或者进行赋值");
}
if (Client.TryCanInvoke?.Invoke(Client) == false)
{
throw new RpcException("Rpc无法执行。");
}
object[] parameters = new object[] { account, password };
return Client.InvokeAsync<System.Boolean>("Login", invokeOption, parameters);
}
}
public static class MyRpcServerExtensions
{
///<summary>
///登录
///</summary>
/// <exception cref="System.TimeoutException">调用超时</exception>
/// <exception cref="TouchSocket.Rpc.RpcInvokeException">Rpc调用异常</exception>
/// <exception cref="System.Exception">其他异常</exception>
public static System.Boolean Login<TClient>(this TClient client, System.String account, System.String password, IInvokeOption invokeOption = default) where TClient :
TouchSocket.Rpc.IRpcClient
{
if (client.TryCanInvoke?.Invoke(client) == false)
{
throw new RpcException("Rpc无法执行。");
}
object[] parameters = new object[] { account, password };
System.Boolean returnData = client.Invoke<System.Boolean>("Login", invokeOption, parameters);
return returnData;
}
///<summary>
///登录
///</summary>
public static Task<System.Boolean> LoginAsync<TClient>(this TClient client, System.String account, System.String password, IInvokeOption invokeOption = default) where TClient :
TouchSocket.Rpc.IRpcClient
{
if (client.TryCanInvoke?.Invoke(client) == false)
{
throw new RpcException("Rpc无法执行。");
}
object[] parameters = new object[] { account, password };
return client.InvokeAsync<System.Boolean>("Login", invokeOption, parameters);
}
}
}

使用代理扩展直接调用。

TcpTouchRpcClient client = new TcpTouchRpcClient();
client.Setup(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7789")
.SetVerifyToken("TouchRpc"));
client.Connect();

bool result = client.Login(123, "abc");//Login是扩展方法。可能需要额外添加命名空间。

五、反向Rpc

一般的rpc服务都是客户端发起,服务器响应。但是有时候也需要服务器主动调用客户端,所以需要反向rpc。

5.1 定义、发布反向RPC服务

实际上,所有的Rpc客户端(TcpTouchRpcClientUdpTouchRpc(不区分客户端)、HttpTouchRpcClientWSTouchRpcClient)也实现了IRpcParser接口,这意味着反向RPC其实也是RPC,所以,所有操作一模一样。因为当客户端和服务器建立连接以后,就不再区分谁是客户端,谁是服务器了。只关心,谁能提供服务,谁在调用服务

下列就以简单的示例下,由客户端声明服务,服务器调用服务。

具体步骤:

  1. 客户端项目中定义服务
  2. TouchRpc标记
public class ReverseCallbackServer : RpcServer
{
[TouchRpc]
public string SayHello(string name)
{
return $"{name},hi";
}
}

【客户端发布服务】 发布服务,实际上是让TcpTouchRpcClient也拥有提供RPC的能力。

TcpTouchRpcClient client = new TcpTouchRpcClient();
client.Setup(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7789")
.ConfigureContainer(a =>
{
a.AddConsoleLogger();
a.AddFileLogger();
})
.ConfigureRpcStore(a =>
{
a.RegisterServer<ReverseCallbackServer>();
})
.SetVerifyToken("TouchRpc"));
client.Connect();

5.2 调用反向RPC

服务器回调客户端,最终必须通过服务器辅助类客户端(ISocketClient的派生类),以TcpTouchRpcService为例,其辅助客户端为TcpTouchRpcSocketClient。

因为,TcpTouchRpcSocketClient已实现IRpcClient接口,意味着,反向RPC也可以使用代理调用。所有用法和RPC一致。

下列示例以TcpTouchRpcSocketClient为例,其余一致。

提示

反向RPC也可以使用代理调用。所有用法和RPC一致。

5.2.1 通过服务器直接获取

可以获取所有终端。

foreach (var item in tcpTouchRpcService.GetClients())
{
client.Logger.Info(item.Invoke<string>("ReverseRpcConsoleApp.ReverseCallbackServer.SayHello".ToLower(), InvokeOption.WaitInvoke, "张三"));
}

也可以先筛选ID,然后再调用。

string id = tcpTouchRpcService.GetIDs().FirstOrDefault(a => a.Equals("特定id"));
if (tcpTouchRpcService.TryGetSocketClient(id, out var rpcSocketClient))
{
rpcSocketClient.Invoke<string>("ReverseRpcConsoleApp.ReverseCallbackServer.SayHello".ToLower(), InvokeOption.WaitInvoke, "张三");
}

5.2.2 通过调用上下文获取

具体步骤

  1. 设置调用上下文
  2. 上下文的Caller,即为服务器辅助类终端,进行强转即可。

六、客户端互Call RPC

除了正向RPC,反向RPC,TouchRpc还支持客户端之间互Call RPC。服务的定义与Rpc一样。

6.1 互Call RPC

客户端A调用客户端B的方法,需要知道对方的ID。和方法名。然后使用下列函数调用即可。

提示

互Call RPC也支持调用上下文。

服务器注意

客户端互Call的时候,每个请求,都需要服务同意路由,才可以被转发。所以服务器需要做一些允许操作。

internal class MyTouchRpcPlugin : TouchRpcPluginBase
{
protected override void OnRouting(ITouchRpc client, PackageRouterEventArgs e)
{
if (e.RouterType== RouteType.Rpc)
{
e.IsPermitOperation = true;
}
base.OnRouting(client, e);
}
}