一、服务注册设计
二、服务注册与引用
public interface Registry {
/**
* 注册服务
*
* @param url
*/
void register(URL url);
/**
* 订阅服务
*
* @param url
*/
void subscribe(URL url,NotifyListener listener);
}
2.1 构建服务变更监听器和注册目录
/**
* 服务变更监听器
* @author jinzhou
* @data 2023/4/11 22:03
*/
public interface NotifyListener {
void notify(List<URL> urls);
}
服务注册表
/**
* 注册表
*
* 存储所有可用的调用接口
* @author jinzhou
*/
public class RegistryDirectory <T> implements NotifyListener {
/**
* 注册中心提供者集合
*/
protected volatile Map<URL, Invoker<T>> urlInvokerMap = new ConcurrentHashMap<>();
/**
* 类型
*/
private final Class<T> serviceType;
/**
* 资源
*/
private final URL url;
/**
* 注册中心
*/
private Registry registry;
/**
* 注册协议
*/
protected RpcProtocol protocol;
public RegistryDirectory(Registry registry,RpcProtocol protocol,Class<T> serviceType, URL url) {
this.registry = registry;
this.serviceType = serviceType;
this.url = url;
this.protocol = protocol;
}
/**
* 订阅注册中心
* @param url
*/
public void subscribe(URL url) {
//订阅服务
registry.subscribe(url,this);
}
/**
* 当收到服务更改通知时触发
* @param urls
*/
@Override
public synchronized void notify(List<URL> urls) {
toInvokers(urls);
}
/**
* 将注册中的URL转化为调用器
* @param urls
* @return
*/
private Map<URL, Invoker<T>> toInvokers(List<URL> urls) {
//说明没有可用服务
if (CollectionUtil.isEmpty(urls)){
urlInvokerMap.clear();
return urlInvokerMap;
}
//旧集合
List<URL> oldInvokerList = new ArrayList<>(urlInvokerMap.keySet());
//待移除集合
List<URL> removeUrlList = new ArrayList<>();
//添加集合
List<URL> newUrlList = new ArrayList<>();
//找出旧集合中多余的
for (URL oldUrl : oldInvokerList) {
if (!urls.contains(oldUrl)) {
removeUrlList.add(oldUrl);
}
}
for (URL newUrls : urls) {
if (!oldInvokerList.contains(newUrls)) {
newUrlList.add(newUrls);
}
}
for (URL removeUrl : removeUrlList) {
urlInvokerMap.remove(removeUrl);
}
//新增注册中心提供者
for (URL newUrl : newUrlList) {
urlInvokerMap.put(newUrl,protocol.refer(serviceType,newUrl));
}
return urlInvokerMap;
}
/**
* 获取注册中心调用者集合
* @param invocation
* @return
*/
public List<Invoker<T>> doList(Invocation invocation){
return new ArrayList<>(urlInvokerMap.values());
}
public URL getUrl() {
return url;
}
}
2.2 构建注册协议:RegistryProtocol
负责将本地服务注册到远程的注册中心,以及获取注册中心服务同时还能够监听注册中心的变化,为服务的动态调用提供支持
/**
* 用来定义和暴露远程服务的协议的,为上层提供了统一的调用方式。具体来说,
* Protocol类负责将服务暴露到网络上并监听客户端请求,并将请求转发给对应的服务实现进行处理,
* 最后将处理结果返回给客户端。
*
* @author jinzhou
*/
public interface Protocol {
/**
* 暴露调用服务
* @param invoker
* @param <T>
* @return
* @throws RpcException
*/
<T>void export(Invoker<T> invoker) throws RpcException;
/**
* 获取调用对象
* @param type
* @param url
* @param <T>
* @return
* @throws RpcException
*/
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
}
注册中心协议
/**
* 注册中心协议
*
* @author jinzhou
*/
public class RegistryProtocol implements Protocol {
private RpcProtocol rpcProtocol;
private NacosRegistryFactory registryFactory = new NacosRegistryFactory();
public RegistryProtocol(RpcProtocol rpcProtocol) {
this.rpcProtocol = rpcProtocol;
}
/**
* 向注册中心发送请求,将自己的地址、服务名称和版本等信息注册到注册中心。
*
* @param originInvoker
* @param <T>
* @return
* @throws RpcException
*/
@Override
public <T> void export(Invoker<T> originInvoker) throws RpcException {
//暴露服务到本地
URL registryUrl = originInvoker.getUrl();
String interfaceName= registryUrl.getInterfaceName();
//创建本地服务url
URL providerUrl = new URL();
ProtocolConfig protocolConfig = RpcManager.RPC_MANAGER.getProtocolConfig();
providerUrl.setHost(protocolConfig.getIp());
providerUrl.setPort(protocolConfig.getPort());
providerUrl.setInterfaceName(interfaceName);
providerUrl.setProtocol(ProtocolEnum.RPC.getProtocolName());
providerUrl.setParameters(registryUrl.getParameters());
doLocalExport(originInvoker,providerUrl);
//获取注册表
Registry registry = registryFactory.getRegistry(registryUrl);
//注册服务到注册中心
registry.register(providerUrl);
}
/**
* 向注册中心发送请求,获取可调用的服务信息
*
* @param type
* @param url
* @param <T>
* @return
*/
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) {
Registry registry = registryFactory.getRegistry(url);
return getInvoker(registry, type, url);
}
/**
* 获取执行器
* @param registry 注册中心
* @param type 请求类型
* @param url 请求资源
* @param <T>
* @return
*/
private <T> Invoker<T> getInvoker(Registry registry, Class<T> type, URL url) {
//创建注册表(存储从注册中心获取的服务)
RegistryDirectory<T> directory = createDirectory(registry, type, url);
//从注册中心获取服务
directory.subscribe(url);
return new FailoverClusterInvoker<T>(directory);
}
/**
* 创建注册表
*
* @param type 类型
* @param url url
* @param <T>
* @return
*/
private <T> RegistryDirectory<T> createDirectory(Registry registry, Class<T> type, URL url) {
return new RegistryDirectory<>(registry, this.rpcProtocol, type, url);
}
/**
* 本地暴露
*
* @param originInvoker
*/
private <T> void doLocalExport(Invoker<T> originInvoker,URL providerUrl) {
InvokerWrapper<T> invokerWrapper = new InvokerWrapper(originInvoker,providerUrl);
rpcProtocol.export(invokerWrapper);
}
}
2.3构建Nacos服务管理类:NacosNamingService
NacosNamingService 是Nacos服务的核心管理类,提供了服务管理的相关功能,例如:
- 服务注册:当一个服务启动时,它可以向NacosNamingService注册自己的实例信息(IP地址、端口号、健康状态等)。
- 服务发现:当一个服务需要调用另一个服务时,它可以通过NacosNamingService查询对应服务的实例信息,以便进行调用。
- 服务订阅:当一个服务需要获取某个服务的最新实例信息时,它可以向NacosNamingService订阅该服务,以便在有新实例注册或注销时及时获得通知。
我们如果想要完成在Nacos上的对服务的管理就需要先构建出NacosNamingService
/**
* @author jinzhou
*/
public class NacosNamingServiceUtils {
private static final Logger logger = LoggerFactory.getLogger(NacosNamingServiceUtils.class);
/**
* 创建Nacos服务管理器
* @param serverAddr 注册中心地址
* @param username 注册中心用户名
* @param password 注册中心密码
* @return
*/
public static NamingService createNamingService(String serverAddr,String username,String password) {
Properties nacosProperties = buildNacosProperties(serverAddr,username,password);
NamingService namingService;
try {
namingService = NacosFactory.createNamingService(nacosProperties);
} catch (NacosException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getErrMsg(), e);
}
throw new IllegalStateException(e);
}
return namingService;
}
/**
* 构建Nacos参数集合
* @param serverAddr 注册中心地址
* @param username 注册中心用户名
* @param password 注册中心密码
* @return
*/
private static Properties buildNacosProperties(String serverAddr,String username,String password) {
Properties properties = new Properties();
properties.put(SERVER_ADDR, serverAddr);
properties.put(USERNAME,username);
properties.put(PASSWORD,password);
return properties;
}
}
2.4 Nacos服务注册器
public class NacosRegistry implements Registry {
private final Logger logger = LogProvider.getLogger(getClass());
private final NamingService namingService;
public NacosRegistry(NamingService namingService) {
this.namingService = namingService;
}
/**
* 注册服务
*
* @param url
*/
@Override
public void register(URL url) {
final String serviceName = getServiceName(url);
final Instance instance = createInstance(url);
execute(namingService -> namingService.registerInstance(serviceName,
Constants.DEFAULT_GROUP, instance));
}
/**
* 获取服务
*
* @param url
* @param listener
*/
@Override
public void subscribe(URL url, NotifyListener listener) {
final String serviceName = getServiceName(url);
execute(namingService -> {
List<Instance> instances = namingService.getAllInstances(serviceName,
Constants.DEFAULT_GROUP);
List<URL> urls = buildURLs(instances);
//通知订阅者
listener.notify(urls);
//监听实例
subscribeEventListener(serviceName, url, listener);
});
}
/**
* 监听服务变更
* @param serviceName
* @param url
* @param listener
* @throws NacosException
*/
private void subscribeEventListener(String serviceName, final URL url, final NotifyListener listener)
throws NacosException {
EventListener eventListener = event -> {
if (event instanceof NamingEvent) {
NamingEvent e = (NamingEvent) event;
List<Instance> instances = e.getInstances();
notifySubscriber(url, listener, instances);
}
};
namingService.subscribe(serviceName,
Constants.DEFAULT_GROUP,
eventListener);
}
private void notifySubscriber(URL url, NotifyListener listener, Collection<Instance> instances) {
List<Instance> enabledInstances = new LinkedList<>(instances);
if (enabledInstances.size() > 0) {
// 过滤出可用实例
enabledInstances = enabledInstances.stream().filter(Instance::isEnabled).collect(Collectors.toList());
}
List<URL> urls = buildURLs(enabledInstances);
listener.notify(urls);
}
/**
* 获取服务名称
*
* @param url
* @return
*/
private String getServiceName(URL url) {
return url.getServiceKey();
}
/**
* 创建注册实例
*
* @param url
* @return
*/
private Instance createInstance(URL url) {
HashMap<String, String> newParamsMap = MapUtil.copyHashMap(url.getParameters());
newParamsMap.put(INTERFACE_KEY, url.getInterfaceName());
String ip = url.getHost();
int port = url.getPort();
Instance instance = new Instance();
instance.setIp(ip);
instance.setPort(port);
instance.setMetadata(newParamsMap);
return instance;
}
/**
* 将实例装换为URL
*
* @param instances
* @return
*/
private List<URL> buildURLs(Collection<Instance> instances) {
List<URL> urls = new LinkedList<>();
if (instances != null && !instances.isEmpty()) {
for (Instance instance : instances) {
URL url = buildURL(instance);
urls.add(url);
}
}
return urls;
}
private URL buildURL(Instance instance) {
Map<String, String> metadata = instance.getMetadata();
URL url = new URL();
url.setHost(instance.getIp());
url.setPort(instance.getPort());
url.setInterfaceName(metadata.get(INTERFACE_KEY));
url.setParameters(metadata);
return url;
}
/**
* 执行NamingService任务异常统一处理
*
* @param callback
*/
private void execute(TaskCallback<NamingService> callback) {
try {
callback.callback(namingService);
} catch (NacosException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getErrMsg(), e);
}
} catch (Throwable throwable) {
logger.error(throwable.getMessage(), throwable);
}
}
}
Nacos服务注册构建工厂
public class NacosRegistryFactory {
private final Map<String, Registry> REGISTRIES = new HashMap<>();
private final ReentrantLock LOCK = new ReentrantLock();
/**
* 获取注册中心
* @param registryUrl
* @return
*/
public Registry getRegistry(URL registryUrl) {
String serverAddr = registryUrl.getParameter(URLParameterConstants.ADDRESS);
String username = registryUrl.getParameter(URLParameterConstants.USERNAME);
String password = registryUrl.getParameter(URLParameterConstants.PASSWORD);
Registry registry = REGISTRIES.get(serverAddr);
if (registry == null){
LOCK.lock();
try {
registry = REGISTRIES.get(serverAddr);
if (registry == null){
registry = createRegistry(serverAddr,username,password);
if (registry == null) {
throw new IllegalStateException("Can not create registry :" + serverAddr);
}
}
}finally {
LOCK.unlock();
}
}
return registry;
}
/**
* 创建注册中心
* @param serverAddr 注册中心地址
* @param username 用户名
* @param password 密码
* @return
*/
public Registry createRegistry(String serverAddr, String username, String password) {
NamingService namingService = NacosNamingServiceUtils.createNamingService(serverAddr,username,password);
return new NacosRegistry(namingService);
}
}