论坛首页 Java企业应用论坛

不同应用间互相调用,兼谈重用性

浏览 2898 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (2) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-08-06  
我们的应用,包含若干个子系统,运行完部署脚本,会打出若干个war包,部署在同一个jboss里

子系统之间有接口,是通过web service来互相调用的。我昨天突然想到,既然这些应用都是跑在同一个jboss里的,那就是同处一个进程,为什么不能直接互相调用呢

下面就开始一步步做试验,首先我在tomcat里,部署了3个应用,分别是Spring、AnotherServlet、ThirdServlet,用jps命令,可以看到只有一个进程



1800是我的eclipse,5404就是tomcat进程,用jconsole连进去看线程



都是tomcat内部的一些线程,以及等待处理HTTP请求的work thread(http-bio-8080-exec-x)

以上证明了,无论多少个应用(我这里部署了3个),都是在同一个进程里的,也就是共享了内存区域,所以从理论上说,互相调用是有可能实现的

然后就做一下试验,我在AnotherServlet里创建一个类



然后在spring工程里,用反射的方法,试图在运行时创建这个类的实例
try {
			Class.forName("net.kyfxbl.jbosstest.TestRemoteInvoke")
					.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}

结果果然报了ClassNotFoundException:
java.lang.ClassNotFoundException: net.kyfxbl.jbosstest.TestRemoteInvoke
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1711)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1556)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)

附带一提,这里用反射,是为了避免编译期编译不通过。

如果用new的方式来创建
TestRemoteInvoke test = new TestRemoteInvoke();

就无法通过编译,当然可以通过把AnotherServlet设置为关联工程的方式,使编译通过



不过这样只是通过编译而已,运行时效果是一样的,同样是ClassNotFoundException,因为打出的war包里,是不包含这个类的

想了一下,应该是跟servlet容器启动时的ClassLoader机制有关,不同工程的.class,应该是互相隔离的,所以虽然同处一个进程,但是却是没有办法互相调用的

有一个办法,如果能得到源码的话,可以将源码拷贝到工程里,这样在打war包时,就会把需要的类也打进去了。这也是最低层次的共享,即“代码重用”

如果得不到源码,但是可以得到jar包的话,可以将jar包放到WEB-INF/lib目录下,这样的话也可以获取到所需的类,这种方式比直接的“代码重用”高级一点,即“二进制重用”。平时我们引入各种jar包,也都属于这种方式

如果源码和jar包都得不到,那就只能用更高层次的重用方法,即所谓的“服务重用”。用这种方式,服务提供方的类,是完全私有的,外部完全不可见。通过“某种方式”,暴露一个接口给服务使用方,服务使用方按照接口的规定,来调用隐藏的具体实现。

暴露接口的方式,可以有很多种。

最常见的就是web service。比如说,前几年挺火的SaaS概念,就是这种方式。比如我是一个金融公司,开发了一套股价走势预测系统,并开放API,让其他的应用来调用。那么我肯定是不能把源码给出来的,二进制文件也不能给(因为存在被反编译的风险),那这个时候我就在互联网上开放一个web service,需要的客户端就调用这个web service,我内部处理之后,用soap返回结果,中间可以校验、鉴权、计费什么的

另外一种是山寨版web service,比如开放一个servlet地址,http://ip:port/myService/method1.do,然后告知用户这个地址可以接受哪些参数,用户调这个地址,通过http response返回结果。这种思想实际上和web service是一样的,只是通用性比较差点,编程也比较麻烦

此外还有类似的RMI方法,在客户端开发Stub,在服务端提供具体实现。同样的,客户端也是需要得到具体实现的接口才可以

android开发里也有类似的情况,即IPC(跨进程调用),通过AIDL,将自己的服务发布出去

总的来说,本文说的是关于“重用”的话题:

最低层次的重用,是“代码重用”。这种方式局限性最大,共享的难度也最大

高级一点是“二进制重用”,但这种方式是无法跨平台的,并且存在安全性的隐患,而且服务提供方的修改,无法及时传递给服务使用者(需要重新编译发布)

最高级的是“服务重用”,这种方式具备了即时性,跨平台的优点。但是一定要保证接口的稳定,如果接口发生了变化,那需要通知到所有的客户端。不过这种方式依赖网络,可能是局域网,也可能是互联网,但服务提供方和使用方之间,必须保证网络的连通性
  • 大小: 1.9 KB
  • 大小: 16 KB
  • 大小: 8.8 KB
  • 大小: 74 KB
   发表时间:2012-08-06  
同一个web容器中的不同app,必然是classloader隔离的,
不然怎么单独部署。
0 请登录后投票
   发表时间:2012-08-07   最后修改:2012-08-07
kimmking 写道
同一个web容器中的不同app,必然是classloader隔离的,
不然怎么单独部署。

classloader好像可以像树一样挂载
那么如果有一个框架classloader先挂上去之后 ,别的web挂在它之下....这样是否能解决楼主的问题?

PS:以前看API里貌似有得到父classloader的代码
0 请登录后投票
   发表时间:2012-08-07  
抛出异常的爱 写道
kimmking 写道
同一个web容器中的不同app,必然是classloader隔离的,
不然怎么单独部署。

classloader好像可以像树一样挂载
那么如果有一个框架classloader先挂上去之后 ,别的web挂在它之下....这样是否能解决楼主的问题?

PS:以前看API里貌似有得到父classloader的代码


是有api可以直接获的parent classloader ,但是获取来也没用。。类似下面的,app1获取到了tomcat的classload,好像没办法获取app2的classload,所以也拿不到它里面加载的类。

                 tomcat-classload
                        |
app1-classload---------- ---------app2-classload
0 请登录后投票
   发表时间:2012-08-07  
rmi
jmx
0 请登录后投票
   发表时间:2012-08-08  
既然是不同的app,应该有各自不懂的context,除非你们把spring集成进tomcat,
0 请登录后投票
   发表时间:2012-08-08  
总的来说,本文说的是关于“重用”的话题:

最低层次的重用,是“代码重用”。这种方式局限性最大,共享的难度也最大

高级一点是“二进制重用”,但这种方式是无法跨平台的,并且存在安全性的隐患,而且服务提供方的修改,无法及时传递给服务使用者(需要重新编译发布)

最高级的是“服务重用”,这种方式具备了即时性,跨平台的优点。但是一定要保证接口的稳定,如果接口发生了变化,那需要通知到所有的客户端。不过这种方式依赖网络,可能是局域网,也可能是互联网,但服务提供方和使用方之间,必须保证网络的连通性
----------------------
我倒是觉得从重用来说,越不透明,重用越笨重。
最低级的是服务重用,最高级的是代码重用。
0 请登录后投票
   发表时间:2012-08-08  
因为代码重用的局限性是比较大的,源码你不一定能得到的,怎么重用呢?而且代码重用无法跨平台,比如一个基于.NET的服务,你在JAVA平台想重用的话,只能是通过服务重用的方式,代码重用和二进制重用都不可行
0 请登录后投票
   发表时间:2012-08-08  
jconsole是个好东西。
是的,不同应用的context不同,classloader也不同,估计很难实现楼主所要。不过,RMI,JMS应该可以,不过还是得在源码层编码,可能还是开放web service实际。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics