从dubbo
的服务架构可以看出, Registry
主要提供服务的注册与发现功能:即服务的提供方将服务注册在Registry
中,而服务的消费方可以从注册的服务里获取提供方的ip地址等信息,从而进行远程过程调用。
服务注册发现中心
接口定义
@SPI
public interface ServiceRegistry {
/**
* 注册服务
* @param rpcServiceName
* @param inetSocketAddress
*/
void registerService(String rpcServiceName, InetSocketAddress inetSocketAddress);
/**
* 下线服务
* @param rpcServiceName
* @param inetSocketAddress
*/
void unregisterService(String rpcServiceName, InetSocketAddress inetSocketAddress);
}
@SPI
public interface ServiceDiscovery {
/**
* lookup service by rpcServiceName
* @param rpcServiceName
* @return
*/
InetSocketAddress lookupService(String rpcServiceName);
}
Zookeeper
服务存储分层
Zookeeper数据存储方式类似于树的组织方式,一个节点下可以有多个子节点。
参考上面dubbo
中zookeeper
的分层方式,在该rpc框架中简化分为root
、service
和address
三层,如下。root
层主要就是标识框架的名字,无实际含义;service
存储服务的名字,包括Service接口类的全限定名和分组(group用于区分一个接口的多个实现类);address
层主要存储的就是提供该服务的服务提供者ip地址和端口。
服务注册发现流程
调用关系说明
- 服务提供方启动,注册本地服务到zookeeper
- 服务消费方启动,订阅感兴趣的服务,从zookeeper上拉取感兴趣的服务及服务的提供者地址到本地;同时监听zookeeper中感兴趣的服务节点。
- 服务提供方一旦上线新服务,或者服务提供方关机而从zookeeper中删除自己注册的服务,此时会触发服务消费方监听节点的事件响应,服务消费方会重新拉取最新的服务提供列表到本地。
注意
- 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动及服务变更时与注册中心交互,不是每次都需要去注册中心查找服务列表,注册中心不转发请求,压力较小
- 注册中心,服务提供者,服务消费者三者之间均为长连接
- 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者(通过Zookeeper的监听事件感知)
代码实现
ZkServiceRegistry 服务注册
public class ZkServiceRegistry implements ServiceRegistry {
@Override
public void registerService(String rpcServiceName, InetSocketAddress inetSocketAddress) {
String servicePath = CuratorUtils.ZK_REGISTER_ROOT_PATH + "/" + rpcServiceName + inetSocketAddress.toString();
CuratorFramework zkClient = CuratorUtils.getZkClient();
CuratorUtils.createEphemeralNode(zkClient, servicePath);
}
@Override
public void unregisterService(String rpcServiceName, InetSocketAddress inetSocketAddress) {
String servicePath = CuratorUtils.ZK_REGISTER_ROOT_PATH + "/" + rpcServiceName + inetSocketAddress.toString();
try {
CuratorUtils.getZkClient().delete().forPath(servicePath);
} catch (Exception e) {
e.printStackTrace(