当前位置: 首页 > 工具软件 > Orchestrator > 使用案例 >

orchestrator的discover模块

夏侯衡
2023-12-01

orchestrator的discover原理

当我们发出命令:

orchestrator-client -c discover -i hostname:port

首先,orchestrator会发现给定的实例,即命令中的hostname:port这个实例,然后进而通过该实例来发现整个拓补结构。
具体如何进行发现?通过以下两步进行discover:
1、使用MySQLHostnameResolveMethod和HostnameResolveMethod两个参数来发现给定实例,即 -i hostname:port这个实例。
2、使用DiscoverByShowSlaveHosts来发现整个拓补结构。

MySQLHostnameResolveMethod和HostnameResolveMethod详解

一、HostnameResolveMethod

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

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

那么在通过以上代码发现给定的实例后,现在就会通过DiscoverByShowSlaveHosts这个参数来完成整个拓补结构的发现。具体如何发现的,且看下文:

代码位于inst/instance_dao.go下。
该参数有truefalse两种选项。

我们来一段一段解读,首先解读的部分是使用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
也欢迎一起交流,可以私信我加我微信一起交流
原创文章,转载请注明哟

 类似资料: