python分布式事务方案(一)tcc

随着单体应用的拆分以及服务化的流行,现在分布式事务已经比较常见,分布式事务理论ACID、CAP、BASE等我就不说了,现在就直接说一下一种常见的解决方案-tcc
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

  • Try 阶段主要是对业务系统做检测及资源预留
  • Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
  • Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

优点: 跟和两阶段提交比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些

缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

下面介绍下我们应用的一种场景,有一个运维系统需要运用到zabbix,而运维系统拆分出了一个配置中心,下面是子系统依赖图

在配置告警策略时需要调用zabbix接口

这时就涉及到一个分布式事务。由于我们这里只涉及到两个事务,所以我这里就写了一个zabbix代理client,来作为事务协调器

class ZabbixClientProxy(object):
'''
zabbix client simple proxy
'''
client = models.get_zbx_client() def __init__(self):
self.create_triggers = list()
self.update_triggers = list()
self.delete_triggers = list()
self.update_macros = list() def trigger_create(self, name, expression,uuid):
try:
trigger = self.client.hosts.trigger_create(name, expression, 1)
trigger["uuid"]=uuid
self.create_triggers.append(trigger)
logger.debug("trigger_create " + name)
return trigger
except Exception, e:
logger.error("trigger_create fail,cause by " + e.message)
raise def trigger_update(self, triggerid, name, expression,uuid):
try:
logger.debug("trigger_update " + name)
old_trigger = self.client.hosts.trigger_get(triggerid)
update_result = self.client.hosts.trigger_update(
triggerid, name=name, expression=expression, priority=1, enable=True)
old_trigger["uuid"]=uuid
logger.debug(old_trigger)
self.update_triggers.append(old_trigger)
return update_result
except Exception, e:
logger.error("trigger_update fail,cause by " + e.message) def trigger_delete(self, triggerid,uuid):
try:
logger.debug("trigger_delete " + triggerid)
old_trigger = self.client.hosts.trigger_get(triggerid)
delete_result = self.client.hosts.trigger_delete(triggerid)
old_trigger["uuid"]=uuid
self.delete_triggers.append(old_trigger)
return delete_result
except Exception, e:
logger.error("trigger_delete fail,cause by " + e.message) def update_trigger_macro(self, uuid, item_threshold, alert_duration):
all_hmacros = self.get_macro_by_name(uuid)
if all_hmacros and len(all_hmacros) > 2:
self.update_macro(all_hmacros, "DISK_USER_MAX", item_threshold)
self.update_macro(all_hmacros, "DISK_USER_TIMES", str(alert_duration) + "m")
self.update_macro(all_hmacros, "DISK_USER_ENABLE", 1)
else:
self.create_macro("DISK_USER_MAX", item_threshold, uuid)
self.create_macro("DISK_USER_TIMES", str(alert_duration) + "m", uuid)
self.create_macro("DISK_USER_ENABLE", 1, uuid) def stop_trigger(self, assets):
if assets:
for asset in assets:
if asset.host is None:
continue
all_hmacros = self.get_macro_by_name(asset.host.uuid)
if all_hmacros and len(all_hmacros) > 2:
self.update_macro(all_hmacros, "DISK_USER_ENABLE", 0)
else:
self.create_macro("DISK_USER_MAX", 80, asset.host.uuid)
self.create_macro("DISK_USER_TIMES", "5m", asset.host.uuid)
self.create_macro("DISK_USER_ENABLE", 0, asset.host.uuid) def get_macro_by_name(self, uuid):
return self.client.macros.list(uuid) def update_macro(self, all_hmacros, macro_name, value):
for macro in all_hmacros:
if macro['macro'] == ('{$' + macro_name + '}'):
try:
self.client.macros.update(macro['hostmacroid'], macro=macro_name, value=value)
macro['name'] = macro_name
self.update_macros.append(macro)
logger.debug('update_macro ' + macro_name + ' to ' + str(value))
except Exception, e:
logger.error('update_macro ' + macro_name + ' fail,case by ' + e.message) def create_macro(self, macro_name, value, uuid):
try:
hostid = self.client.macros._get_hostid(uuid)
hmacro = self.client.macros.create(macro_name, value, hostid)
logger.debug("create_macro success,macro_name:" + macro_name + ",value:" + str(value))
except Exception, e:
logger.error("create_macro fail,cause by " + e.message) def trigger_get(self, triggerid):
return self.client.hosts.trigger_get(triggerid) def trigger_list(self, hostid):
return self.client.hosts.trigger_list(hostid) def item_list(self, uuid):
return self.client.hosts.item_list(uuid) def rollback(self):
logger.debug("start rollback")
# rollback create
for trigger in self.create_triggers:
try:
self.client.hosts.trigger_delete(trigger["triggerid"])
logger.debug('rollback_create_trigger ' + trigger["name"])
except Exception, e:
logger.error('rollback_create_trigger ' + trigger["triggerid"] + ' fail,case by ' + str(e.message))
self.create_triggers = []
for trigger in self.update_triggers:
try:
expression=trigger["expression"].replace(trigger['uuid']+']','{HOST.HOST}]')
self.client.hosts.trigger_update(trigger["triggerid"], name=trigger["name"],
expression=expression, priority=1, enable=True)
logger.debug('rollback_update_trigger ' + trigger["name"]) except Exception, e:
logger.error('rollback_update_trigger ' + trigger["triggerid"] + ' fail,case by ' + str(e.message))
self.update_triggers = []
for trigger in self.delete_triggers:
try:
expression=trigger["expression"].replace(trigger['uuid']+']','{HOST.HOST}]')
new_trigger = self.client.hosts.trigger_create(trigger["name"], expression, 1)
logger.debug(new_trigger)
logger.debug('rollback_delete_trigger ' + trigger["name"])
# 更新数据中的zabbix trigger id
alert_models.ConditionTrigger.objects.filter(zabbix_trigger_id=trigger["triggerid"]).update(
zabbix_trigger_id=new_trigger["triggerid"])
except Exception, e:
logger.error('rollback_delete_trigger ' + trigger["triggerid"] + ' fail,case by ' + str(e.message))
self.delete_triggers = [] for macro in self.update_macros:
try:
self.client.macros.update(macro['hostmacroid'], macro=macro['name'], value=macro['value'])
except Exception, e:
logger.error('rollback_update_macro ' + macro['name'] + ' fail,case by ' + str(e.message))
logger.debug("end rollback")

事务成功,则提交本地事务,如果失败则调用rollback

def create(self, request, *args, **kwargs):
'''
policy add
'''
assets = request.data["data"]
client = ZabbixClientProxy()
try:
with transaction.atomic():
#save policy
#将client作为参数,对主机、监控项、触发器进行增删改
except rest_framework_serializers.ValidationError, e:
logger.exception(e)
client.rollback()
raise

这样做还有一个问题就是,在回滚中如果网络突然断了这时会回滚失败,这里我们记录了日志,后面我们会通过扫描日志来做到最终一致性,这里我们后面坐了补偿,下一次修改时会自动修正回滚失败问题。

python分布式事务方案(一)tcc的更多相关文章

  1. Dubbo学习系列之十五(Seata分布式事务方案TCC模式)

    上篇的续集. 工具: Idea201902/JDK11/Gradle5.6.2/Mysql8.0.11/Lombok0.27/Postman7.5.0/SpringBoot2.1.9/Nacos1.1 ...

  2. Dubbo学习系列之十四(Seata分布式事务方案AT模式)

    一直说写有关最新技术的文章,但前面似乎都有点偏了,只能说算主流技术,今天这个主题,我觉得应该名副其实.分布式微服务的深水区并不是单个微服务的设计,而是服务间的数据一致性问题!解决了这个问题,才算是把分 ...

  3. [转帖]深度剖析一站式分布式事务方案 Seata-Server

    深度剖析一站式分布式事务方案 Seata-Server https://www.jianshu.com/p/940e2cfab67e 金融级分布式架构关注 22019.04.10 16:59:14字数 ...

  4. 对比7种分布式事务方案,还是偏爱阿里开源的Seata,真香!(原理+实战)

    前言 这是<Spring Cloud 进阶>专栏的第六篇文章,往期文章如下: 五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强? openFeign夺命连环9问,这谁受得了? 阿里面 ...

  5. ebay分布式事务方案中文版

    http://cailin.iteye.com/blog/2268428 不使用分布式事务实现目的  -- ibm https://www.ibm.com/developerworks/cn/clou ...

  6. 分布式事务(2)---TCC理论

    分布式事务(2)---TCC理论 上篇讲过有关2PC和3PC理论知识,博客:分布式事务(1)---2PC和3PC理论 我的理解:2PC.3PC还有TCC都蛮相似的.3PC大致是把2PC的第一阶段拆分成 ...

  7. 分布式事务之:TCC (Try-Confirm-Cancel) 模式

    在当前如火如荼的互联网浪潮下,如何应对海量数据.高并发成为大家面临的普遍难题.广大IT公司从以往的集中式网站架构,纷纷转向分布式的网站架构,随之而来的就是进行数据库拆分和应用拆分,如何在跨数据库.跨应 ...

  8. 分布式事务专题笔记(三)分布式事务解决方案之TCC(三阶段提交)

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 1.什么是TCC事务 TCC是Try.Confifirm.Cancel三个词语的缩写,TCC要求每个分支 ...

  9. 分析 5种分布式事务方案,还是选了阿里的 Seata(原理 + 实战)

    好长时间没发文了,最近着实是有点忙,当爹的第 43 天,身心疲惫.这又赶上年底,公司冲 KPI 强制技术部加班到十点,晚上孩子隔两三个小时一醒,基本没睡囫囵觉的机会,天天处于迷糊的状态,孩子还时不时起 ...

  10. 如何选择分布式事务形态(TCC,SAGA,2PC,补偿,基于消息最终一致性等等)

    各种形态的分布式事务 分布式事务有多种主流形态,包括: 基于消息实现的分布式事务 基于补偿实现的分布式事务(gts/fescar自动补偿的形式) 基于TCC实现的分布式事务 基于SAGA实现的分布式事 ...

随机推荐

  1. C# 语言在AGI 赛道上能做什么

    自从2022年11月OpenAI正式对外发布ChatGPT依赖,AGI 这条赛道上就挤满了重量级的选手,各大头部公司纷纷下场布局.原本就在机器学习.深度学习领域占据No.1的Python语言更是继续稳 ...

  2. OpenSSL静态库交叉编译

    一.编译前环境准备 使用的内核:4.15.0-118-generic(命令:uname -r可以查看) 交叉编译器:aarch64-linux-gnu-gcc openssl源码:openssl-1. ...

  3. C语言:不定长结构体的实现方式

    需求 有时候,我们会遇到一些情况:数据前部分相同,但是后部分长度不固定:数据格式相似,只是尾缀的长度不同,例如某些数据包,需要不定长度. 为了能够同时使用上不同长度的数据.可以用以下的方式实现. 方案 ...

  4. 一次Java服务内存过高的分析过程

    现象 年前,收到了短信报警,显示A服务的某台机器内存过高,超过80% 如上图所示,内存会阶段性增加.奇怪的是,十多台机器中只有这一台有这个问题 堆内内存分析 最先怀疑是内存泄漏的问题,所以首先使用jm ...

  5. Spring声明事务和分布式事务处理技术

    Spring声明事务的两种方式 方式一.传统的编程式事务管理: 需要手动编写代码在业务层注入事务管理模板(一般不用) 方式二.基于 AOP 技术实现的声明式事务管理: Spring 声明式事务管理在底 ...

  6. mac svn管理工具

    App Store中搜索snailsvn 分付费(98元)和免费试用

  7. Git常用命令汇总以及其它相关操作

    --文件目录操作命令 1 mkdir * 创建一个空目录 *指目录名 2 pwd 显示当前目录的路径. 3 cat * 查看*文件内容 4 git rm * 删除**文件 --git初始化操作 1 g ...

  8. [oeasy]python0101_尾声_PC_wintel_8080_诸神的黄昏_arm_riscv

    尾声 回忆上次内容 回顾了 ibm 使用开放架构 用 pc兼容机 战胜了 dec 小型机 apple 个人电脑 触击牺牲打 也破掉了 自己 软硬一体全自主的 金身 借助了 各种 软硬件厂商的 力量 最 ...

  9. [oeasy]python0088_字节_Byte_存储单位_KB_MB_GB_TB

    编码进化 回忆上次内容 上次 回顾了 字符大战的结果 ibm 曾经的 EBCDIC 由于字符不连续的隐患 导致后续 出现 无数问题 无法补救 7-bit 的 ASA X3.4-1963 字母序号连续 ...

  10. TIER 1: Sequel

    TIER 1: Sequel MySQL MySQL 是一种流行的开源关系型数据库管理系统(RDBMS),它被广泛用于存储和管理大量的结构化数据.MySQL 是由瑞典公司 MySQL AB 开发的,后 ...