当前位置: 首页 > 工具软件 > Zeroc ICE > 使用案例 >

Zero Ice 客户端工具类

夏奕
2023-12-01
package com.videtek.vacp.common.utils;
import Ice.ObjectPrx;
import java.lang.reflect.Method;
import java.util.*;
/**
 * @author hehaifeng
 * @date 2018-6-6 10:55:30
 * @version 1.0
 * 作用:此类用于方便ice客户端进行ice通信
 *
 * Locator 地址不能写死在代码里,属于共用部分配置。
 * Communicator 是重量级全局共享的对象,涉及线程池,Socket连接等,每次的创建和销毁既影响性能又容易产生泄露问题
 * Ice  Object 的标识符 (Identity)与类名有99%的可能是重合的,Identity+Prx相当于  Object Proxy的类名
 *
 *  这里有一个隐含的契约式编程,即Ice Object 的Proxy  名字与在IceGrid中定义的 Object Identity 保持之前提到的这种关系(99%相似),契约这样的做法,
 *  省去了很多不必要的代码,这也是契约式编程在很多地方都被或多或少地使用的重要原因
 *
 * 针对一个 iceServerHostUrl 创建一个工具类对象,该对象可以在整个服务过程中持续使用
 *
 * 使用示例:车综里图片识别的写法
 * public class TestDemo {
 *     private  static final Logger logger = LoggerFactory.getLogger(ImageServiceImpl.class);
 *
 *     private  static IceClientUtil iceClientUtil = new IceClientUtil("carDetectSDK:tcp -h 192.168.101.90 -p 12570");
 *
 *     @Test
 *     public void  testIceClientUtil(){
 *         // 建议从配置文件里取
 *         String  iceServerHostUrl="carDetectSDK:tcp -h 192.168.101.90 -p 12570";
 *         // 可创建整个类甚至整个服务的 工具类对象,长期重复使用
 *         CarOutputInfo[] result=null;
 *       try{
 *           SDKInfoPrx sdkInfoPrx = (SDKInfoPrx)iceClientUtil.getServicePrx(SDKInfoPrx.class);
 *           String  imgUrl = "http://192.168.101.90:5000/9EE72232-F5F6-445E-874C-AE82A06A13AC/Snapshot/51C27FB5-6C1E-44FA-960D-AAF0EADFFECF.jpg";
 *           result =  sdkInfoPrx.detect(imgUrl);
 *       }catch (Exception e){
 *           e.printStackTrace();
 *           logger.error("Ice请求服务异常:"+e);
 *       }
 *         String  mm = result[0].vehicleFeature;
 *         System.out.println(mm);
 *         // 使用完毕可以主动释放资源,如果没关闭,超过闲置时间也会自动释放资源
 *         iceClientUtil.closeCommunicator(true);
 *     }
 * }
 *
 */
public class IceClientUtil{
    /**
     *  ice 服务端的 服务名称  通信协议  ip 和端口号
     *  carDetectSDK:tcp -h 192.168.101.90 -p 12570
     */
    private  String iceServerHostUrl;

    /**
     * Ice 客户端和服务端传输文件的最大限制
     */
    private  String iceMessageSizeMax= "10240";

    /**
     * 消息超时时间
     */
    private String iceOverrideTimeout= "60000";

    /**
     * 连接超时时间
     */
    private String iceOverrideConnectTimeout= "60000";

    /**
     * 线程闲置时间  超过闲置时间自动关闭线程  单位秒
     */
    private long idleTimeOutSeconds =3000;

    /**
     * 必须的构造方法
     * @param iceServerHostUrl  ice的服务名称,通信协议,ip  端口
     *  示例:carDetectSDK:tcp -h 192.168.101.90 -p 12570
     */
    public IceClientUtil(String iceServerHostUrl){
        this.iceServerHostUrl=iceServerHostUrl;
    }

    public IceClientUtil(String iceServerHostUrl, String iceMessageSizeMax, String iceOverrideTimeout, String iceOverrideConnectTimeout, long idleTimeOutSeconds) {
        this.iceServerHostUrl = iceServerHostUrl;
        this.iceMessageSizeMax = iceMessageSizeMax;
        this.iceOverrideTimeout = iceOverrideTimeout;
        this.iceOverrideConnectTimeout = iceOverrideConnectTimeout;
        this.idleTimeOutSeconds = idleTimeOutSeconds;
    }

    public String getIceServerHostUrl() {
        return iceServerHostUrl;
    }

