by su 3 лет назад
Родитель
Сommit
337f1283e4

+ 3 - 1
.gitignore

@@ -32,6 +32,8 @@ hs_err_pid*
 *.iml
 *.iml
 out
 out
 gen
 gen
-#/4dkankan-agent/target/
+/4dkankan-center-scene/target/
+/4dkankan-common/target/
+/4dkankan-gateway/target/
 
 
 
 

+ 70 - 0
4dkankan-center-scene/pom.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.fdkankan</groupId>
+        <artifactId>4dkankan-parent</artifactId>
+        <version>1.0.0</version>
+    </parent>
+
+    <groupId>com.fdkankan</groupId>
+    <artifactId>4dkankan-center-scene</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.fdkankan</groupId>
+            <artifactId>4dkankan-common</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <scope>runtime</scope>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 29 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/SceneApplication.java

@@ -0,0 +1,29 @@
+package com.fdkankan.scene;
+
+import com.fdkankan.common.util.StrExtUtil;
+import com.fdkankan.scene.dto.TestDto;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@SpringBootApplication
+@RestController
+public class SceneApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(SceneApplication.class, args);
+    }
+
+    @PostMapping("/test/test")
+    public TestDto test(@RequestBody TestDto test){
+        System.out.println("进入了");
+        int i = 1;
+        StrExtUtil.isNotBlank("123");
+        i = i/0;
+        return test;
+    }
+
+}

+ 10 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/dto/TestDto.java

@@ -0,0 +1,10 @@
+package com.fdkankan.scene.dto;
+
+import lombok.Data;
+
+@Data
+public class TestDto {
+
+    private int id;
+    private String name;
+}

+ 2 - 0
4dkankan-center-scene/src/main/resources/application.properties

@@ -0,0 +1,2 @@
+server.port=8090
+spring.application.name=center-scene

+ 13 - 0
4dkankan-center-scene/src/test/java/com/fdkankan/scene/SceneApplicationTests.java

@@ -0,0 +1,13 @@
+package com.fdkankan.scene;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class ApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}

+ 32 - 0
4dkankan-common/pom.xml

@@ -11,6 +11,7 @@
 
 
     <groupId>com.fdkankan</groupId>
     <groupId>com.fdkankan</groupId>
     <artifactId>4dkankan-common</artifactId>
     <artifactId>4dkankan-common</artifactId>
+    <version>1.0.0</version>
 
 
     <dependencies>
     <dependencies>
 
 
@@ -19,7 +20,38 @@
             <artifactId>hutool-all</artifactId>
             <artifactId>hutool-all</artifactId>
         </dependency>
         </dependency>
 
 
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
     </dependencies>
     </dependencies>
 
 
+    <build>
+        <plugins>
+                        <plugin>
+                            <groupId>org.springframework.boot</groupId>
+                            <artifactId>spring-boot-maven-plugin</artifactId>
+                            <configuration>
+                                <skip>true</skip>
+                            </configuration>
+                        </plugin>
+<!--            <plugin>-->
+<!--                <groupId>org.apache.maven.plugins</groupId>-->
+<!--                <artifactId>maven-compiler-plugin</artifactId>-->
+<!--            </plugin>-->
+
+<!--            <plugin>-->
+<!--                <groupId>org.apache.maven.plugins</groupId>-->
+<!--                <artifactId>maven-surefire-plugin</artifactId>-->
+<!--                <version>${maven-surefire-plugin.version}</version>-->
+<!--                <configuration>-->
+<!--                    <skipTests>true</skipTests>    &lt;!&ndash;默认关掉单元测试 &ndash;&gt;-->
+<!--                </configuration>-->
+<!--            </plugin>-->
+        </plugins>
+    </build>
+
 
 
 </project>
 </project>

+ 41 - 0
4dkankan-common/src/main/java/com/fdkankan/common/response/ResultData.java

