成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

怎么實(shí)現(xiàn)CloudStackHighAvailability源碼分析

怎么實(shí)現(xiàn)CloudStack High Availability源碼分析,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

創(chuàng)新互聯(lián)專注于淮安區(qū)網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供淮安區(qū)營銷型網(wǎng)站建設(shè),淮安區(qū)網(wǎng)站制作、淮安區(qū)網(wǎng)頁設(shè)計(jì)、淮安區(qū)網(wǎng)站官網(wǎng)定制、微信小程序開發(fā)服務(wù),打造淮安區(qū)網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供淮安區(qū)網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

我們先來看DirectAgentAttache的內(nèi)部類PingTask,首先我們要知道每一個注冊到CS中的主機(jī)都有一個對應(yīng)的DirectAgentAttache,這也就意味著每一個HOST都有一個PingTask線程在后臺循環(huán)運(yùn)行,時間間隔是由全局變量ping.interval來指定的,默認(rèn)是60s.

我們來看PingTask的代碼

ServerResource resource = _resource;
if (resource != null) {
      PingCommand cmd = resource.getCurrentStatus(_id);
      int retried = 0;
      while (cmd == null && ++retried <= _HostPingRetryCount.value()) {
            Thread.sleep(1000*_HostPingRetryTimer.value());
            cmd = resource.getCurrentStatus(_id);
      }
      if (cmd == null) {
            s_logger.warn("Unable to get current status on ">

_id代表host_id,當(dāng)getCurrentStatus能返回正確的cmd就說明能夠Ping通該host,那接下來就是執(zhí)行_agentMgr.handleCommands

public void handleCommands(final AgentAttache attache, final long sequence, final Command[] cmds) {
    for (final Pair<Integer, Listener> listener : _cmdMonitors) {
        final boolean processed = listener.second().processCommands(attache.getId(), sequence, cmds);
    }
}

其中我們關(guān)心BehindOnPingListener,我們來看它的processCommands方法

@Override
public boolean processCommands(final long agentId, final long seq, final Command[] commands) {
    final boolean processed = false;
    for (final Command cmd : commands) {
        if (cmd instanceof PingCommand) {
            pingBy(agentId);
        }
    }
    return processed;
}

接下來是pingBy方法

public void pingBy(final long agentId) {
    // Update PingMap with the latest time if agent entry exists in the PingMap
    if (_pingMap.replace(agentId, InaccurateClock.getTimeInSeconds()) == null) {
        s_logger.info("PingMap for agent: " + agentId + " will not be updated because agent is no longer in the PingMap");
    }
}

這里重點(diǎn)就是這個_pingMap,我們看到它其實(shí)是一個ConcurrentHashMap,key是agentId(比如hostId),value是一個時間戳,就是當(dāng)我們這一次如果Ping通之后會把當(dāng)前時間作為value插入到_pingMap 中。我們回顧一下上面說過PingTask是每ping.interval時間間隔執(zhí)行一次,所以如果我們的主機(jī)是在正常運(yùn)行的話那么_pingMap就會幾乎每ping.interval更新一次。(當(dāng)然執(zhí)行g(shù)etCurrentStatus方法會有一定的延遲)那如果主機(jī)出現(xiàn)突然的故障導(dǎo)致網(wǎng)絡(luò)無法連接的情況下,那_pingMap中的時間就會一直停留在上一次Ping通的那個時間戳。

所以我們來總結(jié)一下PingTask的邏輯:就是每隔ping.interval(默認(rèn)60s)去Ping我們的主機(jī),如果能夠Ping通就更新_pingMap中的value為當(dāng)前時間戳,否則什么都不做。

接下來我們要看的另一個后臺線程是MonitorTask,同樣是每隔ping.interval執(zhí)行一次,先是方法findAgentsBehindOnPing

    protected List<Long> findAgentsBehindOnPing() {
        final List<Long> agentsBehind = new ArrayList<Long>();
        final long cutoffTime = InaccurateClock.getTimeInSeconds() - getTimeout();
        for (final Map.Entry<Long, Long> entry : _pingMap.entrySet()) {
            if (entry.getValue() < cutoffTime) {
                agentsBehind.add(entry.getKey());
            }
        }
        return agentsBehind;
    }     

    protected long getTimeout() {
        return (long) (PingTimeout.value() * PingInterval.value());
    }

全局變量ping.timeout默認(rèn)值是2.5,這段代碼的意思就是找出上一次Ping通的時間距離現(xiàn)在超過ping.interval的2.5倍的主機(jī),簡單講就是Ping不通或者Ping通的延時超過我們認(rèn)為的不合理時間的主機(jī)。 正常情況下該方法返回的會是一個空的List,這個時候MonitorTask就結(jié)束當(dāng)前任務(wù)。但是如果出現(xiàn)網(wǎng)絡(luò)延時或者主機(jī)故障的時候,就要執(zhí)行接下來的代碼。

final List<Long> behindAgents = findAgentsBehindOnPing();
for (final Long agentId : behindAgents) {
    final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
    sc.and(sc.entity().getId(), Op.EQ, agentId);
    final HostVO h = sc.find();
    if (h != null) {
        final ResourceState resourceState = h.getResourceState();
        if (resourceState == ResourceState.Disabled || resourceState == ResourceState.Maintenance || resourceState == ResourceState.ErrorInMaintenance) {
            disconnectWithoutInvestigation(agentId, Event.ShutdownRequested);
        } else {
            final HostVO host = _hostDao.findById(agentId);
            if (host != null && (host.getType() == Host.Type.ConsoleProxy || host.getType() == Host.Type.SecondaryStorageVM
                            || host.getType() == Host.Type.SecondaryStorageCmdExecutor)) {
                disconnectWithoutInvestigation(agentId, Event.ShutdownRequested);
            } else {
                disconnectWithInvestigation(agentId, Event.PingTimeout);
            }
        }
    }
}

我們假設(shè)出問題的是一臺計(jì)算節(jié)點(diǎn),那么一路往下將要執(zhí)行的將是AgentManagerImpl的handleDisconnectWithInvestigation方法

protected boolean handleDisconnectWithInvestigation(final AgentAttache attache, Status.Event event) {
    final long hostId = attache.getId();
    HostVO host = _hostDao.findById(hostId);
    if (host != null) {
        Status nextStatus = null;
        nextStatus = host.getStatus().getNextStatus(event);
        if (nextStatus == Status.Alert) {
            Status determinedState = investigate(attache);
            if (determinedState == null) {
                if ((System.currentTimeMillis() >> 10) - host.getLastPinged() > AlertWait.value()) {
                    determinedState = Status.Alert;
                } else {
                    return false;
                }
            }
            final Status currentStatus = host.getStatus();
            if (determinedState == Status.Down) {
                event = Status.Event.HostDown;
            } else if (determinedState == Status.Up) {
                agentStatusTransitTo(host, Status.Event.Ping, _nodeId);
                return false;
            } else if (determinedState == Status.Disconnected) {
                if (currentStatus == Status.Disconnected) {
                    if ((System.currentTimeMillis() >> 10) - host.getLastPinged() > AlertWait.value()) {
                        event = Status.Event.WaitedTooLong;
                    } else {
                        return false;
                    }
                } else if (currentStatus == Status.Up) {
                    event = Status.Event.AgentDisconnected;
                }
            }
        } 
    }
    handleDisconnectWithoutInvestigation(attache, event, true, true);
    host = _hostDao.findById(hostId); // Maybe the host magically reappeared?
    if (host != null && host.getStatus() == Status.Down) {
        _haMgr.scheduleRestartForVmsOnHost(host, true);
    }
    return true;
}

我們先看一下該方法最后的那個if,就是在特定的條件下我們最終的目的就是重啟該主機(jī)上的所有虛擬機(jī),這才是 HA的真正目的。但是我們要記住我們進(jìn)入這個handleDisconnectWithInvestigation方法的前提其實(shí)是很簡單的,就是只要我們發(fā)現(xiàn)距離上一次Ping通該主機(jī)的時間超過比如說2分半鐘就會進(jìn)入該方法,而我們要真正執(zhí)行HA應(yīng)該是要非常確定該主機(jī)確實(shí)是掛掉了的情況下才發(fā)生的。所以該方法前面一大堆都是在反復(fù)的確認(rèn)主機(jī)的狀態(tài),就如方法名所示Inverstigation(調(diào)查)。 我們假設(shè)該主機(jī)的currentStatus是UP,event我們知道是PingTimeout,所以nextStatus就是Alert。接下來就是執(zhí)行investigate方法

protected Status investigate(final AgentAttache agent) {
    final Long hostId = agent.getId();
    final HostVO host = _hostDao.findById(hostId);
    if (host != null && host.getType() != null && !host.getType().isVirtual()) {
        final Answer answer = easySend(hostId, new CheckHealthCommand());
        if (answer != null && answer.getResult()) {
            final Status status = Status.Up;
            return status;
        }
        return _haMgr.investigate(hostId);
    }
    return Status.Alert;
}

該方法先會向該hostId發(fā)送一個CheckHealthCommand,這個時候會有兩種可能:

1、如果能夠接受到應(yīng)答說明此時該主機(jī)是正常的就直接返回UP狀態(tài),我們在回到handleDisconnectWithInvestigation就會發(fā)現(xiàn)此時該任務(wù)也就基本結(jié)束了意思就是觸發(fā)該方法的僅僅是臨時的網(wǎng)絡(luò)不通或者什么情況現(xiàn)在主機(jī)已經(jīng)恢復(fù)正常

2.那另一種情況就是CheckHealthCommand沒有得到應(yīng)答,也就是說我直接從management-server去請求你主機(jī)你沒有反應(yīng),那也不代表你就真的掛了,接下來怎么辦呢,我們?nèi)フ腋鞣N偵探(investigators)去調(diào)查你是否alive

@Override
public Status investigate(final long hostId) {
    final HostVO host = _hostDao.findById(hostId);
    if (host == null) {
        return Status.Alert;
    }
    Status hostState = null;
    for (Investigator investigator : investigators) {
        hostState = investigator.isAgentAlive(host);
        if (hostState != null) {
            return hostState;
        }
    }
    return hostState;
}

那假如我們的主機(jī)是一臺XenServer的主機(jī)的話,最重要的當(dāng)然是XenServerInvestigator,我們來看它的isAgentAlive方法

public Status isAgentAlive(Host agent) {
    CheckOnHostCommand cmd = new CheckOnHostCommand(agent);
    List<HostVO> neighbors = _resourceMgr.listAllHostsInCluster(agent.getClusterId());
    for (HostVO neighbor : neighbors) {
        Answer answer = _agentMgr.easySend(neighbor.getId(), cmd);
        if (answer != null && answer.getResult()) {
            CheckOnHostAnswer ans = (CheckOnHostAnswer)answer;
            if (!ans.isDetermined()) {
                continue;
            }
            return ans.isAlive() ? null : Status.Down;
        }
    }
    return null;
}

邏輯也很簡單就是我直接找不到你我就去找你同一個Cluster中的鄰居,我向你的每一個鄰居主機(jī)發(fā)送一個CheckOnHostCommand命令,看它們能不能知道你到底怎么了。關(guān)于CheckOnHostCommand命令的具體實(shí)現(xiàn)在開頭那篇官網(wǎng)的文章里有詳細(xì)的說明

If the network ping investigation returns that it cannot detect the status of the host, CloudStack HA then relies on the hypervisor specific investigation. For VmWare, there is no such investigation as the hypervisor host handles its own HA. For XenServer and KVM, CloudStack HA deploys a monitoring script that writes the current timestamp on to a heartbeat file on shared storage. If the timestamp cannot be written, the hypervisor host self-fences by rebooting itself. For these two hypervisors, CloudStack HA sends a CheckOnHostCommand to a neighboring hypervisor host that shares the same storage. The neighbor then checks on the heartbeat file on shared storage and see if the heartbeat is no longer being written. If the heartbeat is still being written, the host reports that the host in question is still alive. If the heartbeat file’s timestamp is lagging behind, after an acceptable timeout value, the host reports that the host in question is down and HA is started on the VMs on that host.

大致的意思是CS會在每一個XenServer和KVM的主機(jī)上運(yùn)行一段監(jiān)控腳本,這個腳本會將當(dāng)前時間戳寫入一個在共享存儲的文件中。如果某一臺主機(jī)發(fā)現(xiàn)自己無法往文件中寫入數(shù)據(jù)將會強(qiáng)制自己重啟。 那上面那段代碼的邏輯就是向與該被調(diào)查的主機(jī)共享存儲的其他主機(jī)發(fā)送CheckOnHostCommand命令,鄰居主機(jī)接受到命令就去查看文件中被調(diào)查主機(jī)有沒有持續(xù)的更新時間戳,如果有它就返回相應(yīng)說該主機(jī)is still alive,否則就返回說該主機(jī)is down. 這樣只有主機(jī)確實(shí)出了故障無法連接的情況下,handleDisconnectWithInvestigation方法中的determinedState才會是Status.Down,那么event就變成了Status.Event.HostDown,接下來就執(zhí)行HighAvailabilityManagerImpl的scheduleRestartForVmsOnHost方法重起該主機(jī)上的所以虛擬機(jī) ,然后是在數(shù)據(jù)庫中出入一個HaWorkVO,然后喚醒CS啟動的時候初始化好的WorkerThread,到很重要的同樣是HighAvailabilityManagerImpl的restart方法

protected Long restart(final HaWorkVO work) {
    boolean isHostRemoved = false;
    Boolean alive = null;
    if (work.getStep() == Step.Investigating) {
        if (!isHostRemoved) {
            Investigator investigator = null;
            for (Investigator it : investigators) {
                investigator = it;
                try
                {
(1)                alive = investigator.isVmAlive(vm, host);
                    break;
                } catch (UnknownVM e) {
                    s_logger.info(investigator.getName() + " could not find " + vm);
                }
            }
            boolean fenced = false;
            if (alive == null) {
                for (FenceBuilder fb : fenceBuilders) {
(2)            Boolean result = fb.fenceOff(vm, host);
                    if (result != null && result) {
                        fenced = true;
                        break;
                    }
                }
            }
(3)        _itMgr.advanceStop(vm.getUuid(), true);
        }
    }
    vm = _itMgr.findById(vm.getId());
(4)if (!_forceHA && !vm.isHaEnabled()) {
        return null; // VM doesn't require HA
    }
    try {
        HashMap<VirtualMachineProfile.Param, Object> params = new HashMap<VirtualMachineProfile.Param, Object>();
(5)    if (_haTag != null) {
            params.put(VirtualMachineProfile.Param.HaTag, _haTag);
        }
        WorkType wt = work.getWorkType();
        if (wt.equals(WorkType.HA)) {
            params.put(VirtualMachineProfile.Param.HaOperation, true);
        }
(6)   try{
            _itMgr.advanceStart(vm.getUuid(), params, null);
        }catch (InsufficientCapacityException e){
            s_logger.warn("Failed to deploy vm " + vmId + " with original planner, sending HAPlanner");
            _itMgr.advanceStart(vm.getUuid(), params, _haPlanners.get(0));
        }
    }
    return (System.currentTimeMillis() >> 10) + _restartRetryInterval;
}

如上代碼我所標(biāo)記的有5個重點(diǎn)需要關(guān)注的。 大致的流程如下:

(1)調(diào)用各個investigator.isVmAlive方法,如果isAlive則什么都不做,否則往下走

(2)調(diào)用fb.fenceOff方法

(3)執(zhí)行_itMgr.advanceStop方法

(4)關(guān)于_forceHA變量,因?yàn)槲以谌肿兞亢蛿?shù)據(jù)庫的configuration表中都沒有找到,所以初始化的值為 FALSE,那么也就是說只有vm.isHaEnabled為ture的VM才會繼續(xù)執(zhí)行下去,否則直接return了

