Orchestrator解读
第一部分
最近在二次开发Orchestrator,所以研读了一下优雅切换部分的代码。
入口
首先,通过orchestrator-client来做为客户端请求入口来说明:
代码位于:
orchestrator/resources/bin/orchestrator-client
main:
function main {
check_requirements
detect_leader_api
instance_hostport=$(to_hostport $instance)
destination_hostport=$(to_hostport $destination)
run_command
}
代码主要位于: run_command 这一部分。
找到优雅切换模块: graceful-master-takeover-auto
function graceful_master_takeover_auto {
assert_nonempty "instance|alias" "${alias:-$instance}"
if [ -z "$destination_hostport" ] ; then
# No destination given.
api "graceful-master-takeover-auto/${alias:-$instance}"
else
# Explicit destination (designated master) given
# 这里请求了server端的API
api "graceful-master-takeover-auto/${alias:-$instance}/${destination_hostport}"
fi
print_details | jq '.SuccessorKey' | print_key
}
server端的API代码路径在:
go/http/api.go
API入口函数:
func (this *HttpAPI) registerAPIRequestInternal(m *martini.ClassicMartini, path string, handler martini.Handler, allowProxy bool)
...
this.registerAPIRequest(m, "graceful-master-takeover/:host/:port", this.GracefulMasterTakeover)
this.registerAPIRequest(m, "graceful-master-takeover/:host/:port/:designatedHost/:designatedPort", this.GracefulMasterTakeover)
this.registerAPIRequest(m, "graceful-master-takeover/:clusterHint", this.GracefulMasterTakeover)
this.registerAPIRequest(m, "graceful-master-takeover/:clusterHint/:designatedHost/:designatedPort", this.GracefulMasterTakeover)
this.registerAPIRequest(m, "graceful-master-takeover-auto/:host/:port", this.GracefulMasterTakeoverAuto)
this.registerAPIRequest(m, "graceful-master-takeover-auto/:host/:port/:designatedHost/:designatedPort", this.GracefulMasterTakeoverAuto)
this.registerAPIRequest(m, "graceful-master-takeover-auto/:clusterHint", this.GracefulMasterTakeoverAuto)
this.registerAPIRequest(m, "graceful-master-takeover-auto/:clusterHint/:designatedHost/:designatedPort", this.GracefulMasterTakeoverAuto)
func (this *HttpAPI) GracefulMasterTakeoverAuto(params martini.Params, r render.Render, req *http.Request, user auth.User) {
this.gracefulMasterTakeover(params, r, req, user, true)
}
然后我们找到API 优雅切换的代码模块:
func (this *HttpAPI) gracefulMasterTakeover(params martini.Params, r render.Render, req *http.Request, user auth.User, auto bool) {
if !isAuthorizedForAction(req, user) {
Respond(r, &APIResponse{Code: ERROR, Message: "Unauthorized"})
return
}
// 获取到整个集群的集群名,默认就是实例的hostname:port名称
clusterName, err := figureClusterName(getClusterHint(params))
if err != nil {
Respond(r, &APIResponse{Code: ERROR, Message: err.Error()})
return
}
// 传入进来的,是具体的instance,这里获取到提升的实例
designatedKey, _ := this.getInstanceKey(params["designatedHost"], params["designatedPort"])
// designatedKey may be empty/invalid
// 这里调用了logic包下的GracefulMasterTakeover,真正的切换逻辑-->手动优雅切换模块
// 文件位于: go/logic/topology_recovery.go
topologyRecovery, _, err := logic.GracefulMasterTakeover(clusterName, &designatedKey, auto)
if err != nil {
Respond(r, &APIResponse{Code: ERROR, Message: err.Error(), Details: topologyRecovery})
return
}
if topologyRecovery == nil || topologyRecovery.SuccessorKey == nil {
Respond(r, &APIResponse{Code: ERROR, Message: "graceful-master-takeover: no successor promoted", Details: topologyRecovery})
return
}
Respond(r, &APIResponse{Code: OK, Message: "graceful-master-takeover: successor promoted", Details: topologyRecovery})
}
topology_recovery.go:
// 这里是做逻辑切换的优雅切换后端代码入口
func GracefulMasterTakeover(clusterName string, designatedKey *inst.InstanceKey, auto bool) (topologyRecovery *TopologyRecovery, promotedMasterCoordinates *inst.BinlogCoordinates, err error) {
// inst.ReadClusterMaster --> 根据传入的集群名称,去读取到该集群的主库。这里就是去自身后端的数据库,执行了一条SQL语句:
###############################################################
select
*,
unix_timestamp() - unix_timestamp(last_checked) as seconds_since_last_checked,
ifnull(last_checked <= last_seen, 0) as is_last_check_valid,
unix_timestamp() - unix_timestamp(last_seen) as seconds_since_last_seen,
candidate_database_instance.last_suggested is not null
and candidate_database_instance.promotion_rule in ('must', 'prefer') as is_candidate,
ifnull(nullif(candidate_database_instance.promotion_rule, ''), 'neutral') as promotion_rule,
ifnull(unresolved_hostname, '') as unresolved_hostname,
(database_instance_downtime.downtime_active is not null and ifnull(database_instance_downtime.end_timestamp, now()) > now()) as is_downtimed,
ifnull(database_instance_downtime.reason, '') as downtime_reason,
ifnull(database_instance_downtime.owner, '') as downtime_owner,
ifnull(unix_timestamp() - unix_timestamp(begin_timestamp), 0) as elapsed_downtime_seconds,
ifnull(database_instance_downtime.end_timestamp, '') as downtime_end_timestamp
from
database_instance
left join candidate_database_instance using (hostname, port)
left join hostname_unresolve using (hostname)
left join database_instance_downtime using (hostname, port)
where
cluster_name = 'xxxxx'
and (replication_depth = 0 or is_co_master)
###############################################################
clusterMasters, err := inst.ReadClusterMaster(clusterName)
if err != nil {
return nil, nil, fmt.Errorf("Cannot deduce cluster master for %+v; error: %+v", clusterName, err)
}
// 如果该集群没有master或有好几个,那么直接返回失败
if len(clusterMasters) != 1 {
return nil, nil, fmt.Errorf("Cannot deduce cluster master for %+v. Found %+v potential masters", clusterName, len(clusterMasters))
}
// 返回的是一个数组,所以取第一个值,这就是master的instance,clusterMaster里面包含了一大串主库的信息,也就是那条sql取出来的东西
clusterMaster := clusterMasters[0]
// 根据传入的主库的hostname,也就是clusterMaster.Key,找到他下面所有的从库实例
clusterMasterDirectReplicas, err := inst.ReadReplicaInstances(&clusterMaster.Key)
if err != nil {
return nil, nil, log.Errore(err)
}
if len(clusterMasterDirectReplicas) == 0 {
return nil, nil, fmt.Errorf("Master %+v doesn't seem to have replicas", clusterMaster.Key)
}
if designatedKey != nil && !designatedKey.IsValid() {
// An empty or invalid key is as good as no key
designatedKey = nil
}
// 这里获取到待提升到实例,也就是新主库实例.具体实现逻辑在go/inst/instance_
designatedInstance, err := getGracefulMasterTakeoverDesignatedInstance(&clusterMaster.Key, designatedKey, clusterMasterDirectReplicas, auto)
if err != nil {
return nil, nil, log.Errore(err)
}
if inst.IsBannedFromBeingCandidateReplica(designatedInstance) {
return nil, nil, fmt.Errorf("GracefulMasterTakeover: designated instance %+v cannot be promoted due to promotion rule or it is explicitly ignored in PromotionIgnoreHostnameFilters configuration", designatedInstance.Key)
}
masterOfDesignatedInstance, err := inst.GetInstanceMaster(designatedInstance)
if err != nil {
return nil, nil, err
}
if !masterOfDesignatedInstance.Key.Equals(&clusterMaster.Key) {
return nil, nil, fmt.Errorf("Sanity check failure. It seems like the designated instance %+v does not replicate from the master %+v (designated instance's master key is %+v). This error is strange. Panicking", designatedInstance.Key, clusterMaster.Key, designatedInstance.MasterKey)
}
// 判断是否超过参数配置中的延迟,默认60秒,如果延迟大于60秒则切换失败
if !designatedInstance.HasReasonableMaintenanceReplicationLag() {
return nil, nil, fmt.Errorf("Desginated instance %+v seems to be lagging to much for thie operation. Aborting.", designatedInstance.Key)
}
// 这里开始真正的拓补结构变更,这段主要是把一主双从或者一主多从结果变更为级联状态
if len(clusterMasterDirectReplicas) > 1 {
log.Infof("GracefulMasterTakeover: Will let %+v take over its siblings", designatedInstance.Key)
relocatedReplicas, _, err, _ := inst.RelocateReplicas(&clusterMaster.Key, &designatedInstance.Key, "")
if len(relocatedReplicas) != len(clusterMasterDirectReplicas)-1 {
// We are unable to make designated instance master of all its siblings
relocatedReplicasKeyMap := inst.NewInstanceKeyMap()
relocatedReplicasKeyMap.AddInstances(relocatedReplicas)
// Let's see which replicas have not been relocated
for _, directReplica := range clusterMasterDirectReplicas {
if relocatedReplicasKeyMap.HasKey(directReplica.Key) {
// relocated, good
continue
}
if directReplica.Key.Equals(&designatedInstance.Key) {
// obviously we skip this one
continue
}
if directReplica.IsDowntimed {
// obviously we skip this one
log.Warningf("GracefulMasterTakeover: unable to relocate %+v below designated %+v, but since it is downtimed (downtime reason: %s) I will proceed", directReplica.Key, designatedInstance.Key, directReplica.DowntimeReason)
continue
}
return nil, nil, fmt.Errorf("Desginated instance %+v cannot take over all of its siblings. Error: %+v", designatedInstance.Key, err)
}
}
}
log.Infof("GracefulMasterTakeover: Will demote %+v and promote %+v instead", clusterMaster.Key, designatedInstance.Key)
replicationCreds, replicationCredentialsError := inst.ReadReplicationCredentials(&designatedInstance.Key)
// 这里主要是为了记录日志
analysisEntry, err := forceAnalysisEntry(clusterName, inst.DeadMaster, inst.GracefulMasterTakeoverCommandHint, &clusterMaster.Key)
if err != nil {
return nil, nil, err
}
preGracefulTakeoverTopologyRecovery := &TopologyRecovery{
SuccessorKey: &designatedInstance.Key,
AnalysisEntry: analysisEntry,
}
// 执行hook,优雅切换独有的hook,可以用来作为告警沉默等前置操作
if err := executeProcesses(config.Config.PreGracefulTakeoverProcesses, "PreGracefulTakeoverProcesses", preGracefulTakeoverTopologyRecovery, true); err != nil {
return nil, nil, fmt.Errorf("Failed running PreGracefulTakeoverProcesses: %+v", err)
}
log.Infof("GracefulMasterTakeover: Will set %+v as read_only", clusterMaster.Key)
// 设置read_only
if clusterMaster, err = inst.SetReadOnly(&clusterMaster.Key, true); err != nil {
return nil, nil, err
}
// 获取到bonlog执行到位点,对应show slave status中的execute_master_position这个字段
demotedMasterSelfBinlogCoordinates := &clusterMaster.SelfBinlogCoordinates
log.Infof("GracefulMasterTakeover: Will wait for %+v to reach master coordinates %+v", designatedInstance.Key, *demotedMasterSelfBinlogCoordinates)
// 如果小于配置文件中配置的延迟时间,则等到sql县城应用完毕,等待函数是一个for循环,每隔500毫秒循环一次。二次开发的日志补齐系统也是在这里判断的
if designatedInstance, _, err = inst.WaitForExecBinlogCoordinatesToReach(&designatedInstance.Key, demotedMasterSelfBinlogCoordinates, time.Duration(config.Config.ReasonableMaintenanceReplicationLagSeconds)*time.Second); err != nil {
return nil, nil, err
}
promotedMasterCoordinates = &designatedInstance.SelfBinlogCoordinates
log.Infof("GracefulMasterTakeover: attempting recovery")
// 这里调用了强制切换的函数,将待提升实例提升为新主库
recoveryAttempted, topologyRecovery, err := ForceExecuteRecovery(analysisEntry, &designatedInstance.Key, false)
if err != nil {
log.Errorf("GracefulMasterTakeover: noting an error, and for now proceeding: %+v", err)
}
if !recoveryAttempted {
return nil, nil, fmt.Errorf("GracefulMasterTakeover: unexpected error: recovery not attempted. This should not happen")
}
if topologyRecovery == nil {
return nil, nil, fmt.Errorf("GracefulMasterTakeover: recovery attempted but with no results. This should not happen")
}
// 如果失败,则会将老主设置read_only,这里逻辑不完善,二次开发后,会进行强制提升与剔除集群
if topologyRecovery.SuccessorKey == nil {
// Promotion fails.
// Undo setting read-only on original master.
inst.SetReadOnly(&clusterMaster.Key, false)
return nil, nil, fmt.Errorf("GracefulMasterTakeover: Recovery attempted yet no replica promoted; err=%+v", err)
}
var gtidHint inst.OperationGTIDHint = inst.GTIDHintNeutral
if topologyRecovery.RecoveryType == MasterRecoveryGTID {
gtidHint = inst.GTIDHintForce
}
// 这里执行change master to语句
clusterMaster, err = inst.ChangeMasterTo(&clusterMaster.Key, &designatedInstance.Key, promotedMasterCoordinates, false, gtidHint)
if !clusterMaster.SelfBinlogCoordinates.Equals(demotedMasterSelfBinlogCoordinates) {
log.Errorf("GracefulMasterTakeover: sanity problem. Demoted master's coordinates changed from %+v to %+v while supposed to have been frozen", *demotedMasterSelfBinlogCoordinates, clusterMaster.SelfBinlogCoordinates)
}
if !clusterMaster.HasReplicationCredentials && replicationCredentialsError == nil {
_, credentialsErr := inst.ChangeMasterCredentials(&clusterMaster.Key, replicationCreds)
if err == nil {
err = credentialsErr
}
}
if designatedInstance.AllowTLS {
_, enableSSLErr := inst.EnableMasterSSL(&clusterMaster.Key)
if err == nil {
err = enableSSLErr
}
}
if auto {
_, startReplicationErr := inst.StartReplication(&clusterMaster.Key)
if err == nil {
err = startReplicationErr
}
}
// 执行hook,也是优雅切换独有的,可以将告警沉默取消
executeProcesses(config.Config.PostGracefulTakeoverProcesses, "PostGracefulTakeoverProcesses", topologyRecovery, false)
return topologyRecovery, promotedMasterCoordinates, err
}