@@ -0,0 +1,41 @@
+package com.fdkankan.common.response;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+@Data
+@NoArgsConstructor
+public class ResultData<T> implements Serializable {
+    /**
+     * 状态码
+     */
+    private int status;
+    /**
+     * 响应信息
+     */
+    private String message;
+    /**
+     * 后端返回结果
+     */
+    private T data;
+    /**
+     * 后端响应状态
+     */
+    private boolean success;
+    /**
+     * 响应时间戳
+     */
+    private long timestamp ;
+
+    public ResultData(int status, String message){
+        this.status = status;
+        this.message = message;
+    }
+
+    public static <T> ResultData<T> fail(int status, String message){
+        return  new ResultData<>(status, message);
+    }
+
+}

+ 16 - 1
4dkankan-gateway/pom.xml

@@ -17,9 +17,20 @@
         <java.version>1.8</java.version>
         <java.version>1.8</java.version>
     </properties>
     </properties>
     <dependencies>
     <dependencies>
+
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-starter-web</artifactId>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>com.fdkankan</groupId>
+            <artifactId>4dkankan-common</artifactId>
+        </dependency>
+
         <dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
+            <artifactId>spring-boot-starter</artifactId>
         </dependency>
         </dependency>
 
 
         <dependency>
         <dependency>
@@ -38,6 +49,10 @@
             <artifactId>spring-boot-starter-test</artifactId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
+        </dependency>
     </dependencies>
     </dependencies>
 
 
     <build>
     <build>

+ 60 - 0
4dkankan-gateway/src/main/java/com/fdkankan/gateway/config/ExceptionHandlerConfiguration.java

@@ -0,0 +1,60 @@
+//package com.fdkankan.gateway.config;
+//
+//import com.fdkankan.gateway.exception.JsonExceptionHandler;
+//import org.springframework.beans.factory.ObjectProvider;
+//import org.springframework.boot.autoconfigure.web.ResourceProperties;
+//import org.springframework.boot.autoconfigure.web.ServerProperties;
+//import org.springframework.boot.context.properties.EnableConfigurationProperties;
+//import org.springframework.boot.web.reactive.error.ErrorAttributes;
+//import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
+//import org.springframework.context.ApplicationContext;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.core.Ordered;
+//import org.springframework.core.annotation.Order;
+//import org.springframework.http.codec.ServerCodecConfigurer;
+//import org.springframework.web.reactive.result.view.ViewResolver;
+//
+//import java.util.Collections;
+//import java.util.List;
+//
+//@Configuration
+//@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
+//public class ExceptionHandlerConfiguration {
+//
+//    private final ServerProperties serverProperties;
+//
+//    private final ApplicationContext applicationContext;
+//
+//    private final ResourceProperties resourceProperties;
+//
+//    private final List<ViewResolver> viewResolvers;
+//
+//    private final ServerCodecConfigurer serverCodecConfigurer;
+//
+//    public ExceptionHandlerConfiguration(ServerProperties serverProperties,
+//                                         ResourceProperties resourceProperties,
+//                                         ObjectProvider<List<ViewResolver>> viewResolversProvider,
+//                                         ServerCodecConfigurer serverCodecConfigurer,
+//                                         ApplicationContext applicationContext) {
+//        this.serverProperties = serverProperties;
+//        this.applicationContext = applicationContext;
+//        this.resourceProperties = resourceProperties;
+//        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
+//        this.serverCodecConfigurer = serverCodecConfigurer;
+//    }
+//
+//    @Bean
+//    @Order(Ordered.HIGHEST_PRECEDENCE)
+//    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
+//        JsonExceptionHandler exceptionHandler = new JsonExceptionHandler(
+//                errorAttributes,
+//                this.resourceProperties,
+//                this.serverProperties.getError(),
+//                this.applicationContext);
+//        exceptionHandler.setViewResolvers(this.viewResolvers);
+//        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
+//        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
+//        return exceptionHandler;
+//    }
+//}

