62
README.md
62
README.md
@@ -76,11 +76,20 @@ API地址:http://demo.fizzgate.com/proxy/[服务名]/[API Path]
|
||||
| v1.0.0 | v1.0.0 | v1.0.0 |
|
||||
| v1.1.0 | v1.1.0 | v1.1.0 |
|
||||
| v1.1.1 | v1.1.1 | v1.1.1 |
|
||||
| v1.2.0 | v1.2.0 | v1.1.1 |
|
||||
| v1.2.0 | v1.2.0 | v1.2.0 |
|
||||
|
||||
从v1.3.0开始管理后台的前端和服务端合并成一个包
|
||||
|
||||
- Fizz-gateway-community: 社区版
|
||||
|
||||
- Fizz-manager-professional:管理后台
|
||||
|
||||
| Fizz-gateway-community | Fizz-manager-professional |
|
||||
| ---------------------- | ------------------------- |
|
||||
| v1.3.0 | v1.3.0 |
|
||||
|
||||
请根据社区版的版本下载对应的管理后台版本
|
||||
|
||||
|
||||
## 部署说明
|
||||
|
||||
[详细部署教程>>>](http://www.fizzgate.com/guide/installation/)
|
||||
@@ -100,40 +109,22 @@ API地址:http://demo.fizzgate.com/proxy/[服务名]/[API Path]
|
||||
|
||||
#### 一、安装管理后台
|
||||
|
||||
从github的releases(https://github.com/wehotel/fizz-gateway-community/releases) 下载 fizz-manager-professional 和 fizz-admin-professional 的安装包
|
||||
从github的releases(https://github.com/wehotel/fizz-gateway-community/releases) 下载 fizz-manager-professional 安装包
|
||||
|
||||
##### 管理后台服务端(fizz-manager-professional)
|
||||
##### 管理后台(fizz-manager-professional)
|
||||
|
||||
1. 首次安装执行`fizz-manager-professional-1.2.0-mysql.sql`数据库脚本
|
||||
2. 将`application-prod.yml`、`boot.sh`、`fizz-manager-professional-1.2.0.jar`拷贝到`/data/webapps/fizz-manager-professional`目录下
|
||||
说明:
|
||||
|
||||
1. 以下安装步骤出现的`{version}`表示所使用管理后台的版本号,例如`1.3.0`。
|
||||
|
||||
安装:
|
||||
|
||||
1. 解压`fizz-manager-professional-{version}.zip`安装包
|
||||
2. 首次安装执行`fizz-manager-professional-{version}-mysql.sql`数据库脚本,从低版本升级至高版本选择执行update目录下对应升级脚本
|
||||
3. 修改`application-prod.yml`文件,将相关配置修改成部署环境的配置
|
||||
4. 修改`boot.sh`文件,将`RUN_CMD`变量值修改成部署环境的JAVA实际路径
|
||||
5. 执行 `chmod +x boot.sh` 命令给`boot.sh`增加执行权限
|
||||
6. 执行 `./boot.sh start` 命令启动服务,支持 start/stop/restart/status命令
|
||||
7. 服务启动后访问前端登录地址,使用超级管理员账户`admin`密码`Aa123!`登录
|
||||
|
||||
##### 管理后台前端(fizz-admin-professional)
|
||||
|
||||
1.解压zip资源包,取文件夹【fizzAdmin】放置于服务器静态数据存放目录 如:/home/data/
|
||||
2.配置nginx服务器
|
||||
```
|
||||
server {
|
||||
listen 9000;
|
||||
server_name localhost:9000;
|
||||
location / {
|
||||
root /home/data/fizzAdmin;
|
||||
}
|
||||
location ^~ /api {
|
||||
rewrite ^/api/(.*) /$1 break;
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
}
|
||||
}
|
||||
# 注:root中地址需与资源包存放目录路径一致
|
||||
# 注:http://127.0.0.1:8000 为管理后台(fizz-manager-professional)的访问地址
|
||||
```
|
||||
3.访问地址
|
||||
【资源部署服务器IP + 端口号】如:http://127.0.0.1:9000/
|
||||
(端口号与nginx配置端口号一致)
|
||||
4. Linux启动 执行 `chmod +x boot.sh` 命令给`boot.sh`增加执行权限;执行 `./boot.sh start` 命令启动服务,支持 start/stop/restart/status命令
|
||||
5. Windows启动 执行`.\boot.cmd start` 命令启动服务,支持 start/stop/restart/status命令
|
||||
6. 服务启动后访问 http://{部署机器IP地址}:8000/#/login,使用超级管理员账户`admin`密码`Aa123!`登录
|
||||
|
||||
#### 二、安装fizz-gateway-community社区版
|
||||
|
||||
@@ -141,10 +132,11 @@ server {
|
||||
|
||||
1. 支持配置中心:apollo、nacos,支持注册中心:eureka、nacos,详细配置方法查看application.yml文件。
|
||||
2. 如果使用apollo配置中心,可把application.yml文件内容迁到配置中心(apollo上应用名为:fizz-gateway);如果不使用apollo可去掉下面启动命令里的apollo参数。
|
||||
3. 以下安装步骤出现的`{version}`表示所使用网关的版本号,例如`1.3.0`。
|
||||
|
||||
安装方式一:脚本启动:
|
||||
|
||||
1. 下载fizz-gateway-community的最新代码,修改application.yml配置文件里配置中心、注册中心、redis(redis配置需与管理后台一致)的配置,使用maven命令`mvn clean package -DskipTests=true`构建并把构建好的fizz-gateway-community-1.2.0.jar和boot.sh放同一目录
|
||||
1. 下载fizz-gateway-community的最新代码,修改application.yml配置文件里配置中心、注册中心、redis(redis配置需与管理后台一致)的配置,使用maven命令`mvn clean package -DskipTests=true`构建并把构建好的fizz-gateway-community-{version}.jar和boot.sh放同一目录
|
||||
2. 修改boot.sh脚本的apollo连接,JVM内存配置
|
||||
3. 执行 `./boot.sh start` 命令启动服务,支持 start/stop/restart/status命令
|
||||
|
||||
@@ -158,7 +150,7 @@ server {
|
||||
|
||||
1. 本地clone仓库上的最新代码,修改application.yml配置文件里配置中心、注册中心、redis(redis配置需与管理后台一致)的配置
|
||||
2. 在项目根目录fizz-gateway-community下执行Maven命令`mvn clean package -DskipTests=true`打包
|
||||
3. 进入target目录,使用命令`java -jar -Denv=DEV -Dapollo.meta=http://localhost:66 fizz-gateway-community-1.2.0.jar`启动服务
|
||||
3. 进入target目录,使用命令`java -jar -Denv=DEV -Dapollo.meta=http://localhost:66 fizz-gateway-community-{version}.jar`启动服务
|
||||
|
||||
最后访问网关,地址形式为:http://127.0.0.1:8600/proxy/[服务名]/[API Path]
|
||||
|
||||
|
||||
29
pom.xml
29
pom.xml
@@ -5,12 +5,12 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.10.RELEASE</version>
|
||||
<version>2.2.12.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>we</groupId>
|
||||
<artifactId>fizz-gateway-community</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<version>1.3.0</version>
|
||||
<name>fizz-gateway-community</name>
|
||||
|
||||
<repositories>
|
||||
@@ -34,10 +34,13 @@
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<log4j2.version>2.13.3</log4j2.version>
|
||||
<netty.version>4.1.53.Final</netty.version>
|
||||
<httpclient.version>4.5.13</httpclient.version>
|
||||
<spring-framework.version>5.2.12.RELEASE</spring-framework.version>
|
||||
<reactor-bom.version>Dysprosium-SR15</reactor-bom.version>
|
||||
<lettuce.version>5.3.5.RELEASE</lettuce.version>
|
||||
<nacos.version>0.2.7</nacos.version>
|
||||
<netty.version>4.1.56.Final</netty.version>
|
||||
<httpcore.version>4.4.14</httpcore.version>
|
||||
<log4j2.version>2.13.3</log4j2.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -67,7 +70,7 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.74</version>
|
||||
<version>1.2.75</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -116,7 +119,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.tapestry</groupId>
|
||||
<artifactId>tapestry-json</artifactId>
|
||||
<version>5.4.4</version>
|
||||
<version>5.4.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -128,7 +131,7 @@
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>2.4.20</version>
|
||||
<version>2.4.21</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -146,13 +149,19 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
<version>2.2.5.RELEASE</version>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.boot</groupId>
|
||||
<artifactId>nacos-config-spring-boot-starter</artifactId>
|
||||
<version>${nacos.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.boot</groupId>
|
||||
@@ -166,7 +175,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
52
sh/boot.sh
52
sh/boot.sh
@@ -1,21 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
cygwin=false
|
||||
darwin=false
|
||||
os400=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true;;
|
||||
Darwin*) darwin=true;;
|
||||
OS400*) os400=true;;
|
||||
esac
|
||||
|
||||
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java
|
||||
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java
|
||||
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/local/java
|
||||
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if $darwin; then
|
||||
|
||||
if [ -x '/usr/libexec/java_home' ] ; then
|
||||
export JAVA_HOME=`/usr/libexec/java_home`
|
||||
|
||||
elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; then
|
||||
export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"
|
||||
fi
|
||||
else
|
||||
JAVA_PATH=`dirname $(readlink -f $(which javac))`
|
||||
if [ "x$JAVA_PATH" != "x" ]; then
|
||||
export JAVA_HOME=`dirname $JAVA_PATH 2>/dev/null`
|
||||
fi
|
||||
fi
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
echo "ERROR: Please set the JAVA_HOME variable in your environment!!!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
#进入脚本所在目录
|
||||
cd `dirname $0`
|
||||
|
||||
#变量定义
|
||||
APOLLO_META_SERVER=http://localhost:66
|
||||
ENV=dev
|
||||
APP_NAME=fizz-gateway-community-1.2.0.jar
|
||||
APP_DEP_DIR=/data/webapps/fizz-gateway
|
||||
APP_LOG_DIR=/data/logs/fizz-gateway
|
||||
JAVA_CMD=/usr/local/java/bin/java
|
||||
APP_NAME=fizz-gateway-community-1.3.0.jar
|
||||
APP_DEP_DIR="` pwd`"
|
||||
APP_LOG_DIR=${APP_DEP_DIR}'/logs'
|
||||
JAVA_CMD=${JAVA_HOME}'/bin/java'
|
||||
PID_FILE="${APP_LOG_DIR}/tpid"
|
||||
CHECK_COUNT=3
|
||||
SERVER_IP="` ip a | egrep "brd" | grep inet | awk '{print $2}' | sed 's#/24##g'`"
|
||||
|
||||
#创建应用目录
|
||||
mkdir -p ${APP_DEP_DIR}
|
||||
# 远程执行shell脚本初始化环境变量
|
||||
source '/etc/profile'
|
||||
|
||||
SERVER_IP="` ip a |egrep "brd" |grep inet|awk '{print $2}'|sed 's#/24##g'|head -1`"
|
||||
|
||||
#创建日志目录
|
||||
mkdir -p ${APP_LOG_DIR}
|
||||
@@ -24,7 +60,7 @@ mkdir -p ${APP_LOG_DIR}
|
||||
cd ${APP_DEP_DIR}
|
||||
|
||||
JAVA_OPTS="-Xms256m -Xmx4096m \
|
||||
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m \
|
||||
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m \
|
||||
-XX:+AggressiveOpts \
|
||||
-XX:+UseBiasedLocking \
|
||||
-XX:+UseG1GC \
|
||||
|
||||
@@ -1,13 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package we;
|
||||
|
||||
import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import we.config.AggregateRedisConfig;
|
||||
import we.log.LogSendAppender;
|
||||
|
||||
/**
|
||||
* fizz gateway application boot entrance
|
||||
*
|
||||
* @author linwaiwai
|
||||
* @author francis
|
||||
* @author hongqiaowei
|
||||
* @author zhongjie
|
||||
*/
|
||||
@SpringBootApplication(
|
||||
exclude = {ErrorWebFluxAutoConfiguration.class, RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class},
|
||||
scanBasePackages = {"we"}
|
||||
@@ -15,8 +44,30 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
@NacosPropertySource(dataId = "${nacos.config.data-id}", groupId = "${nacos.config.group}", autoRefreshed = true)
|
||||
@EnableDiscoveryClient
|
||||
public class FizzGatewayApplication {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FizzGatewayApplication.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
FizzAppContext.appContext = SpringApplication.run(FizzGatewayApplication.class, args);
|
||||
SpringApplication springApplication = new SpringApplication(FizzGatewayApplication.class);
|
||||
springApplication.setApplicationContextClass(CustomReactiveWebServerApplicationContext.class);
|
||||
FizzAppContext.appContext = springApplication.run(args);
|
||||
}
|
||||
|
||||
private static class CustomReactiveWebServerApplicationContext extends AnnotationConfigReactiveWebServerApplicationContext {
|
||||
@Override
|
||||
protected void onClose() {
|
||||
super.onClose();
|
||||
if (AggregateRedisConfig.proxyLettuceConnectionFactory != null) {
|
||||
LOGGER.info("FizzGatewayApplication stopped.");
|
||||
// set LogSendAppender.logEnabled to false to stop send log to fizz-manager
|
||||
LogSendAppender.logEnabled = Boolean.FALSE;
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
// the ProxyLettuceConnectionFactory remove DisposableBean interface, so invoke destroy method here
|
||||
AggregateRedisConfig.proxyLettuceConnectionFactory.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
170
src/main/java/we/config/AggregateRedisConfig.java
Normal file
170
src/main/java/we/config/AggregateRedisConfig.java
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package we.config;
|
||||
|
||||
import com.alibaba.nacos.api.config.annotation.NacosValue;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisClusterConnection;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisClusterConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConnection;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.data.redis.listener.ReactiveRedisMessageListenerContainer;
|
||||
import we.log.LogSendAppender;
|
||||
import we.log.RedisLogSendServiceImpl;
|
||||
|
||||
/**
|
||||
* aggregate Redis config
|
||||
*
|
||||
* @author zhongjie
|
||||
*/
|
||||
@Configuration
|
||||
public class AggregateRedisConfig extends RedisReactiveConfig {
|
||||
static final String AGGREGATE_REACTIVE_REDIS_PROPERTIES = "aggregateReactiveRedisProperties";
|
||||
private static final String AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY = "aggregateReactiveRedisConnectionFactory";
|
||||
public static final String AGGREGATE_REACTIVE_REDIS_TEMPLATE = "aggregateReactiveRedisTemplate";
|
||||
public static final String AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER = "aggregateReactiveRedisMessageListenerContainer";
|
||||
|
||||
private static final String SEND_LOG_TYPE_REDIS = "redis";
|
||||
public static ProxyLettuceConnectionFactory proxyLettuceConnectionFactory;
|
||||
|
||||
@NacosValue(value = "${send-log.open:false}", autoRefreshed = true)
|
||||
@Value("${send-log.open:false}")
|
||||
private boolean sendLogOpen;
|
||||
|
||||
@NacosValue(value = "${send-log.channel:fizz_send_log_channel}", autoRefreshed = true)
|
||||
@Value("${send-log.channel:fizz_log_channel}")
|
||||
private String sendLogChannel;
|
||||
|
||||
@NacosValue(value = "${send-log.type:redis}", autoRefreshed = true)
|
||||
@Value("${send-log.type:redis}")
|
||||
private String sendLogType;
|
||||
|
||||
@ConfigurationProperties(prefix = "aggregate.redis")
|
||||
@Configuration(AGGREGATE_REACTIVE_REDIS_PROPERTIES)
|
||||
public static class AggregateRedisReactiveProperties extends RedisReactiveProperties {
|
||||
}
|
||||
|
||||
public AggregateRedisConfig(@Qualifier(AGGREGATE_REACTIVE_REDIS_PROPERTIES) RedisReactiveProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY)
|
||||
public ReactiveRedisConnectionFactory lettuceConnectionFactory() {
|
||||
LettuceConnectionFactory lettuceConnectionFactory = (LettuceConnectionFactory) super.lettuceConnectionFactory();
|
||||
if (SEND_LOG_TYPE_REDIS.equals(sendLogType)) {
|
||||
proxyLettuceConnectionFactory = new ProxyLettuceConnectionFactory(lettuceConnectionFactory);
|
||||
proxyLettuceConnectionFactory.afterPropertiesSet();
|
||||
return proxyLettuceConnectionFactory;
|
||||
} else {
|
||||
return lettuceConnectionFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_TEMPLATE)
|
||||
public ReactiveStringRedisTemplate reactiveStringRedisTemplate(
|
||||
@Qualifier(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY) ReactiveRedisConnectionFactory factory) {
|
||||
ReactiveStringRedisTemplate reactiveStringRedisTemplate = super.reactiveStringRedisTemplate(factory);
|
||||
|
||||
// test redis can connect
|
||||
reactiveStringRedisTemplate.getConnectionFactory().getReactiveConnection().ping().block();
|
||||
|
||||
if (SEND_LOG_TYPE_REDIS.equals(sendLogType)) {
|
||||
// set LogSendAppender.logSendService here to let send log as early as possible
|
||||
LogSendAppender.logSendService = new RedisLogSendServiceImpl(this, reactiveStringRedisTemplate);
|
||||
}
|
||||
|
||||
return reactiveStringRedisTemplate;
|
||||
}
|
||||
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER)
|
||||
public ReactiveRedisMessageListenerContainer aggregateReactiveRedisMessageListenerContainer(
|
||||
@Qualifier(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY) ReactiveRedisConnectionFactory factory) {
|
||||
return new ReactiveRedisMessageListenerContainer(factory);
|
||||
}
|
||||
|
||||
public boolean getSendLogOpen() {
|
||||
return sendLogOpen;
|
||||
}
|
||||
|
||||
public String getSendLogChannel() {
|
||||
return sendLogChannel;
|
||||
}
|
||||
|
||||
public static class ProxyLettuceConnectionFactory implements RedisConnectionFactory, ReactiveRedisConnectionFactory {
|
||||
ProxyLettuceConnectionFactory(LettuceConnectionFactory lettuceConnectionFactory) {
|
||||
this.lettuceConnectionFactory = lettuceConnectionFactory;
|
||||
}
|
||||
|
||||
private LettuceConnectionFactory lettuceConnectionFactory;
|
||||
|
||||
public void destroy() {
|
||||
lettuceConnectionFactory.destroy();
|
||||
}
|
||||
|
||||
void afterPropertiesSet() {
|
||||
lettuceConnectionFactory.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveRedisConnection getReactiveConnection() {
|
||||
return lettuceConnectionFactory.getReactiveConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveRedisClusterConnection getReactiveClusterConnection() {
|
||||
return lettuceConnectionFactory.getReactiveClusterConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisConnection getConnection() {
|
||||
return lettuceConnectionFactory.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisClusterConnection getClusterConnection() {
|
||||
return lettuceConnectionFactory.getClusterConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getConvertPipelineAndTxResults() {
|
||||
return lettuceConnectionFactory.getConvertPipelineAndTxResults();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisSentinelConnection getSentinelConnection() {
|
||||
return lettuceConnectionFactory.getSentinelConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return lettuceConnectionFactory.translateExceptionIfPossible(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,12 @@ public class CommonConstants {
|
||||
public static final String HEADER_TRACE_ID = "X-TRACE-ID";
|
||||
|
||||
|
||||
/**
|
||||
* Prefix of traceId
|
||||
*/
|
||||
public static final String TRACE_ID_PREFIX = "fizz-";
|
||||
|
||||
|
||||
/**
|
||||
* WildCard for PathMapping
|
||||
*/
|
||||
|
||||
@@ -55,8 +55,8 @@ public class FilterExceptionHandlerConfig {
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(ex.getData().toString().getBytes())));
|
||||
}
|
||||
}
|
||||
if (t instanceof RedirectException) {
|
||||
RedirectException ex = (RedirectException) t;
|
||||
if (t instanceof RedirectException) {
|
||||
RedirectException ex = (RedirectException) t;
|
||||
if (ex.getRedirectUrl() != null) {
|
||||
ServerHttpResponse resp = exchange.getResponse();
|
||||
resp.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
|
||||
@@ -64,23 +64,22 @@ public class FilterExceptionHandlerConfig {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
if (t instanceof ExecuteScriptException) {
|
||||
ExecuteScriptException ex = (ExecuteScriptException) t;
|
||||
ServerHttpResponse resp = exchange.getResponse();
|
||||
resp.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
|
||||
RespEntity rs = null;
|
||||
String reqId = exchange.getRequest().getId();
|
||||
if (ex.getStepContext() != null && ex.getStepContext().returnContext()) {
|
||||
rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), reqId, ex.getStepContext());
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(JacksonUtils.writeValueAsString(rs).getBytes())));
|
||||
}else {
|
||||
rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), reqId);
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(rs.toString().getBytes())));
|
||||
if (t instanceof ExecuteScriptException) {
|
||||
ExecuteScriptException ex = (ExecuteScriptException) t;
|
||||
ServerHttpResponse resp = exchange.getResponse();
|
||||
resp.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
RespEntity rs = null;
|
||||
String reqId = exchange.getRequest().getId();
|
||||
if (ex.getStepContext() != null && ex.getStepContext().returnContext()) {
|
||||
rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), reqId, ex.getStepContext());
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(JacksonUtils.writeValueAsString(rs).getBytes())));
|
||||
} else {
|
||||
rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), reqId);
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(rs.toString().getBytes())));
|
||||
}
|
||||
}
|
||||
Mono<Void> vm = WebUtils.responseError(exchange, filterExceptionHandler, HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), t);
|
||||
return vm;
|
||||
Mono<Void> vm = WebUtils.responseError(exchange, filterExceptionHandler, HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), t);
|
||||
return vm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,17 +19,22 @@ package we.filter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import com.alibaba.nacos.api.config.annotation.NacosValue;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -50,6 +55,7 @@ import we.fizz.ConfigLoader;
|
||||
import we.fizz.Pipeline;
|
||||
import we.fizz.input.Input;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.plugin.auth.ApiConfig;
|
||||
import we.util.Constants;
|
||||
import we.util.MapUtil;
|
||||
import we.util.WebUtils;
|
||||
@@ -64,25 +70,35 @@ public class FizzGatewayFilter implements WebFilter {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FizzGatewayFilter.class);
|
||||
|
||||
private static final DataBuffer emptyBody = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false, true)).wrap(Constants.Symbol.EMPTY.getBytes());
|
||||
|
||||
|
||||
@Resource
|
||||
private ConfigLoader configLoader;
|
||||
|
||||
|
||||
@NacosValue(value = "${need-auth:false}", autoRefreshed = true)
|
||||
@Value("${need-auth:false}")
|
||||
private boolean needAuth;
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
String serviceId = WebUtils.getBackendService(exchange);
|
||||
if ( serviceId == null || (ApiConfig.Type.SERVICE_ARRANGE != WebUtils.getApiConfigType(exchange) && needAuth) ) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
ServerHttpResponse serverHttpResponse = exchange.getResponse();
|
||||
|
||||
if (WebUtils.getServiceId(exchange) == null) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
String path = WebUtils.getPathPrefix(exchange) + WebUtils.getServiceId(exchange) + WebUtils.getReqPath(exchange);
|
||||
|
||||
String path = WebUtils.getClientReqPathPrefix(exchange) + serviceId + WebUtils.getBackendPath(exchange);
|
||||
String method = request.getMethodValue();
|
||||
AggregateResource aggregateResource = configLoader.matchAggregateResource(method, path);
|
||||
if (aggregateResource == null) {
|
||||
return chain.filter(exchange);
|
||||
if (WebUtils.getApiConfigType(exchange) == ApiConfig.Type.SERVICE_ARRANGE) {
|
||||
return WebUtils.responseError(exchange, HttpStatus.INTERNAL_SERVER_ERROR.value(), "no aggregate resource: " + path);
|
||||
} else {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
Pipeline pipeline = aggregateResource.getPipeline();
|
||||
@@ -93,11 +109,16 @@ public class FizzGatewayFilter implements WebFilter {
|
||||
if(fizzHeaders != null && !fizzHeaders.isEmpty()) {
|
||||
headers.putAll(fizzHeaders);
|
||||
}
|
||||
|
||||
|
||||
// traceId
|
||||
String traceId = exchange.getRequest().getId();
|
||||
String tmpTraceId = CommonConstants.TRACE_ID_PREFIX + exchange.getRequest().getId();
|
||||
if (StringUtils.isNotBlank(request.getHeaders().getFirst(CommonConstants.HEADER_TRACE_ID))) {
|
||||
tmpTraceId = request.getHeaders().getFirst(CommonConstants.HEADER_TRACE_ID);
|
||||
}
|
||||
final String traceId = tmpTraceId;
|
||||
LogService.setBizId(traceId);
|
||||
serverHttpResponse.getHeaders().add(CommonConstants.HEADER_TRACE_ID, traceId);
|
||||
|
||||
LOGGER.debug("matched aggregation api: {}", path);
|
||||
|
||||
// 客户端提交上来的信息
|
||||
Map<String, Object> clientInput = new HashMap<>();
|
||||
@@ -105,8 +126,8 @@ public class FizzGatewayFilter implements WebFilter {
|
||||
clientInput.put("method", method);
|
||||
clientInput.put("headers", headers);
|
||||
clientInput.put("params", MapUtil.toHashMap(request.getQueryParams()));
|
||||
|
||||
|
||||
|
||||
|
||||
Mono<AggregateResult> result = null;
|
||||
if (HttpMethod.POST.name().equalsIgnoreCase(method)) {
|
||||
result = DataBufferUtils.join(request.getBody()).defaultIfEmpty(emptyBody).flatMap(buf -> {
|
||||
@@ -125,6 +146,7 @@ public class FizzGatewayFilter implements WebFilter {
|
||||
return result.subscribeOn(Schedulers.elastic()).flatMap(aggResult -> {
|
||||
LogService.setBizId(traceId);
|
||||
String jsonString = JSON.toJSONString(aggResult.getBody());
|
||||
LOGGER.debug("response body: {}", jsonString);
|
||||
if (aggResult.getHeaders() != null && !aggResult.getHeaders().isEmpty()) {
|
||||
aggResult.getHeaders().remove("Content-Length");
|
||||
serverHttpResponse.getHeaders().addAll(aggResult.getHeaders());
|
||||
@@ -133,7 +155,11 @@ public class FizzGatewayFilter implements WebFilter {
|
||||
// defalut content-type
|
||||
serverHttpResponse.getHeaders().add("Content-Type", "application/json; charset=UTF-8");
|
||||
}
|
||||
|
||||
List<String> headerTraceIds = serverHttpResponse.getHeaders().get(CommonConstants.HEADER_TRACE_ID);
|
||||
if (headerTraceIds == null || !headerTraceIds.contains(traceId)) {
|
||||
serverHttpResponse.getHeaders().add(CommonConstants.HEADER_TRACE_ID, traceId);
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
pipeline.getStepContext().addElapsedTime("总耗时", end - start);
|
||||
LOGGER.info("ElapsedTimes={}", JSON.toJSONString(pipeline.getStepContext().getElapsedTimes()));
|
||||
|
||||
@@ -92,6 +92,7 @@ public class PreFilter extends ProxyAggrFilter {
|
||||
Mono m;
|
||||
if (authRes instanceof ApiConfig) {
|
||||
ApiConfig ac = (ApiConfig) authRes;
|
||||
afterAuth(exchange, ac);
|
||||
m = executeFixedPluginFilters(exchange);
|
||||
m = m.defaultIfEmpty(ReactorUtils.NULL);
|
||||
if (ac.pluginConfigs == null || ac.pluginConfigs.isEmpty()) {
|
||||
@@ -101,6 +102,7 @@ public class PreFilter extends ProxyAggrFilter {
|
||||
.defaultIfEmpty(ReactorUtils.NULL).flatMap(func(exchange, chain));
|
||||
}
|
||||
} else if (authRes == ApiConfigService.Access.YES) {
|
||||
afterAuth(exchange, null);
|
||||
m = executeFixedPluginFilters(exchange);
|
||||
return m.defaultIfEmpty(ReactorUtils.NULL).flatMap(func(exchange, chain));
|
||||
} else {
|
||||
@@ -111,6 +113,19 @@ public class PreFilter extends ProxyAggrFilter {
|
||||
);
|
||||
}
|
||||
|
||||
private void afterAuth(ServerWebExchange exchange, ApiConfig ac) {
|
||||
String bs, bp;
|
||||
if (ac == null) {
|
||||
bs = WebUtils.getClientService(exchange);
|
||||
bp = WebUtils.getClientReqPath(exchange);
|
||||
} else {
|
||||
bs = ac.backendService;
|
||||
bp = ac.transform(WebUtils.getClientReqPath(exchange));
|
||||
}
|
||||
WebUtils.setBackendService(exchange, bs);
|
||||
WebUtils.setBackendPath(exchange, bp);
|
||||
}
|
||||
|
||||
private Mono chain(ServerWebExchange exchange, Mono m, PluginFilter pf) {
|
||||
return m.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
|
||||
@@ -32,7 +32,7 @@ public abstract class ProxyAggrFilter implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
String serviceId = WebUtils.getServiceId(exchange);
|
||||
String serviceId = WebUtils.getClientService(exchange);
|
||||
if (serviceId == null) {
|
||||
return chain.filter(exchange);
|
||||
} else {
|
||||
|
||||
@@ -34,8 +34,8 @@ import reactor.core.publisher.Mono;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.legacy.RespEntity;
|
||||
import we.plugin.auth.ApiConfig;
|
||||
import we.plugin.auth.AuthPluginFilter;
|
||||
import we.proxy.FizzWebClient;
|
||||
import we.util.Constants;
|
||||
import we.util.ThreadContext;
|
||||
import we.util.WebUtils;
|
||||
|
||||
@@ -104,26 +104,26 @@ public class RouteFilter extends ProxyAggrFilter {
|
||||
);
|
||||
}
|
||||
|
||||
ApiConfig ac = null;
|
||||
Object authRes = WebUtils.getFilterResultDataItem(exchange, AuthPluginFilter.AUTH_PLUGIN_FILTER, AuthPluginFilter.RESULT);
|
||||
if (authRes instanceof ApiConfig) {
|
||||
ac = (ApiConfig) authRes;
|
||||
}
|
||||
String rid = clientReq.getId();
|
||||
ApiConfig ac = WebUtils.getApiConfig(exchange);
|
||||
if (ac == null) {
|
||||
String pathQuery = WebUtils.getClientReqPathQuery(exchange);
|
||||
return send(exchange, WebUtils.getClientService(exchange), pathQuery, hdrs);
|
||||
|
||||
} else if (ac.type == ApiConfig.Type.SERVICE_DISCOVERY) {
|
||||
String pathQuery = WebUtils.appendQuery(WebUtils.getBackendPath(exchange), exchange);
|
||||
return send(exchange, WebUtils.getBackendService(exchange), pathQuery, hdrs);
|
||||
|
||||
} else if (ac.type == ApiConfig.Type.REVERSE_PROXY) {
|
||||
String uri = ac.getNextHttpHostPort() + WebUtils.appendQuery(WebUtils.getBackendPath(exchange), exchange);
|
||||
return fizzWebClient.send(rid, clientReq.getMethod(), uri, hdrs, clientReq.getBody()).flatMap(genServerResponse(exchange));
|
||||
|
||||
String relativeUri = WebUtils.getRelativeUri(exchange);
|
||||
if (ac == null || ac.proxyMode == ApiConfig.DIRECT_PROXY_MODE) {
|
||||
return send(exchange, WebUtils.getServiceId(exchange), relativeUri, hdrs);
|
||||
} else {
|
||||
String realUri;
|
||||
String backendUrl = ac.getNextBackendUrl();
|
||||
int acpLen = ac.path.length();
|
||||
if (acpLen == 1) {
|
||||
realUri = backendUrl + relativeUri;
|
||||
} else {
|
||||
realUri = backendUrl + relativeUri.substring(acpLen);
|
||||
}
|
||||
relativeUri.substring(acpLen);
|
||||
return fizzWebClient.send(clientReq.getId(), clientReq.getMethod(), realUri, hdrs, clientReq.getBody()).flatMap(genServerResponse(exchange));
|
||||
String err = "cant handle api config type " + ac.type;
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
WebUtils.request2stringBuilder(exchange, b);
|
||||
log.error(b.append(Constants.Symbol.LF).append(err).toString(), LogService.BIZ_ID, rid);
|
||||
return WebUtils.buildJsonDirectResponseAndBindContext(exchange, HttpStatus.OK, null, RespEntity.toJson(HttpStatus.INTERNAL_SERVER_ERROR.value(), err, rid));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
|
||||
import com.alibaba.nacos.api.config.annotation.NacosValue;
|
||||
|
||||
import we.config.AppConfigProperties;
|
||||
import we.fizz.input.ClientInputConfig;
|
||||
import we.fizz.input.Input;
|
||||
@@ -28,15 +29,18 @@ import we.fizz.input.InputType;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.noear.snack.ONode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static we.listener.AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE;
|
||||
import static we.config.AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE;
|
||||
import static we.util.Constants.Symbol.FORWARD_SLASH;
|
||||
|
||||
import java.io.File;
|
||||
@@ -48,6 +52,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author francis
|
||||
@@ -56,17 +61,21 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
@Component
|
||||
public class ConfigLoader {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigLoader.class);
|
||||
|
||||
/**
|
||||
* 聚合配置存放Hash的Key
|
||||
*/
|
||||
private static final String AGGREGATE_HASH_KEY = "fizz_aggregate_config";
|
||||
|
||||
|
||||
private static Map<String, String> aggregateResources = null;
|
||||
private static Map<String, ConfigInfo> resourceKey2ConfigInfoMap = null;
|
||||
private static Map<String, String> aggregateId2ResourceKeyMap = null;
|
||||
|
||||
|
||||
@Resource
|
||||
private AppConfigProperties appConfigProperties;
|
||||
|
||||
@Resource(name = AGGREGATE_REACTIVE_REDIS_TEMPLATE)
|
||||
private ReactiveStringRedisTemplate reactiveStringRedisTemplate;
|
||||
|
||||
@@ -85,11 +94,11 @@ public class ConfigLoader {
|
||||
clientInputConfig.setHeaders(cfgNode.select("$.headers").toObject(Map.class));
|
||||
clientInputConfig.setMethod(cfgNode.select("$.method").getString());
|
||||
clientInputConfig.setPath(cfgNode.select("$.path").getString());
|
||||
if(clientInputConfig.getPath().startsWith(TEST_PATH_PREFIX)) {
|
||||
if (clientInputConfig.getPath().startsWith(TEST_PATH_PREFIX)) {
|
||||
// always enable debug for testing
|
||||
clientInputConfig.setDebug(true);
|
||||
}else {
|
||||
if(cfgNode.select("$.debug") != null) {
|
||||
} else {
|
||||
if (cfgNode.select("$.debug") != null) {
|
||||
clientInputConfig.setDebug(cfgNode.select("$.debug").getBoolean());
|
||||
}
|
||||
}
|
||||
@@ -112,7 +121,7 @@ public class ConfigLoader {
|
||||
for (Map<String, Object> stepConfig : stepConfigs) {
|
||||
// set the specified env URL
|
||||
this.handleRequestURL(stepConfig);
|
||||
|
||||
|
||||
Step step = new Step.Builder().read(stepConfig);
|
||||
step.setName((String) stepConfig.get("name"));
|
||||
if (stepConfig.get("stop") != null) {
|
||||
@@ -152,22 +161,23 @@ public class ConfigLoader {
|
||||
}
|
||||
return aggregateResources.get(resourceKey);
|
||||
}
|
||||
|
||||
|
||||
private void handleRequestURL(Map<String, Object> stepConfig) {
|
||||
List<Object> requests = (List<Object>) stepConfig.get("requests");
|
||||
for (Object obj : requests) {
|
||||
Map<String, Object> request = (Map<String, Object>) obj;
|
||||
String envUrl = (String) request.get(appConfigProperties.getEnv() + "Url");
|
||||
if(!StringUtils.isEmpty(envUrl)) {
|
||||
if (!StringUtils.isEmpty(envUrl)) {
|
||||
request.put("url", request.get(appConfigProperties.getEnv() + "Url"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public synchronized void init() throws Exception {
|
||||
if (aggregateResources == null) {
|
||||
aggregateResources = new ConcurrentHashMap<>(1024);
|
||||
resourceKey2ConfigInfoMap = new ConcurrentHashMap<>(1024);
|
||||
resourceKey2ConfigInfoMap = new ConcurrentHashMap<>(1024);
|
||||
aggregateId2ResourceKeyMap = new ConcurrentHashMap<>(1024);
|
||||
}
|
||||
|
||||
@@ -210,6 +220,8 @@ public class ConfigLoader {
|
||||
String configId = cfgNode.select("$.id").getString();
|
||||
String configName = cfgNode.select("$.name").getString();
|
||||
long version = cfgNode.select("$.version").getLong();
|
||||
|
||||
LOGGER.debug("add aggregation config, key={} config={}", resourceKey, configStr);
|
||||
if (StringUtils.hasText(configId)) {
|
||||
String existResourceKey = aggregateId2ResourceKeyMap.get(configId);
|
||||
if (StringUtils.hasText(existResourceKey)) {
|
||||
@@ -231,8 +243,9 @@ public class ConfigLoader {
|
||||
JSONArray idArray = JSON.parseArray(configIds);
|
||||
idArray.forEach(it -> {
|
||||
String configId = (String) it;
|
||||
String existResourceKey =aggregateId2ResourceKeyMap.get(configId);
|
||||
String existResourceKey = aggregateId2ResourceKeyMap.get(configId);
|
||||
if (StringUtils.hasText(existResourceKey)) {
|
||||
LOGGER.debug("delete aggregation config: {}", existResourceKey);
|
||||
aggregateResources.remove(existResourceKey);
|
||||
resourceKey2ConfigInfoMap.remove(existResourceKey);
|
||||
aggregateId2ResourceKeyMap.remove(configId);
|
||||
@@ -250,7 +263,7 @@ public class ConfigLoader {
|
||||
}
|
||||
}
|
||||
String key = method.toUpperCase() + ":" + path;
|
||||
if(aggregateResources.containsKey(key) && aggregateResources.get(key) != null) {
|
||||
if (aggregateResources.containsKey(key) && aggregateResources.get(key) != null) {
|
||||
String configStr = aggregateResources.get(key);
|
||||
Input input = null;
|
||||
Pipeline pipeline = null;
|
||||
@@ -268,6 +281,7 @@ public class ConfigLoader {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConfigInfo buildConfigInfo(String configId, String configName, String method, String path, long version) {
|
||||
String serviceName = this.extractServiceName(path);
|
||||
ConfigInfo configInfo = new ConfigInfo();
|
||||
@@ -280,26 +294,27 @@ public class ConfigLoader {
|
||||
return configInfo;
|
||||
}
|
||||
|
||||
private static final String FORMAL_PATH_PREFIX = "/proxy/";
|
||||
private static final int FORMAL_PATH_SERVICE_NAME_START_INDEX = 7;
|
||||
private static final String TEST_PATH_PREFIX = "/proxytest/";
|
||||
private static final int TEST_PATH_SERVICE_NAME_START_INDEX = 11;
|
||||
private static final String FORMAL_PATH_PREFIX = "/proxy/";
|
||||
private static final int FORMAL_PATH_SERVICE_NAME_START_INDEX = 7;
|
||||
private static final String TEST_PATH_PREFIX = "/proxytest/";
|
||||
private static final int TEST_PATH_SERVICE_NAME_START_INDEX = 11;
|
||||
|
||||
private String extractServiceName(String path) {
|
||||
if (path != null) {
|
||||
if (path.startsWith(FORMAL_PATH_PREFIX)) {
|
||||
int endIndex = path.indexOf(FORWARD_SLASH, FORMAL_PATH_SERVICE_NAME_START_INDEX);
|
||||
if (endIndex > FORMAL_PATH_SERVICE_NAME_START_INDEX) {
|
||||
return path.substring(FORMAL_PATH_SERVICE_NAME_START_INDEX, endIndex);
|
||||
}
|
||||
} else if (path.startsWith(TEST_PATH_PREFIX)) {
|
||||
int endIndex = path.indexOf(FORWARD_SLASH, TEST_PATH_SERVICE_NAME_START_INDEX);
|
||||
if (endIndex > TEST_PATH_SERVICE_NAME_START_INDEX) {
|
||||
return path.substring(TEST_PATH_SERVICE_NAME_START_INDEX, endIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (path != null) {
|
||||
if (path.startsWith(FORMAL_PATH_PREFIX)) {
|
||||
int endIndex = path.indexOf(FORWARD_SLASH, FORMAL_PATH_SERVICE_NAME_START_INDEX);
|
||||
if (endIndex > FORMAL_PATH_SERVICE_NAME_START_INDEX) {
|
||||
return path.substring(FORMAL_PATH_SERVICE_NAME_START_INDEX, endIndex);
|
||||
}
|
||||
} else if (path.startsWith(TEST_PATH_PREFIX)) {
|
||||
int endIndex = path.indexOf(FORWARD_SLASH, TEST_PATH_SERVICE_NAME_START_INDEX);
|
||||
if (endIndex > TEST_PATH_SERVICE_NAME_START_INDEX) {
|
||||
return path.substring(TEST_PATH_SERVICE_NAME_START_INDEX, endIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class ConfigInfo implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -339,12 +354,9 @@ public class ConfigLoader {
|
||||
return false;
|
||||
}
|
||||
ConfigInfo that = (ConfigInfo) o;
|
||||
return Objects.equals(configId, that.configId) &&
|
||||
Objects.equals(configName, that.configName) &&
|
||||
Objects.equals(serviceName, that.serviceName) &&
|
||||
Objects.equals(method, that.method) &&
|
||||
Objects.equals(path, that.path) &&
|
||||
Objects.equals(version, that.version);
|
||||
return Objects.equals(configId, that.configId) && Objects.equals(configName, that.configName)
|
||||
&& Objects.equals(serviceName, that.serviceName) && Objects.equals(method, that.method)
|
||||
&& Objects.equals(path, that.path) && Objects.equals(version, that.version);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -95,30 +95,43 @@ public class Pipeline {
|
||||
return Mono.just(aggregateResult);
|
||||
}
|
||||
|
||||
LinkedList<Step> opSteps = (LinkedList<Step>) steps.clone();
|
||||
Step step1 = opSteps.removeFirst();
|
||||
step1.beforeRun(stepContext, null);
|
||||
Mono<List<StepResponse>> result = createStep(step1).expand(response -> {
|
||||
if (opSteps.isEmpty() || response.isStop()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
Step step = opSteps.pop();
|
||||
step.beforeRun(stepContext, response);
|
||||
return createStep(step);
|
||||
}).flatMap(response -> Flux.just(response)).collectList();
|
||||
return result.flatMap(clientResponse -> {
|
||||
// 数据转换
|
||||
long t3 = System.currentTimeMillis();
|
||||
AggregateResult aggResult = this.doInputDataMapping(input, null);
|
||||
this.stepContext.addElapsedTime(input.getName()+"聚合接口响应结果数据转换", System.currentTimeMillis() - t3);
|
||||
if(this.stepContext.isDebug()) {
|
||||
LogService.setBizId(this.stepContext.getTraceId());
|
||||
String jsonString = JSON.toJSONString(aggResult);
|
||||
if(CollectionUtils.isEmpty(steps)) {
|
||||
return handleOutput(input);
|
||||
}else {
|
||||
LinkedList<Step> opSteps = (LinkedList<Step>) steps.clone();
|
||||
Step step1 = opSteps.removeFirst();
|
||||
step1.beforeRun(stepContext, null);
|
||||
Mono<List<StepResponse>> result = createStep(step1).expand(response -> {
|
||||
if (opSteps.isEmpty() || response.isStop()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
Step step = opSteps.pop();
|
||||
step.beforeRun(stepContext, response);
|
||||
return createStep(step);
|
||||
}).flatMap(response -> Flux.just(response)).collectList();
|
||||
return result.flatMap(clientResponse -> {
|
||||
return handleOutput(input);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<AggregateResult> handleOutput(Input input){
|
||||
// 数据转换
|
||||
long t3 = System.currentTimeMillis();
|
||||
AggregateResult aggResult = this.doInputDataMapping(input, null);
|
||||
this.stepContext.addElapsedTime(input.getName()+"聚合接口响应结果数据转换", System.currentTimeMillis() - t3);
|
||||
if(this.stepContext.isDebug() || LOGGER.isDebugEnabled()) {
|
||||
LogService.setBizId(this.stepContext.getTraceId());
|
||||
String jsonString = JSON.toJSONString(aggResult);
|
||||
if(LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("aggResult {}", jsonString);
|
||||
LOGGER.debug("stepContext {}", JSON.toJSONString(stepContext));
|
||||
}else {
|
||||
LOGGER.info("aggResult {}", jsonString);
|
||||
LOGGER.info("stepContext {}", JSON.toJSONString(stepContext));
|
||||
LOGGER.info("stepContext {}", JSON.toJSONString(stepContext));
|
||||
}
|
||||
return Mono.just(aggResult);
|
||||
});
|
||||
}
|
||||
return Mono.just(aggResult);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package we.flume.clients.log4j2appender;
|
||||
|
||||
import we.constants.CommonConstants;
|
||||
|
||||
public enum LogService {
|
||||
|
||||
BIZ_ID, HANDLE_STGY, APP;
|
||||
@@ -14,6 +16,9 @@ public enum LogService {
|
||||
|
||||
public static void setBizId(Object bizId) {
|
||||
ThreadContext.set(Constants.BIZ_ID, bizId);
|
||||
if (bizId != null) {
|
||||
org.apache.logging.log4j.ThreadContext.put(CommonConstants.TRACE_ID, String.valueOf(bizId));
|
||||
}
|
||||
}
|
||||
|
||||
public static String toKF(String topic) {
|
||||
|
||||
@@ -32,7 +32,7 @@ public class RespEntity {
|
||||
private static final String f1 = ",\"message\":\"";
|
||||
private static final String f2 = "\"}";
|
||||
|
||||
public int msgCode;
|
||||
public int msgCode;
|
||||
|
||||
public String message;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import we.fizz.ConfigLoader;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import static we.listener.AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER;
|
||||
import static we.config.AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package we.listener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.data.redis.listener.ReactiveRedisMessageListenerContainer;
|
||||
|
||||
import we.config.RedisReactiveConfig;
|
||||
import we.config.RedisReactiveProperties;
|
||||
|
||||
/**
|
||||
* 聚合配置Redis配置
|
||||
* @author zhongjie
|
||||
*/
|
||||
@Configuration
|
||||
public class AggregateRedisConfig extends RedisReactiveConfig {
|
||||
static final String AGGREGATE_REACTIVE_REDIS_PROPERTIES = "aggregateReactiveRedisProperties";
|
||||
private static final String AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY = "aggregateReactiveRedisConnectionFactory";
|
||||
public static final String AGGREGATE_REACTIVE_REDIS_TEMPLATE = "aggregateReactiveRedisTemplate";
|
||||
static final String AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER = "aggregateReactiveRedisMessageListenerContainer";
|
||||
|
||||
@ConfigurationProperties(prefix = "aggregate.redis")
|
||||
@Configuration(AGGREGATE_REACTIVE_REDIS_PROPERTIES)
|
||||
public static class AggregateRedisReactiveProperties extends RedisReactiveProperties {
|
||||
}
|
||||
|
||||
public AggregateRedisConfig(@Qualifier(AGGREGATE_REACTIVE_REDIS_PROPERTIES) RedisReactiveProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY)
|
||||
public ReactiveRedisConnectionFactory lettuceConnectionFactory() {
|
||||
return super.lettuceConnectionFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_TEMPLATE)
|
||||
public ReactiveStringRedisTemplate reactiveStringRedisTemplate(
|
||||
@Qualifier(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY) ReactiveRedisConnectionFactory factory) {
|
||||
return super.reactiveStringRedisTemplate(factory);
|
||||
}
|
||||
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER)
|
||||
public ReactiveRedisMessageListenerContainer aggregateReactiveRedisMessageListenerContainer(
|
||||
@Qualifier(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY) ReactiveRedisConnectionFactory factory) {
|
||||
return new ReactiveRedisMessageListenerContainer(factory);
|
||||
}
|
||||
}
|
||||
78
src/main/java/we/log/LogSend.java
Normal file
78
src/main/java/we/log/LogSend.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package we.log;
|
||||
|
||||
/**
|
||||
* log send data entity
|
||||
*
|
||||
* @author zhongjie
|
||||
*/
|
||||
public class LogSend {
|
||||
public LogSend(String bizId, String serverIp, int level, Long timeMillis, String content) {
|
||||
this.bizId = bizId;
|
||||
this.serverIp = serverIp;
|
||||
this.level = level;
|
||||
this.timeMillis = timeMillis;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
private String bizId;
|
||||
private String serverIp;
|
||||
private int level;
|
||||
private Long timeMillis;
|
||||
private String content;
|
||||
|
||||
public String getBizId() {
|
||||
return bizId;
|
||||
}
|
||||
|
||||
public void setBizId(String bizId) {
|
||||
this.bizId = bizId;
|
||||
}
|
||||
|
||||
public String getServerIp() {
|
||||
return serverIp;
|
||||
}
|
||||
|
||||
public void setServerIp(String serverIp) {
|
||||
this.serverIp = serverIp;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public Long getTimeMillis() {
|
||||
return timeMillis;
|
||||
}
|
||||
|
||||
public void setTimeMillis(Long timeMillis) {
|
||||
this.timeMillis = timeMillis;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
133
src/main/java/we/log/LogSendAppender.java
Normal file
133
src/main/java/we/log/LogSendAppender.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package we.log;
|
||||
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.Core;
|
||||
import org.apache.logging.log4j.core.Filter;
|
||||
import org.apache.logging.log4j.core.Layout;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
||||
import org.apache.logging.log4j.core.config.Property;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||
import we.FizzAppContext;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.util.NetworkUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* log send appender
|
||||
*
|
||||
* @author zhongjie
|
||||
*/
|
||||
@Plugin(name = LogSendAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
|
||||
public class LogSendAppender extends AbstractAppender {
|
||||
|
||||
static final String PLUGIN_NAME = "LogSend";
|
||||
public static LogSendService logSendService;
|
||||
public static Boolean logEnabled;
|
||||
private static LogSend[] logSends = new LogSend[1000];
|
||||
private static AtomicInteger logSendIndex = new AtomicInteger(0);
|
||||
|
||||
private LogSendAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, Property[] properties) {
|
||||
super(name, filter, layout, ignoreExceptions, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(LogEvent event) {
|
||||
if (logEnabled != null && !logEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (logEnabled == null && FizzAppContext.appContext == null && logSendService == null) {
|
||||
// local cache
|
||||
logSends[logSendIndex.getAndIncrement() % logSends.length] = new LogSend(this.getBizId(event.getMessage().getParameters()),
|
||||
NetworkUtils.getServerIp(), event.getLevel().intLevel(), event.getTimeMillis(), new String(this.getLayout().toByteArray(event)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (logEnabled == null && logSendService == null) {
|
||||
// no legal logSendService, discard the local cache
|
||||
logEnabled = Boolean.FALSE;
|
||||
logSends = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (logEnabled == null) {
|
||||
logEnabled = Boolean.TRUE;
|
||||
|
||||
LogSend[] logSends;
|
||||
synchronized (LogSendAppender.class) {
|
||||
logSends = LogSendAppender.logSends;
|
||||
LogSendAppender.logSends = null;
|
||||
}
|
||||
|
||||
// logSendService is ready, send the local cache
|
||||
if (logSends != null) {
|
||||
int size = Math.min(logSendIndex.get(), logSends.length);
|
||||
for (int i = 0; i < size; i++) {
|
||||
logSendService.send(logSends[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogSend logSend = new LogSend(this.getBizId(event.getMessage().getParameters()), NetworkUtils.getServerIp(),
|
||||
event.getLevel().intLevel(), event.getTimeMillis(), new String(this.getLayout().toByteArray(event)));
|
||||
logSendService.send(logSend);
|
||||
}
|
||||
|
||||
private String getBizId(Object[] parameters) {
|
||||
Object bizId = LogService.getBizId();
|
||||
if (parameters != null) {
|
||||
for (int i = parameters.length - 1; i > -1; --i) {
|
||||
Object p = parameters[i];
|
||||
if (p == LogService.BIZ_ID) {
|
||||
if (i != parameters.length - 1) {
|
||||
bizId = parameters[i + 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bizId == null) {
|
||||
return "";
|
||||
}
|
||||
return bizId.toString();
|
||||
}
|
||||
|
||||
@PluginFactory
|
||||
public static LogSendAppender createAppender(@PluginAttribute("name") String name,
|
||||
@PluginElement("Filter") final Filter filter,
|
||||
@PluginElement("Layout") Layout<? extends Serializable> layout,
|
||||
@PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
|
||||
if (name == null) {
|
||||
LOGGER.error("No name provided for LogSendAppender!");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (layout == null) {
|
||||
layout = PatternLayout.createDefaultLayout();
|
||||
}
|
||||
return new LogSendAppender(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY);
|
||||
}
|
||||
}
|
||||
30
src/main/java/we/log/LogSendService.java
Normal file
30
src/main/java/we/log/LogSendService.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package we.log;
|
||||
|
||||
/**
|
||||
* log send service interface, used by {@link LogSendAppender} to send log to fizz-manager
|
||||
*
|
||||
* @author zhongjie
|
||||
*/
|
||||
public interface LogSendService {
|
||||
/**
|
||||
* send log
|
||||
* @param logSend log data
|
||||
*/
|
||||
void send(LogSend logSend);
|
||||
}
|
||||
43
src/main/java/we/log/RedisLogSendServiceImpl.java
Normal file
43
src/main/java/we/log/RedisLogSendServiceImpl.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package we.log;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import we.config.AggregateRedisConfig;
|
||||
|
||||
/**
|
||||
* {@link LogSendService} impl class, using redis channel to send log
|
||||
*
|
||||
* @author zhongjie
|
||||
*/
|
||||
public class RedisLogSendServiceImpl implements LogSendService {
|
||||
public RedisLogSendServiceImpl(AggregateRedisConfig aggregateRedisConfig, ReactiveStringRedisTemplate reactiveStringRedisTemplate) {
|
||||
this.aggregateRedisConfig = aggregateRedisConfig;
|
||||
this.reactiveStringRedisTemplate = reactiveStringRedisTemplate;
|
||||
}
|
||||
|
||||
private AggregateRedisConfig aggregateRedisConfig;
|
||||
private ReactiveStringRedisTemplate reactiveStringRedisTemplate;
|
||||
|
||||
@Override
|
||||
public void send(LogSend logSend) {
|
||||
if (aggregateRedisConfig.getSendLogOpen()) {
|
||||
reactiveStringRedisTemplate.convertAndSend(aggregateRedisConfig.getSendLogChannel(), JSON.toJSONString(logSend)).subscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,16 +18,17 @@
|
||||
package we.plugin.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import we.plugin.PluginConfig;
|
||||
import we.util.Constants;
|
||||
import we.util.JacksonUtils;
|
||||
import we.util.UrlTransformUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -37,15 +38,20 @@ import java.util.stream.Stream;
|
||||
|
||||
public class ApiConfig {
|
||||
|
||||
public static final int DELETED = 1;
|
||||
public static interface Type {
|
||||
static final byte UNDEFINED = 0;
|
||||
static final byte SERVICE_ARRANGE = 1;
|
||||
static final byte SERVICE_DISCOVERY = 2;
|
||||
static final byte REVERSE_PROXY = 3;
|
||||
}
|
||||
|
||||
public static final char ALLOW = 'a';
|
||||
public static final int DELETED = 1;
|
||||
|
||||
public static final char FORBID = 'f';
|
||||
public static final char ALLOW = 'a';
|
||||
|
||||
public static final byte DIRECT_PROXY_MODE = 1;
|
||||
public static final char FORBID = 'f';
|
||||
|
||||
public static final byte PREFIX_REWRITE_PROXY_MODE = 2;
|
||||
private static final String match_all = "/**";
|
||||
|
||||
// @JsonIgnore
|
||||
public int id; // tb_api_auth.id
|
||||
@@ -57,22 +63,50 @@ public class ApiConfig {
|
||||
|
||||
public String service;
|
||||
|
||||
public String backendService;
|
||||
|
||||
public HttpMethod method = HttpMethod.X;
|
||||
|
||||
public String path = String.valueOf(Constants.Symbol.FORWARD_SLASH);
|
||||
// public String path = String.valueOf(Constants.Symbol.FORWARD_SLASH);
|
||||
public String path = match_all;
|
||||
|
||||
public boolean exactMatch = false;
|
||||
|
||||
public String backendPath;
|
||||
|
||||
public Set<String> apps = Stream.of(App.ALL_APP).collect(Collectors.toSet());
|
||||
|
||||
public byte proxyMode = DIRECT_PROXY_MODE;
|
||||
@JsonProperty("proxyMode")
|
||||
public byte type = Type.SERVICE_DISCOVERY;
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger(-1);
|
||||
|
||||
public List<String> backendUrls;
|
||||
// public List<String> backendUrls;
|
||||
|
||||
public List<String> httpHostPorts;
|
||||
|
||||
public char access = ALLOW;
|
||||
|
||||
public List<PluginConfig> pluginConfigs;
|
||||
|
||||
public static boolean isAntPathPattern(String path) {
|
||||
boolean uriVar = false;
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
char c = path.charAt(i);
|
||||
if (c == '*' || c == '?') {
|
||||
return true;
|
||||
}
|
||||
if (c == '{') {
|
||||
uriVar = true;
|
||||
continue;
|
||||
}
|
||||
if (c == '}' && uriVar) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setGatewayGroup(String ggs) {
|
||||
gatewayGroups.remove(GatewayGroup.DEFAULT);
|
||||
if (StringUtils.isBlank(ggs)) {
|
||||
@@ -101,7 +135,16 @@ public class ApiConfig {
|
||||
|
||||
public void setPath(String p) {
|
||||
if (StringUtils.isNotBlank(p)) {
|
||||
path = p.trim();
|
||||
if ("/".equals(p)) {
|
||||
path = match_all;
|
||||
} else {
|
||||
path = p.trim();
|
||||
if (!isAntPathPattern(path)) {
|
||||
exactMatch = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
path = match_all;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,14 +155,31 @@ public class ApiConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// @JsonIgnore
|
||||
// public String getNextBackendUrl() {
|
||||
// int idx = counter.incrementAndGet();
|
||||
// if (idx < 0) {
|
||||
// counter.set(0);
|
||||
// idx = 0;
|
||||
// }
|
||||
// return backendUrls.get(idx % backendUrls.size());
|
||||
// }
|
||||
|
||||
@JsonIgnore
|
||||
public String getNextBackendUrl() {
|
||||
public String getNextHttpHostPort() {
|
||||
int idx = counter.incrementAndGet();
|
||||
if (idx < 0) {
|
||||
counter.set(0);
|
||||
idx = 0;
|
||||
}
|
||||
return backendUrls.get(idx % backendUrls.size());
|
||||
return httpHostPorts.get(idx % httpHostPorts.size());
|
||||
}
|
||||
|
||||
public String transform(String reqPath) {
|
||||
if (exactMatch) {
|
||||
return backendPath;
|
||||
}
|
||||
return UrlTransformUtils.transform(path, backendPath, reqPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.listener.AggregateRedisConfig;
|
||||
import we.config.AggregateRedisConfig;
|
||||
import we.util.*;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@@ -53,16 +53,20 @@ public class ApiConfigService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ApiConfigService.class);
|
||||
|
||||
private static final String fizzApiConfig = "fizz_api_config";
|
||||
|
||||
private static final String fizzApiConfigChannel = "fizz_api_config_channel";
|
||||
|
||||
private static final String signHeader = "fizz-sign";
|
||||
|
||||
private static final String timestampHeader = "fizz-ts";
|
||||
|
||||
private static final String secretKeyHeader = "fizz-secretkey";
|
||||
|
||||
@NacosValue(value = "${fizz-api-config.key:fizz_api_config_route}", autoRefreshed = true)
|
||||
@Value("${fizz-api-config.key:fizz_api_config_route}")
|
||||
private String fizzApiConfig;
|
||||
|
||||
@NacosValue(value = "${fizz-api-config.channel:fizz_api_config_channel_route}", autoRefreshed = true)
|
||||
@Value("${fizz-api-config.channel:fizz_api_config_channel_route}")
|
||||
private String fizzApiConfigChannel;
|
||||
|
||||
public Map<String, ServiceConfig> serviceConfigMap = new HashMap<>(128);
|
||||
|
||||
private Map<Integer, ApiConfig> apiConfigMap = new HashMap<>(128);
|
||||
@@ -109,9 +113,9 @@ public class ApiConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
@NacosValue(value = "${auth.compatible-wh:false}", autoRefreshed = true)
|
||||
@Value("${auth.compatible-wh:false}")
|
||||
private boolean compatibleWh;
|
||||
@NacosValue(value = "${need-auth:false}", autoRefreshed = true)
|
||||
@Value("${need-auth:false}")
|
||||
private boolean needAuth;
|
||||
|
||||
@Resource(name = AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE)
|
||||
private ReactiveStringRedisTemplate rt;
|
||||
@@ -275,21 +279,21 @@ public class ApiConfigService {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
HttpHeaders hdrs = req.getHeaders();
|
||||
LogService.setBizId(req.getId());
|
||||
return canAccess(exchange, WebUtils.getAppId(exchange), WebUtils.getOriginIp(exchange), hdrs.getFirst(timestampHeader), hdrs.getFirst(signHeader), hdrs.getFirst(secretKeyHeader),
|
||||
WebUtils.getServiceId(exchange), req.getMethod(), WebUtils.getReqPath(exchange));
|
||||
return canAccess(exchange, WebUtils.getAppId(exchange), WebUtils.getOriginIp(exchange), hdrs.getFirst(timestampHeader), hdrs.getFirst(signHeader), hdrs.getFirst(secretKeyHeader),
|
||||
WebUtils.getClientService(exchange), req.getMethod(), WebUtils.getClientReqPath(exchange));
|
||||
}
|
||||
|
||||
private Mono<Object> canAccess(ServerWebExchange exchange, String app, String ip, String timestamp, String sign, String secretKey,
|
||||
String service, HttpMethod method, String path) {
|
||||
|
||||
if (openServiceWhiteList) {
|
||||
if (!whiteListSet.contains(service)) { // TODO XXX
|
||||
return Mono.just(Access.SERVICE_NOT_OPEN);
|
||||
}
|
||||
}
|
||||
// if (openServiceWhiteList) {
|
||||
// if (!whiteListSet.contains(service)) { // TODO XXX
|
||||
// return Mono.just(Access.SERVICE_NOT_OPEN);
|
||||
// }
|
||||
// }
|
||||
ServiceConfig sc = serviceConfigMap.get(service);
|
||||
if (sc == null) {
|
||||
if (compatibleWh) {
|
||||
if (!needAuth) {
|
||||
return Mono.just(Access.YES);
|
||||
} else {
|
||||
return logWarnAndResult(service + Constants.Symbol.BLANK + Access.NO_SERVICE_CONFIG.getReason(), Access.NO_SERVICE_CONFIG);
|
||||
@@ -305,7 +309,7 @@ public class ApiConfigService {
|
||||
}
|
||||
ApiConfig ac = ac0;
|
||||
if (ac == null) {
|
||||
if (compatibleWh) {
|
||||
if (!needAuth) {
|
||||
return Mono.just(Access.YES);
|
||||
} else {
|
||||
return logWarnAndResult(api + " no api config", Access.NO_API_CONFIG);
|
||||
@@ -369,6 +373,6 @@ public class ApiConfigService {
|
||||
private static boolean validate(String app, String timestamp, String secretKey, String sign) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
b.append(app).append(Constants.Symbol.UNDERLINE).append(timestamp).append(Constants.Symbol.UNDERLINE).append(secretKey);
|
||||
return sign.equals(DigestUtils.md532(b.toString()));
|
||||
return sign.equalsIgnoreCase(DigestUtils.md532(b.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.listener.AggregateRedisConfig;
|
||||
import we.config.AggregateRedisConfig;
|
||||
import we.util.Constants;
|
||||
import we.util.JacksonUtils;
|
||||
import we.util.ReactorUtils;
|
||||
|
||||
@@ -51,7 +51,7 @@ public class GatewayGroup2appsToApiConfig {
|
||||
}
|
||||
for (String a : ac.apps) {
|
||||
app2apiConfigMap.put(a, ac);
|
||||
log.info(gg + " add " + a + " -> " + ac);
|
||||
log.info("expose " + ac + " to " + gg + " group and " + a + " app");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class GatewayGroup2appsToApiConfig {
|
||||
if (app2apiConfigMap != null) {
|
||||
for (String a : ac.apps) {
|
||||
ApiConfig r = app2apiConfigMap.remove(a);
|
||||
log.info(gg + " remove " + a + " -> " + r);
|
||||
log.info("remove " + r + " from " + gg + " group and " + a + " app");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public class GatewayGroup2appsToApiConfig {
|
||||
}
|
||||
for (String a : ac.apps) {
|
||||
ApiConfig old = app2apiConfigMap.put(a, ac);
|
||||
log.info(gg + " update " + a + " -> " + old + " with " + ac);
|
||||
log.info(gg + " group and " + a + " app update " + old + " with " + ac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.listener.AggregateRedisConfig;
|
||||
import we.config.AggregateRedisConfig;
|
||||
import we.util.Constants;
|
||||
import we.util.JacksonUtils;
|
||||
import we.util.NetworkUtils;
|
||||
|
||||
@@ -20,15 +20,14 @@ package we.plugin.auth;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import we.util.Constants;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import we.util.ThreadContext;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
@@ -36,7 +35,11 @@ import java.util.Map;
|
||||
|
||||
public class ServiceConfig {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ServiceConfig.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(ServiceConfig.class);
|
||||
|
||||
private static final AntPathMatcher antPathMatcher = new AntPathMatcher();
|
||||
|
||||
private static final String mpps = "$mpps";
|
||||
|
||||
public String id;
|
||||
|
||||
@@ -110,7 +113,8 @@ public class ServiceConfig {
|
||||
|
||||
@JsonIgnore
|
||||
public ApiConfig getApiConfig(HttpMethod method, String path, String gatewayGroup, String app) {
|
||||
GatewayGroup2appsToApiConfig r = getApiConfig0(method, path);
|
||||
// GatewayGroup2appsToApiConfig r = getApiConfig0(method, path);
|
||||
GatewayGroup2appsToApiConfig r = getApiConfig(method, path);
|
||||
if (r == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -120,28 +124,57 @@ public class ServiceConfig {
|
||||
return r.get(gatewayGroup, app);
|
||||
}
|
||||
|
||||
private GatewayGroup2appsToApiConfig getApiConfig0(HttpMethod method, String path) {
|
||||
while (true) {
|
||||
EnumMap<HttpMethod, GatewayGroup2appsToApiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(path);
|
||||
if (method2apiConfigMap == null) {
|
||||
int i = path.lastIndexOf(Constants.Symbol.FORWARD_SLASH);
|
||||
if (i == 0) {
|
||||
method2apiConfigMap = path2methodToApiConfigMapMap.get(Constants.Symbol.FORWARD_SLASH_STR);
|
||||
if (method2apiConfigMap == null) {
|
||||
return null;
|
||||
} else {
|
||||
return getApiConfig1(method, method2apiConfigMap);
|
||||
}
|
||||
} else {
|
||||
path = path.substring(0, i);
|
||||
private GatewayGroup2appsToApiConfig getApiConfig(HttpMethod method, String reqPath) {
|
||||
|
||||
List<String> matchPathPatterns = ThreadContext.getArrayList(mpps, String.class);
|
||||
|
||||
Set<Map.Entry<String, EnumMap<HttpMethod, GatewayGroup2appsToApiConfig>>> es = path2methodToApiConfigMapMap.entrySet();
|
||||
for (Map.Entry<String, EnumMap<HttpMethod, GatewayGroup2appsToApiConfig>> e : es) {
|
||||
String pathPattern = e.getKey();
|
||||
if (ApiConfig.isAntPathPattern(pathPattern)) {
|
||||
if (antPathMatcher.match(pathPattern, reqPath)) {
|
||||
matchPathPatterns.add(pathPattern);
|
||||
}
|
||||
} else {
|
||||
return getApiConfig1(method, method2apiConfigMap);
|
||||
} else if (reqPath.equals(pathPattern)) {
|
||||
return getGatewayGroup2appsToApiConfig(method, e.getValue());
|
||||
}
|
||||
}
|
||||
if (matchPathPatterns.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
Collections.sort(matchPathPatterns, antPathMatcher.getPatternComparator(reqPath));
|
||||
String bestPattern = matchPathPatterns.get(0);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("req path: " + reqPath +
|
||||
"\nmatch patterns: " + matchPathPatterns +
|
||||
"\nbest one: " + bestPattern);
|
||||
}
|
||||
return getGatewayGroup2appsToApiConfig(method, path2methodToApiConfigMapMap.get(bestPattern));
|
||||
}
|
||||
}
|
||||
|
||||
private GatewayGroup2appsToApiConfig getApiConfig1(HttpMethod method, EnumMap<HttpMethod, GatewayGroup2appsToApiConfig> method2apiConfigMap) {
|
||||
// private GatewayGroup2appsToApiConfig getApiConfig0(HttpMethod method, String path) {
|
||||
// while (true) {
|
||||
// EnumMap<HttpMethod, GatewayGroup2appsToApiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(path);
|
||||
// if (method2apiConfigMap == null) {
|
||||
// int i = path.lastIndexOf(Constants.Symbol.FORWARD_SLASH);
|
||||
// if (i == 0) {
|
||||
// method2apiConfigMap = path2methodToApiConfigMapMap.get(Constants.Symbol.FORWARD_SLASH_STR);
|
||||
// if (method2apiConfigMap == null) {
|
||||
// return null;
|
||||
// } else {
|
||||
// return getGatewayGroup2appsToApiConfig(method, method2apiConfigMap);
|
||||
// }
|
||||
// } else {
|
||||
// path = path.substring(0, i);
|
||||
// }
|
||||
// } else {
|
||||
// return getGatewayGroup2appsToApiConfig(method, method2apiConfigMap);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private GatewayGroup2appsToApiConfig getGatewayGroup2appsToApiConfig(HttpMethod method, EnumMap<HttpMethod, GatewayGroup2appsToApiConfig> method2apiConfigMap) {
|
||||
GatewayGroup2appsToApiConfig r = method2apiConfigMap.get(method);
|
||||
if (r == null) {
|
||||
return method2apiConfigMap.get(HttpMethod.X);
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.listener.AggregateRedisConfig;
|
||||
import we.config.AggregateRedisConfig;
|
||||
import we.plugin.PluginFilter;
|
||||
import we.plugin.auth.GatewayGroupService;
|
||||
import we.util.Constants;
|
||||
@@ -104,10 +104,15 @@ public class StatPluginFilter extends PluginFilter {
|
||||
b.append(Constants.Symbol.LEFT_BRACE);
|
||||
b.append(ip); toJsonStringValue(b, WebUtils.getOriginIp(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(gatewayGroup); toJsonStringValue(b, currentGatewayGroups); b.append(Constants.Symbol.COMMA);
|
||||
b.append(service); toJsonStringValue(b, WebUtils.getServiceId(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(appid); toJsonStringValue(b, WebUtils.getAppId(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(service); toJsonStringValue(b, WebUtils.getClientService(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
|
||||
String appId = WebUtils.getAppId(exchange);
|
||||
if (appId != null) {
|
||||
b.append(appid); toJsonStringValue(b, appId); b.append(Constants.Symbol.COMMA);
|
||||
}
|
||||
|
||||
b.append(apiMethod); toJsonStringValue(b, exchange.getRequest().getMethodValue()); b.append(Constants.Symbol.COMMA);
|
||||
b.append(apiPath); toJsonStringValue(b, WebUtils.getReqPath(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(apiPath); toJsonStringValue(b, WebUtils.getClientReqPath(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(reqTime) .append(System.currentTimeMillis());
|
||||
b.append(Constants.Symbol.RIGHT_BRACE);
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* The Eureka implementation of {@code DiscoveryClientUriSelector}
|
||||
@@ -30,11 +32,11 @@ public class EurekaUriSelector extends AbstractDiscoveryClientUriSelector {
|
||||
|
||||
// private static List<InstanceInfo> aggrMemberInsts = new ArrayList<>();
|
||||
// static {
|
||||
// InstanceInfo i0 = InstanceInfo.Builder.newBuilder().setAppName("TRIP-MINI").setIPAddr("xxx.25.63.192").setPort(7094).build();
|
||||
// InstanceInfo i0 = InstanceInfo.Builder.newBuilder().setAppName("MINITRIP").setIPAddr("xxx.xxx.63.192").setPort(7094).build();
|
||||
// aggrMemberInsts.add(i0);
|
||||
// }
|
||||
// private static AtomicLong counter = new AtomicLong(0);
|
||||
// private static final String aggrMember = "trip-mini";
|
||||
// private static final String aggrMember = "minitrip";
|
||||
|
||||
|
||||
private InstanceInfo roundRobinChoose1instFrom(String service) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package we.proxy;
|
||||
|
||||
import com.alibaba.nacos.api.config.annotation.NacosValue;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -219,14 +220,13 @@ public class FizzWebClient {
|
||||
// TODO 请求完成后,做metric, 以反哺后续的请求转发
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String extractServiceOrAddress(String uriOrSvc) {
|
||||
return uriOrSvc.substring(7, uriOrSvc.indexOf(Constants.Symbol.FORWARD_SLASH, 10));
|
||||
}
|
||||
|
||||
private boolean isService(String s) {
|
||||
if (s.indexOf(Constants.Symbol.DOT) > 0 || s.equals(localhost)) {
|
||||
if (StringUtils.indexOfAny(s, Constants.Symbol.DOT, Constants.Symbol.COLON) > 0
|
||||
|| StringUtils.indexOfIgnoreCase(s, localhost) > 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
@@ -28,11 +28,12 @@ public final class Constants {
|
||||
public static final class Symbol {
|
||||
public static final String EMPTY = "";
|
||||
public static final String SPACE_STR = " ";
|
||||
public static final String TWO_SPACE_STR = " ";
|
||||
public static final char COMMA = ',';
|
||||
public static final char COLON = ':';
|
||||
public static final char BLANK = ' ';
|
||||
public static final char SPACE = BLANK;
|
||||
public static final String TWO_SPACE_STR = " ";
|
||||
|
||||
public static final char COMMA = ',';
|
||||
public static final char COLON = ':';
|
||||
public static final char FORWARD_SLASH = '/';
|
||||
public static final String FORWARD_SLASH_STR = "/";
|
||||
public static final char BACK_SLASH = '\\';
|
||||
@@ -51,15 +52,17 @@ public final class Constants {
|
||||
public static final char RIGHT_SQUARE_BRACKET = ']';
|
||||
public static final char LEFT_BRACE = '{';
|
||||
public static final char RIGHT_BRACE = '}';
|
||||
public static final String LINE_SEPARATOR = System.lineSeparator();
|
||||
public static final String COMMA_SPACE = ", ";
|
||||
|
||||
public static final char LF = '\n';
|
||||
public static final char TAB = '\t';
|
||||
public static final char NUL = '\u0000';
|
||||
public static final String HTTP_PROTOCOL_PREFIX = "http://";
|
||||
|
||||
static final char c0 = SystemUtils.IS_OS_WINDOWS ? Constants.Symbol.BACK_SLASH : Constants.Symbol.FORWARD_SLASH;
|
||||
public static final char PATH_SEPARATOR = c0;
|
||||
public static final String LINE_SEPARATOR = System.lineSeparator();
|
||||
|
||||
public static final String COMMA_SPACE = ", ";
|
||||
public static final String HTTP_PROTOCOL_PREFIX = "http://";
|
||||
}
|
||||
|
||||
public static final class Charset {
|
||||
@@ -79,22 +82,28 @@ public final class Constants {
|
||||
public static final class Profiles {
|
||||
public static final String LOCAL = "local";
|
||||
public static final String DEV = "dev";
|
||||
public static final String TEST = "test";
|
||||
public static final String PREPROD = "preprod";
|
||||
public static final String PRE = "pre";
|
||||
public static final String PROD = "prod";
|
||||
|
||||
public static final String HTTP_SERVER = "http_server";
|
||||
public static final String HTTP_CLIENT = "http_client";
|
||||
public static final String MYSQL = "mysql";
|
||||
public static final String REDIS = "redis";
|
||||
public static final String CODIS = "codis";
|
||||
public static final String MONGO = "mongo";
|
||||
public static final String ACTIVEMQ = "activemq";
|
||||
public static final String KAFKA = "kafka";
|
||||
public static final String ELASTICSEARCH = "elasticsearch";
|
||||
public static final String SCHED = "sched";
|
||||
public static final String TEST = "test";
|
||||
public static final String FAT = "fat";
|
||||
|
||||
public static final String PREPROD = "preprod";
|
||||
public static final String UAT = "uat";
|
||||
public static final String PRE = "pre";
|
||||
|
||||
public static final String PROD = "prod";
|
||||
public static final String PRO = "pro";
|
||||
}
|
||||
|
||||
public static final String HTTP_SERVER = "http_server";
|
||||
public static final String HTTP_CLIENT = "http_client";
|
||||
public static final String MYSQL = "mysql";
|
||||
public static final String REDIS = "redis";
|
||||
public static final String CODIS = "codis";
|
||||
public static final String MONGO = "mongo";
|
||||
public static final String ACTIVEMQ = "activemq";
|
||||
public static final String KAFKA = "kafka";
|
||||
public static final String ELASTICSEARCH = "elasticsearch";
|
||||
public static final String SCHED = "sched";
|
||||
|
||||
public static final String BIZ_ID = "bizId";
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package we.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -111,4 +112,19 @@ public abstract class ThreadContext {
|
||||
public static Object remove(String key) {
|
||||
return getMap().remove(key);
|
||||
}
|
||||
|
||||
public static <T> ArrayList<T> getArrayList(String key, Class<T> elementType) {
|
||||
return getArrayList(key, elementType, true);
|
||||
}
|
||||
|
||||
public static <T> ArrayList<T> getArrayList(String key, Class<T> elementType, boolean clear) {
|
||||
ArrayList<T> l = (ArrayList) get(key);
|
||||
if (l == null) {
|
||||
l = new ArrayList<>();
|
||||
set(key, l);
|
||||
} else if (clear) {
|
||||
l.clear();
|
||||
}
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
306
src/main/java/we/util/UrlTransformUtils.java
Normal file
306
src/main/java/we/util/UrlTransformUtils.java
Normal file
@@ -0,0 +1,306 @@
|
||||
package we.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Fizz gateway url transform util class
|
||||
*
|
||||
* @author zhongjie
|
||||
*/
|
||||
public class UrlTransformUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UrlTransformUtils.class);
|
||||
|
||||
private UrlTransformUtils() {}
|
||||
|
||||
private static final FizzGatewayUrlAntPathMatcher ANT_PATH_MATCHER = new FizzGatewayUrlAntPathMatcher();
|
||||
|
||||
/**
|
||||
* transform the backend path to the real backend request path
|
||||
* @param frontendPath frontend path
|
||||
* @param backendPath backend path
|
||||
* @param reqPath request path
|
||||
* @return the transformed backend path
|
||||
* @throws IllegalStateException when the request path does not match the frontend path pattern
|
||||
* @throws IllegalArgumentException The number of capturing groups in the pattern segment does not match the number of URI template variables it defines
|
||||
*/
|
||||
public static String transform(String frontendPath, String backendPath, String reqPath) {
|
||||
Assert.hasText(frontendPath, "frontend path cannot be null");
|
||||
Assert.hasText(backendPath, "backend path cannot be null");
|
||||
Assert.hasText(reqPath, "req path cannot be null");
|
||||
String bp = backendPath;
|
||||
Map<String, String> variables = ANT_PATH_MATCHER.extractUriTemplateVariables(frontendPath, reqPath);
|
||||
for (Map.Entry<String, String> entry : variables.entrySet()) {
|
||||
backendPath = backendPath.replaceAll("\\{" + Matcher.quoteReplacement(entry.getKey()) + "}", Matcher.quoteReplacement(entry.getValue()));
|
||||
}
|
||||
|
||||
if (backendPath.indexOf('{') != -1) {
|
||||
backendPath = backendPath.replaceAll("\\{[^/]*}", "");
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("req: " + reqPath + ", frontend: " + frontendPath + ", backend: " + bp + ", target: " + backendPath);
|
||||
}
|
||||
|
||||
return backendPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义Ant风格路径匹配器
|
||||
* 设置默认路径分隔符为{@code #}
|
||||
* 使用{@link FizzGatewayUrlAntPathMatcher.FizzGatewayAntPathStringMatcher}设置自定义的参数变量值(额外返回变量名为$1...n的键值对)
|
||||
*
|
||||
* @author zhongjie
|
||||
*/
|
||||
static class FizzGatewayUrlAntPathMatcher extends AntPathMatcher {
|
||||
private static final String DEFAULT_PATH_SEPARATOR = "#";
|
||||
|
||||
private static final int CACHE_TURNOFF_THRESHOLD = 65536;
|
||||
|
||||
private volatile Boolean cachePatterns;
|
||||
|
||||
private final Map<String, String> replaceDoubleStarPatternCache = new ConcurrentHashMap<>(256);
|
||||
|
||||
private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<>(256);
|
||||
|
||||
final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<>(256);
|
||||
|
||||
private boolean caseSensitive = true;
|
||||
|
||||
private static AntPathMatcher DEFAULT_ANT_PATH_MATCHER = new AntPathMatcher();
|
||||
|
||||
public FizzGatewayUrlAntPathMatcher() {
|
||||
// 设置默认路径分隔符为#
|
||||
super(DEFAULT_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPathSeparator(String pathSeparator) {
|
||||
throw new RuntimeException("operation not support");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrimTokens(boolean trimTokens) {
|
||||
throw new RuntimeException("operation not support");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCaseSensitive(boolean caseSensitive) {
|
||||
super.setCaseSensitive(caseSensitive);
|
||||
this.caseSensitive = caseSensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCachePatterns(boolean cachePatterns) {
|
||||
super.setCachePatterns(cachePatterns);
|
||||
this.cachePatterns = cachePatterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AntPathStringMatcher getStringMatcher(String pattern) {
|
||||
AntPathStringMatcher matcher = null;
|
||||
Boolean cachePatterns = this.cachePatterns;
|
||||
if (cachePatterns == null || cachePatterns) {
|
||||
matcher = this.stringMatcherCache.get(pattern);
|
||||
}
|
||||
if (matcher == null) {
|
||||
matcher = new FizzGatewayAntPathStringMatcher(pattern, this.caseSensitive);
|
||||
if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
|
||||
// Try to adapt to the runtime situation that we're encountering:
|
||||
// There are obviously too many different patterns coming in here...
|
||||
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
|
||||
deactivatePatternCache();
|
||||
return matcher;
|
||||
}
|
||||
if (cachePatterns == null || cachePatterns) {
|
||||
this.stringMatcherCache.put(pattern, matcher);
|
||||
}
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] tokenizePattern(String pattern) {
|
||||
String[] tokenized = null;
|
||||
Boolean cachePatterns = this.cachePatterns;
|
||||
if (cachePatterns == null || cachePatterns) {
|
||||
tokenized = this.tokenizedPatternCache.get(pattern);
|
||||
}
|
||||
if (tokenized == null) {
|
||||
tokenized = tokenizePath(pattern);
|
||||
if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) {
|
||||
// Try to adapt to the runtime situation that we're encountering:
|
||||
// There are obviously too many different patterns coming in here...
|
||||
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
|
||||
deactivatePatternCache();
|
||||
return tokenized;
|
||||
}
|
||||
if (cachePatterns == null || cachePatterns) {
|
||||
this.tokenizedPatternCache.put(pattern, tokenized);
|
||||
}
|
||||
}
|
||||
return tokenized;
|
||||
}
|
||||
|
||||
private void deactivatePatternCache() {
|
||||
this.cachePatterns = false;
|
||||
this.tokenizedPatternCache.clear();
|
||||
this.stringMatcherCache.clear();
|
||||
this.replaceDoubleStarPatternCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractPathWithinPattern(String pattern, String path) {
|
||||
return DEFAULT_ANT_PATH_MATCHER.extractPathWithinPattern(pattern, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(String pattern1, String pattern2) {
|
||||
return DEFAULT_ANT_PATH_MATCHER.combine(pattern1, pattern2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
|
||||
String replaceDoubleStarPattern = null;
|
||||
if (pattern != null) {
|
||||
replaceDoubleStarPattern = getReplaceDoubleStarPattern(pattern);
|
||||
}
|
||||
return super.doMatch(replaceDoubleStarPattern, path, fullMatch, uriTemplateVariables);
|
||||
}
|
||||
|
||||
private String getReplaceDoubleStarPattern(String pattern) {
|
||||
String replaceDoubleStarPattern = null;
|
||||
Boolean cachePatterns = this.cachePatterns;
|
||||
if (cachePatterns == null || cachePatterns) {
|
||||
replaceDoubleStarPattern = this.replaceDoubleStarPatternCache.get(pattern);
|
||||
}
|
||||
if (replaceDoubleStarPattern == null) {
|
||||
// by-zhongjie 替换**为.*正则模式
|
||||
replaceDoubleStarPattern = pattern.replaceAll("/\\*\\*$", "/{\\$:.*}")
|
||||
.replaceAll("/\\*\\*/", "/{\\$:.*}/")
|
||||
.replaceAll("^\\*\\*/", "{\\$:.*}/");
|
||||
if (cachePatterns == null && this.replaceDoubleStarPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) {
|
||||
// Try to adapt to the runtime situation that we're encountering:
|
||||
// There are obviously too many different patterns coming in here...
|
||||
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
|
||||
deactivatePatternCache();
|
||||
return replaceDoubleStarPattern;
|
||||
}
|
||||
if (cachePatterns == null || cachePatterns) {
|
||||
this.replaceDoubleStarPatternCache.put(pattern, replaceDoubleStarPattern);
|
||||
}
|
||||
}
|
||||
return replaceDoubleStarPattern;
|
||||
}
|
||||
|
||||
protected static class FizzGatewayAntPathStringMatcher extends AntPathStringMatcher {
|
||||
// by-zhongjie 将 \?|\*|\{((?:\{[^/]+?\}|[^/{}]|\\[{}])+?)\} 改为 \?|\*|\{((?:\{[^/]+?\}|[^{}]|\\[{}])+?)\},排除/的限制
|
||||
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^{}]|\\\\[{}])+?)\\}");
|
||||
|
||||
// by-zhongjie 将 (.*) 改为 ([^/]*),限制变量只能匹配在非/的字符内
|
||||
private static final String DEFAULT_VARIABLE_PATTERN = "([^/]*)";
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
private final List<String> variableNames = new LinkedList<>();
|
||||
|
||||
// by-zhongjie 匿名占位符
|
||||
private final String ANONYMOUS_PLACEHOLDER = "$";
|
||||
|
||||
public FizzGatewayAntPathStringMatcher(String pattern) {
|
||||
this(pattern, true);
|
||||
}
|
||||
|
||||
public FizzGatewayAntPathStringMatcher(String pattern, boolean caseSensitive) {
|
||||
super(pattern, caseSensitive);
|
||||
StringBuilder patternBuilder = new StringBuilder();
|
||||
Matcher matcher = GLOB_PATTERN.matcher(pattern);
|
||||
int end = 0;
|
||||
while (matcher.find()) {
|
||||
patternBuilder.append(quote(pattern, end, matcher.start()));
|
||||
String match = matcher.group();
|
||||
if ("?".equals(match)) {
|
||||
// by-zhongjie 对 ? 也使用模式匹配
|
||||
patternBuilder.append('(');
|
||||
patternBuilder.append('.');
|
||||
patternBuilder.append(')');
|
||||
this.variableNames.add(ANONYMOUS_PLACEHOLDER);
|
||||
}
|
||||
else if ("*".equals(match)) {
|
||||
// by-zhongjie 对 * 也使用模式匹配
|
||||
patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
|
||||
this.variableNames.add(ANONYMOUS_PLACEHOLDER);
|
||||
}
|
||||
else if (match.startsWith("{") && match.endsWith("}")) {
|
||||
int colonIdx = match.indexOf(':');
|
||||
if (colonIdx == -1) {
|
||||
patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
|
||||
this.variableNames.add(matcher.group(1));
|
||||
}
|
||||
else {
|
||||
String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
|
||||
patternBuilder.append('(');
|
||||
patternBuilder.append(variablePattern);
|
||||
patternBuilder.append(')');
|
||||
String variableName = match.substring(1, colonIdx);
|
||||
this.variableNames.add(variableName);
|
||||
}
|
||||
}
|
||||
end = matcher.end();
|
||||
}
|
||||
patternBuilder.append(quote(pattern, end, pattern.length()));
|
||||
this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
|
||||
Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
|
||||
private String quote(String s, int start, int end) {
|
||||
if (start == end) {
|
||||
return "";
|
||||
}
|
||||
return Pattern.quote(s.substring(start, end));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean matchStrings(String str, @Nullable Map<String, String> uriTemplateVariables) {
|
||||
Matcher matcher = this.pattern.matcher(str);
|
||||
if (matcher.matches()) {
|
||||
if (uriTemplateVariables != null) {
|
||||
// SPR-8455
|
||||
if (this.variableNames.size() != matcher.groupCount()) {
|
||||
throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
|
||||
this.pattern + " does not match the number of URI template variables it defines, " +
|
||||
"which can occur if capturing groups are used in a URI template regex. " +
|
||||
"Use non-capturing groups instead.");
|
||||
}
|
||||
for (int i = 1; i <= matcher.groupCount(); i++) {
|
||||
String name = this.variableNames.get(i - 1);
|
||||
String value = matcher.group(i);
|
||||
|
||||
if (!ANONYMOUS_PLACEHOLDER.equals(name)) {
|
||||
uriTemplateVariables.put(name, value);
|
||||
}
|
||||
// by-zhongjie 对提取到的变量按序号输出
|
||||
uriTemplateVariables.put(ANONYMOUS_PLACEHOLDER + i, value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,10 @@ import reactor.core.publisher.Mono;
|
||||
import we.filter.FilterResult;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.legacy.RespEntity;
|
||||
import we.plugin.auth.ApiConfig;
|
||||
import we.plugin.auth.AuthPluginFilter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -42,40 +45,46 @@ import java.util.*;
|
||||
|
||||
public abstract class WebUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WebUtils.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(WebUtils.class);
|
||||
|
||||
public static final String APP_HEADER = "fizz-appid";
|
||||
private static final String clientService = "clientService";
|
||||
|
||||
private static final String directResponse = "directResponse";
|
||||
public static final String BACKEND_SERVICE = "backendService";
|
||||
|
||||
public static final String FILTER_CONTEXT = "filterContext";
|
||||
private static final String xForwardedFor = "X-FORWARDED-FOR";
|
||||
|
||||
public static final String APPEND_HEADERS = "appendHeaders";
|
||||
private static final String unknown = "unknown";
|
||||
|
||||
public static final String PREV_FILTER_RESULT = "prevFilterResult";
|
||||
private static final String loopBack = "127.0.0.1";
|
||||
|
||||
public static final String request_path = "reqPath";
|
||||
private static final String binaryAddress = "0:0:0:0:0:0:0:1";
|
||||
|
||||
private static final String SERVICE_ID = "serviceId";
|
||||
private static final String directResponse = "directResponse";
|
||||
|
||||
private static final String xForwardedFor = "X-FORWARDED-FOR";
|
||||
private static final String response = " response ";
|
||||
|
||||
private static final String unknown = "unknown";
|
||||
private static final String originIp = "originIp";
|
||||
|
||||
private static final String loopBack = "127.0.0.1";
|
||||
public static final String APP_HEADER = "fizz-appid";
|
||||
|
||||
private static final String binaryAddress = "0:0:0:0:0:0:0:1";
|
||||
public static final String FILTER_CONTEXT = "filterContext";
|
||||
|
||||
public static boolean logResponseBody = false;
|
||||
public static final String APPEND_HEADERS = "appendHeaders";
|
||||
|
||||
public static Set<String> logHeaderSet = Collections.EMPTY_SET;
|
||||
public static final String PREV_FILTER_RESULT = "prevFilterResult";
|
||||
|
||||
private static final String response = " response ";
|
||||
private static final String CLIENT_REQUEST_PATH = "clientRequestPath";
|
||||
|
||||
private static final String originIp = "originIp";
|
||||
private static final String CLIENT_REQUEST_QUERY = "clientRequestQuery";
|
||||
|
||||
public static final String BACKEND_PATH = "backendPath";
|
||||
|
||||
public static boolean logResponseBody = false;
|
||||
|
||||
public static Set<String> logHeaderSet = Collections.EMPTY_SET;
|
||||
|
||||
public static final String PATH_PREFIX = "/proxy/";
|
||||
|
||||
public static final String PATH_PREFIX = "/proxy/";
|
||||
|
||||
public static String getHeaderValue(ServerWebExchange exchange, String header) {
|
||||
return exchange.getRequest().getHeaders().getFirst(header);
|
||||
}
|
||||
@@ -88,8 +97,8 @@ public abstract class WebUtils {
|
||||
return exchange.getAttribute(APP_HEADER);
|
||||
}
|
||||
|
||||
public static String getServiceId(ServerWebExchange exchange) {
|
||||
String svc = exchange.getAttribute(SERVICE_ID);
|
||||
public static String getClientService(ServerWebExchange exchange) {
|
||||
String svc = exchange.getAttribute(clientService);
|
||||
if (svc == null) {
|
||||
String p = exchange.getRequest().getPath().value();
|
||||
int pl = p.length();
|
||||
@@ -114,18 +123,39 @@ public abstract class WebUtils {
|
||||
break;
|
||||
}
|
||||
}
|
||||
exchange.getAttributes().put(SERVICE_ID, svc);
|
||||
exchange.getAttributes().put(clientService, svc.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
return svc;
|
||||
}
|
||||
|
||||
public static String getPathPrefix(ServerWebExchange exchange) {
|
||||
String p = exchange.getRequest().getPath().value();
|
||||
return p.substring(0, p.indexOf(getServiceId(exchange)));
|
||||
}
|
||||
|
||||
public static void setBackendService(ServerWebExchange exchange, String service) {
|
||||
exchange.getAttributes().put(BACKEND_SERVICE, service);
|
||||
}
|
||||
|
||||
public static String getBackendService(ServerWebExchange exchange) {
|
||||
return exchange.getAttribute(BACKEND_SERVICE);
|
||||
}
|
||||
|
||||
public static byte getApiConfigType(ServerWebExchange exchange) {
|
||||
ApiConfig ac = getApiConfig(exchange);
|
||||
if (ac == null) {
|
||||
return ApiConfig.Type.UNDEFINED;
|
||||
} else {
|
||||
return ac.type;
|
||||
}
|
||||
}
|
||||
|
||||
public static ApiConfig getApiConfig(ServerWebExchange exchange) {
|
||||
Object authRes = getFilterResultDataItem(exchange, AuthPluginFilter.AUTH_PLUGIN_FILTER, AuthPluginFilter.RESULT);
|
||||
if (authRes != null && authRes instanceof ApiConfig) {
|
||||
return (ApiConfig) authRes;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Mono<Void> getDirectResponse(ServerWebExchange exchange) {
|
||||
return (Mono<Void>) exchange.getAttributes().get(WebUtils.directResponse);
|
||||
}
|
||||
@@ -217,28 +247,67 @@ public abstract class WebUtils {
|
||||
return getFilterContext(exchange).get(PREV_FILTER_RESULT);
|
||||
}
|
||||
|
||||
public static String getReqPath(ServerWebExchange exchange) {
|
||||
String path = exchange.getAttribute(request_path);
|
||||
public static String getClientReqPath(ServerWebExchange exchange) {
|
||||
String path = exchange.getAttribute(CLIENT_REQUEST_PATH);
|
||||
if (path == null) {
|
||||
path = exchange.getRequest().getPath().value();
|
||||
path = path.substring(path.indexOf(Constants.Symbol.FORWARD_SLASH, 11), path.length());
|
||||
exchange.getAttributes().put(request_path, path);
|
||||
exchange.getAttributes().put(CLIENT_REQUEST_PATH, path);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String getRelativeUri(ServerWebExchange exchange) {
|
||||
String relativeUri = getReqPath(exchange);
|
||||
String qry = exchange.getRequest().getURI().getQuery();
|
||||
if (qry != null) {
|
||||
if (StringUtils.indexOfAny(qry, Constants.Symbol.LEFT_BRACE, Constants.Symbol.FORWARD_SLASH, Constants.Symbol.HASH) > 0) {
|
||||
qry = exchange.getRequest().getURI().getRawQuery();
|
||||
public static void setBackendPath(ServerWebExchange exchange, String path) {
|
||||
exchange.getAttributes().put(BACKEND_PATH, path);
|
||||
}
|
||||
|
||||
public static String getBackendPath(ServerWebExchange exchange) {
|
||||
return exchange.getAttribute(BACKEND_PATH);
|
||||
}
|
||||
|
||||
public static String getClientReqPathPrefix(ServerWebExchange exchange) {
|
||||
String p = exchange.getRequest().getPath().value();
|
||||
return p.substring(0, p.indexOf(getClientService(exchange)));
|
||||
}
|
||||
|
||||
public static String getClientReqQuery(ServerWebExchange exchange) {
|
||||
String qry = exchange.getAttribute(CLIENT_REQUEST_QUERY);
|
||||
if (qry != null && StringUtils.EMPTY.equals(qry)) {
|
||||
return null;
|
||||
} else {
|
||||
if (qry == null) {
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
qry = uri.getQuery();
|
||||
if (qry == null) {
|
||||
exchange.getAttributes().put(CLIENT_REQUEST_QUERY, StringUtils.EMPTY);
|
||||
} else {
|
||||
if (StringUtils.indexOfAny(qry, Constants.Symbol.LEFT_BRACE, Constants.Symbol.FORWARD_SLASH, Constants.Symbol.HASH) > 0) {
|
||||
qry = uri.getRawQuery();
|
||||
}
|
||||
exchange.getAttributes().put(CLIENT_REQUEST_QUERY, qry);
|
||||
}
|
||||
}
|
||||
return qry;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getClientReqPathQuery(ServerWebExchange exchange) {
|
||||
String relativeUri = getClientReqPath(exchange);
|
||||
String qry = getClientReqQuery(exchange);
|
||||
if (qry != null) {
|
||||
relativeUri = relativeUri + Constants.Symbol.QUESTION + qry;
|
||||
}
|
||||
return relativeUri;
|
||||
}
|
||||
|
||||
public static String appendQuery(String path, ServerWebExchange exchange) {
|
||||
String qry = getClientReqQuery(exchange);
|
||||
if (qry != null) {
|
||||
return path + Constants.Symbol.QUESTION + qry;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static Map<String, String> getAppendHeaders(ServerWebExchange exchange) {
|
||||
return (Map<String, String>) exchange.getAttributes().get(APPEND_HEADERS);
|
||||
}
|
||||
|
||||
@@ -82,4 +82,7 @@ log:
|
||||
|
||||
stat:
|
||||
# switch for push access stat data
|
||||
open: true
|
||||
send-log:
|
||||
# switch for push log data
|
||||
open: true
|
||||
@@ -1,17 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<Configuration status="info">
|
||||
<Configuration status="info" packages="we.log">
|
||||
<properties>
|
||||
<property name="APP_NAME">${sys:APP_NAME}</property>
|
||||
</properties>
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %msg%n" />
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %X{traceId} %msg%n" />
|
||||
</Console>
|
||||
<LogSend name="LogSend">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %msg%n" />
|
||||
</LogSend>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console" />
|
||||
<AppenderRef ref="LogSend" />
|
||||
</Root>
|
||||
<Logger name="we" level="DEBUG"/>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
|
||||
Reference in New Issue
Block a user