// 创建 NamingService 代码如下
public static NamingService createNamingService(String serverList) throws NacosException{
try {
Class> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
Constructor constructor = driverImplClass.getConstructor(String.class);
NamingService vendorImpl = (NamingService)constructor.newInstance(serverList);
return vendorImpl;
} catch (Throwable e) {
throw new NacosException(-400, e.getMessage());
}
}
// 构造函数如下
public NacosNamingService(String serverList){
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);
init(properties);
}
// init 函数如下
private void init(Properties properties){
// 初始化命名空间,如果没有设置,默认值 "public"
namespace = InitUtils.initNamespaceForNaming(properties);
// 初始化服务地址,没有配置 endpoint,使用传入的 ip和端口;
// 如果有配置 endpoint,则将 serverList 置为空字符串
// 域名解析规则:在endpoint端配置网段和环境的映射关系,
// endpoint在接收到客户端的请求之后,根据客户端的来源IP所属网段,计算出该客户端所属环境,
// 然后找到对应环境的IP列表返回给客户端,如上图二
// 可以使用 nginx 的geo 模块实现简单的地址服务器
// 更多参考:https://nacos.io/zh-cn/blog/address-server.html
initServerAddr(properties);
// 用于支持阿里云的 web context
InitUtils.initWebRootContext();
// 初始化缓存目录,默认为 {user.home} + "/nacos/naming/" + namespace;
initCacheDir();
// 初始化日志名,默认为 "naming.log"
initLogName(properties);
// 新建事件分发对象,构造函数如下
eventDispatcher = new EventDispatcher();
// 新建服务代理,如果有配置 endpoint,创建单线程 ScheduledThreadPoolExecutor,定时刷新服务地址列表
serverProxy = new NamingProxy(namespace, endpoint, serverList);
serverProxy.setProperties(properties);
// initClientBeatThreadCount(properties) 初始化客户端心跳检测线程数量,默认值为
// Runtime.getRuntime().availableProcessors() > 1 ? Runtime.getRuntime().availableProcessors() / 2 : 1;
// Runtime.getRuntime().availableProcessors() 返回的处理器数量不一定准确
// 参考: https://blog.csdn.net/zhanghongzheng3213/article/details/83376571
// 新建 BeatReactor 对象,使用 ScheduledThreadPoolExecutor 创建守护线程,定时运行 BeatProcessor
beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
// initPollingThreadCount(properties) 和上面初始化客户端心跳检测线程数量一样
// isLoadCacheAtStart(properties) 启动时是否加载缓存的服务信息,默认值 false
// 新建 HostReactor 对象,HostReactor 创建 ScheduledThreadPoolExecutor 线程池、FailoverReactor 对象和 PushReceiver 对象
// 其中 FailoverReactor 负责故障转移功能,参考下面 FailoverReactor inti() 代码
// PushReceiver 使用 DatagramSocket(UDP),接收消息,如果是 dom 类型,处理接收的数据,
// 如果服务有变动,添加 serviceInfo 到 EventDispatcher 的 changedServices 里面
// 更新本地缓存文件,更新 MetricsMonitor 中服务数量,返回 ack 信息
hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties), initPollingThreadCount(properties));
}
// EventDispatcher 对象构造函数
public EventDispatcher(){
// 新建自定义的单线程线程池,持有一个守护线程
executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r){
Thread thread = new Thread(r, "com.alibaba.nacos.naming.client.listener");
thread.setDaemon(true);
return thread;
}
});
// EventDispatcher 有一个 changedServices = new LinkedBlockingQueue()
// 事件分发
// 新建任务 Notifier 监听 changedServices 的消息,定时拉取数据,如果相关服务变更,通知观察者
executor.execute(new Notifier());
}
// BeatProcessor 对象
class BeatProcessor implements Runnable{
@Override
public void run(){
try {
for (Map.Entry entry : dom2Beat.entrySet()) {
BeatInfo beatInfo = entry.getValue();
// volatile 字段 scheduled,防止重复发送心跳
if (beatInfo.isScheduled()) {
continue;
}
beatInfo.setScheduled(true);
executorService.schedule(new BeatTask(beatInfo), 0, TimeUnit.MILLISECONDS);
}
} catch (Exception e) {
NAMING_LOGGER.error("[CLIENT-BEAT] Exception while scheduling beat.", e);
} finally {
executorService.schedule(this, clientBeatInterval, TimeUnit.MILLISECONDS);
}
}
}
class BeatTask implements Runnable{
BeatInfo beatInfo;
public BeatTask(BeatInfo beatInfo){
this.beatInfo = beatInfo;
}
@Override
public void run(){
// 发送心跳,代码如下
long result = serverProxy.sendBeat(beatInfo);
beatInfo.setScheduled(false);
if (result > 0) {
// 设置新的心跳周期,volatile 字段
clientBeatInterval = result;
}
}
}
// 发送心跳,返回值为新的心跳周期,更新心跳周期
public long sendBeat(BeatInfo beatInfo){
try {
if (NAMING_LOGGER.isDebugEnabled()) {
NAMING_LOGGER.debug("[BEAT] {} sending beat to server: {}", namespaceId, beatInfo.toString());
}
Map params = new HashMap(4);
params.put("beat", JSON.toJSONString(beatInfo));
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, beatInfo.getServiceName());
String result = reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/beat", params, HttpMethod.PUT);
JSONObject jsonObject = JSON.parseObject(result);
if (jsonObject != null) {
return jsonObject.getLong("clientBeatInterval");
}
} catch (Exception e) {
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: " + JSON.toJSONString(beatInfo), e);
}
return 0L;
}
// FailoverReactor 对象的初始化方法如下
public void init(){
// 定时检测故障转移模式是否开启,如果开启,则新建 FailoverFileReader 任务并调用 run()
// run 方法会读取缓存目录下 failover 文件夹的文件
executorService.scheduleWithFixedDelay(new SwitchRefresher(), 0L, 5000L, TimeUnit.MILLISECONDS);
// 一天备份一次服务信息到 failover 文件夹
executorService.scheduleWithFixedDelay(new DiskFileWriter(), 30, DAY_PERIOD_MINUTES, TimeUnit.MINUTES);
// backup file on startup if failover directory is empty.
executorService.schedule(new Runnable() {
@Override
public void run(){
try {
File cacheDir = new File(failoverDir);
if (!cacheDir.exists() && !cacheDir.mkdirs()) {
throw new IllegalStateException("failed to create cache dir: " + failoverDir);
}
File[] files = cacheDir.listFiles();
if (files == null || files.length <= 0) {
new DiskFileWriter().run();
}
} catch (Throwable e) {
NAMING_LOGGER.error("[NA] failed to backup file on startup.", e);
}
}
}, 10000L, TimeUnit.MILLISECONDS);
}