一、序言
本节须知
在学习本节之前,您必须熟悉RRQM中的TcpRpcParser 解析器与TcpRpcClient 客户端(或其派生类,例如文件传输)的创建,如果您不熟悉,请在下列链接中了解。
二、程序集源码、Demo下载
2.1 源码位置
RRQMSocket
2.2 Demo位置
RRQMBox
三、安装
安装RRQMSocket.RPC 即可,具体步骤详看链接博客。
VS、Unity安装和使用Nuget包
四、序列化选择
从下图(图片来源网络)可以看出,序列化是RPC中至关重要的一个环节,可以说,序列化的优劣,会很大程度的影响RPC调用性能。
4.1 RRQM支持的序列化
在RRQMRPC中,内置了四种序列化方式,分别为RRQMBinary 、SystemBinary 、Json 、Xml 。这四种方式的特点,就是其序列化的特点。
| RRQMBinary | SystemBinary | Json | Xml |
---|
特点 | 序列化方式速度快,数据量小,但是兼容的数据格式也比较有限。仅支持基础类型、自定义实体类、数组、List、字典 | 保真度高,支持接口,抽象类,object,泛型等类型的序列化,但是需要Serializable的标签,且必须是同一类型(或重写映射图谱) | 兼容性好,可读性强,但是受字符串影响,性能不出众,且数据量受限制 | 兼容性一般,可读性强,同样受字符串影响,性能不出众,且数据量受限制 |
4.2 使用预设序列化
在RRQMRPC中,选择序列化是非常简单的,且序列化方式完全由调用端 决定。
在实际的调用中,通过InvokeOption 的参数指定。 实际上,只需要传入相关参数即可。
InvokeOption invokeOption = new InvokeOption();
invokeOption.SerializationType = RRQMCore.Serialization.SerializationType.RRQMBinary;
string returnString = client.Invoke<string>("TestOne", invokeOption, "10");
4.3 自定义序列化
a).定义 想要实现自定义序列化,必须通过重写序列化选择器,实现SerializeParameter 和DeserializeParameter 函数。如果还想留用预设序列化,请按下代码示例即可。
public class MySerializationSelector: SerializationSelector
{
public override object DeserializeParameter(SerializationType serializationType, byte[] parameterBytes, Type parameterType)
{
if (parameterBytes == null)
{
return parameterType.GetDefault();
}
switch (serializationType)
{
case SerializationType.RRQMBinary:
{
return SerializeConvert.RRQMBinaryDeserialize(parameterBytes, 0, parameterType);
}
case SerializationType.SystemBinary:
{
return SerializeConvert.BinaryDeserialize(parameterBytes, 0, parameterBytes.Length);
}
case SerializationType.Json:
{
return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(parameterBytes), parameterType);
}
case SerializationType.Xml:
{
return SerializeConvert.XmlDeserializeFromBytes(parameterBytes, parameterType);
}
case (SerializationType)4:
{
return default;
}
default:
throw new RRQMRPCException("未指定的反序列化方式");
}
}
public override byte[] SerializeParameter(SerializationType serializationType, object parameter)
{
if (parameter == null)
{
return null;
}
switch (serializationType)
{
case SerializationType.RRQMBinary:
{
return SerializeConvert.RRQMBinarySerialize(parameter, true);
}
case SerializationType.SystemBinary:
{
return SerializeConvert.BinarySerialize(parameter);
}
case SerializationType.Json:
{
return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(parameter));
}
case SerializationType.Xml:
{
return SerializeConvert.XmlSerializeToBytes(parameter);
}
case (SerializationType)4:
{
return default;
}
default:
throw new RRQMRPCException("未指定的序列化方式");
}
}
}
b).使用 首先在解析器 和客户端 配置中赋值解析器。
然后,因为赋值时是SerializationType 的枚举类型,所以执行强制类型转换即可。
InvokeOption invokeOption = new InvokeOption();
invokeOption.SerializationType = (RRQMCore.Serialization.SerializationType)4;
五、调用反馈类型
5.1 类型说明
RRQMRPC的调用状态有三种状态可选,分别为:OnlySend 、WaitSend 、WaitInvoke 。区别是:
OnlySend | WaitSend | WaitInvoke |
---|
仅发送RPC请求,在TCP底层协议下,能保证发送成功,但是不反馈服务器任何状态,也不会取得返回值、异常等信息。在UDP底层协议下,不保证发送成功,仅仅是具有请求动作而已。 | 发送RPC请求,并且等待收到状态返回,能保证RPC请求顺利到达服务,但是不能得知RPC服务是否成功执行,也不会取得返回值、异常等信息 | 发送RPC请求,且返回所有信息,包括是否成功调用,执行后的返回值或异常等信息。 |
5.2 使用
同样的,在InvokeOption中可以直接赋值使用。
InvokeOption invokeOption = new InvokeOption();
invokeOption.FeedbackType = FeedbackType.WaitInvoke;
string returnString = client.Invoke<string>("TestOne", invokeOption, "10");
六、调用超时设置
调用RPC,不能无限制等待,必须要有计时器,或者任务取消的功能。
6.1 计时器设置
直接对InvokeOption 的Timeout 属性赋值即可,单位为毫秒 。
InvokeOption invokeOption = new InvokeOption();
invokeOption.Timeout = 1000 * 10;
string returnString = client.Invoke<string>("TestOne", invokeOption, "10");
6.2 任务取消
在RPC调用时,计时器是一个好的选择,但是还不够完美,有时候我们希望能手动终结某个调用任务。这时候,计时器就不堪重任,需要能主动取消任务的功能。熟悉.net的小伙伴都知道,CancellationToken是具备这个功能的。同样的,只需要对InvokeOption 的CancellationToken 赋值即可。
InvokeOption invokeOption = new InvokeOption();
CancellationTokenSource tokenSource = new CancellationTokenSource();
invokeOption.CancellationToken = tokenSource.Token;
string returnString = client.Invoke<string>("TestOne", invokeOption, "10");
七、调用服务实例
调用的服务,必须由承载服务的实例 参与完成,但是,实例的不同,其调用结果大相径庭。
例如:在TestInstanceRpcServer服务中定义了Count 属性和Increment 函数。每次调用Increment ,Count 会递增,然后返回Count 值。如果某个客户端连续调用两次,会得到什么值呢?亦或者,某两个客户端,各调用一次,会得到什么值呢?
想必你心里已经有了答案,但是,我要告诉你,你都答案是错误的!Error!Error!
因为,答案既是你想的,也不是你想的。
public class TestInstanceRpcServer : ServerProvider
{
public int Count { get; set; }
[RRQMRPC]
public int Increment()
{
return ++Count;
}
}
7.1 服务实例类型
实际上,RRQMRPC中,支持三种服务实例类型,分别为GlobalInstance 、CustomInstance 、NewInstance ,三者的区别如下。
GlobalInstance | CustomInstance | NewInstance |
---|
全局实例,即所有客户端,调用一个实例,在上述案例中,Count值会一直递增。 | 用户拥有实例,即一个RPC连接调用一个实例,在上述案例中,Count会在同一客户端调用时递增,在不同客户端中重新计数。 | 新实例,即每次调用,都使用新实例,在上述案例中,获得的Count会一直等于1。 |
7.2 服务实例类型选择
服务类型,实际实际上还是由InvokeOption设置。
InvokeOption invokeOption = new InvokeOption();
invokeOption.InvokeType = RRQMSocket.RPC.InvokeType.CustomInstance;
string returnString = client.Invoke<string>("TestOne", invokeOption, "10");
八、获取调用终端
RPC服务是无状态的,即只知道当前服务被调用,但无法得知是被谁调用,这个问题给日志记录、RPC回调等带来了很多麻烦事。但是,RRQMRPC支持调用终端获取。
步骤:
RRQMRPC 标签需要传入IncludeCallContext 参数。- 定义的服务的第一个参数必须是
IServerCallContext 或其派生类。 - 最后获得其Caller属性即可得到调用者。
public class GetCallerRpcServer : ServerProvider
{
[RRQMRPC(MethodFlags.IncludeCallContext)]
public string GetCallerID(IServerCallContext callContext)
{
if (callContext.Caller is RpcSocketClient socketClient)
{
return socketClient.ID;
}
return null;
}
[RRQMRPC(MethodFlags.IncludeCallContext)]
public string GetCallerID_2(RpcServerCallContext callContext)
{
if (callContext.Caller is RpcSocketClient socketClient)
{
return socketClient.ID;
}
return null;
}
}
|