简介:这份源码是一套基于Hadoop与Spark的大数据金融信贷风险控制系统实现,面向金融机构数据工程师、大数据学习者及信贷风控开发者,用于处理海量借贷记录、信用评分与实时申请评估等场景。系统既借助HDFS与MapReduce完成历史数据批量存储与计算,也通过Spark内存计算支撑实时风险评分,并融合逻辑回归、随机森林等算法构建违约预测流程。包体共69个文件,以36个Java源码、8个Scala源码和12个XML配置为主,另含SQL、JS、properties等辅助文件,覆盖数据接入、预处理、模型训练、评估展示与部署配置等模块,压缩包仅72KB,结构紧凑,便于快速查看与二次开发。目前已有1219人学习下载,内容适合希望深入实践大数据技术在金融风控中落地的开发者。源码提供完整实现,包括Spark Streaming实时流处理、Spark SQL数据集成、可视化前端等,可帮助读者理解批流一体架构及敏感数据安全处理思路,是结合业务场景学习Hadoop/Spark生态的实用参考。
1. 金融信贷风控遇上 big data:为什么这套 Hadoop + Spark 源码值得拆
信贷审批场景里最尴尬的现状是:申请人提交的材料越来越薄,但机构手里的历史数据越来越厚。银行流水、征信查询记录、多头借贷、电商消费行为这些数据分散在十几个系统里,靠人工核查既慢又漏。处理这种海量数据,Hadoop 负责把分散的原始数据存下、管住,Spark 负责在内存里快速算特征、跑模型,两者组合已经是金融风控领域最主流的技术底座。这套基于 Hadoop、Spark 的大数据金融信贷风险控制系统源码,正好覆盖了一个完整风控系统的冷启动路径:从原始借贷数据接入、特征加工、模型评分到前端审核工作台。无论你是刚接触大数据金融应用的开发者,还是想给团队找一套可改写的风控基线,都值得拿出来拆一遍。
2. 源码解剖:先看清三个模块到底在干什么
2.1 前端、后端、流处理三层怎么分工
打开压缩包,目录结构比想象中清爽。核心代码分三个部分:h5-credit-risk-control 是 H5 前端,credit-risk-control 是后端服务,data-source-spark-streaming 是实时数据接入模块。如果只看名字,容易把前端当成装饰品,把后端当成全部,实际上三个模块是一条完整的数据流水线。
先说>val kafkaParams = Map( "bootstrap.servers" -> "node01:9092,node02:9092", "key.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer", "value.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer", "group.id" -> "credit-risk-group", "auto.offset.reset" -> "latest", "enable.auto.commit" -> "false" ) val topics = Set("loan-apply") val stream = KafkaUtils.createDirectStream[String, String]( streamingContext, PreferConsistent, Subscribe[String, String](topics, kafkaParams) )
这段代码看着简单,参数里全是坑。bootstrap.servers 是 Kafka 集群地址,生产环境必须写多个节点,只写一个 broker 在它挂掉时会直接断流。group.id 决定消费者组,多个实例跑同一个任务必须保证 group.id 一致,否则每个实例都消费全量数据,特征会被重复写入。auto.offset.reset 设为 latest 意味着启动时只消费新数据,如果你重启任务前积压了一批消息,这批消息就丢了;想要补数据就得改成 earliest 配合手动提交 offset。enable.auto.commit 设为 false 是为了让处理成功后再手动提交,避免数据处理到一半任务重启导致消息丢失。
2.3 后端业务模块:MyBatis 生成器与风险服务的连接方式
credit-risk-control 模块里有一个 GeneratorMapper.xml,熟悉 MyBatis 的开发者一眼就明白,这是代码生成器配置。它定义了数据库连接信息、实体类包路径、Mapper 接口生成规则。这说明后端的持久层不是手写的,而是从数据库表结构自动生成的,所以在改代码前,先找到数据库初始化脚本,把表结构还原出来,再回头对照实体类,才不容易看懵。
后端的技术栈是 Spring Boot,pom.xml 里依赖了 Web、MyBatis、数据源连接池这些常规组件。它暴露的接口可以从 Controller 类看出来,核心接口是风险评分查询和申请单审核提交。风险评分接口接收前端传过来的申请 ID,后端拼接特征,调用评分服务,返回风险分、命中规则列表和审核建议。
@RestController @RequestMapping("/risk") public class RiskController { @Autowired private RiskService riskService; @PostMapping("/score") public Result score(@RequestBody LoanApplyRequest request) { CustomerFeature feature = riskService.buildFeature(request.getApplyId()); RiskResult result = riskService.evaluate(feature); return Result.success(result); } }buildFeature 方法做的事情值得细看,它不是简单查一张表,而是把分散在用户基本信息表、申请流水表、征信查询记录表里的数据聚合到一个特征对象里。常见实现是用 MyBatis 分别查询,然后在 Service 层拼接。这种方式的优点是逻辑直观,缺点是数据库压力大,每个申请要查三四次。性能敏感的场景一般会改成 Spark SQL 预计算宽表,查询时单表直取,第 6 章我会展开讲这个改造思路。
3. 源码落地的第一步:环境搭建和编译跑通
3.1 环境版本怎么选:先看清楚 pom.xml 再动手
很多同学拿到源码就开始装环境,结果装完发现版本不兼容,一天时间就耗在版本冲突上。正确顺序是先打开两个 Maven 模块的 pom.xml 看依赖版本,再决定本机装什么。我根据源码里的依赖梳理了一份推荐环境清单:
| 组件 | 版本建议 | 说明 |
|---|---|---|
| JDK | 1.8 | 金融项目里 JDK 8 最保守,高版本可能遇到 Scala 兼容问题 |
| Maven | 3.6+ | 建议用 Maven 3.8 以下版本,部分镜像源对 3.9 支持不友好 |
| Hadoop | 2.7 或 2.8 | 源码基于 Hadoop 2.x,不要直接上 3.x,HDFS 接口有差异 |
| Spark | 2.3 或 2.4 | 与 Hadoop 2.x 配套,Spark 3.0 后部分 API 已废弃 |
| Kafka | 1.0 以上 | 与 Spark Streaming 的 createDirectStream 兼容 |
| MySQL | 5.7 | 后端业务库,5.7 对 JSON 类型支持也更稳定 |
| Hive | 1.2 以上 | 离线分析需要,可选但建议装上 |
这个表不是随便写的。Spark Streaming 的 createDirectStream API 在 Spark 2.x 里最稳定,到了 3.x 之后虽然还能用但推荐改用 Structured Streaming,源码不做适配直接编译会报一堆 deprecated 和参数错误。Hadoop 也是同理,2.x 的 FileSystem API 和 3.x 在权限模型上有细微信号,强行升级会在跑批时出现意想不到的权限异常。
3.2 编译顺序和 Maven 参数:别跳过 tests,也别直接 package
源码里有两个 Maven 模块,相互之间没有父子依赖关系,需要分别编译。我建议先编译 credit-risk-control 这个后端模块,因为它的依赖少,编起来快,能先验证本机环境的基本状态。再编译>cd credit-risk-control mvn clean package -DskipTests=false cd ../data-source-spark-streaming mvn clean package -DskipTests
-DskipTests=false 的意思是不跳过单元测试,第一次编译建议跑一遍测试,它能帮你发现数据库连接配置、Bean 注入这些隐藏问题。如果嫌测试慢可以用 -Dmaven.test.skip=true,但代价是问题会留到启动时才暴露。依赖冲突时可以先执行 mvn dependency:tree 查看依赖树,定位重复依赖的源头,再决定在 pom.xml 里用 exclusion 排除。
编译过程中最容易翻车的地方是依赖下载。国内网络环境直接访问 Maven 中央仓库经常超时,常见的做法是在 settings.xml 里配置国内镜像源。等两个模块都编译出 JAR 包后,再进入下一步数据库初始化。
3.3 数据库初始化和配置文件:先建库再改连接串
压缩包里的 databases 目录存放了初始化脚本,执行顺序很重要。先创建数据库,再依次导入建表脚本和初始化数据脚本。如果脚本文件命名带有数字前缀,按数字从小到大执行就不会错。导入完成后检查核心几张表的数据量,确认初始化数据已经写入。
后端配置文件里要改的核心项是数据源连接串、Redis 地址(如果用到)和模型文件路径。连接串的格式是 jdbc:mysql://host:port/dbname 这种标准格式,MySQL 8.0 以上要在连接串后加 serverTimezone=Asia/Shanghai 参数,否则会报时区错误。模型文件路径要指到模型包实际存放的位置,源码里一般会有一个 models 目录或 resource 目录,确认配置文件里的相对路径和实际路径一致。
3.4 启动顺序:从 HDFS 到流处理再到后端,顺序错了起不来
环境全部就绪后,启动顺序有一个隐含依赖链。如果># 1. 启动 Hadoop 相关进程(假设已配置好免密) start-dfs.sh # 2. 创建 HDFS 目录(如果源码里写死了路径) hdfs dfs -mkdir -p /user/credit/risk/rawdata # 3. 提交 Spark Streaming 任务(放在后端启动前) spark-submit \ --class com.credit.risk.streaming.LoanApplyStreamingApp \ --master yarn \ --deploy-mode client \ >public double score(Map<String, Double> featureMap, ModelConfig config) { double logit = config.getBaseScore(); for (FeatureCoefficient fc : config.getCoefficients()) { Double value = featureMap.get(fc.getFeatureName()); if (value == null) { value = fc.getDefaultValue(); } logit += fc.getCoefficient() * value; } double odds = Math.exp(logit); return config.getPdo() / Math.log(2) * Math.log(odds); }
参数说明一下:baseScore 是基准分,通常设置成 600 或 650,代表好坏比是 1:1 时的初始分数;coefficient 是每个特征的逻辑回归系数,训练阶段得出来的;pdo 是 point-to-double-odds,表示分数每增加多少分,好坏比翻一倍,行业常用 50。这套计算逻辑在金融圈是标准做法,不理解这几个参数的意思,后面调分数阈值就无从下手。
4.2 特征加工表设计:源码里的特征字段从哪来
源码里后端的特征集中体现了金融信贷领域典型的特征设计习惯。这些特征分三类:申请人静态属性、历史行为统计、征信相关衍生指标。静态属性包括年龄、学历、婚姻状况、职业类别;历史行为统计看的是过去 3 个月、6 个月、12 个月的申请次数、借款总额、还款记录;征信衍生指标包含征信查询次数、逾期最大天数、信用卡使用率等。
| 特征类别 | 典型字段 | 计算逻辑 | 业务含义 |
|---|---|---|---|
| 静态属性 | age、education、marital_status | 直取用户表 | 基础画像 |
| 行为统计 | apply_count_6m、loan_total_6m | 按申请时间分组计数/求和 | 借贷活跃度 |
| 还款表现 | overdue_days_max、repay_rate_12m | 取最大值、计算比例 | 履约能力 |
| 征信衍生 | credit_query_cnt_3m、credit_used_ratio | 按查询时间统计、用量/额度 | 资金紧张程度 |
这些字段定义在数据库表结构里,但从表结构到最终模型入模特征,中间还有一层 SQL 聚合逻辑。源码一般在 dao 层写了一段聚合 SQL,从明细表里按用户 ID 分组算出这些指标。如果这一段看不懂,直接在数据库里手动执行一遍 SQL,对比查询结果和代码里传参的数据,能帮你快速建立对应关系。
4.3 规则引擎和模型分数怎么配合:先硬规则后软评分
风险评估不能只依赖模型分数,生产环境里常见的是双层设计:第一层硬规则兜底,比如年龄小于 18 岁直接拒绝、法院执行记录命中直接拒绝、黑名单命中直接拒绝;第二层软评分做排序,对通过硬规则的客户计算风险分,按分数段决定是自动通过、人工审核还是拒绝。
源码里的规则配置一般在数据库表里面,每一条规则包含规则编号、规则描述、规则表达式和命中后动作。这种设计比硬编码在 Java 代码里灵活得多,运营人员调整规则阈值不需要重新发版。常见的做法是后端启动时加载规则表到本地缓存,每次请求先把申请人的字段逐一和规则列表比对,全部通过后再调用模型评分。
实现规则引擎的代码结构一般是这样:一个 RuleEngine 类负责加载规则和遍历执行,每个规则是独立配置项,命中后返回对应动作。改造新规则只需要往数据库插入一条记录,业务代码基本不用动。你需要特别关注规则执行的短路逻辑,比如一个规则命中直接拒绝,后续规则是否需要继续执行,多数场景不需要,这样可以节省计算资源。
5. 避坑指南:跑这套源码最容易翻车的五个位置
5.1 Spark 版本不匹配导致编译报错或运行期类冲突
现象:mvn 编译时提示找不到 org.apache.spark.streaming.kafka010.KafkaUtils 这个类,或者运行时出现 NoSuchMethodError。
原因:源码用的是 Spark 2.x 的 API,你本机或集群装的是 Spark 3.x。KafkaUtils.createDirectStream 在 Spark 3.x 里虽然仍然存在,但部分内部接口变了,编译时如果依赖没对齐就会直接报缺失。
解决:查看 pom.xml 里 spark-streaming-kafka-0-10 的版本号,把本地 SPARK 环境或依赖版本改到同一条线上。我一般习惯把 Spark 版本固定成源码编译版本,不要盲目追新,跑通之后再考虑升级。
5.2 数据库连接超时,接口偶尔报 CannotGetJdbcConnectionException
现象:后端服务启动正常,前几个请求没问题,跑一会儿后突然报连接超时,过一会又自动恢复。
原因:MySQL 默认的 wait_timeout 是 8 小时,但连接池里的空闲连接超过这个时间就被服务端断开了,连接池没有及时回收,拿到一个已经失效的连接就会报错。
解决:在数据源连接串上增加 validate 相关参数,同时调小连接池的 idleTimeout 和 testWhileIdle。还有更暴力的做法是重启服务,但那只是临时恢复,根子上要把连接池的空闲检测打开。
5.3 Kafka 消费组重复消费,特征数据重复入库
现象:流处理任务正常运行,但数据库里的申请明细偶尔出现重复记录,特征聚合结果翻倍。
原因:enable.auto.commit 设为 true 的时候,如果处理逻辑耗时较长,消费者可能在 offset 自动提交前就崩了,重启后从旧 offset 重新消费,导致一批消息被处理了两遍。
解决:把 enable.auto.commit 改成 false,手动在数据处理完成后提交 offset。同时数据库写入采用 upsert 方式,也就是存在即更新,不存在才插入,用业务主键做去重兜底。
5.4 Spark 任务运行一段时间后越来越慢,疑似内存泄漏
现象:任务刚启动时速度正常,跑几小时后处理延迟越来越大,YARN 日志里的 GC 时间持续上涨。
原因:Spark Streaming 的批次处理时间大于批次间隔,导致处理不过来,积压越来越多。常见原因包括每条消息的 JSON 解析太慢、外部存储写入阻塞、或者缓存对象过多没有被及时清理。
解决:先调大 Spark Executor 内存和并行度参数,然后把 JSON 解析改成更轻量的序列化方式,最后检查写入 MySQL 的批量大小,改成批量提交而不是每条一次提交。
5.5 本地能跑通,提交到集群就报权限或路径错误
现象:同样的代码在本地 IDE 里跑得好好的,打成 JAR 提交到集群后文件找不到或写入失败。
原因:本地模式下当前用户有本地文件系统的完整权限,集群上 Hadoop 的权限模型由 Kerberos 或操作系统用户控制,代码里写死的相对路径在集群上找不到。
解决:把代码里的文件路径全部改成 HDFS 绝对路径,并在提交任务时用 --principal 和 --keytab 指定认证信息。如果是简单的权限问题,也可以用 hdfs dfs -chmod 开放目录权限,但生产环境不建议这么干。
6. 把这套源码改造成自己的风控基线:三个值得做的强化操作
6.1 把多次单表查询改成 Spark SQL 预计算宽表
源码里 buildFeature 方法的实现是逐张表查询后拼接,这种方式在申请量小的时候没问题,但每天处理几万笔申请时,数据库压力会明显上升。我之前改造过一个模拟项目X,把特征查询改成了 Spark SQL 预计算宽表的方案:每天凌晨离线跑一遍当天新增的申请,把特征字段落成一张宽表,后端查询只查宽表。
CREATE TABLE credit_feature_wide AS SELECT a.loan_id, a.user_id, a.apply_amount, b.age, b.education, c.apply_count_6m, c.overdue_days_max FROM loan_apply a LEFT JOIN user_profile b ON a.user_id = b.user_id LEFT JOIN ( SELECT user_id, COUNT(*) AS apply_count_6m, MAX(overdue_days) AS overdue_days_max FROM loan_record WHERE apply_time >= DATE_SUB(CURRENT_DATE, 180) GROUP BY user_id ) c ON a.user_id = c.user_id;这段 SQL 里 apply_count_6m 的窗口条件是 DATE_SUB(CURRENT_DATE, 180),但如果跑批时间是凌晨,而申请时间是当天白天,那么当天新增的申请不会被统计进去。我当时踩过这个时间边界坑,解决办法是窗口起点往前多留一天,然后在查询条件里过滤。宽表方案还有一个注意点,就是模型迭代后特征更新频率问题,评分卡模型的特征一般在 T+1 更新,实时性要求高的场景需要额外补一条实时计算链路。
6.2 模型迭代时做好特征一致性校验
这套源码里模型推理代码只认特征名,不认特征含义。如果你用自己的数据重新训练了模型,导出的特征列表和源码里的特征枚举不一致,线上调用会直接取到默认值,整个评分结果失真。我发现这个问题是在一次模型替换后,AUC 看着没变,但分段统计时拒绝率突然跳了 3 个百分点,排查了很久才发现新模型用了 12 个特征,线上代码只取了 10 个,剩下两个走了默认值。
从那以后,我每次切换模型都强制走一遍特征一致性检查:写一个脚本读取模型参数文件,把特征名、特征类型、默认值导出;再扫描线上的特征枚举类,用集合比对差异;两边不一致直接阻断发布。如果你打算长期用这套源码做二次开发,把这一步固化到 CI 里,能省掉很多半夜排查的精力。
6.3 风险评分的分布监控,建立分数漂移告警
风控模型上线后不是一劳永逸的,申请客群会变,模型效果会衰减。一个便宜可靠的监控方式是每天统计线上评分分布,对比最近一周的分位数变化。常见做法是后端定时任务每天计算分数均值、标准差、以及 600 分以下占比,写进监控表,超过阈值就触发告警。
我个人的习惯是在这个基础上加一个双周维度对照:每两周拉一次最近样本的预测分数分布,和模型训练时的验证集分布做 KS 检验,如果分布差异过大,说明客群结构变了,需要重新训练或做模型校准。这个方法不依赖复杂的模型监控框架,用脚本加一张表就能撑起初期的监控需求。希望帮到你。
如果你也在考虑做金融信贷风控方向的数据改造,建议先拿这份源码搭一套最小闭环,再逐步替换成自己的数据处理逻辑。下载后按第二、三章的步骤把环境跑通,再用第五章的避坑清单排查一轮,你的第一版风控系统就立住了一半。
本文还有配套的精品资源,点击获取