利用zookeeper实现自己的服务注册中心

首先确定服务注册中心的结构信息:具体如下图所示

首先定义客户端注册接口,定义了一些基本方法;

package lin.remoting.framework.register;

import java.util.List;
import java.util.Map;

/**
 * 消费端注册中心
 */
public interface IRegisterCenterInvoker {

    /**
     * 由客户端启动首次从zookeeper中首次拉去信息
     */
    public void initProviderMap();

    /**
     * 消费端获取服务提供者信息
     * @return
     */
    public Map<String,List<ProviderService>> getServiceMetaDataMap4Consume();

    public void registerInvoker(final InvokerService invoker);

}

具体实现类如下:

package lin.remoting.framework.register;

import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务调用者注册中心
 */
public class RegisterCenterInvoker implements IRegisterCenterInvoker {
    //服务提供者列表:key :服务提供者接口,value:服务提供者服务方法列表
    private static final Map<String, List<ProviderService>> providerServiceMap
            = new ConcurrentHashMap<String, List<ProviderService>>();
    public static final Map<String, List<ProviderService>> serviceMetaDataMapConsume
            = new ConcurrentHashMap<String, List<ProviderService>>();
    private static volatile ZkClient zkClient = null;

    private static String ZK_SERVICE = "127.0.0.1";
    private static int ZK_SESSION_TIME_OUT = 999999;
    private static int ZK_CONNECTION_TIME_OUT = 999999;

    private static RegisterCenterInvoker RegisterCenterInvoker = new RegisterCenterInvoker();

    private static String ROOT_PATH = "/config_register/" + "lin";
    private static String PROVIDER_TYPE = "/provider";
    private static String INVOKER_TYPE = "/consumer";

    private RegisterCenterInvoker() {
    }

    public static RegisterCenterInvoker singleton() {
        return RegisterCenterInvoker;
    }

    public void initProviderMap() {
        if (serviceMetaDataMapConsume.isEmpty()) {
            serviceMetaDataMapConsume.putAll(fetchOrUpdateServiceMetaData());
        }
    }

    /**
     * 使用该函数前请确保已经使用initProviderMap()已近从zookeeper拉取消息到本地进行缓存
     *
     * @return
     */
    public Map<String, List<ProviderService>> getServiceMetaDataMap4Consume() {
        return serviceMetaDataMapConsume;
    }

    /**
     * 该函数用于消费者将自身信息注册到zookeeper上
     *
     * @param invoker
     */
    public void registerInvoker(InvokerService invoker) {
        if (invoker == null) {
            return;
        }
        synchronized (RegisterCenterInvoker.class) {
            if (zkClient == null) {
                zkClient = new ZkClient(ZK_SERVICE, ZK_SESSION_TIME_OUT, ZK_CONNECTION_TIME_OUT, new SerializableSerializer());
            }
            boolean exist = zkClient.exists(ROOT_PATH);
            if (!exist) {
                zkClient.createPersistent(ROOT_PATH, true);
            }

            //创建服务消费者节点
            String consumeName = invoker.getTargetInterface().getSimpleName();
            exist = zkClient.exists(ROOT_PATH+"/"+consumeName);
            if (!exist) {
                zkClient.createPersistent(ROOT_PATH+"/"+consumeName);
            }
            String consumeNodePath = ROOT_PATH + "/" + consumeName + INVOKER_TYPE;
            exist = zkClient.exists(consumeNodePath);
            if (!exist) {
                zkClient.createPersistent(consumeNodePath);
            }
            //创建当前服务端节点
            String localIp = invoker.getIP();
            String currentConsumeServicePath = consumeNodePath + "/" + localIp;
            exist = zkClient.exists(currentConsumeServicePath);
            if (!exist) {
                zkClient.createEphemeral(currentConsumeServicePath);
            }


        }


    }

