zoukankan      html  css  js  c++  java
  • 【原创】插件式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");
        }
    }
  • 相关阅读:
    MSSQL 事务说明
    创业课堂之团队
    如何开发HTML编辑器
    IE和Firefox对Documnet,iframe的处理
    jQuery控制iFrame
    如何更高效的制作可通用的HTML页面
    天下武功,无坚不破,唯快不破
    Flash本地通讯
    播放本地MP3 (二)
    播放本地MP3 (一)
  • 原文地址:https://www.cnblogs.com/sper/p/5716819.html
Copyright © 2011-2022 走看看