当我们发出命令:
orchestrator-client -c discover -i hostname:port
首先,orchestrator会发现给定的实例,即命令中的hostname:port这个实例,然后进而通过该实例来发现整个拓补结构。
具体如何进行发现?通过以下两步进行discover:
1、使用MySQLHostnameResolveMethod和HostnameResolveMethod两个参数来发现给定实例,即 -i hostname:port这个实例。
2、使用DiscoverByShowSlaveHosts来发现整个拓补结构。
HostnameResolveMethod代码位于inst/resolve.go第106行:
这是一个解析hostname的函数。可以看到取值有四种:
none:直接return回传入的hostname
default:直接return回传入的hostname
cname:调用getcname函数,解析到cname,然后return
ip:getHostnameIP这个函数主要的动作是调用net.LookupIP(hostname)这个函数,拿到真实ip地址,然后返回之
func resolveHostname(hostname string) (string, error) {
switch strings.ToLower(config.Config.HostnameResolveMethod) {
case "none":
return hostname, nil
case "default":
return hostname, nil
case "cname":
return GetCNAME(hostname)
case "ip":
return getHostnameIP(hostname)
}
return hostname, nil
}
MySQLHostnameResolveMethod代码位于inst/instance_dao.go第421行:
可以看到取值有三种:
none:什么也不做
“default”, “hostname”, “@@hostname”:可以看到db.QueryRow中有个变量叫做mysqlHostname的,他是通过查询实例数据库,使用select @@global.hostname语句来查询到的值
“report_host”, “@@report_host”:这个也很好理解,是通过发出一个ifnull(@@global.report_host, ‘’)语句来实现的
...
db.QueryRow("select @@global.hostname, ifnull(@@global.report_host, ''), @@global.server_id, @@global.version, @@global.version_comment, @@global.read_only, @@global.binlog_format, @@global.log_bin, @@global.log_slave_updates").Scan(
&mysqlHostname, &mysqlReportHost, &instance.ServerID, &instance.Version, &instance.VersionComment, &instance.ReadOnly, &instance.Binlog_format, &instance.LogBinEnabled, &instance.LogReplicationUpdatesEnabled)
...
switch strings.ToLower(config.Config.MySQLHostnameResolveMethod) {
case "none":
resolvedHostname = instance.Key.Hostname
case "default", "hostname", "@@hostname":
resolvedHostname = mysqlHostname
case "report_host", "@@report_host":
if mysqlReportHost == "" {
err = fmt.Errorf("MySQLHostnameResolveMethod configured to use @@report_host but %+v has NULL/empty @@report_host", instanceKey)
goto Cleanup
}
resolvedHostname = mysqlReportHost
default:
resolvedHostname = instance.Key.Hostname
}
那么在通过以上代码发现给定的实例后,现在就会通过DiscoverByShowSlaveHosts这个参数来完成整个拓补结构的发现。具体如何发现的,且看下文:
代码位于inst/instance_dao.go下。
该参数有true和false两种选项。
我们来一段一段解读,首先解读的部分是使用show slave hosts语句来发现的部分,代码如下:
// 这里首先读取配置文件中的DiscoverByShowSlaveHosts配置,如果设置为True的话,进入下面的逻辑
if config.Config.DiscoverByShowSlaveHosts || isMaxScale {
// 这里去给定实例上发出一条SQL:show slave hosts,下面的事情就很简单了,就是做一些解析hostname之类的工作了
err := sqlutils.QueryRowsMap(db, `show slave hosts`,
func(m sqlutils.RowMap) error {
// MaxScale 1.1 may trigger an error with this command, but
// also we may see issues if anything on the MySQL server locks up.
// Consequently it's important to validate the values received look
// good prior to calling ResolveHostname()
host := m.GetString("Host")
port := m.GetIntD("Port", 0)
// 这里判断show slave hosts结果,如果host结果为空,则直接会返回失败
if host == "" || port == 0 {
if isMaxScale && host == "" && port == 0 {
// MaxScale reports a bad response sometimes so ignore it.
// - seen in 1.1.0 and 1.4.3.4
return nil
}
// otherwise report the error to the caller
return fmt.Errorf("ReadTopologyInstance(%+v) 'show slave hosts' returned row with <host,port>: <%v,%v>", instanceKey, host, port)
}
// 接下来,如果host不为空,那么会调用NewResolveInstanceKey(),该函数又吊用了newInstanceKey()这个函数,newInstanceKey()主要作用就是解析hostname,生成一个叫InstanceKey的结构体,内容为:instanceKye = &InstanceKey{Hostname: hostname, Port: port},最后return instanceKye,也就是对应下面的replicaKey这个变量
replicaKey, err := NewResolveInstanceKey(host, port)
if err == nil && replicaKey.IsValid() {
// 这个如果replicaKey有效,那么会进行正则匹配,规则就是配置文件中DiscoveryIgnoreReplicaHostnameFilters的配置,如果匹配到了会返回一个true,然而这里是!RegexpMatchPatterns,所以就是说匹配不到的话,会调用instance.AddReplicaKey(replicaKey)这个函数
if !RegexpMatchPatterns(replicaKey.StringCode(), config.Config.DiscoveryIgnoreReplicaHostnameFilters) {
instance.AddReplicaKey(replicaKey)
}
foundByShowSlaveHosts = true
}
return err
})
logReadTopologyInstanceError(instanceKey, "show slave hosts", err)
}
接下来来解读一下DiscoverByShowSlaveHosts设置为False的部分,foundByShowSlaveHosts这个变量在开始的时候,默认值给设置成了false。所以就相当于DiscoverByShowSlaveHosts为false。
if !foundByShowSlaveHosts && !isMaxScale {
// Either not configured to read SHOW SLAVE HOSTS or nothing was there.
// Discover by information_schema.processlist
// 这里使用了协程,所以使用了wg
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
// 对实例发出一个语句,就是查询processlist表,条件就是command IN ('Binlog Dump', 'Binlog Dump GTID')。后面基本和上面一样,只不过这个逻辑是通过processlist来发现拓补实例的
err := sqlutils.QueryRowsMap(db, `
select
substring_index(host, ':', 1) as slave_hostname
from
information_schema.processlist
where
command IN ('Binlog Dump', 'Binlog Dump GTID')
`,
func(m sqlutils.RowMap) error {
cname, resolveErr := ResolveHostname(m.GetString("slave_hostname"))
if resolveErr != nil {
logReadTopologyInstanceError(instanceKey, "ResolveHostname: processlist", resolveErr)
}
replicaKey := InstanceKey{Hostname: cname, Port: instance.Key.Port}
if !RegexpMatchPatterns(replicaKey.StringCode(), config.Config.DiscoveryIgnoreReplicaHostnameFilters) {
instance.AddReplicaKey(&replicaKey)
}
return err
})
logReadTopologyInstanceError(instanceKey, "processlist", err)
}()
}
当你在使用或配置Orchestrator时,要注意MySQLHostnameResolveMethod、HostnameResolveMethod、DiscoverByShowSlaveHosts这三个参数的配置:
1、DiscoverByShowSlaveHosts设置为True,并且你的主机的hostname在安装mysql之前配置过,并且可以ping通主机名,那么你只需要在mysql配置文件中配置report_host和report_port参数,MySQLHostnameResolveMethod、HostnameResolveMethod这俩个配置只需要使用默认配置即可,即@@hostname和default。
2、DiscoverByShowSlaveHosts设置为True,但是你的主机的hostname是local时,那么你需要配置你的mysql配置文件:添加report_host和report_port。并且MySQLHostnameResolveMethod、HostnameResolveMethod这俩个配置需要配置为:none和none(或者这里可以改成:ip)
3、DiscoverByShowSlaveHosts设置为False,并且你的主机的hostname在安装mysql之前配置过,并且可以ping通主机名,那么你什么也不需要改动,直接discover即可。
4、DiscoverByShowSlaveHosts设置为False,但是你的主机的hostname是local时,那么你需要配置MySQLHostnameResolveMethod、HostnameResolveMethod这俩个配置需要配置为:none和none(或者这里可以改成:ip),使用IP地址来发现。
Orchestrator的配置还是挺复杂的,经过以上解读,是不是清晰了一些呢。
关于hook或者其他生态工具,可以看我开源的二次开发的orchestrator地址: Orchestrator
也欢迎一起交流,可以私信我加我微信一起交流
原创文章,转载请注明哟