+ 53 - 0
4dkankan-gateway/src/main/java/com/fdkankan/gateway/exception/GlobalErrorWebExceptionHandler.java

@@ -0,0 +1,53 @@
+package com.fdkankan.gateway.exception;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fdkankan.common.response.ResultData;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.server.ResponseStatusException;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+@Slf4j
+@Order(-1)
+@Configuration
+@RequiredArgsConstructor(onConstructor = @__(@Autowired))
+public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {
+
+    private final ObjectMapper objectMapper;
+
+    @Override
+    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
+        ServerHttpResponse response = exchange.getResponse();
+        if (response.isCommitted()) {
+            return Mono.error(ex);
+        }
+
+        // 设置返回JSON
+        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
+
+        if (ex instanceof ResponseStatusException) {
+            response.setStatusCode(((ResponseStatusException) ex).getStatus());
+        }
+
+        return response.writeWith(Mono.fromSupplier(() -> {
+            DataBufferFactory bufferFactory = response.bufferFactory();
+            try {
+                //返回响应结果
+                return bufferFactory.wrap(objectMapper.writeValueAsBytes(ResultData.fail(500,ex.getMessage())));
+            }
+            catch (JsonProcessingException e) {
+                log.error("Error writing response", ex);
+                return bufferFactory.wrap(new byte[0]);
+            }
+        }));
+    }
+}

+ 89 - 0
4dkankan-gateway/src/main/java/com/fdkankan/gateway/exception/JsonExceptionHandler.java

@@ -0,0 +1,89 @@
+//package com.fdkankan.gateway.exception;
+//
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.boot.autoconfigure.web.ErrorProperties;
+//import org.springframework.boot.autoconfigure.web.ResourceProperties;
+//import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
+//import org.springframework.boot.web.reactive.error.ErrorAttributes;
+//import org.springframework.context.ApplicationContext;
+//import org.springframework.http.HttpStatus;
+//import org.springframework.web.reactive.function.server.*;
+//
+//import java.util.HashMap;
+//import java.util.Map;
+//
+//@Slf4j
+//public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {
+//
+//    public JsonExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
+//                                ErrorProperties errorProperties, ApplicationContext applicationContext) {
+//        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
+//    }
+//
+//    /**
+//     * 获取异常属性
+//     */
+//    @Override
+//    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
+//        int code = HttpStatus.INTERNAL_SERVER_ERROR.value();
+//        Throwable error = super.getError(request);
+//        if (error instanceof org.springframework.cloud.gateway.support.NotFoundException) {
+//            code = HttpStatus.NOT_FOUND.value();
+//        }
+//        return response(code, this.buildMessage(request, error));
+//    }
+//
+//    /**
+//     * 指定响应处理方法为JSON处理的方法
+//     * @param errorAttributes
+//     */
+//    @Override
+//    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
+//        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
+//    }
+//
+//
+//    /**
+//     * 根据code获取对应的HttpStatus
+//     * @param errorAttributes
+//     */
+//    @Override
+//    protected int getHttpStatus(Map<String, Object> errorAttributes) {
+//        int statusCode = (int) errorAttributes.get("status");
+//        return statusCode;
+//    }
+//
+//    /**
+//     * 构建异常信息
+//     * @param request
+//     * @param ex
+//     * @return
+//     */
+//    private String buildMessage(ServerRequest request, Throwable ex) {
+//        StringBuilder message = new StringBuilder("Failed to handle request [");
+//        message.append(request.methodName());
+//        message.append(" ");
+//        message.append(request.uri());
+//        message.append("]");
+//        if (ex != null) {
+//            message.append(": ");
+//            message.append(ex.getMessage());
+//        }
+//        return message.toString();
+//    }
+//
+//    /**
+//     * 构建返回的JSON数据格式
+//     * @param status        状态码
+//     * @param errorMessage  异常信息
+//     * @return
+//     */
+//    public static Map<String, Object> response(int status, String errorMessage) {
+//        Map<String, Object> map = new HashMap<>();
+//        map.put("code", status);
+//        map.put("message", errorMessage);
+//        map.put("data", null);
+//        log.error(map.toString());
+//        return map;
+//    }
+//}

