UE4网络之(二) 远程调用函数(RPC)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UE4网络之(二) 远程调用函数(RPC)相关的知识,希望对你有一定的参考价值。

参考技术A

所有示例使用第三人称模板创建的项目并带有初始资源StarterContent

Function Replicateion(简称RPC)是在本地调用但在其他机器上远程执行的函数。RPC可以实现客户端或服务器之间相互发送消息。RPC可以设置为Reliable或Unreliable,其中Reliable调用必定发生,而Unreliable调用可能会因为网络问题被丢弃。因此大多处理视觉效果的RPC应该设置为Unreliable来避免过多地占用网络。
RPC主要包括Multicast(广播)、Run On Server(在服务端执行)和Run On Owning Client(在客户端执行)三种类型。其中广播类型在服务器上调用执行,然后自动转发给客户端;在服务端执行的函数有客户端调用,然后仅在服务器执行。在客户端执行的函数由服务器调用,然后仅在自己的客户端上执行。

1、打开ThirdPersonBP/Blueprints中的ThirdPersonCharacter蓝图,添加一个按下空格时在玩家为之生成火焰特效的事件。蓝图非常简单直接上图。

2、更改Number of Players改为4后运行。

可以看到所有窗口只有自己按下空格时才能生成火焰特效并且只能看到自己的特效。

3、在ThirdPersonCharacter蓝图添加一个MulticasTest事件并将Replicates设置为Multicast,将蓝图改为如图所示。

4、点击Play运行。

看到服务器上生成的特效在所有客户端都能看到,而客户端生成的特效只有自己才能看到。

重复步骤3将Replicates分别改为Run On Server。此处直接上效果图。

此时不论谁按空格键,只有服务器上相应的角色可以生成特效,客户端并不能看到任何效果。如果想要在客户端也能看到效果,我们需要确保特效设置为Replicates。
打开特效蓝图,并选中Replicates选项后重新开始运行。

1、打开ThirdPersonCharacter蓝图,创建一个String类型的变量Inventory并设置为Instance Editable和Replicated

2、添加按P键打印Inventory的事件蓝图。

3、在场景中添加一个Box Trigger

4、取消Rendering下面的Actor Hidden In Game

5、添加蓝图,重叠触发器时,如果重叠发生在服务器上,在服务器上运行 Add Item 事件,并将它复制到自己的客户端;当人物退出触发器盒时,在服务器上运行 Remove Item 事件,并将其复制到自己的客户端。其中Add Item 和 Remove Item 事件为自定义事件,并且Replicates属性全都为Run on owning Client。

6、编译运行。

可以看出刚开始启动时,每个角色打印的都为空,当一个角色进入触发器后会显示文本Item added ,按P时文本改为"has the item ",人物退出触发器会打印“Item Removed”,再次按P会打印空字符

Dubbo之RPC框架

上一篇简单介绍了一下Dubbo,Dubbo首先是一个RPC框架,那么这篇我们先从RPC聊起 。

Dubbo是一个基于Java的RPC框架,底层基于高性能通信框架Netty实现。RPC(Remote Procedure Call)翻译过来就是远程过程调用,那么什么是远程过程调用呢?我们应该知道本地过程调用,就像我们本地的A方法调用B方法就是本地过程调用。那么对应的远程过程调用,就是通过网络去远程计算机服务上请求服务,至于 RPC 要如何调用远程计算机上的方法可以走 HTTP协议、也可以是基于 TCP 或者UDP自定义协议。

最开始接触RPC的时候我想当然的把RPC和HTTP来进行对比,现在看来这两个都不是一个层级的东西,不能瞎比。


而 RPC 框架的作用,就是让我们使用远程调用像本地调用一样简单方便,并且能解决一些远程调用中会发生的问题。让用户像调用本地方法一样无感知、放心的使用,不需要了解底层网络的通信机制


  • 那么如何设计一个RPC框架

    通过上面我们了解到什么是RPC以及RPC框架的作用是什么,那么接下来刁钻的面试官肯定得问你怎么设计一个RPC框架,没想到吧,我做了准备。


RPC框架有以下三个核心角色:


1.服务提供者

服务提供者需要提供接口的实现,然后把接口暴露出去,将自己的服务信息注册到注册中心。并定期像注册中心发送心跳,证明自己还活着。

这样服务消费者请求过来之后,要实现反序列化,序列化完成之后的请求放入到线程池,某个线程拿到这个请求去执行对应代码的逻辑,然后返回结果给服务消费者。

2.服务消费者

