这篇文章主要讲解了“web中远程对象调用怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“web中远程对象调用怎么理解”吧!
要说“远程对象”,必先说“远程调用”,也就是RPC。比较著名的RPC框架有,最近很火的gRPC,也就是Google开源的RPC。另外还有Facebook开源的Thrift等等……我厂内部也有很多RPC框架,琳琅满目不暇接。Java在JDK里面也支持RMI(Remote Method Invoke: 远程方法请求)功能,也可以视为一种RPC,但实际上这个更像我们现在要讨论的“远程对象调用”。
在诸多的RPC中,我们都基本认为是通过网络,对运行在另外一个进程(或者电脑)里的某个函数,发起一次调用请求。既然是一次函数调用,那么我们自然要传入参数,然后期望获得返回值。在这个过程中,我们往往只需要输入:函数名+参数,RPC就能找到一个远程的进程,去执行对应的函数,然后传入目标参数。在这个过程里,执行这个函数的进程,会被认为是无状态的,所有的输出,都仅与输入的参数有关,除非有一部分状态是记录在数据库(持久化设备)上的。因此,计算的过程(算法),和计算的数据,实际上分离的,这些计算所需的数据,要么来源于参数,要么是数据库设备。而被请求的函数,以及装载这个函数的容器——进程,是不保证任何的状态维护能力的。
而“远程对象调用”,正是在“状态”这个环节上,和RPC不同——它是由框架去保证某种状态的。当我们发起一个远程对象调用的时候,是需要首先“找到”一个远程对象,然后再发起“方法”(成员函数)调用。这和RPC就产生了两个明显的区别:
我们需要用某种手段定位到对象,而不是仅仅用一个函数名。对象是一个更复杂的远程概念,因为有可能同属于一个类(class),而存在多个状态一致或不一致的对象,在远程的机器上存在。我们就不能仅仅通过一个固定的路由标志(比如类名)去找一个这样的对象。远程对象的路由方式成为不同“远程对象调用”框架之间的一个显著区别。
我们并不需要把所有的数据,在每次请求时都通过参数发给远程对象,因为对于同一个远程对象来说,它是可以包含大量过程状态的。我们只要找到正确的远程对象,就能获得之前操作所造成的结果状态。有远程对象往往是生存在进程的内存中,所以对于访问自己的状态数据,会非常快速,这对于有延迟压力的程序来说,是非常有用的。
所以,远程对象调用,最大的特点,就是数据和计算是合并在一起的——这很好的提高了使用面向对象编程的便利性,也大大降低了远程调用中因为数据拉取产生的延迟。
在传统的“请求-响应”为基础的分布式服务器中,最常见的数据系统是:接入-逻辑-缓存-数据库 这样一个四层结构。为了让承担计算压力的“逻辑”模块能分布到不同的进程上,我们往往会把“逻辑”模块做成“无状态”的,这样我们就可以随意的启动、停止任何一个逻辑模块的进程,而不需要担心因此丢失用户数据。但是这样做,逻辑模块是轻松了,承担状态存储的“缓存-数据库”哥俩压力就大了。因为每一个数据操作,都需要去从他们这里读取数据,然后再回写结果(如果有数据修改操作的话)。
一个客户端程序,想要访问一个EJB对象,一般需要使用一个叫做JNDI的API,来具体连接到EJB对象上。JNDI的全称是Java Naming and Directory Inerface,基本等于我们常说的名字、目录服务接口。Java通过一套API规范,来统一各种目录服务器的使用方法。所有的J2EE容器,都必须提供一个JNDI服务,而客户端程序则通过使用J2EE容器提供的JNDI来访问容器内的EJB对象。JNDI的使用方法,基本上就是输入一个字符串,然后API会返回给你一个对象。在J2EE的环境里,这个对象就是EJB对象的Home接口对象(对应远程EJB对象的一个映像,也叫桩对象)。代码类似:
Context ctx = new InitialContext(env); Object ejbHome = ctx.lookup(“java:comp/env/ejb/HelloBean”); HelloHome empHome = (HelloHome) PortableRemoteObject.narrow (ejbHome, HelloHome.class);
输入lookup()函数的字符串,是用户可以自己定义的任何内容,只要在对应的EJB容器里面登记了这个对应关系即可。从这个代码我们可以看到,如果EJB想要做容灾、负载均衡等功能,是完全可以通过ctx.lookup()
这个接口来实现的。另外,远程对象的Home接口(桩代码)是需要预先部署在客户端测,在上面的例子里是HelloHome.class这个类。而EJB对象的这个Home接口类,是由EJB工具,自动通过来源的EJB对象类定义生成的。对比CORBA,Thrift等技术,EJB可以直接用.java源代码代替IDL定义,然后自动生成桩代码,这确实是简便很多。
EJB规范把远程对象定义为三种:无状态会话Bean,有状态会话Bean,消息驱动Bean。这意味着EJB容器对于EJB对象的生命周期是有管理的。其中无状态会话Bean和消息驱动Bean的声明周期是类似的,都是来一个请求(消息驱动的意思是每来一个JMS消息),就可能new一个Bean对象。当然也可能不是每次请求都新建对象,总之容器不保证会保持Bean对象的生存周期,这样容器可以根据负载压力,灵活的管理众多的Bean对象。而最特别的是“有状态会话Bean”,容器会根据客户端的会话状态(和客户端的context对象对应),来保持Bean对象,也就是说,每个客户端context对应一个有状态Bean。如果你用这个客户端context,发起多次lookup()查找,访问的那个EJB对象都将会是同一个。这对于需要保持登录状态的服务,就非常方便了。客户无需自己去维持一个远程对象的生命周期,而能得到状态保存的功能。
最后说说EJB的部署配置,以前的EJB容器部署异常复杂。除了需要写一个继承于特定基类的业务JAVA类外,还要配置很多细节。而EJB3.0之后,通过JAVA注解功能(Annotation),这些配置都可以和源代码写到一起,而业务JAVA类也无需集成特定的接口和类型,可以是任何一个普通的类(POJO),只是需要加上一些特定的注释即可。EJB容器提供工具对这些加了EJB注释的JAVA类进行处理,一方面把这个JAVA类自动部署到容器中,另一方面生成客户端的Home接口类文件,供用户发布(拷贝)到需要使用的客户方服务器上去。而一些EJB容器(如Weblogic)还提供了Eclipse(IDE)的图形界面工具,让整个过程几乎都不在需要编写额外的配置和命令行操作。
WCF全称Windows Communication Foundation,是微软发布的用于构建面向服务的应用程序框架。这套框架的底层是Windows的COM+技术,而编程接口则更多的使用C#语言/VB语言和.Net平台。这和EJB有一定的类似,差别就是WCF中的远程对象,不需要一个像JVM那样的虚拟机,而是结合在WINDOWS操作系统里。
无独有偶,WCF的远程接口定义,也是直接使用C#/VB代码,加上类似注解的“特性”(Attribute)功能注释,标注在一个定义好的接口(Interface)上来组成的。具体的业务实现类,只要“实现”定义的这个接口就可以了,和一个普通的类没有任何差别。和EJB的差别是,我们还是需要写一段XML配置,把这个远程对象的接口和查找字符串,注册到万能的IIS服务器里面。一旦注册完成,就可以通过URL:http://xx.xx.xx.xx/servicesname/service.svc
这样的字符串去访问了。同时,如果客户端想要访问这个远程对象,则需要使用svcuitl.exe这个工具,输入刚刚注册的那个URL,就可以生成对应的客户端桩代码库。客户端可以直接new这个新建立的桩类型对象,然后直接调用其方法,就和调用本地对象的方法一样。
// Create a client. CalculatorClient client = new CalculatorClient(); // Call the Add service operation. double value1 = 100.00D; double value2 = 15.99D; double result = client.Add(value1, value2); Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
当然,如果你想连接不同的服务器,还是有机会的,一位内生成的客户端代码,会使用一个配置文件。在里面可以修改远程服务器的地址(还是那个注册的URL)。
<client> <endpoint address="http://localhost/servicemodelsamples/service.svc" binding="wsHttpBinding" contract=" Microsoft.ServiceModel.Samples.ICalculator" /> </client>
你除了可以通过IIS来提供WCF的远程对象服务外,还可以自己写一个单独的程序,通过定义main()来完全的控制这些远程对象,从而提供服务。另外,WCF除了通过URL直接对应一个远程对象外,还可以通过编写“路由服务”,来对同一个URL的远程对象调用进行灵活的路由。虽然WCF没有提供类似EJB的远程对象生命周期管理功能,但是你完全可以通过WCF的服务API和路由服务,来自己编码实现任何形式的远程对象生命周期管理。
IBM公司的RMI-IIOP服务,是以JAVA技术为基础的,但是又不同于EJB的另外一套远程对象技术。这套技术更接近于以JAVA为基础实现的CORBA体系。这个技术的使用标准的JAVA RMI接口(RMIInterface)作为远程对象的接口,使用JAVA的序列化、反序列化能力作为编码能力。然后自己写一个main()
函数,建立一个org.omg.CORBA.ORB
对象来构造一个远程服务器。而客户端则是通过一个字符串来定位想要访问的远程对象。这个字符串类似:corbaloc:iiop:1.2@localhost:8080/OurLittleClient
。我们可以看到这里面有IP和端口,还有一个编写服务器远程对象时注册的字符串OurlLittleClient。我们通过rmic –iiop Server
这样的命令行部署远程对象,然后用start java Server启动服务器,用start java Client启动客户机。这些命名,都是包含在IBM Developer Kit for Java technology v1.3.1里面的。我们可以发现,RMI-IIOP是一个更加原始的远程对象方案,基本上就是一个CORBA的API实现的组合。使用起来有点繁琐,但是好处是不需要学习和部署复杂的容器服务,可以完全自己编码去实现一套远程对象服务。这里没有限定你使用什么方法去定位查找远程对象,也没有限定你怎么管理远程对象的生命周期,一切都由开发者自己去编写实现。
规范 | 远程对象定位 | 远程对象生命周期管理 | 服务器部署 |
---|---|---|---|
EJB | JNDI路径字符串查找 | 自动管理,带会话状态对象 | 使用容器服务 |
WCF | URL、路由服务 | 无 | 部署到IIS或自写main() |
RMI-IIOP | COBRA URL定位 | 无 | 自写main() |
在对象定位的选择上,通过字符串查找已经是标准,而复杂的自定义路由也可以隐藏在这个查找操作下面。远程对象的生命周期管理,实际上是对服务器资源的管理,除了EJB有容器支持以外,其他的方案都比较少提供这样的能力,说明这一块是比较困难的。服务器部署方面,可以让用户以API自己写main()去构建服务器,提供了极大的灵活性。
通过上面的分析,我们可以发现,远程对象的生命周期管理,是一个比较重大且复杂的课题。我们要保证这样的生命周期管理程序,能有一个通用的策略,来保持各种业务情况下的服务器资源稳定,是比较困难的。而且在分布式系统的情况下,为了负载均衡,还要把同样类型的远程对象,部署到不同的进程上,这就引入了一个新的问题:数据一致性。
远程对象的生命周期,除了占用服务器的内存资源外,还会占用记录其地址的路由空间,检查维护生命周期的CPU运算时间。如果我们提供自动化的对象生命周期管理,势必就需要在客户使用的时候,提供这方面的教育,以及防止客户使用错误、过载等情况下对象管理失效的防御性策略。所以即便是EJB容器,也仅仅提供了非常简单的生命周期管理策略:会话状态、无状态这两种。
对于一般的互联网应用,只有EJB这两种生命周期管理的远程对象,基本上是够用的。因为一般的互联网应用,大部分数据都是持久化数据,需要读写数据库。临时状态数据一般来说不多,主要是用户登录后的产生的一些过程数据,有一个“会话(Session)”类型的生命周期就足够了。但是,如果我们的业务是网络游戏,那么这么简单的生命周期就是完全不够的,因为游戏中有大量的临时状态,比如组队的状态,玩家所在房间的状态,关卡副本的状态等等。这些临时状态,都是需要我们通过业务逻辑代码,来控制和管理所对应的对象生命周期的。所以一个适合游戏的远程对象系统,需要提供让客户端程序来选择,“新建/初始化”和“销毁”远程对象的能力。
在对远程对象进行管理的时候,我们常常会用到一种叫“对象池”的技术,使用这种技术避免频繁的新建和销毁对象。但是如果这些对象的是带状态的,那么我们的“池”就必须带索引,并且对象也必须有一个key。同时我们的对象还需要有一个“reset”的重置方法,用来让对象回归到初始化状态。
在分布式的系统下,我们的对象池因为是分别存放在不同的机器上,所以其一致性的维护往往是比较困难的。但是,我们可以把这个问题,转换成构建一个“分布式对象池”的问题。假如每个对象池,都按KEY的某个规律,如一致性哈希,存放不同的对象。那么只要在远程调用发起的时候,也就是通过lookup()查找远程对象的时候,把请求导向到对象所在进程,那么就能很方便的从本地进程对象池中获得对象。远程对象的“定位”和“一致性”在查找对象这个环节结合起来,是一个非常好的想法。这样能让远程状态对象的使用进一步简化,用户完全无需关心远程对象在什么地方,又能快速的访问到正确的对象。
[扩容下的远程对象迁移]
当分布式的对象容器出现部分进程故障,或者需要动态扩容的时候,只要我们针对对象查找的数据做某种程度的数据搬迁,或者缓存清理,就能很容易的实现对象的重新分布。如果对象同时能够支持持久化,那么这种数据搬迁,只需要简单的让对象写入持久化。然后在新的机器上,通过缓存建立的策略,从持久化设备读取出对象即可。
感谢各位的阅读,以上就是“web中远程对象调用怎么理解”的内容了,经过本文的学习后,相信大家对web中远程对象调用怎么理解这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。