我们在分析BaseServer的onManagerStart回调函数时,看到有调用Provider注册到zookeeper的过程,这篇我们详细解析下这个过程。
//app启动后向zookeeper注册服务信息
FSOFRegistry::instance()->setParams($this->processName, $this->config, $this->port,
$this->serverProviders, $this->start_without_registry);
FSOFRegistry::instance()->registerZk();
//注册单列类
class FSOFRegistry
{
//设置zookeeper的日志文件及日志级别(1.error; 2.warn; 3.info; 4.debug)
const ZOOKEEPER_LOG_NO = 0;
const ZOOKEEPER_LOG_ERROR = 1;
const ZOOKEEPER_LOG_WARN = 2;
const ZOOKEEPER_LOG_INFO = 3;
const ZOOKEEPER_LOG_DEBUG = 4;
protected $appName;//应用名称
protected $port;//应用监听端口信息
protected $config;//配置信息
protected $serverProviders;//服务接口信息
protected $localIp = '127.0.0.1';
protected $ephemeral = false;
//zookeeper相关
protected $zkService = null;//zk service
protected $fsofUrlList = array();//要注册到zk的信息的列表
protected $start_without_registry;//p2p模式
private static $_instance;//本实例信息,这里使用了单列模式
private $logger;//日志组件
//单列模式创建实例
public static function instance()
{
if (empty(FSOFRegistry::$_instance))
{
FSOFRegistry::$_instance = new FSOFRegistry();
}
return FSOFRegistry::$_instance;
}
public function __construct()
{
$this->logger = \Logger::getLogger(__CLASS__);//初始化日志组件
}
//BaseServer侧调用这个函数设置相关属性信息,这里属性数据已经在类的初始化处解释过,这里不重复解释。
public function setParams($appName, $appConfig, $port, $serverProviders, $registry = true)
{
$this->logger->info("serverProviders:".json_encode($serverProviders));
$this->appName = $appName;
$this->port = $port;
$this->config = $appConfig;
$this->serverProviders = $serverProviders;
$this->start_without_registry = $registry;
try
{
//获取Provider服务所在的机器
$this->localIp = FSOFSystemUtil::getServiceIP();
}
catch (\Exception $e)
{
$this->logger->error('The server network configuration errors',$e);
//当获取IP失败时,禁止往zk注册
$this->start_without_registry = true;
}
}
//注册到zookeeper
public function registerZk()
{
if(!$this->start_without_registry)//非p2p模式,服务启动时,需要注册流程
{
try
{
$this->logger->info("init zk start...");
$this->ServiceSerialize();//生成service urls
$this->createZookeeperService();//创建zk service
$this->setZkLog();//设置日志组件信息
if (!$this->ephemeral)//这个参数按意思是标记是否是短暂注册,但全篇都是false。
{
//静态注册模式
$this->registerServiceToZk();//调用zk的接口,注册到zk
$this->inventZkService();//注销zk服务
}
//连接成功后,通过注入watcherCallFunc函数进行注册
$this->logger->info("init zk end...");
}
catch (\Exception $e)
{
$this->logger->error($e->getMessage(),$e);
}
}
}
//生成要注册到zk的服务信息
protected function ServiceSerialize()
{
try
{
unset($this->fsofUrlList);//清理信息
if (!empty($this->serverProviders))//服务信息不为空
{
if ($this->ephemeral)//短暂的
{
//注册时间
//$this->config["service_properties"]["timestamp"] = (int)(microtime(true) * 1000);
$this->config["service_properties"]["dynamic"] = "true";
}
else//长久的,按逻辑,流程会走这里。
{
$this->config["service_properties"]["dynamic"] = "false";
}
$services = $this->serverProviders;
foreach ($services as $interface => $serviceInfo)
{
//合并全局配置
if (isset($this->config["service_properties"]))
{
//接口配置优先级高于全局配置,所以$serviceInfo放后面
$serviceInfo = array_merge($this->config["service_properties"], $serviceInfo);
}
//不用上报的信息去掉
unset($serviceInfo['service']);
unset($serviceInfo['p2p_mode']);
if (empty($serviceInfo["version"]))
{
$serviceInfo['version'] = FSOFConstants::FSOF_SERVICE_VERSION_DEFAULT;
}
//与dubbo兼容处理
$serviceInfo['interface'] = $interface;//dubbo_admin需要使用
//序列化方式
$serviceInfo['serialization']= "fastjson";
ksort($serviceInfo);//参数排序,与dubbo兼容
//要注册到zk的url信息组成部分
$urlPara = array(
'scheme' => 'dubbo',
'host' => $this->localIp,
'port' => $this->port,
'path' => '/' . $interface,
//http_build_query会进行urlencode导致query参数被多编码一次,使用urldecode抵消
'query' => urldecode(http_build_query($serviceInfo)),
);
$this->logger->debug("serviceInfo:" . json_encode($serviceInfo) . "|urlPara:" . json_encode($urlPara));
try
{
$fsofUrl = new FSOFUrl($urlPara);//按规则拼接dubbo的url协议
}
catch (\Exception $e)
{
$this->logger->error('init url failed|app:' . $this->appName . '|urlPara:' . json_encode($urlPara));
}
$this->fsofUrlList[] = $fsofUrl;
}
}
}
catch(\Exception $e)
{
$errMsg = $e->getMessage();
$this->logger->error('ServiceSerialize:'.$errMsg, $e);
}
}