+ 248 - 0
4dkankan-gateway/src/main/java/com/fdkankan/gateway/filter/AccessLogFilter.java

@@ -0,0 +1,248 @@
+package com.fdkankan.gateway.filter;
+
+import cn.hutool.core.util.StrUtil;
+import com.fdkankan.gateway.util.WebUtil;
+import com.fdkankan.gateway.log.GatewayLog;
+import com.sun.deploy.util.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.reactivestreams.Publisher;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
+import org.springframework.cloud.gateway.route.Route;
+import org.springframework.cloud.gateway.support.BodyInserterContext;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.core.io.buffer.DefaultDataBufferFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.codec.HttpMessageReader;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
+import org.springframework.stereotype.Component;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.HandlerStrategies;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+public class AccessLogFilter  implements GlobalFilter, Ordered {
+
+    private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
+
+    @Override
+    public int getOrder() {
+        return -100;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+
+        ServerHttpRequest request = exchange.getRequest();
+        // 请求路径
+        String requestPath = request.getPath().pathWithinApplication().value();
+
+        Route route = getGatewayRoute(exchange);
+
+
+        String ipAddress = WebUtil.getIpAddress(request);
+
+        GatewayLog gatewayLog = new GatewayLog();
+        gatewayLog.setSchema(request.getURI().getScheme());
+        gatewayLog.setRequestMethod(request.getMethodValue());
+        gatewayLog.setRequestPath(requestPath);
+        gatewayLog.setTargetServer(route.getId());
+        gatewayLog.setRequestTime(new Date());
+        gatewayLog.setIp(ipAddress);
+
+        MediaType mediaType = request.getHeaders().getContentType();
+
+        if(MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)){
+            return writeBodyLog(exchange, chain, gatewayLog);
+        }else{
+            return writeBasicLog(exchange, chain, gatewayLog);
+        }
+    }
+
+    private Mono<Void> writeBasicLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog accessLog) {
+        StringBuilder builder = new StringBuilder();
+        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
+        for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
+            builder.append(entry.getKey()).append("=").append(StringUtils.join(entry.getValue(), ","));
+        }
+        accessLog.setRequestBody(builder.toString());
+
+        //获取响应体
+        ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog);
+
+        return chain.filter(exchange.mutate().response(decoratedResponse).build())
+                .then(Mono.fromRunnable(() -> {
+                    // 打印日志
+                    writeAccessLog(accessLog);
+                }));
+    }
+
+
+    /**
+     * 解决 request body 只能读取一次问题,
+     * 参考: org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory
+     * @param exchange
+     * @param chain
+     * @param gatewayLog
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    private Mono writeBodyLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog gatewayLog) {
+        ServerRequest serverRequest = ServerRequest.create(exchange,messageReaders);
+
+        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
+                .flatMap(body ->{
+                    gatewayLog.setRequestBody(body);
+                    return Mono.just(body);
+                });
+
+        // 通过 BodyInserter 插入 body(支持修改body), 避免 request body 只能获取一次
+        BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
+        HttpHeaders headers = new HttpHeaders();
+        headers.putAll(exchange.getRequest().getHeaders());
+        // the new content type will be computed by bodyInserter
+        // and then set in the request decorator
+        headers.remove(HttpHeaders.CONTENT_LENGTH);
+
+        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
+
+        return bodyInserter.insert(outputMessage,new BodyInserterContext())
+                .then(Mono.defer(() -> {
+                    // 重新封装请求
+                    ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);
+
+                    // 记录响应日志
+                    ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, gatewayLog);
+
+                    // 记录普通的
+                    return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build())
+                            .then(Mono.fromRunnable(() -> {
+                                // 打印日志
+                                writeAccessLog(gatewayLog);
+                            }));
+                }));
+    }
+
+    /**
+     * 打印日志
+     * @author javadaily
+     * @date 2021/3/24 14:53
+     * @param gatewayLog 网关日志
+     */
+    private void writeAccessLog(GatewayLog gatewayLog) {
+        log.info(gatewayLog.toString());
+    }
+
+
+
+    private Route getGatewayRoute(ServerWebExchange exchange) {
+        return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
+    }
+
+
+    /**
+     * 请求装饰器,重新计算 headers
+     * @param exchange
+     * @param headers
+     * @param outputMessage
+     * @return
+     */
+    private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers,
+                                                       CachedBodyOutputMessage outputMessage) {
+        return new ServerHttpRequestDecorator(exchange.getRequest()) {
+            @Override
+            public HttpHeaders getHeaders() {
+                long contentLength = headers.getContentLength();
+                HttpHeaders httpHeaders = new HttpHeaders();
+                httpHeaders.putAll(super.getHeaders());
+                if (contentLength > 0) {
+                    httpHeaders.setContentLength(contentLength);
+                } else {
+                    // TODO: this causes a 'HTTP/1.1 411 Length Required' // on
+                    // httpbin.org
+                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
+                }
+                return httpHeaders;
+            }
+
+            @Override
+            public Flux<DataBuffer> getBody() {
+                return outputMessage.getBody();
+            }
+        };
+    }
+
+
+    /**
+     * 记录响应日志
+     * 通过 DataBufferFactory 解决响应体分段传输问题。
+     */
+    private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, GatewayLog gatewayLog) {
+        ServerHttpResponse response = exchange.getResponse();
+        DataBufferFactory bufferFactory = response.bufferFactory();
+        return new ServerHttpResponseDecorator(response) {
+
+            @Override
+            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
+                if (body instanceof Flux) {
+                        Date responseTime = new Date();
+                    gatewayLog.setResponseTime(responseTime);
+                    // 计算执行时间
+                    long executeTime = (responseTime.getTime() - gatewayLog.getRequestTime().getTime());
+
+                    gatewayLog.setExecuteTime(executeTime);
+
+                    // 获取响应类型,如果是 json 就打印
+                    String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
+
+                    if (this.getStatusCode().equals(HttpStatus.OK)
+                            && StrUtil.isNotBlank(originalResponseContentType)
+                            && originalResponseContentType.contains("application/json")) {
+
+                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);
+                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
+
+                            // 合并多个流集合,解决返回体分段传输
+                            DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
+                            DataBuffer join = dataBufferFactory.join(dataBuffers);
+                            byte[] content = new byte[join.readableByteCount()];
+                            join.read(content);
+
+                            // 释放掉内存
+                            DataBufferUtils.release(join);
+                            String responseResult = new String(content, StandardCharsets.UTF_8);
+
+                            gatewayLog.setResponseData(responseResult);
+
+                            return bufferFactory.wrap(content);
+                        }));
+                    }
+                }
+                // if body is not a flux. never got there.
+                return super.writeWith(body);
+            }
+        };
+    }
+}