服务消费者启动时向注册中心订阅所需要的服务,并缓存到实例列表中。

服务消费者要去消费服务提供者提供的服务,双方就要约定一个协议,比如用http协议来进行通信。调用时,从实例列表中选择一个服务实例去调用。

因为是网络传输,所以序列化是必须的,比如代码中的类信息需要经过序列化之后才能通过网络传输,因此要约定序列化格式。

因为服务消费者是直接调用服务提供者,就算注册中心宕机了也不会影响已经正常运行的消费者和服务者。

3.注册中心

注册中心就像一个平台,用于服务提供者暴露服务,服务消费者在上面发现服务。服务实例列表发生变化时,动态通知消费者,消费者更新本地的实例列表。

这个平台还可以用作配置中心,将所需配置集中存放到此,配置变更之后可以动态通知消费者。

市面上常用的注册中心有:zookeeper,nacos,eureka,consul




  • RPC远程调用过程



总结起来就是这么回事:

  1. 服务调用者以本地调用方式调用方法;

  2. client stub将打包后的消息通过网络发送至服务提供者;

  3. server stub收到消息后进行解码,对应在Java中就是反序列化过程;

  4. server stub根据解码结果调用本地的方法

  5. 本地服务执行处理逻辑并将结果返回给server stub;

  6. server stub将返回结果打包成消息,对应Java里的序列化过程;

  7. server stub将打包后的消息通过网络发送至服务消费者;

  8. client stub接收到消息并解码,对应 Java中的反序列化过程;

  9. 服务调用者得到最终返回结果;



  • 简简单单实现一个RPC框架

    服务提供者相关代码:

 private static void runRPCServer(Object service, int port) {
ServerSocket server = null; try { server = new ServerSocket(port); System.out.println("服务提供者启动!!"); while (true) { final Socket accept = server.accept(); System.out.println("收到请求并放入线程池执行"); pool.submit(new Runnable() { public void run() { ObjectInputStream input = null; ObjectOutputStream output = null; try { //1.反序列化 input = new ObjectInputStream(accept.getInputStream()); //2.获得方法名 String methodName = input.readUTF(); //3.获得参数类型 Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); //4.获得参数 Object[] args = (Object[]) input.readObject(); //5.根据方法名找到方法 Method method = service.getClass().getMethod(methodName, parameterTypes); //6.执行对应的方法 Object result = method.invoke(service, args); //7.从监听的socket中获得输出流 output = new ObjectOutputStream(accept.getOutputStream()); //8.返回执行结果 output.writeObject(result); } catch (Exception e) { e.printStackTrace(); } } }); }
} catch (IOException e) { e.printStackTrace(); } finally { try { if (null != server) { server.close(); } } catch (IOException e) { e.printStackTrace(); } } }

服务消费者相关代码:

 public static Object getClientService(Class interfaceClazz) {
return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz}, (proxy, method, args) -> callRPCServer(method,args));
}
private static Object callRPCServer(Method method, Object[] args){ Object back = null; //默认ip:127.0.0.1 String ip = "127.0.0.1"; //端口号默认设80 int port = 8090; ObjectOutputStream objOutStream = null; Socket client; try { //指定服务提供者ip和端口 client = new Socket(ip,port);
objOutStream = new ObjectOutputStream(client.getOutputStream()); //方法名 objOutStream.writeUTF(method.getName()); //参数类型 objOutStream.writeObject(method.getParameterTypes()); //参数值 objOutStream.writeObject(args);
try (ObjectInputStream objInStream = new ObjectInputStream(client.getInputStream())) { //读取远程方法返回的结果 back = objInStream.readObject();
} catch (ClassNotFoundException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(null != objOutStream) objOutStream.close(); } catch (IOException e) { e.printStackTrace(); } } return back; }

以上便是一个非常简陋的rpc框架demon,就是服务消费者传递了方法名、参数类型和参数值,服务提供者接收到这样参数之后调用对应的方法并返回结果。

以上是关于UE4网络之(二) 远程调用函数(RPC)的主要内容,如果未能解决你的问题,请参考以下文章

UE4 网络同步原理三 RPC

#yyds干货盘点# Python网络爬虫之js逆向之远程调用(rpc)免去抠代码补环境简介

# yyds干货盘点 # Python网络爬虫之js逆向之远程调用(rpc)免去抠代码补环境简介

[架构之路-61]:目标系统 - 平台软件 - 基础中间件 - 远程过程(函数)调用RPC原理与其网络架构

Hadoop之RPC简单使用(远程过程调用协议)

手撸rpc框架,并基于spring进行二次注解开发