forked from sunpeak/riskcontrol
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
02b0ce3
commit eb4d0d8
Showing
35 changed files
with
2,513 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
*.iml | ||
/.idea/ | ||
/.mvn/ | ||
target/ | ||
/logs/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,49 @@ | ||
# riskcontrol | ||
java risk control | ||
## 介绍 | ||
* Java实时处理业务风控系统 | ||
* 基于Spring boot构建 | ||
* 使用drools规则引擎管理风控规则 | ||
* 使用redis、mongodb做风控计算和储存 | ||
* 历史数据支持水平扩展存储 | ||
|
||
## 准备 | ||
* mysql,数据结构在import.sql中定义了 | ||
* redis | ||
* mongodb,建议使用分片集群 | ||
|
||
## 配置 | ||
* 项目配置:application.properties | ||
* 日志配置:logback.xml | ||
|
||
## 运行 | ||
系统默认采用jar打包和运行,建议系统搭建在多台节点上,使用反向代理做负载均衡。 | ||
|
||
``` | ||
java -jar riskcontrol-*.jar | ||
``` | ||
|
||
## 容器部署 | ||
pom.xml | ||
|
||
``` | ||
<packaging>war</packaging> | ||
``` | ||
|
||
## 风控请求入口 | ||
请求http://domain/riskcontrol/req?json=事件对象的json格式 | ||
|
||
响应中score字段代码该事件的风险值,默认超过100分预警 | ||
|
||
|
||
## 原理 | ||
* redis,使用sortedset计算维度,条件维度作为key,统计维度作为value,时间作为score | ||
* mongodb,使用聚合函数统计维度,比如:max,min,sum,avg,first,last,stdDevPop,stdDevSamp等 | ||
|
||
注:redis性能优于mongodb,所以使用较多的频数计算默认在redis中运行,参考代码distinctCountWithRedis | ||
|
||
## TODO | ||
* 扩展黑名单,ip,手机号,设备指纹等; | ||
* 扩展维度信息,比如手机号地域运营商,ip地域运营商,ip出口类型,设备指纹,Referer,ua,密码hash,征信等,维度越多,可以建立规则越多,风控越精准; | ||
* 扩展风控规则,针对需要解决的场景问题,添加特定规则,分值也应根据自身场景来调整。**当然,这将是个漫长的过程;** | ||
* 将用户的行为过程综合考虑,建立复合场景的规则条件。比如:登录->活动->订单->支付,综合考虑; | ||
* **减少误报,提供性能!** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>com.example</groupId> | ||
<artifactId>riskcontrol</artifactId> | ||
<version>0.0.1-SNAPSHOT</version> | ||
<packaging>jar</packaging> | ||
|
||
<name>riskcontrol</name> | ||
<description>risk control</description> | ||
|
||
<parent> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-parent</artifactId> | ||
<version>1.4.0.RELEASE</version> | ||
<relativePath/> <!-- lookup parent from repository --> | ||
</parent> | ||
|
||
<properties> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | ||
<java.version>1.8</java.version> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-data-redis</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-data-mongodb</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>mysql</groupId> | ||
<artifactId>mysql-connector-java</artifactId> | ||
<version>5.1.39</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.alibaba</groupId> | ||
<artifactId>druid</artifactId> | ||
<version>1.0.23</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.mybatis.spring.boot</groupId> | ||
<artifactId>mybatis-spring-boot-starter</artifactId> | ||
<version>1.1.1</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.drools</groupId> | ||
<artifactId>drools-compiler</artifactId> | ||
<version>6.4.0.Final</version> | ||
<scope>runtime</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.kie</groupId> | ||
<artifactId>kie-ci</artifactId> | ||
<version>6.4.0.Final</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.mongodb</groupId> | ||
<artifactId>mongodb-driver</artifactId> | ||
<version>3.2.2</version> | ||
</dependency> | ||
|
||
|
||
<dependency> | ||
<groupId>commons-lang</groupId> | ||
<artifactId>commons-lang</artifactId> | ||
<version>2.6</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>commons-beanutils</groupId> | ||
<artifactId>commons-beanutils</artifactId> | ||
<version>1.9.2</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.alibaba</groupId> | ||
<artifactId>fastjson</artifactId> | ||
<version>1.2.15</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-tomcat</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
|
||
</project> |
15 changes: 15 additions & 0 deletions
15
src/main/java/com/example/riskcontrol/RiskcontrolApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.example.riskcontrol; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
/** | ||
* Created by sunpeak on 2016/8/6. | ||
*/ | ||
@SpringBootApplication | ||
public class RiskcontrolApplication { | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(RiskcontrolApplication.class, args); | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/main/java/com/example/riskcontrol/conf/DruidConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package com.example.riskcontrol.conf; | ||
|
||
import com.alibaba.druid.pool.DruidDataSource; | ||
import com.alibaba.druid.support.http.StatViewServlet; | ||
import com.alibaba.druid.support.http.WebStatFilter; | ||
import org.springframework.boot.bind.RelaxedPropertyResolver; | ||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||
import org.springframework.boot.web.servlet.ServletRegistrationBean; | ||
import org.springframework.context.EnvironmentAware; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.env.Environment; | ||
|
||
import javax.sql.DataSource; | ||
import java.sql.SQLException; | ||
|
||
/** | ||
* Created by sunpeak on 2016/8/6. | ||
*/ | ||
@Configuration | ||
public class DruidConfiguration implements EnvironmentAware { | ||
private RelaxedPropertyResolver propertyResolver; | ||
|
||
@Override | ||
public void setEnvironment(Environment env) { | ||
this.propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource."); | ||
} | ||
|
||
|
||
@Bean | ||
public ServletRegistrationBean druidServlet() { | ||
return new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); | ||
} | ||
|
||
@Bean | ||
public DataSource druidDataSource() { | ||
DruidDataSource datasource = new DruidDataSource(); | ||
datasource.setUrl(propertyResolver.getProperty("url")); | ||
datasource.setDriverClassName(propertyResolver.getProperty("driver-class-name")); | ||
datasource.setUsername(propertyResolver.getProperty("username")); | ||
datasource.setPassword(propertyResolver.getProperty("password")); | ||
datasource.setInitialSize(Integer.valueOf(propertyResolver.getProperty("initialSize"))); | ||
datasource.setMinIdle(Integer.valueOf(propertyResolver.getProperty("minIdle"))); | ||
datasource.setMaxWait(Long.valueOf(propertyResolver.getProperty("maxWait"))); | ||
datasource.setMaxActive(Integer.valueOf(propertyResolver.getProperty("maxActive"))); | ||
datasource.setPoolPreparedStatements(Boolean.valueOf(propertyResolver.getProperty("poolPreparedStatements"))); | ||
datasource.setMaxPoolPreparedStatementPerConnectionSize(Integer.valueOf(propertyResolver.getProperty("maxPoolPreparedStatementPerConnectionSize"))); | ||
try { | ||
datasource.setFilters(propertyResolver.getProperty("filters")); | ||
} catch (SQLException e) { | ||
e.printStackTrace(); | ||
} | ||
return datasource; | ||
} | ||
|
||
@Bean | ||
public FilterRegistrationBean filterRegistrationBean() { | ||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); | ||
filterRegistrationBean.setFilter(new WebStatFilter()); | ||
filterRegistrationBean.addUrlPatterns("/*"); | ||
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); | ||
return filterRegistrationBean; | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
src/main/java/com/example/riskcontrol/controller/BlackListController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package com.example.riskcontrol.controller; | ||
|
||
import com.example.riskcontrol.model.BlackList; | ||
import com.example.riskcontrol.model.CodeMap; | ||
import com.example.riskcontrol.model.RCRuntimeException; | ||
import com.example.riskcontrol.model.Result; | ||
import com.example.riskcontrol.service.BlackListService; | ||
import org.apache.commons.lang.StringUtils; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestMethod; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* Created by sunpeak on 2016/8/6. | ||
*/ | ||
@RestController | ||
@RequestMapping("/blacklist") | ||
public class BlackListController { | ||
|
||
private static Logger logger = LoggerFactory.getLogger(BlackListController.class); | ||
|
||
@Autowired | ||
private BlackListService blackListService; | ||
|
||
|
||
@RequestMapping(path = "/queryall", method = RequestMethod.GET) | ||
public Result<List<BlackList>> queryAll() { | ||
Result r = Result.success(); | ||
try { | ||
r.setData(blackListService.queryAll()); | ||
} catch (RCRuntimeException e) { | ||
r = Result.fail(); | ||
r.setRetCode(e.getId()); | ||
} catch (Exception e) { | ||
r = Result.fail(); | ||
} | ||
return r; | ||
} | ||
|
||
|
||
@RequestMapping(value = "/add", method = RequestMethod.GET) | ||
public Result add(String dimension, String type, String value, String detail) { | ||
Result r = Result.success(); | ||
try { | ||
if (StringUtils.isEmpty(dimension) || StringUtils.isEmpty(type) || StringUtils.isEmpty(value)) { | ||
throw new RCRuntimeException(CodeMap.PARAM_ERROR); | ||
} | ||
|
||
BlackList.EnumDimension enumDimension = BlackList.EnumDimension.valueOf(dimension.toUpperCase()); | ||
if (enumDimension == null) { | ||
throw new RCRuntimeException(CodeMap.PARAM_ERROR); | ||
} | ||
BlackList.EnumType enumType = BlackList.EnumType.valueOf(type.toUpperCase()); | ||
if (enumType == null) { | ||
throw new RCRuntimeException(CodeMap.PARAM_ERROR); | ||
} | ||
|
||
BlackList blackList = new BlackList(); | ||
blackList.setDimension(enumDimension); | ||
blackList.setType(enumType); | ||
blackList.setValue(value); | ||
blackList.setDetail(detail); | ||
blackListService.pub(blackList); | ||
} catch (RCRuntimeException e) { | ||
r = Result.fail(); | ||
r.setRetCode(e.getId()); | ||
} catch (Exception e) { | ||
logger.error("添加黑名单失败", e); | ||
r = Result.fail(); | ||
} | ||
return r; | ||
} | ||
|
||
} |
69 changes: 69 additions & 0 deletions
69
src/main/java/com/example/riskcontrol/controller/ConfigController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package com.example.riskcontrol.controller; | ||
|
||
import com.example.riskcontrol.model.CodeMap; | ||
import com.example.riskcontrol.model.Config; | ||
import com.example.riskcontrol.model.RCRuntimeException; | ||
import com.example.riskcontrol.model.Result; | ||
import com.example.riskcontrol.service.ConfigService; | ||
import org.apache.commons.lang.StringUtils; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestMethod; | ||
import org.springframework.web.bind.annotation.ResponseBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* Created by sunpeak on 2016/8/6. | ||
*/ | ||
@RestController | ||
@RequestMapping("/config") | ||
public class ConfigController { | ||
|
||
private static Logger logger = LoggerFactory.getLogger(ConfigController.class); | ||
|
||
@Autowired | ||
private ConfigService configService; | ||
|
||
@RequestMapping(value = "/queryall", method = RequestMethod.GET) | ||
public Result<List<Config>> query() { | ||
Result r = Result.success(); | ||
try { | ||
List<Config> configs = configService.queryAll(); | ||
r.setData(configs); | ||
} catch (RCRuntimeException e) { | ||
r = Result.fail(); | ||
r.setRetCode(e.getId()); | ||
} catch (Exception e) { | ||
logger.error("查询配置失败", e); | ||
r = Result.fail(); | ||
} | ||
return r; | ||
} | ||
|
||
@RequestMapping(value = "/update", method = RequestMethod.GET) | ||
public Result update(String key, String value) { | ||
Result r = Result.success(); | ||
try { | ||
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { | ||
throw new RCRuntimeException(CodeMap.PARAM_ERROR); | ||
} | ||
|
||
Config config = new Config(); | ||
config.setKey(key); | ||
config.setValue(value); | ||
configService.pub(config); | ||
} catch (RCRuntimeException e) { | ||
r = Result.fail(); | ||
r.setRetCode(e.getId()); | ||
} catch (Exception e) { | ||
logger.error("配置更新失败", e); | ||
r = Result.fail(); | ||
} | ||
return r; | ||
} | ||
|
||
} |
Oops, something went wrong.