Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
[email protected] committed Aug 6, 2016
1 parent 02b0ce3 commit eb4d0d8
Show file tree
Hide file tree
Showing 35 changed files with 2,513 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.iml
/.idea/
/.mvn/
target/
/logs/
49 changes: 48 additions & 1 deletion README.md
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,征信等,维度越多,可以建立规则越多,风控越精准;
* 扩展风控规则,针对需要解决的场景问题,添加特定规则,分值也应根据自身场景来调整。**当然,这将是个漫长的过程;**
* 将用户的行为过程综合考虑,建立复合场景的规则条件。比如:登录->活动->订单->支付,综合考虑;
* **减少误报,提供性能!**
105 changes: 105 additions & 0 deletions pom.xml
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 src/main/java/com/example/riskcontrol/RiskcontrolApplication.java
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 src/main/java/com/example/riskcontrol/conf/DruidConfiguration.java
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;
}
}
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;
}

}
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;
}

}
Loading

0 comments on commit eb4d0d8

Please sign in to comment.