我不懂微服务:rpc远程调用(代码片段)

杨友山 杨友山     2022-12-04     177

关键词:

RPC(Remote Procedure Call)远程过程调用协议,简单来说是一个节点请求另一个节点提供的服务。RPC是伴随着分布式的出现的,因为分布式客户端和服务端部署在不同的机器上,所以需要远程调用。

一、基本模型
RPC基本组件有如下几个:
1、客户端
服务的调用方
2、客户端存根
存放服务端信息,包括地址信息,对象结构等等,用于对服务端的信息进行序列化和反序列化。
3、服务端存根
存放服务端信息,用户对客户端发送的信息序列化和反序列化,以及调用服务端本地的方法。
4、服务端
服务的提供者

通讯过程如下

二、具体实现
以上是rpc基本通讯模型。
下面我以C#语言为例,通过代码来实现远程调用,代码实现过程中有如下关键元素:
1、远程对象
运行在服务端的对象,不能在客户端本地直接使用。一般是通过代理调用这个远程对象。最终体现出来的是,调用时和使用本地对象一样。在C#中远程对象时继承自MarshalByRefObject类。
2、信道
Chanel,用于客户端与服务端通信的通道。.net中提供了TCP、Http、IPC通讯,本文中我们稍后会使用TCP和Http通讯。
3、消息
是通讯的内容,以及附属信息。比如包含远程对象信息,被调用的方法和参数等。
4、代理
客户端不能直接访问远程对象的成员和方法,只能通过代理来访问。使用代理,让客户端访问远程对象的过程看起来和访问本地对象一样。
5、激活器
服务端通过激活器,激活需要让客户端访问的对象,或者提供一个代理;
客户端通过激活器获取服务端的对象,或者获取一个被服务端激活的代理。

三、代码分析
接下来,边看代码变理解。
程序的目标:完成一个客户端向服务端发起的请求,服务端返回结果。客户端和服务端是两个完全独立的控制台程序,表示这两个端可以分开部署在不同的机器上。

3.1、定义远程对象
远程对象,一般定义在服务端。客户端需要通过代理调用远程对象的方法,经过信道通讯,客户端的请求到了服务端,服务端再到具体的处理逻辑中,处理完结果后再将结果一远程对象的结构返回给客户端。
定义一个独立类库:TestRpcEntity工程,其中有一个类叫Person类。代码如下:

   public class Person: MarshalByRefObject
    
        public Person()
        
            Console.WriteLine("远程对象构造函数被调用");
        

        public virtual string Greet(string name)
        
            return null;
        
    

为了演示结果更清晰,此处我定义了虚方法。服务端继承后重写这个虚方法。

3.2、服务端
服务端是服务的提供者,需要先注册通道,包括指定开放的端口。然后激活通道,包括在注册的通道中激活远程对象,以便让客户端可以访问端口,并取得代理。
一个控制台应用程序:TestRpcServer。需要注意,这里通过第三方引用dll方式,将TestRpcEntity引用进来。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using System.Text;
using System.Threading.Tasks;
using TestRpcEntity;

namespace TestRpcServer

    class Program
    
        static void Main(string[] args)
        
            //服务端注册通道(带指定端口)
            TcpChannel tcpChannel1 = new TcpChannel(8090);
            HttpChannel httpChannel1 = new HttpChannel(8091);

            //服务端注册通道
            ChannelServices.RegisterChannel(tcpChannel1, false);
            ChannelServices.RegisterChannel(httpChannel1, false);

            //服务端激活
            //注册激活对象方式一:有状态模式
            RemotingConfiguration.RegisterWellKnownServiceType(typeof(PersonServer), "MyFirstRpcServeObj", WellKnownObjectMode.Singleton);

            //注册激活对象方式二:无状态模式
            //RemotingConfiguration.RegisterWellKnownServiceType(typeof(PersonServer), "MyFirstRpcServeObj", WellKnownObjectMode.SingleCall);

            //客户端激活
            //RemotingConfiguration.ApplicationName = "MyFirstRpcServeObj";
            //RemotingConfiguration.RegisterActivatedServiceType(typeof(PersonServer));


            System.Console.WriteLine("按任意键退出");
            System.Console.ReadLine();
        
    