    /**
     * 设置最大传输限制
     */
    public void setIceMessageSizeMax(String iceMessageSizeMax) {
        this.iceMessageSizeMax = iceMessageSizeMax;
    }
    /**
     * 设置消息响应超时时间
     */
    public void setIceOverrideTimeout(String iceOverrideTimeout) {
        this.iceOverrideTimeout = iceOverrideTimeout;
    }
    /**
     * 设置连接超时时间
     */
    public void setIceOverrideConnectTimeout(String iceOverrideConnectTimeout) {
        this.iceOverrideConnectTimeout = iceOverrideConnectTimeout;
    }
    /**
     * 设置线程闲置时间,如果超过这个闲置时间程序没有被调用,程序会主动释放资源
     */
    public void setIdleTimeOutSeconds(long idleTimeOutSeconds) {
        this.idleTimeOutSeconds = idleTimeOutSeconds;
    }

    /**
     * 通信调度对象
     */
    private static volatile  Ice.Communicator ic =null;

    /**
     * 存放 ObjectPrx  服务对象的集合
     */
    private  static Map<Class, ObjectPrx> cls2PrxMap = new HashMap<>();

    /**
     * 最后存取时间标记
     */
    private static volatile long lastAccessTimestamp;

    /**
     * 守护线程  后面自己创建的线程内部类
     */
    private  volatile MonitorThread nonitorThread;


    /**
     * 延迟加载 Communicator
     */
    private  Ice.Communicator getIceCommunictor(){
        // 如果 通信调度器对象为null
        if (ic==null){
            // 锁定当前调用对象
            synchronized (this){
                if (ic==null){
                        // 创建 ic 调度器对象
                         ic = Ice.Util.initialize(initIData());
                         // 创建守护线程
                        createMonitorThread();
                }
            }
        }
        lastAccessTimestamp =System.currentTimeMillis();
        return ic;
    }

    /**
     * 创建守护线程
     */
    private  void createMonitorThread() {
        // 创建线程
        nonitorThread = new MonitorThread();
        // 将其设置成守护线程
        nonitorThread.setDaemon(true);
        // 启动守护线程
        nonitorThread.start();
    }

    /**
     * 关闭Conmuicator,释放资源
     */
    public  void closeCommunicator(boolean removeServiceCache){
        // 锁定当前类的字节码对象
        synchronized (this){
            // 如果ic不为null则安全关闭
            if (ic!=null){
                // 调用方法安全关闭
                safeShutdown();
                //  中断守护线程
                nonitorThread.interrupt();
                // 如果决定要关闭 Communicator对象(removeServiceCache=true),并且cls2PrxMap对象不为空
                if (removeServiceCache && !cls2PrxMap.isEmpty()){
                    try {
                        // 清空 cls2PrxMap集合
                        cls2PrxMap.clear();
                    }catch (Exception e){
                        e.printStackTrace();
                       throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    private  void safeShutdown(){
        try{
            // 调用线程的shutdown()方法停止接受新的任务,当前任务执行完毕后就停止
            ic.shutdown();
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            // 最后销毁ic
            ic.destroy();
            // 为进一步却表不会内存泄露,给它null值交给 GC
            ic=null;
        }
    }

    /**
     * 用反射方式创建 Object  Proxy
     * 创建客户端代理对象
     * @param communicator  ic 通信调度器对象
     * @param serviceCls  所用方法接口类  serviceCls  : 接口类字节码 SDKInfoPrx.Class
     * @return   SDKInfoPrx向上转型成的ObjectPrx的对象 (已经是加入通信调度的对象)
     */
    private  ObjectPrx createIceProxy(Ice.Communicator communicator,Class serviceCls){
        // 定义客户端代理对象  ObjectPrx 是所有客户端代理接口的父类
        ObjectPrx proxy =null;
        // 获取  serviceCls 的类全名  示例:com.videtek.ice.carDetectSDK.SDKInfoPrx
        String  clsName =serviceCls.getName();
        // 获取简单名字  示例: SDKInfoPrx
        String  serviceName =serviceCls.getSimpleName();
        // 获取 SDKInfoPrx 中最后一次出现 Prx 的下标  SDKInfoPrx 中pos =7
        int pos =serviceName.lastIndexOf("Prx");
        // 如果 serviceName 里没有  Prx 则 pos 会等于 -1
        if (pos<=0){
            throw new IllegalArgumentException("无效的 ObjectPrx class , class 名必须以 Prx  结尾!");
        }
        //  截取前面的一部分  示例:SDKInfoPrx  -- 》 SDKInfoPrx
        String realSvName =serviceName.substring(0,pos);
        try{
            // 创建基础 Ice.ObjectPrx 对象  realSvName:SDKInfoPrx
            Ice.ObjectPrx base =communicator.stringToProxy(iceServerHostUrl);
            // 通过反射获取接口实现类对象
            // clsName+"Helper" --》 "com.videtek.ice.carDetectSDK.SDKInfoPrx"+"Helper" --> com.videtek.ice.carDetectSDK.SDKInfoPrxHelper
            //获取SDKInfoPrxHelper 对象  向上转型成  ObjectPrx 类型(所有接口的父类型)
            proxy =(ObjectPrx)Class.forName(clsName+"Helper").newInstance();
            // 通过反射 获取 SDKInfoPrxHelper 对象里的 uncheckedCast()方法
            Method m1 =proxy.getClass().getDeclaredMethod("uncheckedCast",ObjectPrx.class);
            // 通过 反射的方式获取  利用SDKInfoPrxHelper对象的
            // public static SDKInfoPrx uncheckedCast(Ice.ObjectPrx __obj, String __facet){
            //        return uncheckedCastImpl(__obj, __facet, SDKInfoPrx.class, SDKInfoPrxHelper.class);
            //    }
            // 方法获取 SDKInfoPrx对象,并向上转型成 ObjectPrx对象
            proxy = (ObjectPrx)m1.invoke(proxy,base);
            // 将 SDKInfoPrx对象返回
            return proxy;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    /**
     * 用于客户端API获取ice服务的示例场景
     * @param serviceCls  SDKInfoPrx.Class
     * @return  包含了 id通信调度器的 并向上转型成 ObjectPrx 对象的  SDKInfoPrx 的实现类对象
     */
    public  ObjectPrx getServicePrx(Class serviceCls){
        // 从 cls2PrxMap里取 serviceCls
        ObjectPrx  proxy = cls2PrxMap.get(serviceCls);
        // 如果集合里有该 serviceCls 对应的  ObjetcPrx 对象  proxy!=null
        // 说明 之前该对象被使用过,可以继续使用
        if (proxy!=null){
            // 将最近存取时间设置为当前系统时间
            lastAccessTimestamp =System.currentTimeMillis();
            // 并且将该  ObjectPrx 对象返回给客户使用
            return proxy;
        }
        // 如果 proxy==null 说明 该对象是第一次被使用  就使用createIceProxy(getIceCommunictor(),serviceCls);方法创建一个ObjectPrx对象
        proxy =createIceProxy(getIceCommunictor(),serviceCls);
        // 将创建的对象放入 cls2PrxMap集合中  key值:SDKInfoPrx.Class  vlaue值:可通信的SDKInfoPrx对象向上转型的 ObjectPrx对象
        cls2PrxMap.put(serviceCls,proxy);
        // 将最近存取时间设置为当前系统时间
        lastAccessTimestamp =System.currentTimeMillis();
        // 将服务用的 ObjectPrx对象返回给用户
        return proxy;
    }

    /**
     * 内部类创建守护线程
     * 主要是防止客户用完后忘记关闭,导致内存泄露
     */
     class MonitorThread extends Thread{
        @Override
        public void run(){
            // 如果当前线程没有被中断,如果被中断说明客户调用了closeCommunicator(true)方法关闭了线程
            while (!Thread.currentThread().isInterrupted()){
                try{
                    // 让当前线程休眠5000秒
                    Thread.sleep(5000L);
                    // 如果最后存取时间加上闲置时间小于当前系统时间,说明该线程已经超时,则将其关闭
                    if (lastAccessTimestamp+idleTimeOutSeconds*1000L<System.currentTimeMillis()){
                        // 关闭该线程  传值true表示主动关闭
                        closeCommunicator(true);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * 规范请求参数
     */
    private  Ice.InitializationData initIData() {
        Ice.Properties properties = Ice.Util.createProperties();
        // 设置最大传输信息量
        properties.setProperty("Ice.MessageSizeMax",iceMessageSizeMax);
        // 消息超时时间
        properties.setProperty("Ice.Override.Timeout", iceOverrideTimeout);
        // 连接超时时间
        properties.setProperty("Ice.Override.ConnectTimeout", iceOverrideConnectTimeout);
        Ice.InitializationData iData = new Ice.InitializationData();
        iData.properties = properties;
        return iData;
    }

}
 类似资料: