互联网金融是最近几年的长期风口,它经历了野蛮生长期,目前处于强监管期,2016 年 8 月 24 日出台的《网络借贷信息中介机构业务活动管理暂行办法》中明确要求“网络借贷信息中介机构应当实行自身资金与出借人和借款人资金的隔离管理,并选择符合条件的银行业金融机构作为出借人与借款人的资金存管机构。”

截至 2017 年 5 月 17 日,正常运营平台共有 396 家正常运营平台宣布与银行签订直接存管协议,约占同期 P2P 网贷行业正常运营平台总数量的 17.89%,其中有 209 家正常运营平台与银行完成直接存管系统对接并上线,占 P2P 网贷行业正常运营平台总数量的 9.44%。这就意味着 2017 年 8 月份之后,可能 80% 多的 p2p 平台(约 2000 家平台)都要面临淘汰或者整改。因为,除了对平台的体量规模有要求,还对平台的技术实力也是一次大考验。

首先人人聚财平台采取的是直接存管形式,直接存管才是真正符合《暂行办法》要求的银行资金存管的方式,在这种模式下,客户的资金不在平台流转,能够有效隔离平台与投资人的资金,平台全程无法触碰资金,杜绝了资金池的产生。

人人聚财于 2016 年 10 月底上线银行存管系统,已稳定运行 8 个多月,每天处理异步消息 200 万条,峰值 400 万条,人人聚财的技术架构经过了 1.0 版本的所谓的巨石(Monolith)系统,发展到了 2.0 版的分布式版本,整体技术架构如下图所示:

在此架构下,我们来看看,实现银行存管的过程中有哪些关键点。

数据一致性如何解决 订单化机制

平台每天都与银行有着非常频繁的交互,如何保证平台与银行之间的数据一致呢?这个问题本质上是属于分布式事务的问题,业界一般的解思想方案有:2PC(两阶段提交)、TCC(Try/Confirm/Cancel)、事务消息等等,这些方案优缺点本文不作涉及,我们来看看人人聚财是如何应对这一问题的。

我们知道,分布式应用具有 CAP 特性,一般来讲,不论电商,类电商,还是社交应用,都会适当牺牲 C,保证最终一致 (Eventually Consistency)和 AP,我们的做法是平台与银行的所有接口进行交互均采用订单化机制。

我们把每一个与银行的操作交互抽象为一个订单,订单化机制就是指交互开始的时候平台插入 N(N>=1)个订单, 这 N 个订单根据业务的不同可能是一个父的订单,也可能是父子模式的订单,每当接口或业务完成的时候就更新对应的订单的业务状态。

当交互期间因网络或其他原因导致掉单时,平台的补单机制可以及时根据当前订单的业务状态来判断接下来应该从哪里开始补单。比如说在给用户账户进行返现时由于网络原因导致超时了,那么补单机制会先去银行查询该订单的状态,根据业务处理状态来进行下一步操作;若由于银行系统异常处理失败了,那么补单机制会自动更换流水号重新发起请求,整个交互过程如下图所示:

定时数据核对

平台每天晚上固定时间点会主动核对平台用户账户的可用余额、冻结金额等数据,并与银行的用户账户数据进行比对,对于有异常数据会及时通知到相关责任人进行排查。

对账

对于涉及到资金交互的系统,对账是必不可少的。平台每天固定时间点会主动去银行获取前一天的对账文件,与平台数据进行核对。

流水号如何生成?

在与银行的交互过程中,必须使用一个唯一的流水号来标识一次请求操作,不然业务操作会乱作一团,在对比了一些优秀的唯一 ID 生成算法后,最终选择使用 Twitter 的 Snowflake 算法来为我们提供银行存管中的流水号服务。此算法的好处有两点:一是纯数字,二是整体上来说是按时间顺序的。

原理不作过多介绍,网络上的文章也很多,算法原版是 Scala 写的,网上也有 Java 版本,但是有 bug,会产生重复的 ID,无法用于生产环境的集群部署,我对其进行了修改,修改后,可用于生产环境,已稳定运行 8 个月,下面是完整核心代码:

https://gist.github.com/xishuixixia/f0f8684805d0504289b7a40f3b327dd6

需要注意的是,集群机器需关闭 NTP 的时间同步功能,至于原因,留给读者,看上面代码便知。

任务调度平台如何落地?

在金融行业中,一定会存在大量定时任务,即在一个特定的时间点,系统执行指定的一个或多个业务逻辑,诸如跑批,对账等等。

为什么要用任务调度平台

业界优秀的调度框架当属 Quartz,比较成熟,但是它有一个不足,就是任务的执行和调度严重耦合,代码里面有很多硬编码,很容易出 bug,因此构建一个任务调度平台就显得很有必要。

怎样搭建任务调度平台

指导原则是轻量化,不需要太重,我们在 Quartz 的基础上,将任务调度行为抽象形成“调度中心”公共平台,而平台自身并不承担业务逻辑,“调度中心”按照调度配置发出调度请求。“调度”应用和“任务执行”应用之间用 HTTP 协议进行通讯,因此,“调度”和“任务执行”两部分相互解耦,提高系统整体稳定性和扩展性;即使某个任务执行模块宕机也不会影响其它业务。整体架构如下图所示:

我们业务模块只需要提供“任务执行”接口,任意部署到一台机器上,然后调度任务平台配置调用业务接口的基本 Meta 信息(如调用地址端口,bean 名字,监控报警邮件地址)即可。需要注意的是,我们在开发“任务执行”接口必须保证四个特性:

  1. 可重复执行:即幂等性, 数学表示为:f(f(x))=f(x), 在此处是指调度模块执行多次,不会影响业务本身逻辑。

  2. 可延迟执行:如果调度任务在指定的时间没有调度。可以通过手动执行来弥补。业务执行结果和指定时间执行的结果一致。

  3. 可并发执行:如果调度任务同时调用 A B 两台业务服务(处理相同业务)。这个时候需要我们业务需要保证事务一致性。

  4. 可暂停、可恢复执行:随时可以暂停正在执行的任务,也可以恢复继续执行。

当然业界也有一些比较优秀的任务调度系统,在此不作对比。

如何提升性能

下面以定投宝(人人聚财平台专有的一款智能投顾产品,以下简称定投宝)债权转让业务为例来具体说明。

平台每天都有一部分定投宝产品到期,需要将本金和利息返还给用户。在用户购买了平台的定投宝产品且定投宝复审通过后,用户的资金就会匹配并投资到对应的借款项目中去。而由于定投宝的期限相对于借款项目来说较短,以及两者的到期时间不一样,因此在定投宝到期的时候借款项目并未到期,因此定投宝只有将用户持有的债权转让出去之后才可以退出,才能将资金返还给用户。

在匹配之初,基于为用户的投资风险考虑,用户投资定投宝的资金被打散成很小的粒度分散投资到不同的借款项目中去了,因此在定投宝到期需要退出的时候会有大量的债权需要转让出去。在接入银行之前,平台只需要处理自身的数据即可。而在接入银行之后,平台严重依赖银行的操作结果。接入之初,定投宝债权转让需要经历两个阶段,一个是出让,用于业务校验,另一个是转让,进行转让动作。其中出让是同步接口,转让是异步接口。

经过压力测试和数据观察发现大部分时间都消耗在出让阶段,这种模式下无法满足平台业务峰值下的业务要求。于是,在与银行多次沟通讨论的情况下,银行取消了出让接口。同时平台自身也优化了定投宝债权转让模块,通过 MQ 来接收异步回调消息、代码重构、优化相关的 SQL 语句以及由原本的单线程模式转为线程池模式,大大的提高了业务处理效率。

通过上面这个例子,我们知道平台主要通过以下几个方面来提高业务处理性能:

  • 数据库层面:优化 SQL 语句,提高执行效率;

  • 代码层面:优化代码结构,使用线程池来提高并行处理能力,使用 MQ 来对系统进行解耦并提高异步处理能力;

  • 业务层面:在业务的合理性与效率方面进行权衡,对不合理的接口 say no。

http://mp.weixin.qq.com/s/FNBoppyJKF4t-TqxIzOsfg

如何解决数据一致性、任务调度、流水号生成等问题?相关推荐

  1. 成功解决pillow模块内生成的非UTF-8格式(比如label中有汉字时)

    成功解决pillow模块内生成的非UTF-8格式(比如label中有汉字时) 目录 解决问题 解决方法 解决问题 解决pillow模块内生成的非UTF-8格式(比如label中有汉字时),需要进行格式 ...

  2. 解决IDEA中自动生成返回值带final修饰的问题

    解决IDEA中自动生成返回值带final修饰的问题 参考文章: (1)解决IDEA中自动生成返回值带final修饰的问题 (2)https://www.cnblogs.com/andrew-303/p ...

  3. 解决VS+QT无法生成moc文件的问题

    解决VS+QT无法生成moc文件的问题 参考文章: (1)解决VS+QT无法生成moc文件的问题 (2)https://www.cnblogs.com/pupilLZT/p/10760399.html ...

  4. 解决数据一致性方案(库存问题)

    解决数据一致性方案 本文只讲述原理,均为伪代码,具体实现还得小伙伴实现. Redis Redis中第一种库存存储方式 hset(key,value,goods) == set(key,goods) 秒 ...

  5. 微服务架构-分布式解决方案-110:基于canal整合kafka异步解决数据一致性的问题

    110:基于canal整合kafka异步解决数据一致性的问题 1 canal整合kafka的效果演示 2 构建zookeeper环境 3 构建kafka的环境 4 canal将消息异步投递到kafka ...

  6. 如何按照DocNum+LineNum+流水号生成批次号

    概述 按照其它字段如:主表的DocDate.CrdID.细表的ItmID.ItmName等无法生成想要的批次号,本文将介绍如何按照DocNum+LineNum+流水号生成批次号. 在介绍如何设置之前, ...

  7. java流水号生成规则_流水号生成规则

    流水号生成规则,时间戳+手机号加密16位: 生成时间戳:Time.java import java.text.SimpleDateFormat; import java.util.Date; /** ...

  8. java 数字 字母 递增_JAVA流水号生成规则(按默认规则递增,数字不够添加字母递增,位数不够自动加1)...

    写道 在某些应用场景中,因业务需要,一般的流水号(前缀+日期或时间+流水号)规则无法满足业务需要,以下是因业务需要拓展出来的流水号生成规则 业务要求: 1.默认限定位数的数字递增 2.在限定位数数字达 ...

  9. 商城系统中常用的订单流水号生成工具OrderIdUtil工具类(亲测好用)

    在我们写商城项目时,由于系统业务数据量巨大,所以唯一订单号的生成成为了必须,下面提供了易使用的订单流水号生成工具类 OrderIdUtil工具类代码如下 package com.zyw.seckill ...

  10. 解决vue-pdf 打包后生成worker文件报错问题

    解决vue-pdf 打包后生成worker文件报错问题 安装 vue-pdf npm install --save vue-pdf 进入目录 node_modules/worker-loader/di ...

最新文章

  1. 2021年大数据HBase(六):HBase的高可用!【建议收藏】
  2. 我的数字万用表怎么了?
  3. HTML、CSS 和 JS框架之Bootstrap
  4. 动手开发第一个 Cypress 测试应用
  5. Linux实现ICMP PING代码
  6. Spring-context-AnnotationConfigUtils类
  7. 英语总结系列(九):百忙中依然坚持的九月
  8. vue仿追书神器,vue小说项目源码
  9. 使用Navicat连接Mycat的诡异事件
  10. python判断火车票座位号分布图_火车座位分布图
  11. msm8996平台的一些debug方法
  12. 把数字用拼音表示并输出
  13. 2020-4-24 Open Web Application Security Project (OWASP)
  14. VirtualBox 常用设置说明
  15. 2017校招信息每日汇总(更新至8.4)
  16. opencv-python 小白笔记(3)
  17. SwiftUI 小专栏20200817汇总
  18. 在线播放ppt html5,强大的HTML5幻灯片系统:H5Slides
  19. 程序员吐槽:四年被三家公司裁员补偿,网友:去BAT转转?
  20. Oracle表空间及表分区

热门文章

  1. OpenCV腐蚀和膨胀Eroding and Dilating
  2. C++sparse matrix 稀疏矩阵的实现算法(附完整源码)
  3. QT绘制嵌套的圆饼状图
  4. QT的QStack类的使用
  5. QT的QDesignerFormWindowInterface类的使用
  6. C++对C的加强之C++中所有的变量和函数都必须有类型
  7. 4、JVM垃圾回收机制、新生代的GC、GC(Minor GC、FullGC)、GC日志、JVM参数选项、元空间(笔记)
  8. 闪回的用途与实战(闪回表,闪回删除,闪回重名删除,闪回版本查询)
  9. Spring管理session的一些认识和用法心得
  10. struts2之单个文件上传(特别推荐)