此文转自:http://hellohank.iteye.com/blog/773894
前段时间在做一个日终系统(统一管理所有客户端系统中各定时任务的一个系统)时,实现了其中一个远程管理客户端程序运行状态的模块。这里写出来与大家共享,如有不足之处,希望大家指出共同探讨完善!
在这个远程控制的模块中,对其的基本的需求如下:
· 启动/终止等远程客户端系统中指定的程序;
· 请求要异步,不能占用服务端系统(即日终系统)的资源,即启动客户端程序之后,就与客户端断开连接,让客户端自己运行;
· 可以随时查看指定客户端的指定程序的运行状态,便于监控;
· 如果客户端程序有发生线程死琐、无法连接服务等环境异常时,能够自动预警(即实时监控);
· 程序执行过程中(包括执行成功后)各种对应的状态可以返回到服务端;
根据上面的需求,首先想到的是这个模块的结构将是服务器/客户端模式。考虑使用这种模式的设计,则接下来应该考虑选择什么样的远程方式。当然,如果自己实现一个远程连接功能,小弟自认为能力还不足,呵呵,所以不得不从现有的远程连接方式中选择一个作为自己远程连接的基础。
在对比各种远程方式的对比中,我发现两种远程技术满足我的要求(可以用于异构系统间、调用方式灵活、使用上可以无限扩展):xmlrpc和JMX。
再继续对比这两种技术的优缺点:
优点:
xmlrpc:配置简单、使用方便;
JMX:JDK中自带的,因此不需要第三方jar包支持即可使用;同时也可以动态加载/卸载客户端bean(这是我最想要的功能)
缺点:
xmlrpc:需要第三方jar包支持;
JMX:需要服务端开通相应的端口支持,因此可能会存在很大的安全隐患;
对比上面主要、简单的优缺点之后,考虑到在企业应用中安全问题往往是占优先位置的,而且最主要的是,JMX在企业应用的发布时,需要开端口,这个过程挺痛苦的。因此,这里不得不摒弃JMX(虽然我也用JMX验证并实现了整个模块的控制功能)。
在选择了xmlrpc为远程连接的工具之后,就着手设计整个结构了:
整体设计
从使用角度来划分其总体设计:先在服务端发起一个操作请求,传输到客户端,客户端作相应的响应,然后将操作的最终结果返回服务端,便于服务端的后续操作。在额外的角度来说,还有一个实时监控功能。从这里来看,由于在请求方面来说,服务端与客户端都相互有信息传递的功能,因此,这里的整体分为三个模块:服务端——公共端——客户端。
其总体设计的UML图如下:
在上图中,灰色表示服务端的系统(即日终系统)、深蓝色表示服务端模块、淡黄色表示公共模块、桔黄色表示客户端模块、青色表示客户端系统。其中,灰色和青色是在具体使用时的结合部分。真正的该远程控制模块的部分为:深蓝色、淡黄色和桔黄色。
详细设计
服务端模块
在服务端模块中主要的功能是主动发起请求与实时监控!这两个类分别是OssXmlrpcClientFactory、OssListenClientStatusExcutor。
OssXmlrpcClientFactory类主要是提供对客户端操作的主要一些接口,当然,也有方便扩展的接口。
在上面的代码中,其方法中主要提供了常用的操作!再看监听程序:OssListenClientStatusExcutor。从其中的代码(见附件中的代码)也可以看出,这里是监控指定的客户端系统中指定的方法。
客户端模块
在客户端需要说明客户端受控程序遵守的规则,即需要继承OssXmlClientSupport类,表示该类接受服务端的控制,也就是相当于接受协议一样,这样可以防止一些不愿意被控制的类也被误控!
为了将程序执行的状态返回到服务端,客户端需要一个执行回返状态的功能类:ConnectServerExcutor。
这两个类的详细功能请参考代码!
公共模块
除了上面两个模块所说的功能以外,其余的功能(如:线程管理、远程连接响应处理等)都属于公共模块中内容,这一模块在客户端/服务端都需要!
在这个模块中,重要的类有如下:
· OssXmlrpcRemoteExcutor。这是远程响应的类。
· OssXmlrpcServlet。这是初始化OssXmlrpcRemoteExcutor到xmlrpc的环境中以及将每次远程请求转为单例模式。xmlrpc执行时是需要将被调用的类的信息存放到它的容器中,这里有比较详细的说明:http://former.iteye.com/blog/223673,我这里因为只需要通过OssXmlrpcRemoteExcutor作为“网关”,因此我因需要将它加入到xmlrpc中即可,其关键代码如下:
- mapping.load(Thread.currentThread().getContextClassLoader(), getDefaultProps());
- ……
- /**
- * 将提供服务的类,从系统中加载进来
- */
- protected Properties getDefaultProps() {
- Properties props = new Properties();
- props.put(OssXmlrpcRemoteExcutor.class.getSimpleName(), OssXmlrpcRemoteExcutor.class.getName());
- return props;
- }
mapping.load(Thread.currentThread().getContextClassLoader(), getDefaultProps());
……
/**
* 将提供服务的类,从系统中加载进来
*/
protected Properties getDefaultProps() {
Properties props = new Properties();
props.put(OssXmlrpcRemoteExcutor.class.getSimpleName(), OssXmlrpcRemoteExcutor.class.getName());
return props;
}
使用说明
服务端系统
1、环境配置
· 加载xmlrpc的三个jar包。
· 服务端需要实现:OssXmlrpcRemoteResponse接口的类。如果不是Spring容器的话,需要重新实现FindInstanceClass接口并在OssXmlrpcUtils中重新指定。
· 由于需要接受客户端传递回来的请求,因此需要在web.xml中配置如下:
- <servlet>
- <servlet-name>XmlRpcServlet</servlet-name>
- <servlet-class>com.cloudtech.xmlrpc.common.OssXmlrpcServlet
- </servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>XmlRpcServlet</servlet-name>
- <url-pattern>/xmlrpc</url-pattern>
- </servlet-mapping>
<servlet> <servlet-name>XmlRpcServlet</servlet-name> <servlet-class>com.cloudtech.xmlrpc.common.OssXmlrpcServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>XmlRpcServlet</servlet-name> <url-pattern>/xmlrpc</url-pattern> </servlet-mapping>
2、初始化
这里的初始化其实是初始化两个内容:远程连接的连接配置信息,即xmlrpc远程配置信息;监听程序。具体的初始化方式示例如下:
- import java.net.URL;
- import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
- import com.cloudtech.xmlrpc.common.OssXmlrpcPoolManager;
- import com.cloudtech.xmlrpc.server.OssListenClientStatusExcutor;
- public class OssXmlrpcRemoteInit {
- public void init() {
- // TODO 如果不希望影响系统启动速度,可以启用新线程执行下面的过程
- initRemote();
- initListener();
- }
- /**
- * 初始化监听器
- */
- protected void initListener() {
- // TODO 先初始化需要监听的程序,这里就不实现了
- OssListenClientStatusExcutor.startListen();
- }
- /**
- * 初始化远程连接。
- */
- protected void initRemote() {
- XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
- try {
- // 连接到客户端。
- config.setServerURL(new URL("http://tfc.vemic.com:8081/xmlrpc"));// 这里是客户端的xmlrpc配置
- config.setEnabledForExtensions(true);
- config.setEnabledForExceptions(true);
- config.setBasicEncoding("GBK");
- config.setConnectionTimeout(30000);
- config.setContentLengthOptional(false);
- config.setReplyTimeout(0);
- OssXmlrpcPoolManager.createXmlRpcClient("localhost", config);// 这是连接池管理类。
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
import java.net.URL;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import com.cloudtech.xmlrpc.common.OssXmlrpcPoolManager;
import com.cloudtech.xmlrpc.server.OssListenClientStatusExcutor;
public class OssXmlrpcRemoteInit {
public void init() {
// TODO 如果不希望影响系统启动速度,可以启用新线程执行下面的过程
initRemote();
initListener();
}
/**
* 初始化监听器
*/
protected void initListener() {
// TODO 先初始化需要监听的程序,这里就不实现了
OssListenClientStatusExcutor.startListen();
}
/**
* 初始化远程连接。
*/
protected void initRemote() {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
try {
// 连接到客户端。
config.setServerURL(new URL("http://tfc.vemic.com:8081/xmlrpc"));// 这里是客户端的xmlrpc配置
config.setEnabledForExtensions(true);
config.setEnabledForExceptions(true);
config.setBasicEncoding("GBK");
config.setConnectionTimeout(30000);
config.setContentLengthOptional(false);
config.setReplyTimeout(0);
OssXmlrpcPoolManager.createXmlRpcClient("localhost", config);// 这是连接池管理类。
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端系统
1、环境配置
· 加载xmlrpc三个jar包。
· 加载远程控制模块的公共模块包、客户端模块包。
· 由于需要被服务端连接操作,因此需要对外提供xmlrpc远程服务,因此需要在web.xml中配置如下:
- <servlet>
- <servlet-name>XmlRpcServlet</servlet-name>
- <servlet-class>com.cloudtech.xmlrpc.common.OssXmlrpcServlet
- </servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>XmlRpcServlet</servlet-name>
- <url-pattern>/xmlrpc</url-pattern><!-- 注意这里的配置与上面的代码的客户端连接的对应关系 -->
- </servlet-mapping>
<servlet>
<servlet-name>XmlRpcServlet</servlet-name>
<servlet-class>com.cloudtech.xmlrpc.common.OssXmlrpcServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XmlRpcServlet</servlet-name>
<url-pattern>/xmlrpc</url-pattern><!-- 注意这里的配置与上面的代码的客户端连接的对应关系 -->
</servlet-mapping>
· 为了将执行的状态返回到服务端,这里还需要配置ConnectServerExcutor类到系统环境中(如果该类中的默认值与服务端的配置是一致的,则倒可以不需要配置),其配置示例:
- <bean class="com.cloudtech.xmlrpc.client.ConnectServerExcutor">
- <property name="serverURL" value="http://tfc.vemic.com/xmlrpc" />
- </bean>
<bean class="com.cloudtech.xmlrpc.client.ConnectServerExcutor"> <property name="serverURL" value="http://tfc.vemic.com/xmlrpc" /> </bean>
2、编写代码
接下来就可以使用它的控制功能了:
- import org.springframework.stereotype.Service;
- import com.cloudtech.xmlrpc.client.OssXmlClientSupport;
- @Service
- public class OssTriggerDemo extends OssXmlClientSupport {//注意这里继承了OssXmlClientSupport
- public void execute() {
- System.err.println("executeDemo...");
- for (int i = 1; true; i++) {
- System.out.println("第" + i + "次循环!");
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
import org.springframework.stereotype.Service;
import com.cloudtech.xmlrpc.client.OssXmlClientSupport;
@Service
public class OssTriggerDemo extends OssXmlClientSupport {//注意这里继承了OssXmlClientSupport
public void execute() {
System.err.println("executeDemo...");
for (int i = 1; true; i++) {
System.out.println("第" + i + "次循环!");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
注意:上面的@Service是使用Spring的注解将该类扫描到Spring容器中去,这样“网关”类OssXmlrpcRemoteExcutor就可以通过Spring获取到该类。
3、控制演示
在服务端,发起控制请求:
- public void testRemote() {
- try {
- OssXmlrpcClientFactory.execute("localhost", "OssTriggerDemo.execute");
- } catch (OssXmlrpcException e) {
- e.printStackTrace();
- }
- }
public void testRemote() {
try {
OssXmlrpcClientFactory.execute("localhost", "OssTriggerDemo.execute");
} catch (OssXmlrpcException e) {
e.printStackTrace();
}
}
说明
· 如果需要认证,则可以在OssXmlrpcServlet、ConnectServerExcutor有对应的用户名、密码设置。
· 客户端受控的类需要加载到环境中,或者,可以重新实现接口FindInstanceClass来获取到指定类的实例化。
结语
· 这是一开始的雏形,现在应用于公司的日终系统中之后,有些地方已经完善过了,具体改的地方也不记得了……
· 由于能力有限,写出的也点这个水平了,希望大家能够给出建议完善它!