(5)_haTag的值是由全局變量ha.tag來指定的,默認(rèn)為空,如果指定了這個值對后面確定VM分配主機(jī)很重要,記住這行代碼params.put(VirtualMachineProfile.Param.HaTag, _haTag);

(6)這里有沒有很熟悉,是的,凡是讀過CS創(chuàng)建VM實(shí)例的過程代碼的人都知道這個方法就是去分配一個VM,那么到這里整個CS的HA執(zhí)行代碼就完成大部分了,接下來就是重啟VM,至于該VM能否重啟就要依賴各種條件了,比如該Cluster中有沒有合適的主機(jī)、主機(jī)的物理資源是否充足、有沒有設(shè)置ha.tag、VM有沒有使用標(biāo)簽等等這里就不再詳述了。

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。

新聞標(biāo)題:怎么實(shí)現(xiàn)CloudStackHighAvailability源碼分析
文章URL:http://jinyejixie.com/article18/jjjigp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、用戶體驗(yàn)、靜態(tài)網(wǎng)站、App開發(fā)、營銷型網(wǎng)站建設(shè)小程序開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化
朝阳市| 呼和浩特市| 颍上县| 闽侯县| 巫溪县| 蚌埠市| 利津县| 新昌县| 延庆县| 石林| 颍上县| 榆社县| 湟源县| 利津县| 高雄县| 惠安县| 日喀则市| 乐安县| 册亨县| 定兴县| 哈尔滨市| 昌平区| 辽宁省| 奉新县| 遂川县| 汕尾市| 浠水县| 天全县| 张家界市| 蒙山县| 鹿邑县| 龙泉市| 驻马店市| 栾川县| 灯塔市| 静海县| 永昌县| 安仁县| 大洼县| 同德县| 上栗县|