zoukankan      html  css  js  c++  java
  • 入账

    实现方式一:

    入账需要什么参数呢?

    1,付款方账号。2,收款方账号。3,金额(发生额)。4,资金变动方向(增加,减少)。5,业务订单号。

    需不需要记录一些调用者信息呢?

    嗯,账户与业务别太强关联了。这些东西交给账务系统吧。

    A.为了控制并发,获取分布式锁。(tryLock)

    细节:分布式锁的key是收款方账号加一个随机整数数字(通过Random.nextInt()生成,可以配置,目前是10)。也就是说并发控制是按照收款方账户的维度和一个随机数字控制的,锁的超时时间也是这个随机数字。

    B.校验金额为正数。

    C.保存交易流水,流水号是唯一主键,所以起到去重,防止重复入账的功能。(有一个独立的交易流水表)

    D.根据收款方账户查账户,采用FOR UPDATE WITH RS的方式,进行行锁。

    E.校验收款方账户的状态。(非冻结啊什么的。根本就没有付款方账户什么事啊,难道因为参数要统一?)

    F.校验当前账户余额与数字签名是否合法。(Base64加密,保证金额不能被篡改)

    G.判断当前日期,如果当前交易日与上一个交易日字段(这是个字段)里存的值不同(不是同一天),则更新上一交易日余额与上一交易日。

    H.增加当前账户余额并根据金额生成签名。

    I.检查账户中各种额度与当前账户余额的关系。保证各种额度不大于当前账户余额。

    J.保存账户历史。

    K.保存账户快照。

    L.保存新的当前账户余额。

    疑问点:

    1. A部分的分布式锁的key,只用收款方账号好理解,加上一个随机数字能好好的控制并发吗?

    解答:呃,其实对入账交易而言,真正控制并发的是数据库层面。采用了乐观锁(version)和select for update两种方式同时加锁来保证数据的准确性。所以这个redisLock其实类似一个连接池的概念。可以通过配置来控制同时最多有几个请求同时操作一个账户。避免针对某一个收款方账户的入账操作过多,占用过多资源。

    2. B部分,其实保存完交易流水号就可以放开分布式锁了,对吧。锁最小粒度原则。

    解答:不是为了控制并发,是为了控制同时访问数量。所以~

    实现方式二:

     A.创建redisLock,key是请求流水号(全局唯一)。此处redisLock用来做并发控制。

    B.创建第二个redisLock,key是账户ID。超时时间6秒,tryLock 5秒。

    Lock lock = Locks.getRedisLock(Locks.ACCOUNT_LOCK_KEY+ command.getAccountId(),
    Configs.getConfig(Configs.MERCHANT_ACCOUNT_SINGLE_LOCK_TIME, 6));
    try {
    if (lock.tryLock(Configs.getConfig(Configs.SINGLE_LOCK_RETRY_TIME, 5))) {
    Future<?> future = executor.submit(new Runnable() {
    @Override
    public void run() {
    command.exe(accountService);
    }
    });
    future.get(Configs.getConfig(Configs.THREAD_POOL_TIME_OUT, 10), TimeUnit.SECONDS);
    } else {
    throw MerchantAccountException.ACCOUNT_LOCK_FAIL.newInstance("获取不到锁,lockKey:{0}",
    Locks.ACCOUNT_LOCK_KEY_PRE + command.getAccountId());
    }
    } finally {
    lock.unlock();
    }

    小伙伴们认为,这个多线程是多余的,然后这个redis锁也是多余的,因为都是用了数据库的行锁。

    但是,阅读代码发现任务最后都是交给executor这个线程池来处理的,它起到一个限流的作用,一共只有20个线程在工作。

    C.开始执行入账任务,这里用到了TransactionTemplate

    transactionTemplate.execute(new TransactionCallback<Boolean>() {
    @Override
    public Boolean doInTransaction(TransactionStatus status) {
    }
    });

    这样使用事务,跟普通的注解方式有什么区别呢?

    参考:https://blog.csdn.net/memery_last/article/details/54573691

    D.查询账户,同样使用了行锁FOR UPDATE WITH RS。

    E.针对操作对传入的参数进行校验。

    F.保存交易流水和账户历史。

    G.采用乐观锁更新账户余额。

    H.更新余额快照。

     

     
  • 相关阅读:
    .NET平台下不借助Office实现Word、Powerpoint等文件的解析
    C#智能视频监控软件
    关于“线程”与“阻塞”
    asp.net 页面静态化
    纸上谈兵: 数学归纳法, 递归, 栈
    OSGI:C#如何实现简单的OSGI
    windows service (服务)创建流程
    轻松Scrum之旅——Sprint1:新手上路
    发布本人所有博客文章中涉及的代码与工具(大部分是C++和Java)
    多个常见代码设计缺陷
  • 原文地址:https://www.cnblogs.com/coolgame/p/9984268.html
Copyright © 2011-2022 走看看