基于ZooKeeper为注册中心实现的RPC
一、原理
一个能够动态注册和获取服务信息的地方,来统一管理服务名称和其对应的服务器列表信息,称之为服务配置中心
。如图所示
- 服务提供在启动时,将其提供的服务名称、服务器地址注册到服务配置中心
- 服务消费者通过服务配置中心来获得需要调用的服务的机器列表,通过相应的负载均衡算法,选取其中一台服务器进行调用
- 当服务器宕机或者下线时,相应的机器需要能够动态地从服务配置中心里面移除,并通知相应地服务消费者
二、统一配置管理
主要把服务名以及服务相关的服务器IP地址注册到注册中心,在使用服务的时候,只需要根据服务名,就可以得到所有服务地址IP,然后根据一定的负载均衡策略来选择IP地址
1、服务的注册
关于服务的注册,其实就是把服务和IP注册到ZooKeeper节点中。
- 服务名用的是永久节点
- 服务IP地址用的是临时节点(为后面对节点进行注册监听做铺垫)
(用端口号的不同区别不同的机器)
CuratorUtils
类提供createPersistentNode()
和createEphemeralNode()
方法
// 创建服务名永久节点PERSISTENT
public static void createPersistentNode(CuratorFramework zkClient, String path) {
try {
// 永久节点已存在
if (PERSISTENT_REGISTERED_PATH_SET.contains(path) || zkClient.checkExists().forPath(path) != null) {
logger.info("永久节点已经存在,永久节点是:[{}]", path);
} else {
// 永久节点不存在,则创建永久节点
//eg: /MyRPC/com.whc.rpc.api.UserService
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path);
logger.info("永久节点成功被创建,永久节点是:[{}]", path);
}
PERSISTENT_REGISTERED_PATH_SET.add(path);
} catch (Exception e) {
logger.error("创建永久节点失败[{}]", path);
}
}
// 创建服务地址为临时节点EPHEMERAL
// 临时节点,当客户端与 Zookeeper 之间的连接或者 session 断掉时会被zk自动删除。开源 Dubbo 框架,使用的就是临时节点
// 优点: 当服务节点下线或者服务节点不可用,Zookeeper 会自动将节点地址信息从注册中心删除
public static void createEphemeralNode(CuratorFramework zkClient, String path) {
try {
// 临时节点已存在
if (EPHEMERAL_REGISTERED_PATH_SET.contains(path) || zkClient.checkExists().forPath(path) != null) {
logger.info("临时节点已经存在,临时节点是:[{}]", path);
} else {
// 临时节点不存在,则创建临时节点
//eg: /MyRPC/com.whc.rpc.api.UserService/127.0.0.1:9000
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path);
logger.info("临时节点成功被创建,临时节点是:[{}]", path);
}
EPHEMERAL_REGISTERED_PATH_SET.add(path);
} catch (Exception e) {
logger.error("创建临时节点失败[{}]", path);
}
}
2、服务的发现
服务的发现就是根据服务名来获取ZooKeeper节点中的IP地址
CuratorUtils
类提供了getChildrenNodes()
方法
// 获取一个节点下的孩子节点
public static List<String> getChildrenNodes(CuratorFramework zkClient, String rpcServiceName) {
if (SERVICE_ADDRESS_MAP.containsKey(rpcServiceName)) {
return SERVICE_ADDRESS_MAP.get(rpcServiceName);
}
List<String> result = null;
String servicePath = ZK_REGISTER_ROOT_PATH + "/" + rpcServiceName;
try {
<