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 单主和多主的架构图。
代码分析
搜索代码,可知 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.php
,core/ormlinkset.class.inc.php
以及 core/ownershiplock.class.inc.php
来测试 MGR 的影响。其他文件基本是关于日志和备份的。暂时没有想到测试用例。
测试用例
core/counter
此文件定义了 iTopCounter
类,IncClass()
方法用到了 iTopMutex
,此方法最终用在了 Ticket
的 MakeTicketRef()
里,被用户生成工单编号,即 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
MGR单主和单节点未出现重复工单编号。
core/ormlinkset
OrmLinkSet
类的 DBWrite()
方法使用了 iTopMutex
,DBWrite
被 DBObject
的 DBWriteLinks
调用。DBWriteLinks
又被 DBOject
的 DBInsertNoReload
和 DBUpdate
调用。
/**
* 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);
}
}
测试思路,并发更新 Person
的 team_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
。
此项测试单节点也有问题,有可能是测试用例设计的有问题。
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
{
主要用在 CMDBAbstractObject
的 DisplayModifyForm
和 DisplayStimulusForm
中。用于阻止并发修改。
注意到代码中检查了配置项 concurrent_lock_enabled
:
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
可知阻止并发修改是一个可选的功能,默认是关闭的,如需使用,要在配置文件中开启[3]。 此功能效果是,当有人正在编辑一个对象时,其他人浏览该对象,会提示对象被锁住,正在被某人修改,并且不显示修改按钮。
测试结果显示
- MGR多主模式下不同iTop实例编辑同一对象,此功能无效
- MGR多主模式下在同一iTop实例操作,此功能正常
- MGR单主模式及单节点,此功能正常
结论
测试结果对照表
测试项 | MGR多主 | MGR单主 | 单节点 | 备注 |
工单编号 | 出现重复项 | 未见异常 | 未见异常 | |
Ormlinkset | 出现多lnk情况 | 出现多lnk情况 | 出现多lnk情况 | 可能测试用例错误 |
阻止并发修改 | 不同实例下异常 | 未见异常 | 未见异常 |
因此,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
发表回复