iTop使用MGR集群

MySQL Group Replication 能比较方便的实现高可用,但是 iTop 文档里明确说不支持多主的 MySQL 集群:

Galera clusters with multiple masters are NOT supported by iTop, because such clusters do not properly implement the GET_LOCK MySQL function (for more information: Galera cluster known limitations).

MGR 文档里也明确说不支持 GET_LOCK

Table Locks and Named Locks.  The certification process does not take into account table locks (see Section 13.3.6, “LOCK TABLES and UNLOCK TABLES Statements”) or named locks (see GET_LOCK()).

那么到底是哪些功能会受到影响呢?

架构

计划分别测试 iTop 使用 单主(Single Primary) 和 多主(Multi Primary) 模式的 MGR 集群,以及单节点,对比结果。以下是 MGR 单主和多主的架构图。

iTop MGR单主模式
iTop MGR单主模式
iTop MGR多主模式
iTop MGR多主模式

代码分析

搜索代码,可知 GET_LOCK 主要在 iTopMutex 类的 Lock() 方法里用到。

/**
* Acquire the mutex. Uses a MySQL lock. <b>Warn</b> : can have an abnormal behavior on MySQL clusters (see R-016204)
*
* @see https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock
*/	
public function Lock()
{
if ($this->bLocked)
	{
	// Lock already acquired
	return;
	}
	if (self::$aAcquiredLocks[$this->sName] == 0)
	{
	do
	{
	$res = $this->QueryToScalar("SELECT GET_LOCK('".$this->sName."', 3600)");

继续搜索 ->Lock(,找到以下文件:

./core/counter.class.inc.php:		$oiTopMutex->Lock();
./core/log.class.inc.php:			$oLock->Lock();
./core/ormlinkset.class.inc.php:		$oMtx->Lock();
./core/ownershiplock.class.inc.php:		$oMutex->Lock();
./core/ownershiplock.class.inc.php:		$oMutex->Lock();
./core/ownershiplock.class.inc.php:		$oMutex->Lock();
./core/ownershiplock.class.inc.php:		$oMutex->Lock();
./core/ownershiplock.class.inc.php:		$oMutex->Lock();
./datamodels/2.x/itop-backup/ajax.backup.php:				$oRestoreMutex->Lock();
./datamodels/2.x/itop-backup/main.itop-backup.php:		$oMutex->Lock();
./datamodels/2.x/itop-core-update/src/Service/CoreUpdater.php:		$oMutex->Lock();
./datamodels/2.x/itop-hub-connector/ajax.php:	$oMutex->Lock();
./synchro/synchro_import.php:		$oMutex->Lock();
./synchro/synchrodatasource.class.inc.php:			$oMutex->Lock();

下面基于 core/counter.class.inc.phpcore/ormlinkset.class.inc.php以及 core/ownershiplock.class.inc.php 来测试 MGR 的影响。其他文件基本是关于日志和备份的。暂时没有想到测试用例。

测试用例

core/counter

此文件定义了 iTopCounter 类,IncClass() 方法用到了 iTopMutex,此方法最终用在了 TicketMakeTicketRef() 里,被用户生成工单编号,即 R-000001 这种编号。

    public function MakeTicketRef()
    {
          $iNextId = ItopCounter::IncClass(get_class($this));
          $sRef = $this->MakeTicketRef($iNextId);
          $this->SetIfNull('ref', $sRef);
          $iKey = parent::DBInsertNoReload();
          return $iKey;
    }

测试思路,并发创建工单,查看是否有重复的工单编号。编写以下 Shell 脚本:

#!/bin/bash

[ $# -lt 2 ] && echo "$0 password url" && exit 1
user=admin
password=$1
url=$2
json_data='{"operation":"core/create","comment":"test mgr","class":"UserRequest","output_fields":"id,friendlyname","fields":{"org_id":"SELECT Organization WHERE name = \"Demo\"","caller_id":{"name":"Christie", "first_name":"Agatha"},"title":"Test MGR From API", "description":"Test"}}'

curl -s "$url/webservices/rest.php?version=1.3" -d "auth_user=$user&auth_pwd=$password&json_data=$json_data"

使用 parallel 同时在 3 个 iTop 实例上并发调用:

#!/bin/bash

for id in `seq 1 100`;do echo $id;done |parallel -j 3 ./ticket-api.sh admin http://192.168.10.101 &
for id in `seq 1 100`;do echo $id;done |parallel -j 3 ./ticket-api.sh admin http://192.168.10.102 &
for id in `seq 1 100`;do echo $id;done |parallel -j 3 ./ticket-api.sh admin http://192.168.10.103 &

结果出现较多重复编号:

      2 R-000686
      3 R-000596
      3 R-000685
      4 R-000593
      4 R-000598
      5 R-000597
iTop 使用MGR集群创建工单出现重复编号

MGR单主和单节点未出现重复工单编号。

core/ormlinkset

OrmLinkSet 类的 DBWrite() 方法使用了 iTopMutexDBWriteDBObjectDBWriteLinks 调用。DBWriteLinks 又被 DBOjectDBInsertNoReloadDBUpdate 调用。

/**
 * used both by insert/update
 *
 * @internal
 *
 * @throws \CoreException
 */
private function DBWriteLinks()
{
    foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
    {
        if (!$oAttDef->IsLinkSet()) continue;
        if (!array_key_exists($sAttCode, $this->m_aTouchedAtt)) continue;
        if (array_key_exists($sAttCode, $this->m_aModifiedAtt) && ($this->m_aModifiedAtt[$sAttCode] == false)) continue;

        /** @var \ormLinkSet $oLinkSet */
        $oLinkSet = $this->m_aCurrValues[$sAttCode];
        $oLinkSet->DBWrite($this);
    }
}

测试思路,并发更新 Personteam_list,观察是否创建重复的 lnkPersonToTeam。编写以下 Shell 脚本:

#!/bin/bash

[ $# -lt 2 ] && echo "$0 password url" && exit 1
user=admin
password=$1
url=$2
json_data='{"operation":"core/update","comment":"test mgr","class":"Person","key":"SELECT Person WHERE name=\"Xing\" AND first_name=\"Ming\"","output_fields":"id,team_list,friendlyname","fields":{"team_list":[{"team_id":{"name":"Helpdesk"}}]}}'

curl -s "$url/webservices/rest.php?version=1.3" -d "auth_user=$user&auth_pwd=$password&json_data=$json_data" |jq .

使用 parallel 同时在3 个节点上并发操作:

#!/bin/bash

for id in `seq 1 10`;do echo $id;done |parallel -j 3 ./ormlinkset.sh admin http://192.168.10.101 &
for id in `seq 1 10`;do echo $id;done |parallel -j 3 ./ormlinkset.sh admin http://192.168.10.102 &
for id in `seq 1 10`;do echo $id;done |parallel -j 3 ./ormlinkset.sh admin http://192.168.10.103 &

多主,单主及单节点均出现重复创建的 lnkPersonToTeam

iTop MGR ormLinkSet测试

此项测试单节点也有问题,有可能是测试用例设计的有问题。

core/ownershiplock

/**
 * Mechanism to obtain an exclusive lock while editing an object
 *
 * @package     iTopORM
 */

/**
 * Persistent storage (in the database) for remembering that an object is locked 
 */
class iTopOwnershipToken extends DBObject
{
...
}

/**
 * Utility class to acquire/extend/release/kill an exclusive lock on a given persistent object,
 * for example to prevent concurrent edition of the same object.
 * Each lock has an expiration delay of 120 seconds (tunable via the configuration parameter 'concurrent_lock_expiration_delay')
 * A watchdog (called twice during this delay) is in charge of keeping the lock "alive" while an object is being edited.
 */
class iTopOwnershipLock
{

主要用在 CMDBAbstractObjectDisplayModifyFormDisplayStimulusForm 中。用于阻止并发修改。

注意到代码中检查了配置项 concurrent_lock_enabled

$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');

可知阻止并发修改是一个可选的功能,默认是关闭的,如需使用,要在配置文件中开启[3]。 此功能效果是,当有人正在编辑一个对象时,其他人浏览该对象,会提示对象被锁住,正在被某人修改,并且不显示修改按钮。

iTop 阻止对象并发修改

测试结果显示

  • MGR多主模式下不同iTop实例编辑同一对象,此功能无效
  • MGR多主模式下在同一iTop实例操作,此功能正常
  • MGR单主模式及单节点,此功能正常

结论

测试结果对照表

测试项MGR多主MGR单主单节点备注
工单编号出现重复项未见异常未见异常 
Ormlinkset出现多lnk情况出现多lnk情况出现多lnk情况可能测试用例错误
阻止并发修改不同实例下异常未见异常未见异常 
iTop MGR测试结果

因此,iTop 使用 MGR 单主模式可以在实现高可用的前提下获得最好的兼容性。

参考资料

1. https://www.itophub.io/wiki/page?id=2_7_0%3Ainstall%3Ainstalling_itop#software_requirements
2. https://dev.mysql.com/doc/refman/8.0/en/group-replication-limitations.html
3. https://www.itophub.io/wiki/page?id=2_2_0%3Aadmin%3Alocking

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注