    private Map<String, List<ProviderService>> fetchOrUpdateServiceMetaData() {

        final Map<String, List<ProviderService>> providerServiceMap = new ConcurrentHashMap<String, List<ProviderService>>();
        List<ProviderService> providerServices = null;
        //连接zk 加锁防止重复注册。
        synchronized (IRegisterCenterInvoker.class) {
            if (zkClient == null) {
                zkClient = new ZkClient(ZK_SERVICE, ZK_SESSION_TIME_OUT,
                        ZK_CONNECTION_TIME_OUT, new SerializableSerializer());
            }
            //从这里开始从服务器获取服务提供者列表
            String providerPath = ROOT_PATH;
            //获取根节点下所有的子节点
            List<String> provideServices = zkClient.getChildren(providerPath);
            for (String serviceName : provideServices) {
                //指定服务名下的所有提供者路劲
                String servicePath = ROOT_PATH + "/" + serviceName + PROVIDER_TYPE;
                //所有提供者ip
                List<String> ipPathList = zkClient.getChildren(servicePath);
                for (String ipPath : ipPathList) {
                    String[] ipAndPort = ipPath.split("\\|");
                    String serverIp = ipAndPort[0];
                    String serverPort = ipAndPort[1];
                    //引用型 与初始创的为同一个引用
                    providerServices = providerServiceMap.get(serviceName);
                    if (providerServices == null) {
                        providerServices = new ArrayList<ProviderService>();
                        providerServiceMap.put(serviceName, providerServices);
                    }
                    ProviderService providerService = new ProviderService();
                    try {
                        //将服务名转化为类  这里根据自己的实际情况设置,事实上这里可以通过配置
                        //文件来确定
                        providerService.setTargetInterface(Class.forName("lin.remoting.framework.register." + serviceName));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    providerService.setIP(serverIp);
                    providerService.setPort(Integer.parseInt(serverPort));
                    //将服务添加到列表当中
                    providerServices.add(providerService);


                }
                //遍历完后将服务列表添加到其中
                providerServiceMap.put(serviceName, providerServices);
                zkClient.subscribeChildChanges(servicePath, new IZkChildListener() {
                    public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                        if (currentChilds == null) {
                            currentChilds = new ArrayList<String>();
                        }
                        List<String> tmp = new ArrayList<String>();
                        for (String ipPort : currentChilds) {
                            tmp.add(ipPort.split("\\|")[0]);
                        }
                        //  System.out.println("父路径"+parentPath);
                        //调用函数重新刷新本地服务器数据
                        refreshServiceMetaDataMap(tmp);

                    }
                });

            }


        }
        return providerServiceMap;
    }

    //  根据ip来判断 一个服务提供者是否失去作用
    public void refreshServiceMetaDataMap(List<String> ipList) {
        if (ipList == null) {
            ipList = new ArrayList<String>();
        }
        Map<String, List<ProviderService>> currentServiceMetaDataMap = new ConcurrentHashMap<String, List<ProviderService>>();
        for (Map.Entry<String, List<ProviderService>> entry : serviceMetaDataMapConsume.entrySet()) {
            String serviceName = entry.getKey();
            List<ProviderService> providerServices = entry.getValue();

            List<ProviderService> currentProviders = currentServiceMetaDataMap.get(serviceName);
            if (currentProviders == null) {
                currentProviders = new ArrayList<ProviderService>();
            }
            //需要全部遍历,因为一台机器可能提供多个服务
            for (ProviderService providerService : providerServices) {
                if (ipList.contains(providerService.getIP())) {
                    currentProviders.add(providerService);
                }
            }
            currentServiceMetaDataMap.put(serviceName, currentProviders);
        }

        //hashMap 函数此时每个对用的serviceName已经对用当前的新服务提供者列表
        //服务名相同,当前currentProviders会覆盖原键对应得值
        serviceMetaDataMapConsume.putAll(currentServiceMetaDataMap);
       /* Set<String> keys = serviceMetaDataMapConsume.keySet();

        for (String key : keys) {
            List<ProviderService> providerServices = RegisterCenterInvoker.serviceMetaDataMapConsume.get(key);

          //  System.out.println("跟新大小" + providerServices.size() + "size");
            for (ProviderService providerService : providerServices) {

                System.out.println(providerService);
            }

        }

*/
    }
}

下面是上面所用到的一些类

package lin.remoting.framework.register;

/**
 * 用于封装一个服务消费者的基本信息
 */
public class InvokerService {
    private Class<?> targetInterface;

    private String IP;

    public InvokerService(Class<?> targetInterface, String IP) {
        this.targetInterface = targetInterface;
        this.IP = IP;

    }

    public Class<?> getTargetInterface() {
        return targetInterface;
    }

    public void setTargetInterface(Class<?> targetInterface) {
        this.targetInterface = targetInterface;
    }

    public String getIP() {
        return IP;
    }

    public void setIP(String IP) {
        this.IP = IP;
    }


}
package lin.remoting.framework.register;

//该类用于封装提供服务类消息
public class ProviderService {
    //服务接口 即该服务提供者面向的对象
    private Class<?> targetInterface;
    //服务提供者地址
    private String IP;
    //服务提供者的端口号;
    private int port;

    public Class<?> getTargetInterface() {
        return targetInterface;
    }

    public void setTargetInterface(Class<?> targetInterface) {
        this.targetInterface = targetInterface;
    }

    public String getIP() {
        return IP;
    }

    public void setIP(String IP) {
        this.IP = IP;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    @Override
    public String toString() {
        return this.targetInterface.getName() + " IP: " + this.IP + " port: " + this.port;
    }
}
package lin.remoting.framework.register;

public class Myservice {
}

关于服务器端的注册中心的实现将在我的下一篇博文为大家实现

猜你喜欢

转载自blog.csdn.net/qq_32459653/article/details/81147055