Java Spring RMI一些嘗試


最近在做一個調度服務器的項目,因為各個服務器上面的定時任務比較多,分別執行的話運維檢查起來比較麻煩.就想到搞一個專門的調度服務器來統一管理這些任務.因為隨時可能增加新的服務器或者新的命令,要是全寫在一起每次要把整個程序替換掉,上線不方便.發現spring可以很好的解決這個問題,新加的程序可以以單獨jar包的方式添加,只需要修改ApplicationContext就行了,client端只需要改配置就行了程序可以不用改.個人感覺spinrg自身的RMI類比Java自帶的好理解一些.

首先是Server端,需要有一個啟動函數,以及各命令對應的實現接口,啟動類 .為了部署方便期間,配置文件路徑直接改外部輸入了

public class StartRMIServer {
    public static void startRMIServer(String path) {
        if(path != null && path.startsWith("/")) {
            new FileSystemXmlApplicationContext("/" + path + "applicationContext.xml");
        }else{
            new FileSystemXmlApplicationContext(path + "applicationContext.xml");
        }
        System.out.println("RMI服務端啟動!!!");
    }

    public static void main(String[] args) {
        startRMIServer(args[0]);
    }
}

 關於FileSystemXmlApplicationContext這里一開始不知道,在Linux下輸入的根路徑會被當成當前路徑處理,去查了之后發現是構造函數會把第一個'/'給去掉,所以這里需要加上.也可以用ClassPathXmlApplicationContext直接以file:開頭

然后加入接口和實現類,當客戶端發起命令時就會調用該接口,接口對應的實現類由spring定義文件來控制,所以就能夠實現不同的命令對應不同的實現類.這里定義了兩個不同的實現類

public interface IRmiService{
    String getMsg(String params) throws Exception;
}

 調用的類預留一個字符串參數,便於有需要的實現類解析后使用

public class HelloWordService implements IRmiService{
    public String getMsg(String params) throws Exception{
        return "Hello World!"; 
    }
}


public class RealTimeService implements IRmiService {
    public String getMsg(String params){
        return "Real Time!" +  params; 
    }
}

ApplicationContext配置定義如下,兩個實現類分別對應不同的命令

   <!-- 定義接口實現類 --> 
    <bean id="helloWordService" class="com.cmb.SpringRmi.HelloWordService" scope="prototype"/> 
   
   <!-- 定義服務端接口 -->
    <bean class="org.springframework.remoting.rmi.RmiServiceExporter">
        <!-- 將遠程接口實現類對象設置到RMI服務中 -->
        <property name="service" ref="helloWordService" />
        <!-- 設置RMI服務名,將作為RMI客戶端的調用接口-->
        <property name="serviceName" value="helloWord" />
        <!-- 將遠程接口設置為RMI服務接口 -->
        <property name="serviceInterface" value="com.cmb.SpringRmi.IRmiService" />
        <!-- 為RMI服務端遠程對象注冊表設置端口號-->
        <property name="registryPort" value="9090" />
    </bean>

   <bean id="realTimeService" class="com.cmb.SpringRmi.RealTimeService" scope="prototype"/>
    <bean class="org.springframework.remoting.rmi.RmiServiceExporter">
        <property name="service" ref="realTimeService" />
        <property name="serviceName" value="realTime" />
        <property name="serviceInterface" value="com.cmb.SpringRmi.IRmiService" />
        <property name="registryPort" value="9090" />
    </bean>

 這樣server端就配置完成了,可以直接啟動試一下

 

client端相當於是每次給server發一個請求命令,server端接收到之后去執行對應的實現類.我這邊實現類加了log4j功能,參數是context路徑,命令和命令對應的參數

public class RMIClient {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("file:" + args[0] + "/applicationContext.xml");
        IRmiServer helloWord = (IRmiServer) ctx.getBean(args[1]);
        LogBean log = (LogBean) ctx.getBean("log");
        PropertyConfigurator.configure(log.getPath());
        Logger logger = Logger.getLogger(RMIClient.class);
        logger.info("Call " + args[1]);
        try {
            String params = "";
            if (args.length > 2 && args[2] != null && !"".equals(args[2])) {
                params = args[2];
            }
            logger.info(helloWord.getMsg(params));
            logger.info(args[1] + " succeed");
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            System.exit(1);
        }
    }
}

 接口名字其實不需要和server端一樣

public interface IRmiServer{
    String getMsg(String params) throws Exception;
}

spring設置如下,這里設置了兩條命令helloWorld和printDate,分別對應類HelloWordService和RealTimeService

    <!--bean id為程序調用時輸入的第一參數-->    
    <bean id="helloWord" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> 
        <!--ip和端口為server的ip和剛才注冊的端口號,服務名為設置的serviceName-->
        <property name="serviceUrl" value="rmi://localhost:9090/helloWord"/>
        <!--調用的接口-->
        <property name="serviceInterface" value="com.cmb.SpringRmiClient.IRmiServer"/>
        <!-- 當連接失敗時是否刷新遠程調用stub -->
        <property name="refreshStubOnConnectFailure" value="true"/>
    </bean>
    
    <bean id="printDate" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> 
        <property name="serviceUrl" value="rmi://localhost:9090/realTime"/>
        <property name="serviceInterface" value="com.cmb.SpringRmiClient.IRmiServer"/>
        <property name="refreshStubOnConnectFailure" value="true"/>
    </bean>

此時就可以執行client,可以看到如果輸入的命令不同,打印出的結果是不同的,說明調用的實現類不同.

部署的時候采用命令行指定java.ext.dirs的方式,這樣可以把所有相關的程序jar包都放到文件夾里面,便於后續擴展程序的部署.

java -Djava.ext.dirs="rmiclient/lib" -jar rmiclient/rmiclient.jar D:/test/rmiclient printDate

關於后續添加部署.我現在想要添加一個程序,那么應該把這個jar包放置到server上的lib目錄中,把server進程結束,然后在server的ApplicationContext增加一組bean,client的ApplicationContext也對應增加一組,重啟之后就可以使用新的程序了.

 通過這個小工具的開發對spring工廠和IOC有了些了解,后面要多研究吃透spring


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM