【云+社区年度征文】hmily之初识

2020-12-23 10:16:02 浏览数 (1)

Hmily

hmily是什么

Hmily 全称 How much I love you . 意为“我多么爱你”,是不是很浪漫~ 作者(称他为宇哥)可能对感情比较专一,故命名为“hmily”。说完它的意义,那么它到底是做啥的呢?官方给的定义如下:Hmily是一款高性能,高可靠,易使用的柔性分布式事务解决方案,目前提供了对dubbo,spring-cloud,motan,grpc等rpc框架的支持,在易用性上提供零侵入性式的 Spring-Boot, Spring-Namespace 快速集成,目标是打造金融级的一体系分布式事务解决方案。当然,上面这个解释是官方定义的,我的理解,它是一款柔性的分布式事务解决方案,性能高不高,不是我说了算,而是得进行压测,后续会出这类压测的文章。

功能

  1. 高可靠性 :支持分布式场景下,事务异常回滚,超时异常恢复,防止事务悬挂
  2. 易用性 :提供零侵入性式的 Spring-Boot, Spring-Namespace 快速与业务系统集成
  3. 高性能 :去中心化设计,与业务系统完全融合,天然支持集群部署
  4. 可观测性 :Metrics多项指标性能监控,以及admin管理后台UI展示
  5. 多种RPC : 支持 Dubbo, SpringCloud,Motan, Sofa-rpc, brpc, tars 等知名RPC框架
  6. 日志存储 : 支持 mysql, oracle, mongodb, redis, zookeeper file 等方式,存储方式均使用异步
  7. 复杂场景 : 支持RPC嵌套调用事务

使用

RPC以springcloud为例,用简单的下单业务进行实验

springcloud demo注册中心使用eureka,eureka配置这里就不在赘述,搭建项目的代码可以去github下,这里也不在多余说

服务: 订单服务,账户服务,库存服务

首先配置这几个服务的配置文件

代码语言:javascript复制
hmily:
  server:
    configMode: local
    appName: account-sc/order-sc/inventory-sc
  #  如果server.configMode eq local 的时候才会读取到这里的配置信息.
  config:
    appName: account-sc/order-sc/inventory-sc
    # 序列化方式 kryo会高一些
    serializer: kryo
    #上下文传输 默认使用局部线程
    contextTransmittalMode: threadLocal
    #定时任务线程池最大数
    scheduledThreadMax: 16
    #定时恢复事务时间(补偿) 默认60s跑一次
    scheduledRecoveryDelay: 60
    #定时清除事务日志时间
    scheduledCleanDelay: 60
    # 定时清理
    scheduledPhyDeletedDelay: 600
    scheduledInitDelay: 30
    #定时恢复事务时间(补偿)
    recoverDelayTime: 60
    cleanDelayTime: 180
  #查询limit
    limit: 200
    retryMax: 10
    bufferSize: 8192
    consumerThreads: 16
    #开启异步存储
    asyncRepository: true
    autoSql: true
    phyDeleted: true
    storeDays: 3
    #日志存储方式 本地文件
    repository: file

序列化除了kryo,同时还支持以下四种序列化方式

hessian,jdk,msgpack,protobuf

日志存储也可以使用database etcd mongodb redis zk

这里模拟几个场景:

  • 模拟下单付款操作在try阶段时候,账户rpc异常,此时订单状态会回滚,达到数据的一致性(注意:这里模拟的是系统异常,或者rpc异常
  • 模拟下单付款操作在try阶段时候,账户rpc超时异常(但是最后自身又成功了),此时订单状态会回滚,账户系统依赖自身的事务日志进行调度恢复,达到数据的一致性(异常指的是超时异常
  • 模拟下单付款操作在try阶段时候,库存异常,此时账户系统和订单状态会回滚,达到数据的一致性(注意:这里模拟的是系统异常,或者rpc异常)
  • 模拟下单付款操作在try阶段时候,库存超时异常(但是自身最后又成功了),此时账户系统和订单状态会回滚,(库存依赖事务日志进行恢复),达到数据的一致性(异常指的是超时异常)
  • 订单支付接口(里模拟的是rpc的嵌套调用 order--> account--> inventory, inventory异常情况

以第一个场景为例进行解析:

模拟下单付款操作在try阶段时候,账户rpc异常,此时订单状态会回滚,达到数据的一致性(注意:这里模拟的是系统异常,或者rpc异常

代码语言:javascript复制
@Override
    @HmilyTCC(confirmMethod = "confirmOrderStatus", cancelMethod = "cancelOrderStatus")
    public String mockPaymentAccountWithTryException(Order order) {
        updateOrderStatus(order, OrderStatusEnum.PAYING);
        //执行扣款超时   ---> 异常
        accountService.mockTryPaymentException(buildAccountDTO(order));
        return "success";
    }

下方是调用账户扣款服务超时的情况

代码语言:javascript复制
  @Override
    @HmilyTCC(confirmMethod = "confirm", cancelMethod = "cancel")
    public boolean mockWithTryException(AccountDTO accountDTO) {
        throw new HmilyRuntimeException("账户扣减异常!");
    }

这里会报扣减库存的异常,调用完成之后然后我们再去数据库里面看订单和账户的数据,发现账户金额冻结了1000元,于是在等待了大约20秒的时候发现hmily会通过定时任务进行恢复数据,再一次查看的时候发现余额已经恢复为10000了

于是,最终数据都完成了一致性校验,但是可能会存在延迟的过程,通过查看源码看到它的自我恢复机制是通过用户手动去设置的,因此延迟问题完全可以避免,只不过是牺牲性能来换时间。

代码语言:javascript复制
private void selfTccRecovery() {
    selfTccRecoveryExecutor
            .scheduleWithFixedDelay(() -> {
                try {
                    List<HmilyParticipant> hmilyParticipantList =
                            hmilyRepository.listHmilyParticipant(acquireDelayData(hmilyConfig.getRecoverDelayTime()), TransTypeEnum.TCC.name(), hmilyConfig.getLimit());
                       ...
                       //这里是通过方法调用来进行回滚的
                       if (Objects.isNull(globalHmilyTransaction)) {
                                tccRecovery(hmilyParticipant.getStatus(), hmilyParticipant);
                            } else {
                                tccRecovery(globalHmilyTransaction.getStatus(), hmilyParticipant);
                            }
                    }
                } catch (Exception e) {
                    LOGGER.error("hmily scheduled transaction log is error:", e);
                }
            }, hmilyConfig.getScheduledInitDelay(), hmilyConfig.getScheduledRecoveryDelay(), TimeUnit.SECONDS);
}
剖析它的日志存储模块

1、hmily的日志存储使用的是异步存储的,使用disruptor框架来实现异步的,那么这么做有什么好处?首先想到的是可以提高系统的吞吐量,可以充分利用cpu的性能,而且discruptor是可以实现秒级的到达率,据官网统计,它可以实现一秒几十万的并发,所以应用场景显而易见(秒杀,交易所);其次通过异步可以解耦代码,可以将发布者和实现者区分开。

微服务接入hmily 类图流程,如下图

hmily类图执行图hmily类图执行图

由此可见,所有操作均在切面上进行,切面贯穿了所有服务之间的联系,以上流程可用时序图表示,如下图所示

tcc时序图tcc时序图

图片摘选自 分布式事务

看到上面的流程图,有没有对hmily有一个简单的认识?如果没有,那我的这篇文章就太失败辣!我就得考虑重新编辑咯>

Hmily,曾经有一份真挚的感情摆在我的面前,而我却选择了代码,现在我也不后悔,因为我是年轻人,我讲“码德”!

谢谢您的浏览,我叫奕仁,人人都是产品经理后端开发

0 人点赞