学习黄勇smart-rpc的笔记
黄勇的码云地址:https://gitee.com/huangyong/rpc
黄勇的oschina博客地址:https://my.oschina.net/huangyong/blog/361751
客户端要调接口,肯定要知道服务端的地址,我们可以把服务端地址告诉给客户端,也可以让客户端自己去寻找地址,注册中心就是这个作用,客户端调接口之前,先去注册注册中心寻找服务端的地址,我们可以对寻找的方式进行控制,实现不同的目的。
从理论上来说,只要能存储数据就可以当注册中心,smart-rpc通过zookeeper实现。
1、服务注册
/**
* 只有当永久节点不存在时才会创建
* 而临时节点每次注册都会创建一个
* @param serviceName 服务名称
* @param serviceAddress 服务地址
*/
@Override
public void register(String serviceName, String serviceAddress) {
// 创建 registry 节点(持久)
String registryPath = Constant.ZK_REGISTRY_PATH;
if (!zkClient.exists(registryPath)) {
zkClient.createPersistent(registryPath);
LOGGER.debug("create registry node: {}", registryPath);
}
// 创建 service 节点(持久)
String servicePath = registryPath + "/" + serviceName;
if (!zkClient.exists(servicePath)) {
zkClient.createPersistent(servicePath);
LOGGER.debug("create service node: {}", servicePath);
}
// 创建 address 节点(临时)
String addressPath = servicePath + "/address-";
String addressNode = zkClient.createEphemeralSequential(addressPath, serviceAddress);
LOGGER.debug("create address node: {}", addressNode);
}
2、服务发现
/**
* 通过服务名称寻找服务器地址
* 同一个服务名称可以有多个服务器地址
* @param name
* @return
*/
@Override
public String discover(String name) {
// 创建 ZooKeeper 客户端
ZkClient zkClient = new ZkClient(zkAddress, Constant.ZK_SESSION_TIMEOUT, Constant.ZK_CONNECTION_TIMEOUT);
LOGGER.debug("connect zookeeper");
try {
// 获取 service 节点
String servicePath = Constant.ZK_REGISTRY_PATH + "/" + name;
if (!zkClient.exists(servicePath)) {
throw new RuntimeException(String.format("can not find any service node on path: %s", servicePath));
}
List<String> addressList = zkClient.getChildren(servicePath);
if (CollectionUtil.isEmpty(addressList)) {
throw new RuntimeException(String.format("can not find any address node on path: %s", servicePath));
}
// 获取 address 节点
String address;
int size = addressList.size();
if (size == 1) {
// 若只有一个地址,则获取该地址
address = addressList.get(0);
LOGGER.debug("get only address node: {}", address);
} else {
// 若存在多个地址,则随机获取一个地址
address = addressList.get(ThreadLocalRandom.current().nextInt(size));
LOGGER.debug("get random address node: {}", address);
}
// 获取 address 节点的值
String addressPath = servicePath + "/" + address;
return zkClient.readData(addressPath);
} finally {
zkClient.close();
}
}