+ 31 - 0
4dkankan-gateway/src/main/java/com/fdkankan/gateway/log/GatewayLog.java

@@ -0,0 +1,31 @@
+package com.fdkankan.gateway.log;
+
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class GatewayLog {
+
+    /**访问实例*/
+    private String targetServer;
+    /**请求路径*/
+    private String requestPath;
+    /**请求方法*/
+    private String requestMethod;
+    /**协议 */
+    private String schema;
+    /**请求体*/
+    private String requestBody;
+    /**响应体*/
+    private String responseData;
+    /**请求ip*/
+    private String ip;
+    /**请求时间*/
+    private Date requestTime;
+    /**响应时间*/
+    private Date responseTime;
+    /**执行时间*/
+    private long executeTime;
+
+}

+ 38 - 0
4dkankan-gateway/src/main/java/com/fdkankan/gateway/util/WebUtil.java

@@ -0,0 +1,38 @@
+package com.fdkankan.gateway.util;
+
+import cn.hutool.core.util.StrUtil;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+
+public class WebUtil {
+
+    private final static String UNKNOWN = "unknown";
+
+    /**
+     * 获取客户端请求终端地址
+     * @param request  客户端请求request
+     * @return  终端ip地址
+     */
+    public static String getIpAddress(ServerHttpRequest request) {
+        HttpHeaders headers = request.getHeaders();
+        String ip = headers.getFirst("X-Forwarded-For");
+        if(StrUtil.isNotBlank(ip) && UNKNOWN.equalsIgnoreCase(ip)){
+            if(ip.indexOf(",") != -1){
+                ip = ip.split(",")[0];
+                return ip;
+            }
+        }
+        ip = headers.getFirst("Proxy-Client-IP");
+        if(StrUtil.isNotBlank(ip)) return ip;
+        ip = headers.getFirst("WL-Proxy-Client-IP");
+        if(StrUtil.isNotBlank(ip)) return ip;
+        ip = headers.getFirst("HTTP-CLIENT-IP");
+        if(StrUtil.isNotBlank(ip)) return ip;
+        ip = headers.getFirst("HTTP-X-FORWARDED-FOR");
+        if(StrUtil.isNotBlank(ip)) return ip;
+        ip = headers.getFirst("X-Real-IP");
+        if(StrUtil.isNotBlank(ip)) return ip;
+        ip = request.getRemoteAddress().getAddress().getHostAddress();
+        return ip;
+    }
+}

