【原創】插件式ICE服務框架


Zero ICE在跨平台、跨語言的環境中是一種非常好的RPC方案,而且使用簡單。早期在使用ICE時,每一個后端功能模塊都以獨立服務方式部署,在功能模塊較少時不會有明顯的問題,但是隨着功能模塊的增多,部署的服務越來越多,產生的直接問題有:

  1. 每個服務都需要開啟一個監聽端口,新增服務必須配置防火牆,且影響安全性;
  2. 每個服務即為一個進程,增大系統負擔。

想到能否按照插件方式來開發功能模塊,同時還能解決上面兩個問題。因為所有的后端服務使用Java語言開發,於是選擇了java平台下的輕量級插件框架pf4j(關於pf4j的詳細資料,請參考github.com上項目說明)。

下面,我以最少的代碼來闡述這個插件式ICE服務框架。

  • 定義插件擴展接口

IceService.java

1 package server;
2 
3 import ro.fortsoft.pf4j.ExtensionPoint;
4 
5 public interface IceService extends ExtensionPoint {
6     Ice.Object getObject();
7     String getName();
8     String getGUID();
9 }

為了演示插件接口的可擴展性,定義了一個IceServiceV2.java

package server;

public interface IceServiceV2 extends IceService {
    String getVersion();
}
  • 開發插件

插件1—Echo

package plugin.echo;

import Ice.Object;
import ro.fortsoft.pf4j.Extension;
import ro.fortsoft.pf4j.Plugin;
import ro.fortsoft.pf4j.PluginWrapper;
import server.IceService;
import server.IceServiceV2;


public class EchoPlugin extends Plugin {

    public EchoPlugin(PluginWrapper wrapper) {
        super(wrapper);
    }

    @Override
    public void start()
    {
        System.out.println("start plugin " + this.getClass().getName());
    }
    
    @Override
    public void stop()
    {
        System.out.println("stop plugin " + this.getClass().getName());
    }
    
    @Extension
    public static class EchoService implements IceServiceV2 {
        Ice.Object object;
        
        public Object getObject() {
            object = new EchoI();
            return object;
        }

        public String getName() {
            return "Echo";
        }
        
        public String getGUID()
        {
            return "1234-5678";
        }

        @Override
        public String getVersion() {
            return "V2";
        }
    }
}

其中,EchoI類的定義如下

package plugin.echo;

import Ice.Current;

public class EchoI extends rpc._EchoDisp {

    @Override
    public String reply(String message, Current __current) {
        System.out.println("Receive " + message);
        return message;
    }
}

其實現了如下ice接口

module rpc
{
    interface Echo
    {
        string reply(string message); 
    };
};

插件2—Hello

package plugin.hello;

import Ice.Object;
import ro.fortsoft.pf4j.Extension;
import ro.fortsoft.pf4j.Plugin;
import ro.fortsoft.pf4j.PluginWrapper;
import server.IceService;

public class HelloPlugin extends Plugin {

    public HelloPlugin(PluginWrapper wrapper) {
        super(wrapper);
    }

    @Override
    public void start()
    {
        System.out.println("start plugin " + this.getClass().getName());
        
    }
    
    @Override
    public void stop()
    {
        System.out.println("stop plugin " + this.getClass().getName());
    }
    
    
    @Extension
    public static class HelloService implements IceService
    {
        Ice.Object object;
        
        @Override
        public Object getObject() {
            object = new HelloI();
            return object;
        }

        @Override
        public String getName() {
            return "Hello";
        }
        
        public String getGUID()
        {
            return "5678-5678";
        }
    }
}

其中,HelloI類的定義如下

package plugin.hello;

import Ice.Current;

public class HelloI extends rpc._HelloDisp {
    
    public void sayHello(String message, Current __current) {
        System.out.println("Hello " + message);
    }
}

其實現了如下ice接口

module rpc
{
    interface Hello
    {
        void sayHello(string message); 
    };
};
  • 制作插件

在這里,有必要提一下我遇到的波折。查看了pf4j的文檔后得知,每個插件的目錄結構為

echo + 
     |
     -- classes +
     |          |
     |          -- META-INF +
     |          |           |
     |          |           --extensions.idx
     |          |           |
     |          |           --MANIFEST.MF
     |          -- (編譯后的插件代碼)
     -- lib +
            |
            -- (插件依賴的第三方jar

由於我使用eclipse編輯和生成代碼,因此在編譯代碼時並沒有為@Extension標注生成extensions.idx文件,所以第一次驗證插件是否生效時,loadplugins和startplugins都沒有問題,但是執行List<IceService> exts = manager.getExtensions(IceService.class)時並沒有找到extension,這是掉的第一個坑。

由於我並不想使用maven來生成代碼,於是手動創建了插件的extensions.idx文件,其中echo插件的內容是

plugin.echo.EchoPlugin$EchoService

MANIFEST.MF文件的內容為

Manifest-Version: 1.0
Plugin-Dependencies: 
Plugin-Version: 0.0.1
Plugin-Id: echo-plugin
Plugin-Provider: Super
Plugin-Class: plugin.echo.EchoPlugin
Build-Jdk: 1.8.0_92

在准備好插件必要的文件后,再次執行代碼,遇到了第二個坑,這個坑有點深——將整個項目編譯后的代碼都拷貝到了兩個插件中,這直接導致的后果是依然找不到extension。直至我在看了N遍pf4j的demo代碼,並使用maven生成並成功執行其demo后,才確定了這個深坑。

  • 執行主程序
package server;

import java.util.List;

import ro.fortsoft.pf4j.DefaultPluginManager;

public class Entry {

    public static void main(String[] args) {
        
        try {
            DefaultPluginManager manager = new DefaultPluginManager();
            manager.loadPlugins();
            manager.startPlugins();

            Ice.Communicator ic = Ice.Util.initialize();
            Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("IceAdapter", "default -p 9005");

            List<IceService> exts = manager.getExtensions(IceService.class);
            for (IceService ext : exts) {
                System.out.println("Add object " + ext.getName() + ", GUID=" + ext.getGUID());
                if (ext instanceof IceServiceV2)
                {
                    System.out.println(((IceServiceV2)ext).getVersion());
                }
                adapter.add(ext.getObject(), ic.stringToIdentity(ext.getName()));
                
            }
            System.out.println("Active adapter");
            adapter.activate();
            ic.waitForShutdown();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}

當然,我實際編寫的ICE服務框架比這個要復雜的多,包括使用到Java Service Wrapper,僅裝載.zip格式的插件,增加異常處理等。

最后給出Ice的Client代碼

package client;

public class Echo {

    public static void main(String[] args) {
        Ice.Communicator ic = null;
        try {
            ic = Ice.Util.initialize();
            Ice.ObjectPrx base = ic.stringToProxy("Echo:default -p 9005");
            rpc.EchoPrx proxy = rpc.EchoPrxHelper.checkedCast(base);
            if (proxy == null) {
                throw new Error("Invalid proxy");
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(proxy.reply("message " + i));
            }

        } catch (Ice.LocalException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (ic != null) {
            try {
                ic.destroy();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        System.out.println("exit");
    }
}

 


免責聲明!

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



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