通道
这里用到了TcpChannel和HttpChannel。

激活方式
服务端激活
知道已注册的已知对象的 URI 的任何客户端都可以通过注册 ChannelServices所首选的通道,并通过调用 new 或 Activator.GetObject 方法来激活对象,从而获取该对象的代理。 若要使用 new激活已知对象,必须先使用 RegisterWellKnownClientType 方法在客户端上注册众所周知的对象类型。 调用 RegisterWellKnownClientType 方法将为远程处理基础结构提供远程对象的位置,这允许 new 关键字创建该对象。 另一方面,如果使用 Activator.GetObject 方法激活已知对象,则必须将该对象的 URL 作为参数提供,因此不需要在客户端上进行事先注册。
当调用到达服务器时,.NET Framework 从消息中提取 URI,检查远程处理表以找到与 URI 匹配的对象的引用,然后根据需要实例化对象(如有必要),将方法调用转发给对象。 如果将对象注册为 SingleCall,则在方法调用完成后将其销毁。 将为每个调用的方法创建对象的新实例。 Activator.GetObject 和 new 之间唯一的区别在于前者允许你指定一个 URL 作为参数,后者从配置中获取 URL。
注册过程不会实例化远程对象本身。 仅当客户端尝试调用对象上的方法或从客户端激活对象时,才会发生这种情况。

. Net Remoting把服务器端激活又分为SingleTon模式和SingleCall模式两种。
 SingleTon模式:为有状态模式。如果设置为SingleTon激活方式,则Remoting将为所有客户端建立同一个对象实例。当对象处于活动状态时, SingleTon实例会处理所有后来的客户端访问请求,而不管它们是同一个客户端,还是其他客户端。SingleTon实例将在方法调用中一直维持其状态。
  SingleCall模式:SingleCall是一种无状态模式。一旦设置为SingleCall模式,则当客户端调用远程对象的方法时, Remoting会为每一个客户端建立一个远程对象实例,至于对象实例的销毁则是由GC自动管理的。

客户端激活
若要在服务器上创建客户端激活的对象的实例,必须知道其 Type,并且必须通过使用 RegisterActivatedServiceType 方法在服务器端上注册它。 若要获取客户端激活对象的新实例的代理,客户端必须先向 ChannelServices 注册通道,然后通过调用 new 或 Activator.CreateInstance来激活该对象。
若要使用 new 关键字激活客户端激活的对象类型,必须先使用 RegisterActivatedClientType 方法在客户端上注册对象类型。 调用 RegisterActivatedClientType 方法将为远程处理基础结构提供远程应用程序的位置,new 尝试创建远程应用程序。 另一方面,如果使用 CreateInstance 方法来创建客户端激活对象的新实例,则必须将远程应用程序的 URL 作为参数提供,因此不需要在客户端上进行事先注册。 若要向 CreateInstance 方法提供要在其中创建对象的服务器的 URL,则必须将该 URL 封装在 UrlAttribute 类的实例中。

另外在服务端定义了一个PersonServer类,用于服务端逻辑处理。

namespace TestRpcServer

    public class PersonServer : Person
     
        public override string Greet(string name)
        
            Console.WriteLine("服务端的Greet方法被调用");
            return "你好 " + name;
        
    

3.3、客户端
客户端时服务的发起者,
也是一个控制台应用程序:TestRpcClient。需要注意,这里通过第三方引用dll方式,将TestRpcEntity引用进来了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using System.Text;
using System.Threading.Tasks;
using TestRpcEntity;

namespace TestRpcClient

    class Program
    
        static void Main(string[] args)
        
            //客户端注册通道
            TcpChannel tcpChannel = new TcpChannel();
            HttpChannel httpChannel = new HttpChannel();

            ChannelServices.RegisterChannel(tcpChannel,false);
            ChannelServices.RegisterChannel(httpChannel,false);

            //客户端激活
            //方式一:服务端激活
            Person clientRpcObj1 = (Person)Activator.GetObject
                (typeof(Person), "tcp://localhost:8090/MyFirstRpcServeObj");

            if (clientRpcObj1 == null)
            
                Console.WriteLine("连接到远程TCP服务器失败");
            
            else
             
                Console.WriteLine("调用Hello:0", clientRpcObj1.Greet("TCP通道"));
            

            Person clientRpcObj2 = (Person)Activator.GetObject
         (typeof(Person), "http://localhost:8091/MyFirstRpcServeObj");

            if (clientRpcObj2 == null)
            
                Console.WriteLine("连接到远程Http服务器失败");
            
            else
             
                Console.WriteLine("调用Hello:0", clientRpcObj2.Greet("Http通道"));
            

            //方式二:客户端激活
            //(二)客户端激活模式
            //方法一
            //RemotingConfiguration.RegisterActivatedClientType(typeof(Person), "tcp://localhost:8090/MyFirstRpcServeObj");
            //Person obj = new Person();
            //obj.Name = "TCP";
            //Console.Write("调用HelloMethod:0", obj.Hello());

            //RemotingConfiguration.RegisterActivatedClientType(typeof(Person), "http://localhost:8091/MyFirstRpcServeObj");
            //Person obj2 = new Person();
            //obj2.Name = "HTTP";
            //Console.Write("调用HelloMethod:0", obj2.Hello());

            //方法二
            //object[] atts =  new UrlAttribute("tcp://localhost:8090/MyFirstRpcServeObj") ;
            //object[] parm = new object[3]; //要传达的构造函数的参数个数
            //parm[0] = "d";
            //Person obj = (Person)Activator.CreateInstance(typeof(Person), parm, atts);

            Console.WriteLine("按任意键退出");
            Console.ReadLine();
        
    

通道
注册两种类型通道

激活
服务端有服务端和客户端两种激活方式,对应的在客户端中就有服务端激活和客户端激活的使用方式,具体自行看代码。它们的调用方法不同。它们的区别可以再继续看3.2中服务端激活和客户端激活的说明。

代理
客户端使用服务端激活方式为例,Activator.GetObject返回的是一个服务端的代理,用此代理访问远程对象。

四、程序演示
启动服务端,只有服务端启用了,激活了通道和远程对象,服务端才能获取到代理。


启动客户端
客户端中完成一系列动作:注册通道,注册通道,获取代理,调用方法。

服务端调用了两次,所以就有了两次打印结果。从结果看,说明客户端确实调用到了服务端的PersonServer类中的方法。

其他服务端的激活方式和对应的客户端调用方式,大家可以自行下载代码练习。

全部代码下载链接:https://download.csdn.net/download/yysyangyangyangshan/12760970

我不懂微服务:http服务

一、前言上文说到rpc实现远程通讯,rpc通讯基于tcp/ip,也支持http协议。rpc最大特点是可以像调用本地方法一样,调用另一个服务的方法,是通过服务端的一个代理来实现的。rpc可用于内部服务间的通讯。RPC协议假... 查看详情

我不懂微服务:http服务

一、前言上文说到rpc实现远程通讯,rpc通讯基于tcp/ip,也支持http协议。rpc最大特点是可以像调用本地方法一样,调用另一个服务的方法,是通过服务端的一个代理来实现的。rpc可用于内部服务间的通讯。RPC协议假... 查看详情

我不懂微服务:tcp三次握手

一、关于TCPTCP是一种网络传输协议,这个协议只有资深网络工程是才能描述清楚,其他人的描述都是片面的。博主也试图从自己认识的角度讲解。先来看看OSI七层模型,这个模型很多很都认识。如图,这个图我们... 查看详情

[ue4]rpc,远程调用(代码片段)

...程上的函数由于“Server-shoot”方法被标记为“在服务器上运行”,所以尽管是在第二个窗口(客户端)开火,输出的信息是:Server:准备射击,表明这是在服务器上运行。 在服务器上“Server-shoot”方法又调... 查看详情

