Spring+Hessian简介:
Hessian能够完成RMI就是远程过程调用,实际上Hessian自己就可以完成这个功能,但是我的目的是将与Spring相关的框架与Spring结合起来,一起完成任务,所有不单独拿出一个章节来讲Hessian。不够,使用方式都是相通的。
这次需要完成的任务是这样的,假设在远程有一个作业需要执行,我们写一个客户端,并将这个客户端安装到远程主机上,然后再本机上远程调用客户端的服务,让客户端执行远程主机的作业任务。
从上面的分析可知,我们需要完成两个服务,一个是远程执行主机的服务,一个是本地的调用服务。整个程序的结构如下:
远程执行主机服务:
需要用到的jar包:
本地执行主机目录:
需要的jar包:
第一步:编写远程主机服务:
要在远程主机上执行作业必然需要对需要执行的作业进行描述,这里对作业的描述如下:
package com.guan.springHessianClientTest.logicModel;
import java.io.File;
import java.io.Serializable;
public class Job implements Serializable{
/**
* 注意作业必须要实现Serializable接口,很明显,作业作为参数在两个主机间传递,必然要进行串行化。Hessian是以二进制的形式进行传输的。
*/
private static final long serialVersionUID = 1L;
private String executable;//可执行文件的名字
private File directory;//执行目录
private String stdOutFile;//标准输出文档
private String stdErrFile;//标准错误输出文档
private String[] args;//程序执行参数
public File getDirectory() {
return directory;
}
public void setDirectory(File directory) {
this.directory = directory;
}
public String getExecutable() {
return executable;
}
public void setExecutable(String executable) {
this.executable = executable;
}
public String[] getArgs() {
return args;
}
public void setArgs(String[] args) {
this.args = args;
}
public String getStdOutFile() {
return stdOutFile;
}
public void setStdOutFile(String stdOutFile) {
this.stdOutFile = stdOutFile;
}
public String getStdErrFile() {
return stdErrFile;
}
public void setStdErrFile(String stdErrFile) {
this.stdErrFile = stdErrFile;
}
}
上面这个类是个实体类,不多说,大家都明白。
然后实现一个ServiceDao就是服务接口,对应Hessian来说,这个接口是必须的,因为Hessian建立的远程调用,在远程不能过直接对服务的实现调用,而是对服务的接口进行调用,后面还会分析其原因。
package com.guan.springHessianClientTest.ServiceDao;
import java.io.IOException;
import com.guan.springHessianClientTest.logicModel.Job;
public interface ExecuteServiceDao {
public void submintJob(Job job) throws IOException;
}
这个接口很简单,就是提交作业到远程执行。
下面给出上面接口的实现:
package com.guan.springHessianClientTest.ServiceImpl;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import com.guan.springHessianClientTest.ServiceDao.ExecuteServiceDao;
import com.guan.springHessianClientTest.logicModel.Job;
public class ExecuteServiceDaoImpl implements ExecuteServiceDao{
public void submintJob(Job job) throws IOException {
Runtime runtime = Runtime.getRuntime();//执行时环境
Process process = runtime.exec(job.getExecutable(),job.getArgs(),job.getDirectory());
//开启一个进程来执行提交过来的程序
BufferedInputStream in = new BufferedInputStream(process.getInputStream());//获得可执行程序的标准输出
BufferedReader br = new BufferedReader(new InputStreamReader(in));//创建一个reader读取标准输出
BufferedInputStream err = new BufferedInputStream(process.getErrorStream());/*获得可执行程序标准错误输出*/
BufferedReader berr = new BufferedReader(new InputStreamReader(err));
PrintStream myout = new PrintStream(new FileOutputStream(new File(job.getStdOutFile()))); /*打开标准输出文件*/
PrintStream myerr = new PrintStream(new FileOutputStream(new File(job.getStdErrFile())));/*打开标准错误输出文件*/
String s;
while ((s = br.readLine()) != null)
myout.println(s);/*读取,然后输出到文件*/
while ((s = berr.readLine()) != null)
myerr.println(s);
}
}
在执行程序的过程中,服务先开启一个进程来执行这个程序,然后将执行的结果输出到文件中。
接下来,需要配置远程调用的接口,首先要在web.xml中配置Spring的过滤器,这个可以参考我前面的Spring+Hibernate那篇文章。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>remoting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/remoting/*</url-pattern>
</servlet-mapping>
</web-app>
通过这个配置,很容易看到,凡是以/remoting开始的请求,都会被传递给Spring来处理。
下面要配置Spring:
Remoting-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="excuteServiceDaoImpl" class="com.guan.springHessianClientTest.ServiceImpl.ExecuteServiceDaoImpl"/>
<bean name="/executeManagerService" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="excuteServiceDaoImpl"/>
<property name="serviceInterface" value="com.guan.springHessianClientTest.ServiceDao.ExecuteServiceDao"/>
</bean>
</beans>
首先创建了一个ExecuteServiceDaoImpl的实现,然后创建了一个远程调用的服务,这个服务中注入了ExecuteServiceDaoImpl的实现。并且说明了接口是ExecuteServiceDao。在远程需要获得这个远程调用的服务(/executeManagerService,注意呀,这个名字前有个/),然后使用接口来完成服务的访问。
第二部分 服务端本地调用的编写:
本地调用只写了个测试程序,需要直接从执行服务端拷贝model和dao。然后需要写一个Spring的配置文件,bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="myServiceClient" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl">
<value>http://localhost:8080/SpringHessianClientTest/remoting/executeManagerService</value>
</property>
<property name="serviceInterface">
<value>com.guan.springHessianClientTest.ServiceDao.ExecuteServiceDao</value>
</property>
</bean>
</beans>
先创建一个Service的bean,并指明这个Service的url是刚才我们配置那个服务的url,注意我是本机测试,所以使用localHost,并且我的tomcat配置的是8080端口,如果执行服务放在远程主机上,那么要将localHost更换成其ip地址。然后指明要使用的接口是什么。
注意在提交端,并没有拷贝服务的实现,只是拷贝了服务的接口。
最后的测试部分代码如下:
package com.guan.springHessianClientTest.RomoteService;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.guan.springHessianClientTest.ServiceDao.ExecuteServiceDao;
import com.guan.springHessianClientTest.logicModel.Job;
public class RMIServiceTest {
@Test
public void remoteTest() throws IOException
{
ApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml");//分析Spring配置文档
ExecuteServiceDao studentService = (ExecuteServiceDao) appContext.getBean("myServiceClient");//获取配置的bean
Job job = new Job();//新创建一个job,并进行配置
job.setExecutable("F://programs//vs6.0//redirectionTest//Debug//redirectionTest.exe");
job.setDirectory(new File("F://programs//NFSWorkSpace"));
job.setStdErrFile("F://test.err");
job.setStdOutFile("F://test.out");
studentService.submintJob(job);//直接调用完成执行
}
}
从上面的例子我们可以看出,整个执行过程就像在本机执行一样,是不是很方便呀!!
测试的程序redirectionTest是用c++写的,代码如下:
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
using std::cerr;
void main()
{
cout<<"hello world!!"<<endl;
cout<<"您好!!!"<<endl;
cout<<"good good "<<endl;
cerr<<"err message"<<endl;
cout<<"after err message"<<endl;
cerr<<"second errmessage"<<endl;
}
最后结果是:在F盘生成两个文件
Test.err
err message
second errmessage
和
Test.out
hello world!!
您好!!!
good good
after err message
实际上这已经是个小的网格的概念,作业调度系统在远程调度作业到执行主机去执行作业。
远程过程调用就像您在本地调用方法一样轻松的完成您的任务,这是一个好方法。
如果您在提交端不想使用Spring,可以用下面的方法,似乎更简练些:
String url = " http://localhost:8080/SpringHessianClientTest/remoting/executeManagerService ";
HessianProxyFactory factory = new HessianProxyFactory();
ExecuteServiceDao executeServiceDao= (ExecuteServiceDao) factory.create(ExecuteServiceDao.class, url);