Java实现轻量级方法路由

在工作中碰到了这样的一个问题,签电子合同,发送短信往往都是多个服务提供商,但是使用每个服务商的权重不同,每个业务线可能使用的服务提供商也会不同,像这种服务提供商的路由选择,可以通过设计表来进行实现,不过我用了另外一种方式实现。

这个小工具的实现使用的zookeeper, 支持动态的配置各个权重。下面是具体的实现过程。

1.为了降低处理的复杂度,并没有使用注解,而是自定义一个spring命名空间,就像dubbo一样.

public class ThorNamespaceHandler extends NamespaceHandlerSupport{

    @Override
    public void init() {
        registerBeanDefinitionParser("routing", new RuleBeanDefinitionParser());
    }

}


public class RuleBeanDefinitionParser implements BeanDefinitionParser{

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        if(element == null) {
            return null;
        }
        RoutingSchemaBean routing = new RoutingSchemaBean();
        routing.setCode(element.getAttribute("code"));
        routing.setDefaultValue(element.getAttribute("default"));

        NodeList nodeList = element.getChildNodes();
        setRoutingRules(routing, nodeList);
        RoutingRuleSchemaContext.put(routing);
        return null;
    }
    
    private void setRoutingRules(RoutingSchemaBean routing, NodeList nodeList){
        if(nodeList == null) {
            return;
        }
        for(int i=0;i<nodeList.getLength();i++){
            Node node = nodeList.item(i);
            if(node == null) {
                continue;
            }
            if(!node.getNodeName().contains("rules")){
                continue;
            }
            NodeList subList = node.getChildNodes();
            if(subList == null) {
                continue;
            }
            for(int j=0;j<subList.getLength();j++){
                Node sub = subList.item(j);
                if(sub == null) {
                    continue;
                }
                if(!sub.getNodeName().contains("rule")){
                    continue;
                }
                RuleSchemaBean rule = getRule(sub);
                routing.addRules(rule);
            }
        }
    }
    
    private RuleSchemaBean getRule(Node sub){
        RuleSchemaBean bean = new RuleSchemaBean();
        NamedNodeMap attrs = sub.getAttributes();
        for(int i=0;i<attrs.getLength();i++) {
            Node each = attrs.item(i);
            try {
                BeanUtilEx.setProperty(bean, each.getNodeName(), each.getNodeValue());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return bean;
    }
}

上面的代码就是实现自定义spring命名空间(就是自定义标签)的代码部分,后面会贴出一个示例

2. 将配置数据存储到zookeeper上,这里要处理zookeeper链接断开的问题,因为连接一旦断开,会话就失效了,所以这个时候要重新创建客户端示例,重新监听所有节点数据的变更。

下面时zookeeper的一些封装处理:

public abstract class ConnectionWatcher implements Watcher , AutoCloseable {

    protected static Logger logger = LoggerFactory.getLogger(ConnectionWatcher.class);

    private CountDownLatch countDownLatch = new CountDownLatch(1);
    // 10秒 会话时间,避免频繁的session expired
    private static final int SESSION_TIMEOUT = 10000;
    // 3秒 连接超时时间
    private static final int CONNECT_TIMEOUT = 5000;
    // 重试间隔时间
    protected static final int RETRY_PERIOD_INTERVAL = 2;

    protected ZooKeeper zk;

    private String host;

    @Override
    public void process(WatchedEvent event) {

        // 连接状态处理
        keeperStateResolve(event);

        // 节点事件处理
        if(event.getType() != null && !event.getType().equals(Event.EventType.None)) {
            nodeEventResolve(event);
        }
    }

    @Override
    public void close() throws InterruptedException {
        zk.close();
    }

    /**
     * 连接zk server
     * @throws Exception
     */
    protected void connect(String host) throws Exception {
        this.host = host;

        zk = new ZooKeeper(host,SESSION_TIMEOUT,this);
        // 可能连接超时
        countDownLatch.await(CONNECT_TIMEOUT,TimeUnit.MILLISECONDS);
    }

    /**
     * 事件处理业务逻辑
     * @param path 节点
     * @param eventType 事件类型
     */
    protected abstract void eventHandle(String path, Event.EventType eventType);

    /**
     * 处理连接状态
     */
    protected abstract void keeperStatHandle(WatchedEvent event);

    /**
     * 重新建立zk客户端
     */
    private void reConnect() {

        logger.info("zookeeper 会话(session)过期,即将重新建立连接");

        int retries = 0;

        while (true) {

            try {

                if(zk != null && !ZooKeeper.States.CLOSED.equals(zk.getState())) {
                    break;
                }

                // 关闭连接
                close();

                // 创建连接
                connect(this.host);

            } catch (Exception e) {

                logger.error("第 {} 次重试异常,异常信息:{}",(++retries),e.getMessage(),e);

                try {
                    logger.info("sleep {}s",RETRY_PERIOD_INTERVAL);
                    TimeUnit.SECONDS.sleep(RETRY_PERIOD_INTERVAL);
                } catch (Exception ex) {
                    // nothing to do
                }
            }
        }
    }

    /**
     * 处理连接状态
     * @param event 事件类型
     */
    private void keeperStateResolve(WatchedEvent event) {
        switch (event.getState()) {
            case SyncConnected:

                keeperStatHandle(event);

                countDownLatch.countDown();

                logger.info("Connect zk server success");

                break;
            case Expired:

                /*session expired: 会话过期 此场景下需创建新的zk实例,因旧实例已不再有效,无法再与server建立连接.*/
                logger.warn("Session expired to zk server,will reconnect.");
                reConnect();
                break;
            case Disconnected:

                /*
                 * 未与任何server建立连接.
                 * (可能zk server 宕机,需要等待其重启.但首次)
                 */
                logger.error("zk disconnected");
                break;
        }
    }

    /**
     * 处理节点事件
     * @param event 事件类型
     */
    private void nodeEventResolve(WatchedEvent event) {
        if(event.getType() != null) {
            logger.info("Got Event:{}",event.getType().toString());
            eventHandle(event.getPath(),event.getType());
        }
    }

}


/**
 * Zookeeper客户端(单例)
 * @author caoawei
 */
public class ZkClient extends ConnectionWatcher {

    // 最大重试次数
    private static final int MAX_RETRIES = 3;

    private static final Charset CHARSET = Charset.forName("UTF-8");

    private static class SingletonHolder {
        private static ZkClient zkClient = new ZkClient();
    }

    private AtomicBoolean alive = new AtomicBoolean(false);

    private WatcherDispatcher watcherDispatcher;

    private ZkClient () {}

    public static ZkClient getInstance() {
        return SingletonHolder.zkClient;
    }

    /**
     * 初始化ZkClient:
     * 初始化方法抛出异常时,建议使用者
     * 在应用启动时终止虚拟机的启动,待zookeeper正常启动后
     * 再启动应用系统.
     *
     * @param host zookeeper 地址
     * @throws Exception 初始化异常
     */
    public ZkClient init(String host) throws Exception {

        if(alive.get()) {
            return this;
        }

        synchronized (this) {
            if(alive.get()) {
                return this;
            }

            if(watcherDispatcher == null) {
                watcherDispatcher = new WatcherDispatcher();
            }

            connect(host);

            alive.set(true);

            return this;
        }
    }

    /**
     * 注册节点事件处理器
     * @param eventHandler 节点事件处理器
     */
    public ZkClient registerHandler(EventHandler eventHandler) {
        synchronized (this) {
            if(watcherDispatcher == null) {
                watcherDispatcher = new WatcherDispatcher();
            }
            watcherDispatcher.registerHandler(eventHandler);
        }
        return this;
    }

    /**
     * zk节点是否存在
     * @param path 节点
     * @param watch 是否监控
     * @return boolean
     * @throws KeeperException zookeeper异常
     * @throws InterruptedException zookeeper异常
     */
    public boolean exists(String path,boolean watch) throws KeeperException,InterruptedException {

        int retries = 0;

        while (true) {
            try {

                Stat stat = zk.exists(path,watch);

                return stat != null;

            } catch (KeeperException.SessionExpiredException e) {
                throw e;
            } catch (KeeperException e) {

                logger.error("The method exists execute error,error info is "+ e.toString());

                if(retries++ == ZkClient.MAX_RETRIES) {
                    throw e;
                }

                try {

                    int sleepTime = ConnectionWatcher.RETRY_PERIOD_INTERVAL * retries;

                    logger.warn("Method exists 第 {} 次重试,sleep {}s",retries,sleepTime);

                    TimeUnit.SECONDS.sleep(sleepTime);

                } catch (Exception ex) {
                    // nothing to do
                }
            }
        }

    }

    /**
     * 创建zk节点
     * @param path 节点
     * @param data 节点数据
     * @param createMode 节点类型
     * @return 节点路径
     * @throws KeeperException zookeeper异常
     * @throws InterruptedException zookeeper异常
     */
    public String create(String path,String data,CreateMode createMode) throws KeeperException,InterruptedException {

        int retries = 0;

        while (true) {
            try {
                Stat stat = zk.exists(path,false);

                byte[] val = data == null ? "".getBytes(CHARSET) : data.getBytes(CHARSET);

                if(stat == null) {
                    zk.create(path,val, ZooDefs.Ids.OPEN_ACL_UNSAFE,createMode);
                } else {
                    zk.setData(path,val,stat.getVersion());
                }

                return path;
            } catch (KeeperException.SessionExpiredException e) {
                throw e;
            } catch (KeeperException e) {
                logger.error("The method create execute failure,the error info is "+e.getMessage(),e);

                if(retries++ == ZkClient.MAX_RETRIES) {
                    throw e;
                }

                try {

                    int sleepTime = ConnectionWatcher.RETRY_PERIOD_INTERVAL * retries;

                    logger.warn("Method create 第 {} 次重试,sleep {}s",retries,sleepTime);

                    TimeUnit.SECONDS.sleep(sleepTime);

                } catch (Exception ex) {
                    // nothing to do
                }
            }
        }
    }

    /**
     * 获取zk节点数据
     * @param path 节点
     * @param watch 是否监控
     * @param stat 当前节点元数据
     * @return 节点数据
     * @throws KeeperException zookeeper异常
     * @throws InterruptedException zookeeper异常
     */
    public String getData(String path,boolean watch,Stat stat) throws KeeperException,InterruptedException {
        byte[] data = zk.getData(path,watch,stat);
        return new String(data,CHARSET);
    }

    /**
     * 设置zk节点数据
     * (如果节点不存在则创建节点并设置数据)
     * @param path 节点
     * @param data 节点数据
     * @throws KeeperException zookeeper异常
     * @throws InterruptedException zookeeper异常
     */
    public void setData(String path, String data) throws KeeperException,InterruptedException {
        int retries = 0;

        while (true) {
            try {
                Stat stat = zk.exists(path,false);

                byte[] val = data == null ? "".getBytes(CHARSET) : data.getBytes(CHARSET);

                if(stat == null) {
                    zk.create(path,val, ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
                } else {
                    zk.setData(path,val,stat.getVersion());
                }

                break;

            } catch (KeeperException.SessionExpiredException e) {
                throw e;
            } catch (KeeperException e) {
                logger.error("The method setData execute failure,the error info is "+e.getMessage(),e);

                if(retries++ == ZkClient.MAX_RETRIES) {
                    throw e;
                }

                try {

                    int sleepTime = ConnectionWatcher.RETRY_PERIOD_INTERVAL * retries;

                    logger.warn("Method setData 第 {} 次重试,sleep {}s",retries,sleepTime);

                    TimeUnit.SECONDS.sleep(sleepTime);

                } catch (Exception ex) {
                    // nothing to do
                }
            }
        }
    }

    /**
     * 获取path的子节点
     * @param path 父节点
     * @param watch 是否监控子节点变化(增删)
     * @return 子节点列表(if any)
     */
    public List<String> getChildren(String path,boolean watch) {
        List<String> rs = new ArrayList<>();
        try {

            rs = zk.getChildren(path,watch);

        } catch (KeeperException e) {

            logger.error(e.toString());
            e.printStackTrace();

        } catch (InterruptedException e) {

            logger.error(e.toString());
            e.printStackTrace();
        }

        return rs;
    }

    /**
     * 删除节点
     * @param path 节点
     * @param version 版本
     */
    public void delete(String path,int version)  {
        try {
            zk.delete(path,version);
        } catch (KeeperException e) {
            logger.error("{},delete error: {}",path,e.getMessage(),e);
        } catch (InterruptedException e) {
            logger.error("{},delete error: {},interrupted",path,e.getMessage(),e);
        }
    }

    @Override
    public void close() {
        try {
            super.close();
        } catch (Exception e) {
            logger.error("ZkClient close failure.");
        } finally {
            watcherDispatcher.clear();
        }
    }

    @Override
    protected final void eventHandle(String path, Event.EventType eventType) {

        /**
         * Adjusting for requirement in the feature,if need.
         *
         * eg: Using the way of asynchronous to invoke EventHandler,or with timeout.
         */
        watcherDispatcher.doDispatch(path,eventType);
    }

    @Override
    protected void keeperStatHandle(WatchedEvent event) {
        watcherDispatcher.doDispatch(event);
    }
}



public class WatcherDispatcher {

    private static Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    private List<EventHandler> eventHandlers = new ArrayList<>(1);

    private ReentrantLock lock = new ReentrantLock();

    /**
     * 执行节点事件处理器
     * @param nodePath 节点路径
     * @param eventType 事件类型
     */
    public void doDispatch(String nodePath,Watcher.Event.EventType eventType) {
        if(eventType == null || eventType.equals(Watcher.Event.EventType.None)) {
            return;
        }

        logger.info("EventHandler处理器: zk事件: {},关联路径: {}",eventType.toString(),nodePath);

        if(eventHandlers.size() > 0) {

            try {
                for (EventHandler eh : eventHandlers) {
                    switch (eventType) {
                        case NodeCreated:
                            eh.onCreate(nodePath);
                            break;
                        case NodeDataChanged:
                            eh.onDataChanged(nodePath);
                            break;
                        case NodeChildrenChanged:
                            eh.onChildrenChanged(nodePath);
                            break;
                        case NodeDeleted:
                            eh.onDelete(nodePath);
                            break;
                    }
                }
            } catch (Exception e) {
                logger.error("EventHandler invoke failure,error info is {}",e.getMessage(),e);
            }
        }
    }

    /**
     * 执行连接状态事件处理器
     * @param event 连接事件
     */
    public void doDispatch(WatchedEvent event) {
        if(eventHandlers == null || eventHandlers.isEmpty()) {
            return;
        }

        logger.info("EventHandler处理器: zk事件: {}",event.toString());

        for (EventHandler eh : eventHandlers) {
            eh.onConnection();
        }
    }

    /**
     * 注册节点事件Handler
     * @param eventHandler
     */
    public void registerHandler(EventHandler eventHandler) {
        if(eventHandler == null) {
            return;
        }

        lock.lock();

        try {
            eventHandlers.add(eventHandler);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 清空handlers
     */
    public void clear() {

        if(eventHandlers.isEmpty()) {
            return;
        }

        lock.lock();

        try {
            eventHandlers.clear();
        } finally {
            lock.unlock();
        }
    }
}


public interface EventHandler {

    /**
     * 连接建立
     * 连接重新建立也会调用此方法
     */
    void onConnection();

    /**
     * 创建新节点事件
     * @param path 节点路径
     * @see org.apache.zookeeper.ZooKeeper#exists(String, boolean)
     */
    void onCreate(String path);

    /**
     * 节点数据变更事件
     * @param path 节点路径
     * @see org.apache.zookeeper.ZooKeeper#getData(String, boolean, Stat)
     */
    void onDataChanged(String path);

    /**
     * 节点子节点变更事件
     * @param path 节点路径
     * @see org.apache.zookeeper.ZooKeeper#getChildren(String, boolean)
     */
    void onChildrenChanged(String path);

    /**
     * 节点删除事件
     * @param path 节点路径
     * @see org.apache.zookeeper.ZooKeeper#exists(String, boolean)
     */
    void onDelete(String path);

}

EventHandler 是用来处理zk事件的

WatcherDispatcher 是用来统一管理EventHandler及调用EventHandler的,这个名字取的不是很贴切.

ConnectWatcher 是为了确保zk连接,监听事件用的,当会话失效,需要重新建立连接. 连接重新建立成功的话,也就是会发生onConnection事件,因此EventHandler里的onConnection方法里要做重新监听所有节点的事情,不这样做的话,那么后续的动态修改便不会再有通知

ZkClient 对操作的一些封装.

这个小工具的策略实现有三种,基于内存,基于缓存(redis),基于zookeeper

剩下的代码就是关于配置的解析和存储数据到zookeeper的代码了,暂时就补贴代码了,代码比较多,下面的代码是基于zookeeper的一个类

public abstract class AbstractZookeeperRuleMethodRouting extends AbstractRemoteRuleMethodRouting implements DisposableBean {

    Logger logger = LoggerFactory.getLogger(AbstractZookeeperRuleMethodRouting.class);

    // 提取ip的正则表达式
    private String dubboRegistry = OptimusConfig.getValue("dubbo.registry");
    private String dubboRegistryBack = OptimusConfig.getValue("dubbo.provider.backup");
    private String appName = OptimusConfig.getValue("systemInfo.appName", "thor.api");
    private int timeout = OptimusConfig.getValue("dubbo.timeout", Integer.class, 2000);

    private ThorRemoteConnectionManager thorRemoteConnectionManager;
    private String zkAddressForRouting;
    private String defaultRootPath;
    private ZkClient zkClient;

    @Override
    public void afterPropertiesSet() throws Exception {
        initZkClient();
        super.afterPropertiesSet();
    }

    @Override
    public void destroy() {
        if(zkClient != null) {
            zkClient.close();
        }
    }

    protected void resetAppEntry(List<RoutingRule> list){
        String path = getZKPath(appName);
        try {
            if(!zkClient.exists(path, false)){
                createPath(path, null);
            }
            List<String> nodes = getNodes(path);
            if(nodes == null || nodes.isEmpty()){
                return;
            }
            if(list == null || list.isEmpty()){
                return;
            }
            List<String> forCreate = Lists.transform(list, new Function<RoutingRule, String>(){

                @Override
                public String apply(RoutingRule rule) {
                    return getZKPath(rule.getZkPath());
                }

            });

            for(String each : nodes){
                if(!forCreate.contains(each)){
                    zkClient.delete(each,-1);
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw Exceptions.fault(ErrorMessage.errorMessage("thor zk lost", e.getMessage()), e);
        }
    }

    protected void addZK(final RoutingRule rule){
        if(rule == null) {
            return;
        }
        if(rule.getId() == null){
            rule.setId(UUIDUtil.getID());
        }
        FutureTask<RoutingRule> future = new FutureTask<>(new Callable<RoutingRule>() {

            @Override
            public RoutingRule call() throws Exception {
                String data = JsonUtils.toJson(rule);
                String path = getZKPath(rule.getZkPath());
                if(!zkClient.exists(path, false)){
                    createPath(path, data);
                } else {
                    zkClient.setData(path, data);
                }
                return rule;
            }

        });

        postRemote(future);
    }

    protected RoutingRule watch(final RoutingRule rule){
        if(rule == null) {
            return null;
        }
        String path = getZKPath(rule.getZkPath());
        getData(path);
        return rule;
    }

    /**
     * 获取节点事件回调
     * @return
     */
    protected EventHandler getEventHandler() {
        // Subclass could override this method
        return new DefaultEventHandler();
    }

    /**
     * 可重写此方法:以适应用户需求
     * 但建议保证再启动时zk地址有效且可成功连接的
     * @throws Exception
     */
    private void initZkClient() throws Exception {
        // 建立zk连接
        String host = getZkAddress();
        zkClient = ZkClient.getInstance().registerHandler(getEventHandler()).init(host);
    }

    /**
     * 此方法目前的用途主要是用来监听应用节点的变化,
     * 以达到动态的添加新的配置目的
     * (前提条件是,应用中已写好相应代码,否则虽能感知到新配置,却无法真实的生效)
     */
    private void afterConnection() {
        String appPath = getZKPath(appName);
        try {
            // 监听节点创建事件
            if(ZkClient.getInstance().exists(appPath,true)) {
                // 监听节点子节点变化事件
                ZkClient.getInstance().getChildren(appPath,true);
            }
        } catch (KeeperException | InterruptedException e) {
            logger.error("afterInit invoke failure,error info is {}",e.getMessage(),e);
            throw new RuntimeException(e);
        }
    }

    private void initConnectionManager(){
        if(thorRemoteConnectionManager == null) {
            if(StringUtil.isEmpty(dubboRegistry)) {
                throw new RuntimeException("property dubbo.registry is not set in *.properties");
            }

            ReferenceConfig<ThorRemoteConnectionManager> reference = new ReferenceConfig<>();
            reference.setApplication(new ApplicationConfig(appName));
            reference.setRegistry(new RegistryConfig(dubboRegistry));
            reference.setInterface(ThorRemoteConnectionManager.class);
            reference.setTimeout(timeout);
            thorRemoteConnectionManager = reference.get();

            if(thorRemoteConnectionManager == null){
                reference.setRegistry(new RegistryConfig(dubboRegistryBack));
                thorRemoteConnectionManager = reference.get();
            }
        }
    }
    
    private String getDefaultRootPath(){
        if(StringUtil.isNotEmpty(defaultRootPath)){
            return defaultRootPath;
        }
        initConnectionManager();
        defaultRootPath = thorRemoteConnectionManager.getDefaultPath();
        return defaultRootPath;
    }

    private List<String> getNodes(String path){

        List<String> result = Lists.newArrayList();

        List<String> child = zkClient.getChildren(path, false);
        for(String each : child){
            String subPath = path+ "/" +each;
            List<String> sub = zkClient.getChildren(subPath, false);
            if(sub == null || sub.isEmpty()) {
                result.add(subPath);
            } else {
                List<String> subList = getNodes(subPath);
                result.addAll(subList);
            }
        }

        return result;
    }

    private void createPath(String path, String value){
        String[] pathArray = path.split("/");
        if(pathArray.length < 1){
            return;
        }
        String pathForCreate = "";
        for(String each : pathArray){
            if(StringUtil.isEmpty(each)){
                continue;
            }

            // 层层创建节点
            try {
                pathForCreate += "/"+each;

                if(!zkClient.exists(pathForCreate, false)){
                    if(pathForCreate.equals(path)){
                        zkClient.create(pathForCreate, value,CreateMode.PERSISTENT);
                    } else {
                        zkClient.create(pathForCreate, null,CreateMode.PERSISTENT);
                    }
                }

            } catch (KeeperException | InterruptedException e) {
                logger.error(e.getMessage());
                throw Exceptions.fault(ErrorMessage.errorMessage("thor zk lost", e.getMessage()), e);
            }
        }
    }

    private void getData(final String path) {
        try{

            String data = zkClient.getData(path,true,null);

            if(StringUtil.isEmpty(data)) {
                return;
            }

            RoutingRule rule = JsonUtils.fromJson(data, RoutingRule.class);

            if(rule != null) {
                putLocal(rule);
            }

        } catch (KeeperException | InterruptedException e) {
            logger.error(e.getMessage());
            throw Exceptions.fault(ErrorMessage.errorMessage("thor zk lost", e.getMessage()), e);
        }
    }

    private String getZkAddress() {
        if(StringUtil.isEmpty(zkAddressForRouting)) {
            if(thorRemoteConnectionManager == null) {
                initConnectionManager();
            }

            zkAddressForRouting = thorRemoteConnectionManager.getZkRemoteConnectionString();

            if(StringUtil.isEmpty(zkAddressForRouting)) {
                throw new RuntimeException("zookeeper address for routing no config");
            }
        }

        return zkAddressForRouting;
    }

    private String getZKPath(String path){
        return getDefaultRootPath()+"/"+path;
    }

    private class DefaultEventHandler extends EventHandlerAdapter {

        @Override
        public void onConnection() {
            // 防止session失效后,各种监听失效
            logger.info("Zk EventHandler处理器,事件: 连接建立");

            List<RoutingRule> rules = getRoutings();
            if(rules != null && rules.size() > 0) {
                for (RoutingRule rule : rules) {
                    watch(rule);
                }
            }
            afterConnection();
        }

        @Override
        public void onCreate(String path) {
            /*
            try {

                logger.info("Zk EventHandler处理器,事件: 创建节点, node path: {}",path);
                int level = nodeLevel(path);

                if(level == 4) {
                    getData(path);
                } else {
                    zkClient.getChildren(path,true);

                    // 防止因网络延迟,导致无法监听某些子节点的变化
                    onChildrenChanged(path);
                }
            } catch (Exception e) {
                logger.error("节点:{},获取zk数据异常:{}",path,e.getMessage(),e);
            }
            */

            // FIXME 以上代码待完善
        }

        @Override
        public void onDataChanged(String path) {
            int level = nodeLevel(path);
            if(level == 4) {
                logger.info("Zk EventHandler处理器,事件: 节点数据变更, node path: {}",path);
                getData(path);
            }
        }

        @Override
        public void onChildrenChanged(String path) {
            /*
            List<String> childrenNode = getChildren(path);
            if(childrenNode == null || childrenNode.isEmpty()) {
                return;
            }

            int level = nodeLevel(path);

            logger.info("Zk EventHandler处理器,事件: 子节点变更, parent node path: {}",path);

            try {
                for (String node : childrenNode) {
                    final String nodePath = path + "/" + node;
                    logger.info("Zk EventHandler处理器,事件: 子节点变更, child node path: {}",nodePath);
                    if(level == 3) {
                        getData(nodePath);
                    } else {
                        // 非四层节点,继续监听其子节点变化
                        asynExecute(new Runnable() {
                            @Override
                            public void run() {
                                List<String> childrenNode = ZkClient.getInstance().getChildren(nodePath,true);
                                if(childrenNode.isEmpty()) {
                                    return;
                                }


                            }
                        });

                    }
                }
            } catch (Exception e) {
                logger.error("节点:{},获取zk数据异常:{}",path,e.getMessage(),e);
            }
            */

            // FIXME 以上代码待完善
        }

        private List<String> getChildren(String path) {
            return zkClient.getChildren(path,true);
        }

        private int nodeLevel(String path) {
            if(path == null || "".equals(path)) {
                return 0;
            }
            return StringUtil.delimitedListToStringArray(path.substring(1),"/").length;
        }
    }
}



public interface MethodRouting {
    
    /**
     * 初始化路由规则
     */
    void init();
    
    /**
     * 重置路由规则成配置文件中的配置
     */
    void reset();
    
    /**
     * 设置一个路由
     * @param rule 路由规则
     */
    void setRouting(RoutingRule rule);
    
    /**
     * 更新规则
     * @param id
     * @param percent
     */
    void updateRouting(String id, Integer percent);
    
    /**
     * 更新一条规则
     * @param id
     * @param sharding
     */
    void updateRouting(String id, String sharding);
    
    /**
     * 查看一个路由配置
     * @param code
     * @return
     */
    String getRouting(String code);
    
    /**
     * 根据某个值判断路由
     * @param code
     * @param sharding
     * @return
     */
    String getRouting(String code, String sharding);
    
    /**
     * 获取所有的路由规则, method, routing, rule
     * @return
     */
    List<RoutingRule> getRoutings();
}

ThorRemoteConnectionManager是一个外部应用需要使用的一个api, 为的是获得这个配置所存储的zookeeper的地址及配置的默认根目录

MethodRouting 也是一个外部接口, 引入此程序的jar包, 在Spring里配置一个实现类,就是上面提到的三个中的一个.

下面一个实际当中使用时的示例:

<thor:routing code="orderAccountList" default="orderQueryService">
    <thor:rules>
        <thor:rule routing="orderQueryServiceEmpty" percent="0" sharding="20181101000000-20181102235959"/>
        <thor:rule routing="orderQueryService" percent="100" sharding="20181115000000-20991231235959"/>
    </thor:rules>
</thor:routing>

<thor:routing code="orderUpdatedAccountList" default="orderQueryService">
    <thor:rules>
        <thor:rule routing="orderQueryServiceEmpty" percent="0" sharding="20181101000000-20181102235959"/>
        <thor:rule routing="orderQueryService" percent="100" sharding="20181115000000-20991231235959"/>
    </thor:rules>
</thor:routing>

code属性是方法名, default 是默认选择的bean名称

routing 是bean名称, percent是权重,sharding 这个属性没有统一的定义,使用起来比较灵活,可以使用此属性表达不同的逻辑,在这里这个sharding的所表达的意思使用bean的时间段,会忽略percent的配置

到此就结束了.这个小程序还在完善中。

猜你喜欢

转载自blog.csdn.net/AWEI1024/article/details/86654941