2.nacos-client源码及实践
nacos-client.2.2.1-RC. SDK查看源码及使用
官网JAVA SDK 链接
- 主要内容
<dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>${version}</version> </dependency>
问题:
1. 获取配置api 是获取快照还是远程配置中心?
2. 快照文件默认存储在哪?然后配置?
3. 如何去监听是否动态修改,新文本存储在哪?
4. springboot 实践配置及结合的代码,如何灵活使用配置中心?
配置管理
-
获取配置
描述
用于服务启动的时候从 Nacos 获取配置。
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException
-
监听配置
描述
如果希望 Nacos 推送配置变更,可以使用 Nacos 动态监听配置接口来实现。
public void addListener(String dataId, String group, Listener listener)
-
删除监听
描述
取消监听配置,取消监听后配置不会再推送。
public void removeListener(String dataId, String group, Listener listener)
-
发布配置
描述
用于通过程序自动发布 Nacos 配置,以便通过自动化手段降低运维成本。注意:创建和修改配置时使用的同一个发布接口,当配置不存在时会创建配置,当配置已存在时会更新配置。
点击查看代码
public boolean publishConfig(String dataId, String group, String content) throws NacosException; @Since 1.4.1 public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException;
-
删除配置
描述
用于通过程序自动删除 Nacos 配置,以便通过自动化手段降低运维成本。注意: 当配置已存在时会删除该配置,当配置不存在时会直接返回成功消息。
public boolean removeConfig(String dataId, String group) throws NacosException
服务发现SDK
-
注册实例
描述注册一个实例到服务。
void registerInstance(String serviceName, String ip, int port) throws NacosException;
void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;
void registerInstance(String serviceName, Instance instance) throws NacosException;
-
注销实例
描述
删除服务下的一个实例。
void deregisterInstance(String serviceName, String ip, int port) throws NacosException;
void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;
-
获取全部实例
描述
获取服务下的所有实例。
List<Instance> getAllInstances(String serviceName) throws NacosException;
List<Instance> getAllInstances(String serviceName, List<String> clusters) throws NacosException;
-
监听服务
描述
监听服务下的实例列表变化。
void subscribe(String serviceName, EventListener listener) throws NacosException;
void subscribe(String serviceName, List<String> clusters, EventListener listener) throws NacosException;
源码实现
配置中心
下载源码包,在api包下config文件下,粗略浏览一下我们发现了 com.alibaba.nacos.api.config.ConfigService
接口:
点击查看代码
public interface ConfigService {
/**
* Get config.
*
* @param dataId dataId
* @param group group
* @param timeoutMs read timeout
* @return config value
* @throws NacosException NacosException
*/
String getConfig(String dataId, String group, long timeoutMs) throws NacosException;
/**
* Get config and register Listener.
*
* <p>If you want to pull it yourself when the program starts to get the configuration for the first time, and the
* registered Listener is used for future configuration updates, you can keep the original code unchanged, just add
* the system parameter: enableRemoteSyncConfig = "true" ( But there is network overhead); therefore we recommend
* that you use this interface directly
*
* @param dataId dataId
* @param group group
* @param timeoutMs read timeout
* @param listener {@link Listener}
* @return config value
* @throws NacosException NacosException
*/
String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener)
throws NacosException;
/**
* Add a listener to the configuration, after the server modified the configuration, the client will use the
* incoming listener callback. Recommended asynchronous processing, the application can implement the getExecutor
* method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, May
* block other configurations or be blocked by other configurations.
*
* @param dataId dataId
* @param group group
* @param listener listener
* @throws NacosException NacosException
*/
void addListener(String dataId, String group, Listener listener) throws NacosException;
/**
* Publish config.
*
* @param dataId dataId
* @param group group
* @param content content
* @return Whether publish
* @throws NacosException NacosException
*/
boolean publishConfig(String dataId, String group, String content) throws NacosException;
/**
* Publish config.
*
* @param dataId dataId
* @param group group
* @param content content
* @param type config type {@link ConfigType}
* @return Whether publish
* @throws NacosException NacosException
*/
boolean publishConfig(String dataId, String group, String content, String type) throws NacosException;
/**
* Cas Publish config.
*
* @param dataId dataId
* @param group group
* @param content content
* @param casMd5 casMd5 prev content's md5 to cas.
* @return Whether publish
* @throws NacosException NacosException
*/
boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException;
/**
* Cas Publish config.
*
* @param dataId dataId
* @param group group
* @param content content
* @param casMd5 casMd5 prev content's md5 to cas.
* @param type config type {@link ConfigType}
* @return Whether publish
* @throws NacosException NacosException
*/
boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type)
throws NacosException;
/**
* Remove config.
*
* @param dataId dataId
* @param group group
* @return whether remove
* @throws NacosException NacosException
*/
boolean removeConfig(String dataId, String group) throws NacosException;
/**
* Remove listener.
*
* @param dataId dataId
* @param group group
* @param listener listener
*/
void removeListener(String dataId, String group, Listener listener);
/**
* Get server status.
*
* @return whether health
*/
String getServerStatus();
/**
* add config filter.
* It is recommended to use {@link com.alibaba.nacos.api.config.filter.AbstractConfigFilter} to expand the filter.
*
* @param configFilter filter
* @since 2.3.0
*/
void addConfigFilter(IConfigFilter configFilter);
/**
* Shutdown the resource service.
*
* @throws NacosException exception.
*/
void shutDown() throws NacosException;
}
获取配置信息getConfig
查看getConfig(String dataId, String group, long timeoutMs) 的实现:调用了
getConfigInner(namespace, dataId, group, timeoutMs);
在参数处理中:
grop处理, 未设置返回默认分组DEFAULT_GROUP:
return (StringUtils.isBlank(group)) ? Constants.DEFAULT_GROUP : group.trim();
接下来我看到先去nacos服务器故障本地缓存取:
点击查看代码
// We first try to use local failover content if exists.
// A config content for failover is not created by client program automatically,
// but is maintained by user.
// This is designed for certain scenario like client emergency reboot,
// changing config needed in the same time, while nacos server is down.
String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
若本地缓存没有,则远程读取
点击查看代码
try {
ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false);
cr.setContent(response.getContent());
cr.setEncryptedDataKey(response.getEncryptedDataKey());
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
} catch (NacosException ioe) {
if (NacosException.NO_RIGHT == ioe.getErrCode()) {
throw ioe;
}
LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",
worker.getAgentName(), dataId, group, tenant, ioe.toString());
}
content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
}
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
- 远程获取文本
- 本地生成文本快照
远程获取文本 worker 是在构造参数实例化的时候
this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, clientProperties);
继续查看ClientWorker ,发现是由 ConfigRpcTransportClient agent 远程调用
this.agent.queryConfig(dataId, group, tenant, readTimeout, notify);
至此就获取到了远程配置文本信息
获取文本信息并添加监听getConfigAndSignListener
该方法就是获取远程文本,并监听信息的变化;
点击查看代码
public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener)
throws NacosException {
group = StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group.trim();
ConfigResponse configResponse = worker.getAgent()
.queryConfig(dataId, group, worker.getAgent().getTenant(), timeoutMs, false);
String content = configResponse.getContent();
String encryptedDataKey = configResponse.getEncryptedDataKey();
worker.addTenantListenersWithContent(dataId, group, content, encryptedDataKey, Arrays.asList(listener));
// get a decryptContent, fix https://github.com/alibaba/nacos/issues/7039
ConfigResponse cr = new ConfigResponse();
cr.setDataId(dataId);
cr.setGroup(group);
cr.setContent(content);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
return cr.getContent();
}
ConfigResponse configResponse = worker.getAgent() .queryConfig(dataId, group, worker.getAgent().getTenant(), timeoutMs, false);
并添加一个监听
worker.addTenantListenersWithContent(dataId, group, content, encryptedDataKey, Arrays.asList(listener));
监听既把信息塞入一个缓存对象CacheData中用于监听管理
添加监听addListener
void addListener(String dataId, String group, Listener listener) throws NacosException;
点击查看代码
public void addTenantListeners(String dataId, String group, List<? extends Listener> listeners)
throws NacosException {
group = blank2defaultGroup(group);
String tenant = agent.getTenant();
CacheData cache = addCacheDataIfAbsent(dataId, group, tenant);
synchronized (cache) {
for (Listener listener : listeners) {
cache.addListener(listener);
}
cache.setDiscard(false);
cache.setSyncWithServer(false);
agent.notifyListenConfig();
}
}
其他接口实现也都是类似的,如发布配置
boolean publishConfig(String dataId, String group, String content) throws NacosException;
boolean publishConfig(String dataId, String group, String content, String type) throws NacosException;
与spingboot使用
- nacos 服务下载 https://github.com/alibaba/nacos/releases
以Linux服务器为例,如下:
springboot 应用配置
设置Nacos开机自启
-
添加nacos.service文件命令:
Description=nacos After=network.target [Service] Type=forking ExecStart=/usr/local/nacos/bin/startup.sh -m standalone ExecReload=/usr/local/nacos/bin/shutdown.sh ExecStop=/usr/local/nacos/bin/shutdown.sh PrivateTmp=true [Install] WantedBy=multi-user.target" > /lib/systemd/system/nacos.service```
-
修改nacos的startup.sh
vim /usr/local/nacos/bin/startup.sh
- 修改JAVA_HOME路径并注销之后的3行配置,如下:
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/local/jdk1.8.0_191
#[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java
#[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/opt/taobao/java
#[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME
- 设置开机自启
systemctl daemon-reload #重新加载服务配置
systemctl enable nacos.service #设置为开机启动
systemctl start nacos.service #启动nacos服务
systemctl stop nacos.service #停止nacos服务
systemctl status nacos.service #查看nacos服务状态