+ 0 - 1
4dkankan-gateway/src/main/resources/application.properties

@@ -1 +0,0 @@
-

+ 8 - 0
4dkankan-gateway/src/main/resources/application.yml

@@ -0,0 +1,8 @@
+spring:
+  cloud:
+    gateway:
+      routes:
+        - id: route-center-scene
+          uri: http://localhost:8090
+          predicates:
+            - Path=/test/*

+ 2 - 0
4dkankan-gateway/src/main/resources/bootstrap.properties

@@ -0,0 +1,2 @@
+server.port=8080
+spring.application.name=4dkankan-gateway

+ 12 - 0
pom.xml

@@ -5,6 +5,8 @@
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
     <modules>
     <modules>
         <module>4dkankan-common</module>
         <module>4dkankan-common</module>
+        <module>4dkankan-gateway</module>
+        <module>4dkankan-center-scene</module>
     </modules>
     </modules>
 
 
     <parent>
     <parent>
@@ -23,6 +25,7 @@
         <java.version>1.8</java.version>
         <java.version>1.8</java.version>
         <hutool-version>5.7.17</hutool-version>
         <hutool-version>5.7.17</hutool-version>
         <spring.cloud-version>Hoxton.SR8</spring.cloud-version>
         <spring.cloud-version>Hoxton.SR8</spring.cloud-version>
+        <fdkankan.common-version>1.0.0</fdkankan.common-version>
     </properties>
     </properties>
 
 
     <dependencies>
     <dependencies>
@@ -50,6 +53,12 @@
                 <version>${hutool-version}</version>
                 <version>${hutool-version}</version>
             </dependency>
             </dependency>
 
 
+            <dependency>
+                <groupId>com.fdkankan</groupId>
+                <artifactId>4dkankan-common</artifactId>
+                <version>${fdkankan.common-version}</version>
+            </dependency>
+
         </dependencies>
         </dependencies>
     </dependencyManagement>
     </dependencyManagement>
 
 
@@ -59,6 +68,9 @@
             <plugin>
             <plugin>
                 <groupId>org.springframework.boot</groupId>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
             </plugin>
             </plugin>
 <!--            <plugin>-->
 <!--            <plugin>-->
 <!--                <groupId>org.apache.maven.plugins</groupId>-->
 <!--                <groupId>org.apache.maven.plugins</groupId>-->