rpc一般指远程过程调用协议(代码片段)

...程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。... 查看详情

rpc(代码片段)

...写,想Client-Servier一样的远程过程调用,也就是调用远程服务就跟调用本地服务一样方便,一般用于将程序部署在不同的机器上,供客户端进行调用。就像一个request-response调用系统一样简单。在面向对象编程的程序中,RPC也可以... 查看详情

dubbo的rpc远程过程调用+dubbo的负载均衡+zookeeper注册中心(代码片段)

...基础理论应用架构演变单一应用架构垂直应用架构分布式服务架构RPC远程过程调用dubbo核心概念环境搭建_zookeeper注册中心环境搭建_管理控制台编写提供者,消费者的相关代码服务提供者配置&测试服务消费者配置&测试... 查看详情

java实现简单的rpc框架(代码片段)

...过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Httpinvoker等。另外,RPC是与语言无关的。  假设Computer1在调用sayHi()方法,对于Computer1而... 查看详情

avro实现rpc(代码片段)

...是指远程过程调用【是一种进程间的通信方式】例如两台服务器A,B,一个应用部署在A上面,A想要调B服务器上的函数或者方法,由于不在一个内存空间,不能直接调用,需要通过网络来传达调用的数据RPC的特点?简单:语义清... 查看详情

手写简易版rpc框架,理解远程过程调用原理(代码片段)

...节。调用本地方法和调用远程方法一样。1.2、RPC基本原理服务调 查看详情

手写简易版rpc框架,理解远程过程调用原理(代码片段)

...节。调用本地方法和调用远程方法一样。1.2、RPC基本原理服务调 查看详情

c++rpc调用nodejs(代码片段)

rpc远程过程调用,就是调用远程服务器上的方法,返回结果,将需要运行的函数或者进程放到远程服务器上去执行,降低本地服务的能耗。安装thriftwindows下面直接下载可执行文件就行了产生cpp文件./thrift-0.16.0.exe--... 查看详情

c++rpc调用nodejs(代码片段)

rpc远程过程调用,就是调用远程服务器上的方法,返回结果,将需要运行的函数或者进程放到远程服务器上去执行,降低本地服务的能耗。安装thriftwindows下面直接下载可执行文件就行了产生cpp文件./thrift-0.16.0.exe--... 查看详情

rabbitmq学习:利用rabbitmq实现远程rpc调用(代码片段)

...向一个队列中发送消息,并注册一个回调的队列用于接收服务端返回的消息,该消息需要声明一个叫做correaltionId的属性,该属性将是该次请求的唯一标识。服务端在接受到消息(在需要时可以验证correaltionId)后,处理消息,并... 查看详情

五分钟让你了解rpc原理详解(代码片段)

...单向调用不用返回结果。异步和同步的区分在于是否等待服务端执行完成并返回结果。RPC结构拆解如下图所示。RPC服务方通过?RpcServer?去导出(export)远程接口方法,而客户方通过?RpcClient?去引入(import)远程接口方法。客户方... 查看详情

grpc本地服务搭建(代码片段)

RPCRPC原理主流RPC框架gRPC概述特点服务端创建定义服务生成gRPC代码服务端实现客户端实现踩坑记录源码RPCRPC原理RPC框架的目标就是让远程服务调用更加简单、透明,RPC框架负责屏蔽底层的传输方式(TCP或者UDP)、序列化方式(XML/... 查看详情

微服务学习rpc原理与gorpc(代码片段)

...ocedureCall),即远程过程调用。它允许像调用本地服务一样调用远程服务。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统。首先与RPCÿ 查看详情

分布式思想和rpc解决方案介绍(代码片段)

...)远程过程调用,通过这个rpc协议,调用远程计算机上的服务,就像调用本地的服务一样。不同的服务部署在不同的机器上面,并且在启动后在注册中心进行注册,如果要调用,可以通过rpc调用对应的服务。如图,在不同的Contro... 查看详情