danchaofan1412 5 年 前
コミット
90dcd55646
50 ファイル変更3184 行追加0 行削除
  1. 118 0
      framework-boot/pom.xml
  2. 7 0
      framework-boot/src/main/java/com/mrxu/framework/boot/MrxuConst.java
  3. 27 0
      framework-boot/src/main/java/com/mrxu/framework/boot/entity/BaseCode.java
  4. 9 0
      framework-boot/src/main/java/com/mrxu/framework/boot/entity/BaseEntity.java
  5. 42 0
      framework-boot/src/main/java/com/mrxu/framework/boot/entity/BusinessException.java
  6. 22 0
      framework-boot/src/main/java/com/mrxu/framework/boot/entity/LayuiPage.java
  7. 34 0
      framework-boot/src/main/java/com/mrxu/framework/boot/entity/PageParam.java
  8. 33 0
      framework-boot/src/main/java/com/mrxu/framework/boot/entity/PageResult.java
  9. 109 0
      framework-boot/src/main/java/com/mrxu/framework/boot/entity/ResponseObj.java
  10. 36 0
      framework-boot/src/main/java/com/mrxu/framework/boot/handle/FeignErrorHandler.java
  11. 15 0
      framework-boot/src/main/java/com/mrxu/framework/boot/handle/FreemarkerExceptionHandler.java
  12. 94 0
      framework-boot/src/main/java/com/mrxu/framework/boot/handle/WebExceptionHandler.java
  13. 30 0
      framework-boot/src/main/java/com/mrxu/framework/boot/starter/MybatisPlusConfig.java
  14. 71 0
      framework-boot/src/main/java/com/mrxu/framework/boot/starter/SwaggerConfig.java
  15. 24 0
      framework-boot/src/main/java/com/mrxu/framework/boot/util/MrxuAssert.java
  16. 117 0
      framework-boot/src/main/java/com/mrxu/framework/boot/web/BaseController.java
  17. 151 0
      framework-boot/src/main/java/com/mrxu/framework/boot/web/ServletUtils.java
  18. 30 0
      framework-common/pom.xml
  19. 4 0
      framework-common/src/main/java/com/mrxu/framework/common/util/DateFunc.java
  20. 87 0
      framework-common/src/main/java/com/mrxu/framework/common/util/StrFunc.java
  21. 144 0
      framework-gateway/pom.xml
  22. 26 0
      framework-gateway/src/main/java/com/micro/cloud/GatewayServiceApplication.java
  23. 38 0
      framework-gateway/src/main/java/com/micro/cloud/core/config/GlobalConfiguration.java
  24. 7 0
      framework-gateway/src/main/java/com/micro/cloud/core/constans/Code.java
  25. 52 0
      framework-gateway/src/main/java/com/micro/cloud/core/constans/CommonMessage.java
  26. 60 0
      framework-gateway/src/main/java/com/micro/cloud/core/constans/GatewayConstans.java
  27. 132 0
      framework-gateway/src/main/java/com/micro/cloud/core/constans/GatewayMessage.java
  28. 13 0
      framework-gateway/src/main/java/com/micro/cloud/core/constans/MsgCode.java
  29. 39 0
      framework-gateway/src/main/java/com/micro/cloud/core/http/request/Request.java
  30. 86 0
      framework-gateway/src/main/java/com/micro/cloud/core/http/response/Response.java
  31. 34 0
      framework-gateway/src/main/java/com/micro/cloud/excetions/GatewayException.java
  32. 87 0
      framework-gateway/src/main/java/com/micro/cloud/gateway/config/FilterConfiguration.java
  33. 160 0
      framework-gateway/src/main/java/com/micro/cloud/gateway/filters/RateLimitFilter.java
  34. 508 0
      framework-gateway/src/main/java/com/micro/cloud/gateway/filters/RouteRequestFilter.java
  35. 49 0
      framework-gateway/src/main/java/com/micro/cloud/gateway/filters/TokenAuthRequestFilter.java
  36. 30 0
      framework-gateway/src/main/java/com/micro/cloud/ratelimit/Policy.java
  37. 35 0
      framework-gateway/src/main/java/com/micro/cloud/ratelimit/Rate.java
  38. 54 0
      framework-gateway/src/main/java/com/micro/cloud/ratelimit/RateLimitAutoConfiguration.java
  39. 29 0
      framework-gateway/src/main/java/com/micro/cloud/ratelimit/RateLimitProperties.java
  40. 12 0
      framework-gateway/src/main/java/com/micro/cloud/ratelimit/RateLimiter.java
  41. 72 0
      framework-gateway/src/main/java/com/micro/cloud/ratelimit/repository/MemoryRateLimiter.java
  42. 41 0
      framework-gateway/src/main/java/com/micro/cloud/ratelimit/repository/RedisRateLimiter.java
  43. 85 0
      framework-gateway/src/main/java/com/micro/cloud/utils/HttpServletReqestUtil.java
  44. 112 0
      framework-gateway/src/main/resources/application-dev.yml
  45. 112 0
      framework-gateway/src/main/resources/application-local.yml
  46. 3 0
      framework-gateway/src/main/resources/application.yml
  47. 48 0
      framework-gateway/src/main/resources/config/logback.xml
  48. 13 0
      framework-gateway/src/test/java/com/micro/cloud/GatewayServiceApplicationTests.java
  49. 19 0
      framework-starter/pom.xml
  50. 24 0
      pom.xml

+ 118 - 0
framework-boot/pom.xml

@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.mrxu</groupId>
+    <artifactId>framework-boot</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>framework-boot</name>
+    <description>springboot 框架级别封装</description>
+
+    <parent>
+        <groupId>com.mrxu</groupId>
+        <artifactId>mrxu-upgrader</artifactId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>com.mrxu</groupId>
+            <artifactId>framework-common</artifactId>
+        </dependency>
+
+        <!--springboot 相关的依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
+        <!--springcloud 相关依赖-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
+        </dependency>
+        <!-- spring cloud 引用 web service 客户端 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.retry</groupId>
+            <artifactId>spring-retry</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.github.openfeign.form</groupId>
+            <artifactId>feign-form-spring</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.6.2</version>
+        </dependency>
+
+        <!--数据库相关依赖-->
+        <!-- mysql -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <!-- mybatis-plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+
+        <!--消除冗余代码使用-->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <!--数据验证-->
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+        <!--swagger 文档-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 7 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/MrxuConst.java

@@ -0,0 +1,7 @@
+package com.mrxu.framework.boot;
+
+public class MrxuConst {
+
+    public final static long MAX_PAGE_SIZE = 100;
+
+}

+ 27 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/entity/BaseCode.java

@@ -0,0 +1,27 @@
+package com.mrxu.framework.boot.entity;
+
+import lombok.Data;
+
+@Data
+public class BaseCode {
+
+    public final static BaseCode OK = new BaseCode(0,"成功");
+    public final static BaseCode ERR_NOTAUTH = new BaseCode(401,"未登录或登录过期");
+    public final static BaseCode ERR_FORBIDDEN = new BaseCode(403,"无访问权限");
+    public final static BaseCode ERROR = new BaseCode(500,"系统错误");
+    public final static BaseCode ERR_PARAMS_MISS = new BaseCode(510,"缺少必要参数");
+    public final static BaseCode ERR_PARAMS_VALID = new BaseCode(511,"参数格式错误");
+
+    public int code = 200;
+
+    public String msg = "成功";
+
+    public BaseCode(int code,String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public BaseCode() {
+    }
+
+}

+ 9 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/entity/BaseEntity.java

@@ -0,0 +1,9 @@
+package com.mrxu.framework.boot.entity;
+
+import java.io.Serializable;
+
+public class BaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+}

+ 42 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/entity/BusinessException.java

@@ -0,0 +1,42 @@
+package com.mrxu.framework.boot.entity;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class BusinessException extends RuntimeException implements Serializable {
+
+    private static final long serialVersionUID = -1L;
+
+    private int code;
+
+    private String msg;
+
+    public BusinessException(BaseCode baseCode) {
+        super(baseCode.getMsg());
+        this.code = baseCode.getCode();
+        this.msg = baseCode.getMsg();
+    }
+
+    public BusinessException(BaseCode baseCode, Exception e) {
+        super(baseCode.getMsg(), e);
+        this.code = baseCode.getCode();
+        this.msg = baseCode.getMsg();
+    }
+
+    public BusinessException(BaseCode baseCode, String errMsg) {
+        super(errMsg);
+        this.code = baseCode.getCode();
+        this.msg = errMsg;
+    }
+
+    public BusinessException(String errMsg) {
+        super(errMsg);
+        this.code = BaseCode.ERROR.getCode();
+        this.msg = errMsg;
+    }
+
+}

+ 22 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/entity/LayuiPage.java

@@ -0,0 +1,22 @@
+package com.mrxu.framework.boot.entity;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@ApiModel(description = "layui 分页需要的数据格式")
+@Data
+public class LayuiPage<T> implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private int code;
+
+    private String msg;
+
+    private long count;
+
+    private List<T> data;
+}

+ 34 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/entity/PageParam.java

@@ -0,0 +1,34 @@
+package com.mrxu.framework.boot.entity;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.mrxu.framework.boot.MrxuConst;
+import com.mrxu.framework.boot.util.MrxuAssert;
+import io.swagger.annotations.ApiModelProperty;
+
+public class PageParam <T> extends Page<T> {
+
+    //重写父类方法,加数据验证
+    @Override
+    public Page<T> setSize(long size) {
+        MrxuAssert.isTrue(size<= MrxuConst.MAX_PAGE_SIZE, "每页大小不能大于"+MrxuConst.MAX_PAGE_SIZE);
+        super.setSize(size);
+        return this;
+    }
+
+    public void setPageNum(long pageNum) {
+        this.setCurrent(pageNum);
+    }
+
+    public void setPageSize(long pageSize) {
+        this.setSize(pageSize);
+    }
+
+    public void setPage(long page) {
+        this.setCurrent(page);
+    }
+
+    public void setLimit(long limit) {
+        this.setSize(limit);
+    }
+
+}

+ 33 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/entity/PageResult.java

@@ -0,0 +1,33 @@
+package com.mrxu.framework.boot.entity;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class PageResult <T> implements Serializable {
+
+    @ApiModelProperty(value = "当前页码")
+    private long pageNum;
+
+    @ApiModelProperty(value = "每页大小")
+    private long pageSize;
+
+    @ApiModelProperty(value = "总数条数")
+    private long count;
+
+    @ApiModelProperty(value = "当前页数据")
+    private List<T> data;
+
+    public PageResult(List<T> list, PageParam <T> page) {
+        this.data = list;
+        this.pageNum = page.getCurrent();
+        this.pageSize = page.getSize();
+        this.count = page.getTotal();
+    }
+
+}

+ 109 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/entity/ResponseObj.java

@@ -0,0 +1,109 @@
+package com.mrxu.framework.boot.entity;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+@ApiModel(description = "返回给前端的对象")
+@Data
+@NoArgsConstructor
+public class ResponseObj<T> implements Serializable {
+
+    private static final long serialVersionUID = -1L;
+
+    public final static ResponseObj<Object> OK = new ResponseObj<Object> (BaseCode.OK);
+    public final static ResponseObj<Object> ERROR = new ResponseObj<Object>(BaseCode.ERROR);
+
+    @ApiModelProperty(notes = "状态码", position = 4)
+    private int code = BaseCode.OK.getCode();
+
+    @ApiModelProperty(notes = "提示信息", position = 2)
+    private String msg = BaseCode.OK.getMsg();
+
+    @ApiModelProperty(notes = "数据集合", position = 3)
+    private T result;
+
+    public ResponseObj(int code,String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public ResponseObj(int code,String msg,T result) {
+        this.code = code;
+        this.msg = msg;
+        this.result = result;
+    }
+
+    public ResponseObj(BaseCode baseCode) {
+        this.code = baseCode.getCode();
+        this.msg = baseCode.getMsg();
+    }
+
+    public ResponseObj(BaseCode baseCode,T result) {
+        this.code = baseCode.getCode();
+        this.msg = baseCode.getMsg();
+        this.result = result;
+    }
+
+    /**
+     * 通用的 操作成功
+     */
+    public static ResponseObj<Object> SUCCESS() {
+        return new ResponseObj<Object>(BaseCode.OK);
+    }
+
+    /**
+     * 通用的 操作成功时的 返回对象
+     */
+    public static <T> ResponseObj<T> SUCCESS(T result) {
+        ResponseObj<T> responseObj = new ResponseObj<>(BaseCode.OK);
+        responseObj.result = result;
+        return responseObj;
+    }
+
+    /**
+     * 通用的错误提示方法
+     *
+     * @param baseCode 错误码
+     */
+    public static ResponseObj<Object> ERROR(BaseCode baseCode) {
+        return ERROR(baseCode.msg, baseCode.code);
+    }
+
+    /**
+     * 通用的错误提示方法
+     *
+     * @param msg 错误信息
+     */
+    public static ResponseObj<Object> ERROR(String msg) {
+        return new ResponseObj<Object>(BaseCode.ERROR.code, msg);
+    }
+
+    /**
+     * 通用的错误提示方法
+     *
+     * @param msg     错误信息
+     * @param code 错误码
+     */
+    public static ResponseObj<Object> ERROR(String msg, int code) {
+        return new ResponseObj<Object>(code, msg);
+    }
+
+    /**
+     * 带错误数据的错误提示方法
+     *
+     * @param msg     错误信息
+     * @param code 错误码
+     * @param result  错误数据
+     */
+    public static <T> ResponseObj<T> ERROR(String msg, int code, T result) {
+        ResponseObj<T> responseObj = new ResponseObj<>(code, msg);
+        responseObj.result = result;
+        return responseObj;
+    }
+
+}
+

+ 36 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/handle/FeignErrorHandler.java

@@ -0,0 +1,36 @@
+package com.mrxu.framework.boot.handle;
+
+import com.alibaba.fastjson.JSONObject;
+import com.mrxu.framework.boot.entity.BusinessException;
+import feign.Response;
+import feign.Util;
+import feign.codec.ErrorDecoder;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import java.io.IOException;
+import java.lang.annotation.*;
+
+@Configuration
+public class FeignErrorHandler implements ErrorDecoder {
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE})
+    @Documented
+    @Import({FeignErrorHandler.class})
+    public @interface EnableFeignErrorHandler {
+    }
+
+    @Override
+    public Exception decode(String methodKey, Response response) {
+        try {
+            String rs = Util.toString(response.body().asReader());
+            JSONObject result = JSONObject.parseObject(rs);
+            return new BusinessException(result.getString("message"));
+        }
+        catch (IOException ignored) {
+            return new BusinessException("数据异常");
+        }
+    }
+
+}

+ 15 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/handle/FreemarkerExceptionHandler.java

@@ -0,0 +1,15 @@
+package com.mrxu.framework.boot.handle;
+
+import com.mrxu.framework.boot.entity.BusinessException;
+import freemarker.core.Environment;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateExceptionHandler;
+
+import java.io.Writer;
+
+public class FreemarkerExceptionHandler implements TemplateExceptionHandler {
+    @Override
+    public void handleTemplateException(TemplateException te, Environment env, Writer out) throws TemplateException {
+        throw new BusinessException("模板解析错误");
+    }
+}

+ 94 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/handle/WebExceptionHandler.java

@@ -0,0 +1,94 @@
+package com.mrxu.framework.boot.handle;
+
+import com.mrxu.framework.boot.entity.BaseCode;
+import com.mrxu.framework.boot.entity.BusinessException;
+import com.mrxu.framework.boot.entity.ResponseObj;
+import com.mrxu.framework.boot.web.ServletUtils;
+import com.mrxu.framework.common.util.StrFunc;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.annotation.*;
+
+@ControllerAdvice
+@RestController
+public class WebExceptionHandler {
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE})
+    @Documented
+    @Import({WebExceptionHandler.class})
+    public @interface EnableWebExceptionHandler {
+    }
+
+    private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @ExceptionHandler(Exception.class)
+    public Object handleException(Exception e) {
+        logger.error("系统错误:{}",e.getMessage(), e);
+        return rendError(BaseCode.ERROR);
+    }
+
+    @ExceptionHandler(BusinessException.class)
+    public Object handleLogicalException(BusinessException e) {
+        logger.warn(e.getMessage());
+        return rendError(new BaseCode(e.getCode(),e.getMsg()));
+    }
+
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public Object handleValidException(MethodArgumentNotValidException e) {
+        BindingResult result = e.getBindingResult();
+        FieldError error = result.getFieldError();
+        logger.warn("请求参数错误:{}",error.getDefaultMessage());
+        return rendError(BaseCode.ERR_PARAMS_VALID);
+    }
+
+    /**
+     * 缺少必要参数
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    public Object handleValidException(MissingServletRequestParameterException e) {
+        logger.warn("请求缺少参数:{}",e.getMessage());
+        return rendError(BaseCode.ERR_PARAMS_MISS);
+    }
+
+    public Object rendError(BaseCode code) {
+        HttpServletRequest request = ServletUtils.getRequest();
+        if(ServletUtils.isAjaxRequest(request)) {
+            return new ResponseObj<Object>(code);
+        }
+        else {
+            ModelAndView modelAndView = new ModelAndView();
+            modelAndView.addObject("message", code.getMsg());
+            modelAndView.setViewName("error/500.html");
+
+            //未登录
+            if(BaseCode.ERR_NOTAUTH.getCode() == code.getCode()) {
+                StringBuffer source = request.getRequestURL();
+                String queryString = request.getQueryString();
+                if(StrFunc.isNotEmpty(queryString)) {
+                    source.append("?").append(queryString);
+                }
+                modelAndView.addObject("sourceUrl",source);
+                modelAndView.setViewName("login");
+            }
+            return modelAndView;
+        }
+    }
+
+}

+ 30 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/starter/MybatisPlusConfig.java

@@ -0,0 +1,30 @@
+package com.mrxu.framework.boot.starter;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import java.lang.annotation.*;
+
+@EnableTransactionManagement
+@Configuration
+public class MybatisPlusConfig {
+
+    /**
+     * 分页插件
+     */
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor().setLimit(1000);
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE})
+    @Documented
+    @Import({MybatisPlusConfig.class})
+    public @interface EnableMybatisPlus {
+    }
+
+}

+ 71 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/starter/SwaggerConfig.java

@@ -0,0 +1,71 @@
+package com.mrxu.framework.boot.starter;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration;
+
+import java.lang.annotation.*;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Configuration
+@EnableSwagger2
+@ConditionalOnClass(EnableSwagger2.class)
+@ConditionalOnProperty(value = "swagger.enabled")
+public class SwaggerConfig {
+
+    @Value("${swagger.basePackage:com.mrxu}")
+    private String basePackage = "com.mrxu";
+
+    @Value("${swagger.version:1.0}")
+    private String version ;
+
+    @Value("${swagger.title:Rest API 接口}")
+    private String title ;
+
+    @Value("${swagger.description:Rest API 接口}")
+    private String description ;
+
+    private ApiInfo apiInfo() {
+        ApiInfoBuilder apiInfoBuilder = new ApiInfoBuilder();
+        apiInfoBuilder.title(title);
+        apiInfoBuilder.description(description);
+        apiInfoBuilder.license("科脉");
+        apiInfoBuilder.licenseUrl("");
+        apiInfoBuilder.version(version);
+        apiInfoBuilder.contact(new Contact("科脉-研发中心", "", ""));
+        return apiInfoBuilder.build();
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public Docket customImplementation() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .select()
+                .apis(RequestHandlerSelectors.basePackage(basePackage))
+                .build()
+                .directModelSubstitute(LocalDate.class,  java.sql.Date.class)
+                .directModelSubstitute(LocalDateTime.class, java.util.Date.class)
+                .apiInfo(apiInfo());
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE})
+    @Documented
+    @Import({SwaggerConfig.class})
+    public @interface EnableMrxuSwagger {
+    }
+
+}

+ 24 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/util/MrxuAssert.java

@@ -0,0 +1,24 @@
+package com.mrxu.framework.boot.util;
+
+import com.mrxu.framework.boot.entity.BusinessException;
+
+public class MrxuAssert {
+
+    public static void isTrue(boolean expression, String msg) {
+        if (!expression) {
+            throw new BusinessException(msg);
+        }
+    }
+
+    public static void isNotEmpty(Object str, String msg) {
+        isFalse((str == null) || ("".equals(str.toString())), msg);
+    }
+
+    public static void isEmpty(Object str, String msg) {
+        isTrue((str == null) || ("".equals(str.toString())), msg);
+    }
+
+    public static void isFalse(boolean expression, String msg) {
+        isTrue(!expression, msg);
+    }
+}

+ 117 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/web/BaseController.java

@@ -0,0 +1,117 @@
+package com.mrxu.framework.boot.web;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.mrxu.framework.boot.entity.BaseCode;
+import com.mrxu.framework.boot.entity.LayuiPage;
+import com.mrxu.framework.boot.entity.PageResult;
+import com.mrxu.framework.boot.entity.ResponseObj;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+public class BaseController {
+
+    protected Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * 获取request
+     */
+    public HttpServletRequest getRequest() {
+        return ServletUtils.getRequest();
+    }
+
+    /**
+     * 获取response
+     */
+    public HttpServletResponse getResponse() {
+        return ServletUtils.getResponse();
+    }
+
+    /**
+     * 获取session
+     */
+    public HttpSession getSession() {
+        return getRequest().getSession();
+    }
+
+    /**
+     * 页面跳转
+     */
+    public String redirect(String url) {
+        return String.format("redirect:%s", url);
+    }
+
+    /**
+     * 通用的 操作成功
+     */
+    public static ResponseObj<Object> success() {
+        return ResponseObj.SUCCESS();
+    }
+
+    /**
+     * 通用的 操作成功时的 返回对象
+     */
+    public static <T> ResponseObj<T> success(T result) {
+        return ResponseObj.SUCCESS(result);
+    }
+
+    /**
+     * 通用的错误提示方法
+     *
+     * @param retCode 错误码
+     */
+    public static ResponseObj<Object> error(BaseCode retCode) {
+        return ResponseObj.ERROR(retCode);
+    }
+
+    /**
+     * 通用的错误提示方法
+     *
+     * @param msg 错误信息
+     */
+    public static ResponseObj<Object> error(String msg) {
+        return ResponseObj.ERROR(msg);
+    }
+
+    /**
+     * 通用的错误提示方法
+     *
+     * @param msg     错误信息
+     * @param retCode 编码
+     */
+    public static ResponseObj<Object> error(String msg, int retCode) {
+        return ResponseObj.ERROR(msg, retCode);
+    }
+
+    /**
+     * 带错误类型的错误提示方法
+     *
+     * @param msg     错误信息
+     * @param retCode 编码
+     * @param result  错误类型
+     */
+    public static <T> ResponseObj<T> error(String msg, int retCode, T result) {
+        return ResponseObj.ERROR(msg, retCode, result);
+    }
+
+    public static <T> LayuiPage<T> renderLayuiPage(ResponseObj<Page<T>> rs) {
+        LayuiPage<T> layuiPage = new LayuiPage<T>();
+        layuiPage.setCode(rs.getCode()==BaseCode.OK.getCode()?0:1);
+        layuiPage.setMsg(rs.getMsg());
+        layuiPage.setData(rs.getResult().getRecords());
+        layuiPage.setCount(rs.getResult().getTotal());
+        return layuiPage;
+    }
+
+    public static <T> LayuiPage<T> renderLayuiPage(PageResult<T> rs) {
+        LayuiPage<T> layuiPage = new LayuiPage<T>();
+        layuiPage.setCode(0);
+        layuiPage.setData(rs.getData());
+        layuiPage.setCount(rs.getCount());
+        return layuiPage;
+    }
+
+}

+ 151 - 0
framework-boot/src/main/java/com/mrxu/framework/boot/web/ServletUtils.java

@@ -0,0 +1,151 @@
+package com.mrxu.framework.boot.web;
+
+import com.mrxu.framework.common.util.StrFunc;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * 客户端工具类
+ *
+ * @author Demon-HY
+ */
+public class ServletUtils {
+    /**
+     * 获取String参数
+     */
+    public static String getParameter(String name) {
+        return getRequest().getParameter(name);
+    }
+
+    /**
+     * 获取String参数
+     */
+    public static String getParameter(String name, String defaultValue) {
+        return StrFunc.toStr(getRequest().getParameter(name), defaultValue);
+    }
+
+    /**
+     * 获取Integer参数
+     */
+    public static Integer getParameterToInt(String name) {
+        return StrFunc.toInt(getRequest().getParameter(name),null);
+    }
+
+    /**
+     * 获取Integer参数
+     */
+    public static Integer getParameterToInt(String name, Integer defaultValue) {
+        return StrFunc.toInt(getRequest().getParameter(name), defaultValue);
+    }
+
+    /**
+     * 获取request
+     */
+    public static HttpServletRequest getRequest() {
+        return getRequestAttributes().getRequest();
+    }
+
+    /**
+     * 获取response
+     */
+    public static HttpServletResponse getResponse() {
+        return getRequestAttributes().getResponse();
+    }
+
+    /**
+     * 获取session
+     */
+    public static HttpSession getSession() {
+        return getRequest().getSession();
+    }
+
+    public static ServletRequestAttributes getRequestAttributes() {
+        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
+        return (ServletRequestAttributes) attributes;
+    }
+
+    /**
+     * 将字符串渲染到客户端
+     *
+     * @param response 渲染对象
+     * @param string   待渲染的字符串
+     * @return null
+     */
+    public static String renderString(HttpServletResponse response, String string) {
+        try {
+            response.setContentType("application/json");
+            response.setCharacterEncoding("utf-8");
+            response.getWriter().print(string);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 是否是Ajax异步请求
+     */
+    public static boolean isAjaxRequest(HttpServletRequest request) {
+        String accept = request.getHeader("content-type");
+        if (accept != null && accept.contains("application/json")) {
+            return true;
+        }
+
+        String xRequestedWith = request.getHeader("X-Requested-With");
+        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取request的header
+     */
+    public static Map<String, List<String>> getRequestHeaderMap(HttpServletRequest request) {
+        Map<String, List<String>> headers = new HashMap<String, List<String>>();
+        Enumeration<String> headerNames = request.getHeaderNames();
+        if (headerNames != null) {
+            while (headerNames.hasMoreElements()) {
+                String name = headerNames.nextElement();
+                String value = request.getHeader(name);
+
+                if (name != null && value != null) {
+                    List<String> valueList = new ArrayList<String>();
+                    if (headers.containsKey(name)) {
+                        headers.get(name).add(value);
+                    }
+                    valueList.add(value);
+                    headers.put(name, valueList);
+                }
+            }
+        }
+        return Collections.unmodifiableMap(headers);
+    }
+
+    /**
+     * 获取cookie
+     */
+    public static String getCookie(HttpServletRequest request, String cookieName) {
+        String retVal = null;
+        if (null != request.getCookies()) {
+            for (Cookie cookie : request.getCookies()) {
+                if (cookie.getName().equals(cookieName)) {
+                    retVal = cookie.getValue();
+                    if (StrFunc.isNotEmpty(retVal)) {
+                        break;
+                    }
+                }
+            }
+        }
+        return retVal;
+    }
+}

+ 30 - 0
framework-common/pom.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.mrxu</groupId>
+    <artifactId>framework-common</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>framework-common</name>
+    <description>工具类</description>
+
+    <parent>
+        <groupId>com.mrxu</groupId>
+        <artifactId>mrxu-upgrader</artifactId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <encoding>UTF-8</encoding>
+    </properties>
+
+</project>

+ 4 - 0
framework-common/src/main/java/com/mrxu/framework/common/util/DateFunc.java

@@ -0,0 +1,4 @@
+package com.mrxu.framework.common.util;
+
+public class DateFunc {
+}

+ 87 - 0
framework-common/src/main/java/com/mrxu/framework/common/util/StrFunc.java

@@ -0,0 +1,87 @@
+package com.mrxu.framework.common.util;
+
+public class StrFunc {
+
+    private static final String NULLSTR = "";
+
+    /**
+     * * 判断一个字符串是否为空串
+     * @param str String
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(String str) {
+        return isNull(str) || NULLSTR.equals(str.trim());
+    }
+
+    /**
+     * * 判断一个字符串是否为非空串
+     * @param str String
+     * @return true:非空串 false:空串
+     */
+    public static boolean isNotEmpty(String str) {
+        return !isEmpty(str);
+    }
+
+    /**
+     * * 判断一个对象是否为空
+     * @param object Object
+     * @return true:为空 false:非空
+     */
+    public static boolean isNull(Object object) {
+        return object == null;
+    }
+
+    /**
+     * * 判断一个对象是否非空
+     * @param object Object
+     * @return true:非空 false:空
+     */
+    public static boolean isNotNull(Object object) {
+        return !isNull(object);
+    }
+
+    public static Integer toInt(Object value, Integer defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+        if (value instanceof Integer) {
+            return (Integer) value;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (isEmpty(valueStr)) {
+            return defaultValue;
+        }
+        try {
+            return Integer.parseInt(valueStr.trim());
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    public static String toStr(Object value, String defaultValue) {
+        if (null == value) {
+            return defaultValue;
+        }
+        if (value instanceof String) {
+            return (String) value;
+        }
+        return value.toString();
+    }
+
+    /**
+     * 判断两个字符串是否相等
+     * @param str1
+     * @param str2
+     * @return
+     */
+    public static boolean equals(String str1,String str2) {
+        if(str1 == null) {
+            return  str2 == null;
+        }
+        return str1.equals(str2);
+    }
+
+}

+ 144 - 0
framework-gateway/pom.xml

@@ -0,0 +1,144 @@
+<?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>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.1.5.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.micro.cloud</groupId>
+    <artifactId>framework-gateway</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>framework-gateway</name>
+    <description>Demo project for Spring Boot</description>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
+        <fastjson.version>1.2.29</fastjson.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+
+        <!-- spring boot jar -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <!-- utils -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- 智能网关 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+
+            <!-- spring boot Maven打包插件 -->
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+
+            <!-- 编译jar包资源处理插件 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>**/*.yml</exclude>
+                        <exclude>**/*logback*.xml</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+
+            <!-- 跳过测试的插件 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>true</skipTests>
+                </configuration>
+            </plugin>
+
+            <!-- 生成源码包的插件 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar-no-fork</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 26 - 0
framework-gateway/src/main/java/com/micro/cloud/GatewayServiceApplication.java

@@ -0,0 +1,26 @@
+package com.micro.cloud;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@SpringBootApplication
+@EnableZuulProxy
+@EnableEurekaClient
+@EnableFeignClients
+@Slf4j
+public class GatewayServiceApplication {
+
+    //http://localhost:8080/rest/v1/服务名/merchant/merchant/getById?id=1
+    public static void main(String[] args) {
+        long start = System.currentTimeMillis();
+        SpringApplication.run(GatewayServiceApplication.class, args);
+        log.info("^_^ ######################################################## ^_^");
+        log.info("^_^ ###: Gateway Application was started,{} seconds.", (System.currentTimeMillis()-start)/1000);
+        log.info("^_^ ######################################################## ^_^");
+    }
+
+}

+ 38 - 0
framework-gateway/src/main/java/com/micro/cloud/core/config/GlobalConfiguration.java

@@ -0,0 +1,38 @@
+package com.micro.cloud.core.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+@Configuration
+public class GlobalConfiguration {
+
+	@Bean
+	public CorsFilter corsFilter() {
+		final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+		final CorsConfiguration config = new CorsConfiguration();
+		
+		//允许cookie跨域
+		config.setAllowCredentials(true);
+		//接受任何域名的请求
+		config.addAllowedOrigin("*");
+		//允许访问头信息,*表示全部
+		config.addAllowedHeader("*");
+		//预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不在预检了
+		config.setMaxAge(18000L);
+		
+		//允许请求的方法
+		config.addAllowedMethod("OPTIONS");
+		config.addAllowedMethod("HEAD");
+		config.addAllowedMethod("GET");
+		config.addAllowedMethod("PUT");
+		config.addAllowedMethod("POST");
+		config.addAllowedMethod("DELETE");
+		config.addAllowedMethod("PATCH");
+		
+		source.registerCorsConfiguration("/**", config);
+		return new CorsFilter(source);
+	}
+}

+ 7 - 0
framework-gateway/src/main/java/com/micro/cloud/core/constans/Code.java

@@ -0,0 +1,7 @@
+package com.micro.cloud.core.constans;
+
+import java.io.Serializable;
+
+public interface Code extends Serializable{
+
+}

+ 52 - 0
framework-gateway/src/main/java/com/micro/cloud/core/constans/CommonMessage.java

@@ -0,0 +1,52 @@
+package com.micro.cloud.core.constans;
+
+
+public enum CommonMessage implements MsgCode{
+	/**
+	 * 操作成功
+	 */
+	Success("A00000"),
+
+	/**
+	 * 内部错误
+	 */
+	Internal_Error("A10000"),
+
+	/**
+	 * action参数错误
+	 */
+	Invalid_Parameter_Action_Error("A10001"),
+	
+	/**
+	 * 参数解析错误
+	 */
+	Parameter_Validate_Parsing_Error("A10002"), 
+	
+	/**
+	 * 服务错误
+	 */
+	Service_Error("A10003"), 
+	
+	/**
+	 * 未知错误
+	 */
+	Unkown("A10004"),
+	;
+
+	private String code;
+	
+	private CommonMessage(String code) {
+		this.code = code;
+	}
+	
+	@Override
+	public String code() {
+		return this.code;
+	}
+
+	@Override
+	public String msg() {
+		return this.name();
+	}
+
+}

+ 60 - 0
framework-gateway/src/main/java/com/micro/cloud/core/constans/GatewayConstans.java

@@ -0,0 +1,60 @@
+package com.micro.cloud.core.constans;
+
+import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
+
+public class GatewayConstans{
+
+	public static final String AuthTag = "auth_tag";
+	
+	public static final String RateLimitExpire = "rateLimitExceeded";
+	
+	/**
+	 * from body处理之后(ratelimit)
+	 */
+	public static final int AFTER_FORM_BODY_WRAPPER_FILTER_ORDER = FilterConstants.FORM_BODY_WRAPPER_FILTER_ORDER + 1;
+	
+	/**
+	 * ratelimit处理之后(init)
+	 */
+	public static final int AFTER_RATE_LIMIT_FILTER_ORDER = AFTER_FORM_BODY_WRAPPER_FILTER_ORDER + 1;
+	
+	/**
+	 * init处理之后(token)
+	 */
+	public static final int AFTER_INIT_FILTER_ORDER = AFTER_RATE_LIMIT_FILTER_ORDER + 1;
+	
+	/**
+	 * token处理之后(sign)
+	 */
+	public static final int AFTER_TOKENAUTH_FILTER_ORDER = AFTER_INIT_FILTER_ORDER + 1;
+	
+	/**
+	 * 发送之前(response)
+	 */
+	public static final int BEFORE_SEND_RESPONSE_FILTER_ORDER = FilterConstants.SEND_RESPONSE_FILTER_ORDER -1;
+
+	/**
+	 * 命令模式
+	 */
+	public static final String COMMAND_PREFIX = "/api";
+
+	/**
+	 * restfull模式
+	 */
+	public static final String RESTFUL_PREFIX = "/rest";
+
+	/**
+	 * 命令模式api
+	 */
+	public static final String COMMAND_API = "/api/v1";
+
+	/**
+	 * RESTful模式api
+	 */
+	public final String REST_API = "/rest/v1";
+
+	/**
+	 * 命令的名称
+	 */
+	public static final String API_COMMAND_ACTION = "Action";
+}

+ 132 - 0
framework-gateway/src/main/java/com/micro/cloud/core/constans/GatewayMessage.java

@@ -0,0 +1,132 @@
+package com.micro.cloud.core.constans;
+
+public enum GatewayMessage implements MsgCode{
+	
+	/**
+	 * 服务端限流
+	 */
+	Gateway_Exception_Server_THROTTLED("G10000"),
+	
+	/**
+	 * 服务未发现
+	 */
+	Gateway_Exception_NOT_CHOOSE_SERVICE("G10001"), 
+	
+	/**
+	 * 服务不可用
+	 */
+	Gateway_Exception_Service_UnknownHost("G10002"), 
+	
+	/**
+	 * 一般异常
+	 */
+	Gateway_Exception_GENERAL("G10003"), 
+	
+	/**
+	 * 配置异常
+	 */
+	Gateway_Exception_CONFIGURATION("G10004"), 
+	
+	/**
+	 * 重试过载
+	 */
+	Gateway_Exception_NUMBEROF_RETRIES_EXEEDED("G10005"), 
+	
+	/**
+	 * 负载均衡重试过载
+	 */
+	Gateway_Exception_NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED("G10006"), 
+	
+	/**
+	 * 服务器连接超时
+	 */
+	Gateway_Exception_SOCKET_TIMEOUT_EXCEPTION("G10007"), 
+	
+	/**
+	 * 服务处理超时
+	 */
+	Gateway_Exception_READ_TIMEOUT_EXCEPTION("G10008"), 
+	
+	/**
+	 * 未找到服务主机
+	 */
+	Gateway_Exception_UNKNOWN_HOST_EXCEPTION("G10009"), 
+	
+	/**
+	 * 网络连接异常
+	 */
+	Gateway_Exception_CONNECT_EXCEPTION("G10010"), 
+	
+	/**
+	 * 客户端限流
+	 */
+	Gateway_Exception_CLIENT_THROTTLED("G10011"), 
+	
+	/**
+	 * 服务端限流
+	 */
+	Gateway_Exception_SERVER_THROTTLED("G10012"), 
+	
+	/**
+	 * 未找到服务主机路由
+	 */
+	Gateway_Exception_NO_ROUTE_TO_HOST_EXCEPTION("G10013"), 
+	
+	/**
+	 * 服务缓存丢失
+	 */
+	Gateway_Exception_CACHE_MISSING("G10014"), 
+	
+	/**
+	 * 错误请求
+	 */
+	Gateway_Exception_BAD_REQUEST_EXCEPTION("G10015"), 
+	
+	/**
+	 * 请求异常
+	 */
+	Gateway_Exception_COMMAND_EXCEPTION("G10016"), 
+	
+	/**
+	 * 请求服务超时
+	 */
+	Gateway_Exception_TIMEOUT("G10017"), 
+	
+	/**
+	 * 服务短路
+	 */
+	Gateway_Exception_SHORTCIRCUIT("G10018"), 
+	
+	/**
+	 * 执行信号量被拒绝
+	 */
+	Gateway_Exception_REJECTED_SEMAPHORE_EXECUTION("G10019"), 
+	
+	/**
+	 * 线程执行被拒绝
+	 */
+	Gateway_Exception_REJECTED_THREAD_EXECUTION("G10020"), 
+	
+	/**
+	 * 信号回退被拒绝
+	 */
+	Gateway_Exception_REJECTED_SEMAPHORE_FALLBACK("G10021"),
+	;
+
+	private String code;
+	
+	private GatewayMessage(String code) {
+		this.code = code;
+	}
+	
+	@Override
+	public String code() {
+		return this.code;
+	}
+
+	@Override
+	public String msg() {
+		return this.name();
+	}
+
+}

+ 13 - 0
framework-gateway/src/main/java/com/micro/cloud/core/constans/MsgCode.java

@@ -0,0 +1,13 @@
+package com.micro.cloud.core.constans;
+
+/**
+ * 消息代码的接口
+ * @author zzt
+ *
+ */
+public interface MsgCode extends Code{
+
+	public String code();
+	
+	public String msg();
+}

+ 39 - 0
framework-gateway/src/main/java/com/micro/cloud/core/http/request/Request.java

@@ -0,0 +1,39 @@
+package com.micro.cloud.core.http.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.lang.reflect.Field;
+
+@Getter
+@Setter
+public class Request {
+
+	private String requestId;
+	
+	public String toString() {
+		StringBuilder parameters = new StringBuilder("[");
+		for(Field f : getClass().getDeclaredFields()) {
+			f.setAccessible(true);
+			try {
+				Object value = f.get(this);
+				if(value!=null) {
+					if(parameters.length()>1) {
+						parameters.append(", ");
+					}
+					if(f.getName().toLowerCase().contains("password")
+							|| f.getName().toLowerCase().contains("secretkey")) {
+						value = "********";
+					}
+					parameters.append(f.getName()).append("=").append(value);
+				}
+				
+			}catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+		parameters.append("]");
+		return parameters.toString();
+	}
+	
+}

+ 86 - 0
framework-gateway/src/main/java/com/micro/cloud/core/http/response/Response.java

@@ -0,0 +1,86 @@
+package com.micro.cloud.core.http.response;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.micro.cloud.core.constans.CommonMessage;
+import com.micro.cloud.core.constans.MsgCode;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.http.HttpStatus;
+
+@Data
+@AllArgsConstructor
+@EqualsAndHashCode
+public class Response {
+	
+	/**
+	 * 请求id。每次请求都有一个请求id用于追踪日志
+	 */
+	private String requestId;
+	
+	/**
+	 * 消息代码
+	 */
+	private String code;
+	
+	/**
+	 * 消息
+	 */
+	private String message;
+	
+	/**
+	 * 请求的结果
+	 */
+	private String resultCode;
+	
+	@JSONField(serialize=false)
+	@JsonIgnore
+	private Integer HttpCode = HttpStatus.OK.value();
+	
+	public Response() {
+		MsgCode msgCode = CommonMessage.Success;
+		this.code = msgCode.code();
+		this.resultCode = msgCode.code();
+		this.message = msgCode.msg();
+//		requestId记录tracer的id
+		this.requestId = "xxxxxxtodoxxxx";
+	}
+	
+	/**
+	 * 设置返回的状态
+	 * @param msgCode
+	 * @param message
+	 */
+	public void setCode(MsgCode msgCode, String message) {
+		if(msgCode!=null) {
+			this.code = msgCode.code();
+			this.resultCode = msgCode.code();
+			if(StringUtils.isBlank(message)) {
+				this.message = msgCode.msg();
+			}
+			if(StringUtils.isNotBlank(message)) {
+				this.message = message;
+			}
+		}
+	}
+	
+	/**
+	 * 设置返回的状态
+	 * @param msgCode
+	 */
+	public void setCode(MsgCode msgCode) {
+		this.setCode(msgCode,null);
+	}
+	
+	public void setHttpCode(Integer httpCode) {
+		this.HttpCode = httpCode;
+	}
+	
+	@JSONField(serialize=false)
+	@JsonIgnore
+	public int getHttpStatus() {
+		return HttpCode;
+	}
+}

+ 34 - 0
framework-gateway/src/main/java/com/micro/cloud/excetions/GatewayException.java

@@ -0,0 +1,34 @@
+package com.micro.cloud.excetions;
+
+import com.micro.cloud.core.constans.MsgCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.http.HttpStatus;
+
+@Setter
+@Getter
+public class GatewayException extends Exception{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1372414670953841652L;
+	
+	private HttpStatus httpstatus;
+
+	private MsgCode code;
+
+	public GatewayException() {
+		super();
+	}
+	
+	public GatewayException(MsgCode code, Throwable cause) {
+		super(code.msg(),cause);
+	}
+	
+	public GatewayException(HttpStatus httpstatus, MsgCode code) {
+		super(code.msg());
+		this.httpstatus = httpstatus;
+		this.code = code;
+	}
+}

+ 87 - 0
framework-gateway/src/main/java/com/micro/cloud/gateway/config/FilterConfiguration.java

@@ -0,0 +1,87 @@
+package com.micro.cloud.gateway.config;
+
+import com.micro.cloud.gateway.filters.RateLimitFilter;
+import com.micro.cloud.gateway.filters.RouteRequestFilter;
+import com.micro.cloud.gateway.filters.TokenAuthRequestFilter;
+import com.micro.cloud.ratelimit.RateLimitProperties;
+import com.micro.cloud.ratelimit.RateLimiter;
+import com.netflix.zuul.monitoring.CounterFactory;
+import io.micrometer.core.instrument.MeterRegistry;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
+import org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration;
+import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
+import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
+import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
+import org.springframework.cloud.netflix.zuul.metrics.EmptyCounterFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Configuration
+@ConfigurationProperties(prefix="zuul")
+public class FilterConfiguration extends ZuulProxyAutoConfiguration {
+
+	@Getter
+    @Setter
+	private int chooseRetryCount;
+
+	/**
+	 * 服务别名zuul.service
+	 * zuul.service={base: base-service,k2: 12}
+	 */
+	@Setter
+    @Getter
+	private Map<String,String> service = new HashMap<>();
+	
+	@SuppressWarnings("rawtypes")
+	@Autowired(required=false)
+	private List<RibbonRequestCustomizer> requestCustomizers = Collections.emptyList();
+	
+	@Bean
+	public RouteRequestFilter routingFilter(ProxyRequestHelper helper, RibbonCommandFactory<?> ribbonCommandFactory) {
+		return new RouteRequestFilter(helper, ribbonCommandFactory, requestCustomizers, service, chooseRetryCount);
+	}
+
+	/**
+	 * 限流的过滤器
+	 * @param rateLimiter
+	 * @param rateLimitProperties
+	 * @param routeLocator
+	 * @return
+	 */
+	@Bean
+	public RateLimitFilter rateLimitFilter(RateLimiter rateLimiter, RateLimitProperties rateLimitProperties, RouteLocator routeLocator) {
+		return new RateLimitFilter(rateLimiter,rateLimitProperties,routeLocator);
+	}
+
+	/**
+	 * 权限校验过滤器
+	 * @return
+	 */
+	@Bean
+	public TokenAuthRequestFilter tokenAuthRequestFilter(){
+		return new TokenAuthRequestFilter();
+	}
+
+
+	/**
+	 * zuul启动2.1.x缺少bean
+	 * @return
+	 */
+	@Bean
+	@ConditionalOnClass(name="io.micrometer.core.instrument.MeterRegistry")
+	@ConditionalOnMissingBean({CounterFactory.class,MeterRegistry.class})
+	public CounterFactory counterFactory() {
+		return new EmptyCounterFactory();
+	}
+}

+ 160 - 0
framework-gateway/src/main/java/com/micro/cloud/gateway/filters/RateLimitFilter.java

@@ -0,0 +1,160 @@
+package com.micro.cloud.gateway.filters;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.PascalNameFilter;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.google.common.net.HttpHeaders;
+import com.micro.cloud.core.constans.GatewayConstans;
+import com.micro.cloud.core.constans.GatewayMessage;
+import com.micro.cloud.core.http.response.Response;
+import com.micro.cloud.ratelimit.Policy;
+import com.micro.cloud.ratelimit.Rate;
+import com.micro.cloud.ratelimit.RateLimitProperties;
+import com.micro.cloud.ratelimit.RateLimiter;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.netflix.zuul.filters.Route;
+import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
+import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.web.util.UrlPathHelper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+@Slf4j
+@RequiredArgsConstructor
+public class RateLimitFilter extends ZuulFilter{
+	
+	private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper();
+	
+	private static final PascalNameFilter PascalFilter = new PascalNameFilter();
+	
+	private static final String ANONYMOUS = "anonymous";
+	
+	private final RateLimiter rateLimiter;
+	
+	private final RateLimitProperties properties;
+	
+	private final RouteLocator routLocator;
+	
+	@Override
+	public Object run() throws ZuulException {
+		RequestContext ctx = RequestContext.getCurrentContext();
+		HttpServletResponse response = ctx.getResponse();
+		HttpServletRequest request = ctx.getRequest();
+		Response res = null;
+		Policy policy = getPolicy();
+		if(policy!=null) {
+			//限制逻辑
+			final Rate rate = this.rateLimiter.consume(policy, key(request,policy.getType()));
+			
+			//已达到限流上线返回异常
+			response.setHeader(Headers.LIMIT, rate.getLimit().toString());
+			response.setHeader(Headers.REMAINING, rate.getRemaining().toString());
+			response.setHeader(Headers.REST, rate.getReset().toString());
+			if(rate.getRemaining()<0) {
+				//没有流量访问了
+				log.debug(">>> 触发流量控制,请求过多,请求IP:【{}】",getRemoteAddr(request));
+				log.debug(">>> 每【{}】秒之内,请求最多【{}】此。",policy.getRefreshInterval(),policy.getLimit());
+
+				res = new Response();
+				res.setCode(GatewayMessage.Gateway_Exception_Server_THROTTLED);
+				
+				String body = JSON.toJSONString(res,PascalFilter, SerializerFeature.PrettyFormat);
+				
+				ctx.setResponseBody(body);
+				ctx.setSendZuulResponse(false);
+				ctx.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
+				ctx.addZuulRequestHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
+				
+				ctx.set(GatewayConstans.RateLimitExpire,Boolean.TRUE);
+				log.debug(">>> \r\n{}",body);
+			}else {
+				ctx.set(GatewayConstans.RateLimitExpire,Boolean.FALSE);
+			}
+		}
+		return response;
+	}
+
+	private Policy getPolicy() {
+		Policy policy = null;
+		if(route() !=null ) {
+			policy = this.properties.getPolicies().get(route().getId());
+		}
+		return policy;
+	}
+
+	/**
+	 * 获取路由信息
+	 * @return
+	 */
+	private Route route() {
+		String requestURL = URL_PATH_HELPER.getPathWithinApplication(RequestContext.getCurrentContext().getRequest());
+		return this.routLocator.getMatchingRoute(requestURL);
+	}
+
+	@Override
+	public boolean shouldFilter() {
+		return this.properties.isEnabled() && getPolicy() != null;
+	}
+
+	@Override
+	public int filterOrder() {
+		return GatewayConstans.AFTER_FORM_BODY_WRAPPER_FILTER_ORDER;
+	}
+
+	@Override
+	public String filterType() {
+		return FilterConstants.PRE_TYPE;
+	}
+
+	/**
+	 * 获取限流的生成的key
+	 * @return
+	 */
+	private String key(final HttpServletRequest request , final List<Policy.Type> types) {
+		final Route route = route();
+		final String delimiter = ":";
+		StringBuilder sb = new StringBuilder();
+		sb.append(route.getId());
+		if(types.contains(Policy.Type.URL)) {
+			sb.append(delimiter).append(route.getPath());
+		}
+		if(types.contains(Policy.Type.ORIGIN)) {
+			sb.append(delimiter).append(getRemoteAddr(request));
+		}
+		if(types.contains(Policy.Type.USER)) {
+			sb.append(delimiter).append((request.getUserPrincipal()!=null)?request.getUserPrincipal().getName():ANONYMOUS);
+		}
+		return sb.toString();
+	}
+	
+	private String getRemoteAddr(final HttpServletRequest request) {
+		String ip = request.getHeader("x-forwarded-for");
+		if(ip==null||ip.length()==0||"unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("Proxy-Client-IP");
+		}
+		if(ip==null||ip.length()==0||"unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("X-Real-IP");
+		}
+		if(ip==null||ip.length()==0||"unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("WK-Proxy-Client-IP");
+		}
+		if(ip==null||ip.length()==0||"unknown".equalsIgnoreCase(ip)) {
+			ip = request.getRemoteAddr();
+		}
+		return ip.split(",")[0];
+	}
+	
+	interface Headers{
+		String LIMIT = "X-RateLimit-Limit";
+		String REMAINING = "X-RateLimit-Remaining";
+		String REST = "X-RateLimit-Reset";
+	}
+}

+ 508 - 0
framework-gateway/src/main/java/com/micro/cloud/gateway/filters/RouteRequestFilter.java

@@ -0,0 +1,508 @@
+package com.micro.cloud.gateway.filters;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.PascalNameFilter;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.google.common.net.HttpHeaders;
+import com.micro.cloud.core.constans.CommonMessage;
+import com.micro.cloud.core.constans.GatewayConstans;
+import com.micro.cloud.core.constans.GatewayMessage;
+import com.micro.cloud.core.http.response.Response;
+import com.micro.cloud.excetions.GatewayException;
+import com.micro.cloud.utils.HttpServletReqestUtil;
+import com.netflix.client.ClientException;
+import com.netflix.hystrix.exception.HystrixRuntimeException;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
+import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
+import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
+import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand;
+import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
+import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.MultiValueMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 重写RibbonRoutingFilter的run()和buildCommondConext(),使其支持接口参数Commond模式
+ * 
+ * @author zzt
+ *
+ */
+@Slf4j
+public class RouteRequestFilter extends ZuulFilter {
+
+	private static final PascalNameFilter pascalFilter = new PascalNameFilter();
+	
+	private ProxyRequestHelper helper;
+
+	private boolean useServlet31 = true;
+
+	private RibbonCommandFactory<?> ribbonCommandFactory;
+
+	@SuppressWarnings("rawtypes")
+	private List<RibbonRequestCustomizer> requestCustomizers;
+
+	private Map<String, String> service;
+	
+//	@Autowired
+//	private RedisUtil redisUtil;
+//
+//	@Autowired
+//	private IConfigRemoteService configService;
+
+	private int chooseRetryCount;
+	
+	@Autowired
+	private LoadBalancerClient loadBalancer;
+	
+	@SuppressWarnings("rawtypes")
+	public RouteRequestFilter(ProxyRequestHelper helper, RibbonCommandFactory<?> ribbonCommandFactory,
+                              List<RibbonRequestCustomizer> requestCustomizers,
+                              Map<String,String> service,
+                              int chooseRetryCount) {
+		this.helper = helper;
+		this.ribbonCommandFactory = ribbonCommandFactory;
+		this.requestCustomizers = requestCustomizers;
+		this.service = service;
+		this.chooseRetryCount = chooseRetryCount;
+		try {
+			HttpServletResponse.class.getMethod("getContentLengthLong");
+		}catch (Exception e) {
+			useServlet31  = false;
+		}
+	}
+	
+	@Override
+	public boolean shouldFilter() {
+		RequestContext ctx = RequestContext.getCurrentContext();
+		return (ctx.getRouteHost() ==null
+			    && ctx.get(FilterConstants.SERVICE_ID_KEY) != null
+			    && ctx.sendZuulResponse()
+			    && (Boolean)ctx.get(GatewayConstans.AuthTag)
+			    && !(Boolean)ctx.get(GatewayConstans.RateLimitExpire)
+				);
+	}
+
+	@Override
+	public String filterType() {
+		return FilterConstants.ROUTE_TYPE;
+	}
+
+	@Override
+	public int filterOrder() {
+		return FilterConstants.RIBBON_ROUTING_FILTER_ORDER;
+	}
+	
+
+	@Override
+	public Object run() throws ZuulException {
+		log.debug(">>>>>>>>>>>>>>>>>>>>网关路由转发开始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+		log.debug(">>> 网关路由转发开始.");
+		RequestContext context = RequestContext.getCurrentContext();
+		this.helper.addIgnoredHeaders();
+		try {
+			HttpServletRequest request = context.getRequest();
+			String command = HttpServletReqestUtil.getParameter(request, GatewayConstans.API_COMMAND_ACTION);
+
+			String requestURI = request.getRequestURI();
+			RibbonCommandContext commandContext = null;
+			if(command!=null&&requestURI.startsWith(GatewayConstans.COMMAND_API)) {
+				commandContext = buildCommandContext(context,command);
+			}else {
+				commandContext = buildCommandContext(context,null);
+			}
+			ClientHttpResponse response = forward(commandContext);
+			setResponse(response);
+			log.debug(">>> 网关路由转发结束,响应消息给客户端");
+			return response;
+		}catch (Exception e) {
+			log.error(">>> 网关路由处理失败,异常【{}】",e.getMessage());
+			return buildExceptionResponse(e);
+		}
+	}
+
+	/**
+	 * 执行转发
+	 * @param context
+	 * @return
+	 * @throws GatewayException 
+	 */
+	private ClientHttpResponse forward(RibbonCommandContext context) throws GatewayException {
+		Map<String,Object> info = null;
+		try {
+			info = this.helper.debug(context.getMethod(), 
+					context.getUri(),
+					context.getHeaders(),
+					context.getParams(), context.getRequestEntity());
+			RibbonCommand command = this.ribbonCommandFactory.create(context);
+			ClientHttpResponse response = command.execute();
+			this.helper.appendDebug(info, response.getStatusCode().value(), response.getHeaders());
+			return response;
+		}catch (HystrixRuntimeException e) {
+			return handleException(info,e);
+		}catch (Exception e) {
+			throw new GatewayException(HttpStatus.BAD_GATEWAY,CommonMessage.Service_Error);
+		}
+	}
+
+	/**
+	 * 处理异常
+	 * @param info
+	 * @param ex
+	 * @return
+	 * @throws GatewayException 
+	 */
+	private ClientHttpResponse handleException(Map<String, Object> info, HystrixRuntimeException ex) throws GatewayException {
+		HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
+		ClientException clientException = findClientException(ex);
+		if(clientException == null) {
+			clientException = findClientException(ex.getFallbackException());
+		}
+		if(clientException != null) {
+			if(clientException.getErrorType() == ClientException.ErrorType.SERVER_THROTTLED) {
+				status = HttpStatus.SERVICE_UNAVAILABLE;
+			}
+
+			log.error(">>> Netflix Client Exception-[{}]:{}",clientException.getErrorType(),clientException);
+			
+			switch (clientException.getErrorType()) {
+			case GENERAL:
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_GENERAL);
+			case CONFIGURATION:
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_CONFIGURATION);
+			case NUMBEROF_RETRIES_EXEEDED:
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_NUMBEROF_RETRIES_EXEEDED);
+			case NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED:
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED);
+			case SOCKET_TIMEOUT_EXCEPTION:
+				info.put("status", String.valueOf(HttpStatus.GATEWAY_TIMEOUT.value()));
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_SOCKET_TIMEOUT_EXCEPTION);
+			case READ_TIMEOUT_EXCEPTION:
+				info.put("status", String.valueOf(HttpStatus.GATEWAY_TIMEOUT.value()));
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_READ_TIMEOUT_EXCEPTION);
+			case UNKNOWN_HOST_EXCEPTION:
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_UNKNOWN_HOST_EXCEPTION);
+			case CONNECT_EXCEPTION:
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_CONNECT_EXCEPTION);
+			case CLIENT_THROTTLED:
+				info.put("status", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_CLIENT_THROTTLED);
+			case SERVER_THROTTLED:
+				info.put("status", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_SERVER_THROTTLED);
+			case NO_ROUTE_TO_HOST_EXCEPTION:
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_NO_ROUTE_TO_HOST_EXCEPTION);
+			case CACHE_MISSING:
+				throw new GatewayException(status, GatewayMessage.Gateway_Exception_CACHE_MISSING);
+			default:
+				throw new GatewayException(status,CommonMessage.Unkown);
+			}
+		}
+		
+		if(!info.containsKey("status")) {
+			info.put("status",String.valueOf(status.value()));
+		}
+		
+		log.error(">>> Hystrix Command Exception-[{}]:{}",ex.getFailureType(),ex.getMessage());
+		switch (ex.getFailureType()) {
+		case BAD_REQUEST_EXCEPTION:
+			throw new GatewayException(status, GatewayMessage.Gateway_Exception_BAD_REQUEST_EXCEPTION);
+		case COMMAND_EXCEPTION:
+			throw new GatewayException(status, GatewayMessage.Gateway_Exception_COMMAND_EXCEPTION);
+		case TIMEOUT:
+			info.put("status", String.valueOf(HttpStatus.GATEWAY_TIMEOUT.value()));
+			throw new GatewayException(HttpStatus.GATEWAY_TIMEOUT, GatewayMessage.Gateway_Exception_TIMEOUT);
+		case SHORTCIRCUIT:
+			throw new GatewayException(status, GatewayMessage.Gateway_Exception_SHORTCIRCUIT);
+		case REJECTED_SEMAPHORE_EXECUTION:
+			throw new GatewayException(status, GatewayMessage.Gateway_Exception_REJECTED_SEMAPHORE_EXECUTION);
+		case REJECTED_THREAD_EXECUTION:
+			throw new GatewayException(status, GatewayMessage.Gateway_Exception_REJECTED_THREAD_EXECUTION);
+		case REJECTED_SEMAPHORE_FALLBACK:
+			throw new GatewayException(status, GatewayMessage.Gateway_Exception_REJECTED_SEMAPHORE_FALLBACK);
+		default:
+			throw new GatewayException(status,CommonMessage.Unkown);
+		}
+	}
+
+	/**
+	 * 处理clientException异常
+	 * @param t
+	 * @return
+	 */
+	private ClientException findClientException(Throwable t) {
+		if(t ==null ) {
+			return null;
+		}
+		if(t instanceof ClientException) {
+			return (ClientException) t;
+		}
+		return findClientException(t.getCause());
+	}
+
+	/**
+	 * 处理响应消息
+	 * @param response
+	 * @throws IOException 
+	 */
+	private void setResponse(ClientHttpResponse response) throws IOException {
+		RequestContext.getCurrentContext().set("zuulResponse",response);
+		this.helper.setResponse(response.getStatusCode().value(), 
+				response.getBody(),
+				response.getHeaders());
+	}
+
+	/**
+	 * 处理异常响应消息
+	 * @param e
+	 * @return
+	 */
+	private Response buildExceptionResponse(Exception e) {
+		Response response = new Response();
+		response.setRequestId("服务追踪的traceId://todo");
+		
+		String message = "";
+		if(e instanceof GatewayException) {
+			GatewayException ge = (GatewayException) e;
+			message = ge.getMessage();
+			response.setMessage(message);
+			response.setCode(ge.getCode());
+			response.setHttpCode(ge.getHttpstatus().value());
+			RequestContext.getCurrentContext().setResponseStatusCode(ge.getHttpstatus().value());
+		}else {
+			message = e.getMessage();
+			response.setMessage(message);
+			response.setCode(CommonMessage.Internal_Error);
+			RequestContext.getCurrentContext().setResponseStatusCode(HttpStatus.BAD_GATEWAY.value());
+		}
+		RequestContext.getCurrentContext().set("zuulResponse",response);
+		String body = JSON.toJSONString(response,pascalFilter,SerializerFeature.PrettyFormat);
+		RequestContext.getCurrentContext().setResponseBody(body);
+		RequestContext.getCurrentContext().setSendZuulResponse(false);
+		RequestContext.getCurrentContext().addZuulResponseHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
+		return response;
+	}
+
+	/**
+	 * 构造请求体内容
+	 * @param context
+	 * @param command
+	 * @return
+	 * @throws GatewayException 
+	 */
+	private RibbonCommandContext buildCommandContext(RequestContext context, String command) throws GatewayException {
+		HttpServletRequest request = context.getRequest();
+		MultiValueMap<String, String> headers = helper.buildZuulRequestHeaders(request);
+		MultiValueMap<String, String> params = helper.buildZuulRequestQueryParams(request);
+		InputStream requestEntity = getRequestBody(request);
+		if(request.getContentLength()<0) {
+			context.setChunkedRequestBody();
+		}
+		
+		Boolean retryable = context.getBoolean("retryable");
+		
+		String serviceId = null;
+		String uri = null;
+		if(StringUtils.isEmpty(command)) {
+			//restful风格api请求
+			String originalUri = request.getRequestURI();
+			String uriPrefix = getUriPrefix(originalUri);
+			uri = originalUri.replace("//", "/").substring(uriPrefix.length());
+			log.debug(">>> uri:[{}],original uri:[{}],prefix:[{}].",uri,originalUri,uriPrefix);
+			serviceId = this.getRestService(originalUri);
+			log.debug(">>> 请求类型为RESTful模式,转发URI:[{}],原始URI:[{}].",uri,originalUri);
+		}else {
+			uri = GatewayConstans.COMMAND_API;
+
+			//获取command模式的serviceId
+			serviceId = this.getCommandService(command);
+			log.debug(">>> 请求类型为command模式(action),转发URI:[{}]",uri);
+			log.debug(">>> 请求Action:[{}].",command);
+		}
+		if(serviceId==null) {
+			log.debug(">>> 服务注册中心未找到该请求对应的服务,终止转发。");
+			RequestContext.getCurrentContext().setResponseStatusCode(HttpStatus.BAD_GATEWAY.value());
+			RequestContext.getCurrentContext().setSendZuulResponse(false);
+			throw new GatewayException(HttpStatus.BAD_GATEWAY,CommonMessage.Invalid_Parameter_Action_Error);
+		}
+		
+		//如果yaml配置文件中能找到当前的Service就以当前的为准,否则以数据库中的为准
+		serviceId = (service.get(serviceId)!=null)?service.get(serviceId):serviceId;
+		
+		//检查是否有服务可以用
+		log.debug(">>> 检查是否有可用的服务[{}]可以转发...",serviceId);
+		this.checkLoadblanceActive(serviceId);
+		log.debug(">>> 找到可供转发的后台服务[{}].",serviceId);
+		log.debug(">>> 请求转发到[{}]后台服务.",serviceId);
+		
+		RequestContext.getCurrentContext().set("serviceId",serviceId);
+		
+		long contextLength = useServlet31?request.getContentLengthLong():request.getContentLength();
+		
+		//构造Ribbon请求上下文
+		return new RibbonCommandContext(serviceId,
+				getMethod(request),
+				uri,
+				retryable,
+				headers,
+				params,
+				requestEntity,
+				this.requestCustomizers,
+				contextLength);
+	}
+	
+	/**
+	 * 获取command类型的服务id
+	 * @param command
+	 * @return
+	 */
+	private String getCommandService(String command) throws GatewayException {
+//		ActionDTO actionDto = null;
+//		String key = RedisKeyPrefix.getKey(RedisKeyPrefix.GATEWAY,"command",command);
+//		String serviceId = (String) redisUtil.get(key);
+//		if(StringUtils.isEmpty(serviceId)) {
+//			//如果缓存中没有就重新加载
+//			actionDto = configService.getAction(command);
+//			if(actionDto!=null) {
+//				serviceId = actionDto.getServiceCode();
+//				//重新加载缓存起来
+//				redisUtil.set(key, serviceId);
+//			}
+//		}
+//		return serviceId;
+		//暂不支持command模式
+		throw new GatewayException(HttpStatus.BAD_GATEWAY,CommonMessage.Invalid_Parameter_Action_Error);
+	}
+
+	/**
+	 * 获取restful类型的服务id
+	 * @param uri
+	 * @return
+	 */
+	private String getRestService(String uri) {
+		if(uri.startsWith(GatewayConstans.RESTFUL_PREFIX)) {
+			String uriPrefix = getUriPrefix(uri);
+			String serviceId = uriPrefix.substring(uriPrefix.lastIndexOf("/")+1);
+//			ServiceDTO serviceDto = null;
+//			String key = RedisKeyPrefix.getKey(RedisKeyPrefix.GATEWAY,"rest",tmpUriPrefix);
+//			String serviceId = (String) redisUtil.get(key);
+//			if(StringUtils.isEmpty(serviceId)) {
+//				//如果缓存中没有就重新加载
+//				serviceDto = configService.getService(tmpUriPrefix);
+//				if(serviceDto!=null) {
+//					serviceId = serviceDto.getServiceCode();
+//					//重新加载缓存起来
+//					redisUtil.set(key, serviceId);
+//				}
+//			}
+			return serviceId;
+		}
+		return null;
+	}
+
+	/**
+	 * 解析请求消息体,转化为流
+	 * @param request
+	 * @return
+	 * @throws GatewayException 
+	 */
+	private InputStream getRequestBody(HttpServletRequest request) throws GatewayException {
+		InputStream requestEntity = null;
+		try {
+			requestEntity = (InputStream) RequestContext.getCurrentContext().get(FilterConstants.REQUEST_ENTITY_KEY);
+			if(requestEntity == null) {
+				requestEntity = request.getInputStream();
+			}
+		}catch (Exception e) {
+			throw new GatewayException(HttpStatus.BAD_GATEWAY,CommonMessage.Parameter_Validate_Parsing_Error);
+		}
+		return requestEntity;
+	}
+
+	/**
+	 * 检查服务是否可用
+	 * 主要解决问题是服务首次启动后,立刻访问网关时
+	 * 网关还未更新服务注册表,导致找不到服务的问题
+	 * @param serviceId
+	 * @throws GatewayException 
+	 */
+	private void checkLoadblanceActive(String serviceId) throws GatewayException {
+		try {
+			int count = 0;
+			while(loadBalancer.choose(serviceId)==null && count < chooseRetryCount*60) {
+				count++;
+				Thread.sleep(1000);//每尝试一次,间隔1秒
+			}
+			
+			if(loadBalancer.choose(serviceId)==null) {
+				log.debug(">>> 未找到可供转发的后台服务[{}].",serviceId);
+				throw new GatewayException(HttpStatus.NOT_FOUND, GatewayMessage.Gateway_Exception_Service_UnknownHost);
+			}
+		}catch (Exception e) {
+			log.debug(">>> 检查是否有可用服务[{}]出错:{}",serviceId,e.getMessage());
+			throw new GatewayException(HttpStatus.NOT_FOUND, GatewayMessage.Gateway_Exception_NOT_CHOOSE_SERVICE);
+		}
+	}
+
+	/**
+	 * 获取请求方法
+	 * @param request
+	 * @return
+	 */
+	private String getMethod(HttpServletRequest request) {
+		String method = request.getMethod();
+		if(method == null) {
+			method = "GET";
+		}
+		return method;
+	}
+	
+	/**
+	 * 获取请求前缀
+	 * @param uri
+	 * @return
+	 */
+	private String getUriPrefix(String uri) {
+		String uriPrefix = "";
+		if(uri.startsWith(GatewayConstans.COMMAND_PREFIX)) {
+			//command
+			StringBuffer sb = new StringBuffer();
+			sb.append(GatewayConstans.COMMAND_PREFIX).append("/");
+			String temp = uri.substring(sb.toString().length());
+			String version = temp.substring(0,temp.indexOf("/"));
+			sb.append(version);
+
+			uriPrefix = sb.toString();
+		}else if(uri.startsWith(GatewayConstans.RESTFUL_PREFIX)) {
+			//restful
+			StringBuffer sb = new StringBuffer();
+			sb.append(GatewayConstans.RESTFUL_PREFIX).append("/");
+			String tmp = uri.substring(sb.toString().length());
+			String version = tmp.substring(0,tmp.indexOf("/")+1);
+			sb.append(version);
+			tmp = uri.substring(sb.toString().length());
+			tmp = tmp.replace("?", "/");
+			String product = tmp.substring(0,tmp.indexOf("/"));
+			sb.append(product);
+
+			uriPrefix = sb.toString();
+		}
+		return uriPrefix;
+	}
+
+}

+ 49 - 0
framework-gateway/src/main/java/com/micro/cloud/gateway/filters/TokenAuthRequestFilter.java

@@ -0,0 +1,49 @@
+package com.micro.cloud.gateway.filters;
+
+import com.micro.cloud.core.constans.GatewayConstans;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
+
+/**
+ * 重写RibbonRoutingFilter的run()和buildCommondConext(),使其支持接口参数Commond模式
+ * 
+ * @author zzt
+ *
+ */
+@Slf4j
+public class TokenAuthRequestFilter extends ZuulFilter {
+
+	@Override
+	public boolean shouldFilter() {
+		RequestContext ctx = RequestContext.getCurrentContext();
+		return (ctx.getRouteHost() ==null
+			    && ctx.get(FilterConstants.SERVICE_ID_KEY) != null
+			    && ctx.sendZuulResponse()
+			    && !(Boolean)ctx.get(GatewayConstans.RateLimitExpire)
+				);
+	}
+
+	@Override
+	public Object run() throws ZuulException {
+		log.debug(">>> TokenAuth开始鉴权.");
+		RequestContext ctx = RequestContext.getCurrentContext();
+		ctx.set(GatewayConstans.AuthTag, Boolean.TRUE);
+		//TODO 检验token的权限及登录信息
+		log.debug(">>> TokenAuth鉴权结束");
+		return null;
+	}
+
+	@Override
+	public String filterType() {
+		return FilterConstants.ROUTE_TYPE;
+	}
+
+	@Override
+	public int filterOrder() {
+		return GatewayConstans.AFTER_RATE_LIMIT_FILTER_ORDER;
+	}
+
+}

+ 30 - 0
framework-gateway/src/main/java/com/micro/cloud/ratelimit/Policy.java

@@ -0,0 +1,30 @@
+package com.micro.cloud.ratelimit;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Policy {
+
+	/**
+	 * 默认60秒
+	 */
+	private long refreshInterval = 60L;
+	
+	/**
+	 * 默认10此
+	 */
+	private Long limit = 10L;
+	
+	private List<Type> type = new ArrayList<Type>();
+	
+	public enum Type{
+		ORIGIN, USER, URL
+	}
+}

+ 35 - 0
framework-gateway/src/main/java/com/micro/cloud/ratelimit/Rate.java

@@ -0,0 +1,35 @@
+package com.micro.cloud.ratelimit;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * 给定时间内的速率
+ * @author zzt
+ *
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Rate {
+
+	/**
+	 * 可以执行的请求次数,放在X-RateLimit-Limit头字段中
+	 */
+	private Long limit;
+	
+	/**
+	 * 当前还剩多少个请求次数,放在X-RateLimit-Remaining头字段中
+	 */
+	private Long remaining;
+	
+	/**
+	 * 剩余的多久过期,放到X-RateLimit-Reset头字段中
+	 */
+	private Long reset;
+	
+	private Date expiration;
+}

+ 54 - 0
framework-gateway/src/main/java/com/micro/cloud/ratelimit/RateLimitAutoConfiguration.java

@@ -0,0 +1,54 @@
+package com.micro.cloud.ratelimit;
+
+import com.micro.cloud.ratelimit.repository.MemoryRateLimiter;
+import com.micro.cloud.ratelimit.repository.RedisRateLimiter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.core.RedisTemplate;
+
+@Configuration
+@EnableConfigurationProperties(RateLimitProperties.class)
+@ConditionalOnProperty(prefix= RateLimitProperties.PREFIX,name="enabled",havingValue="true")
+@Slf4j
+public class RateLimitAutoConfiguration {
+
+	/**
+	 * redis缓存限流信息
+	 * @author zzt
+	 *
+	 */
+	@ConditionalOnClass(RedisTemplate.class)
+	@ConditionalOnBean(RedisTemplate.class)
+	@ConditionalOnMissingBean(RateLimiter.class)
+	public static class RedisConfiguration{
+		
+		@Bean
+		@Primary
+		public RateLimiter redisRateLimiter(RedisTemplate<String,Object> redisTemplate) {
+			log.debug("初始化redisRateLimiter!");
+			return new RedisRateLimiter(redisTemplate);
+		}
+	}
+	
+	/**
+	 * 单机内存缓存限流信息
+	 * @author zzt
+	 *
+	 */
+	@ConditionalOnMissingBean(RateLimiter.class)
+	public static class MemoryConfiguration {
+		
+		@Bean
+		public RateLimiter memoryRateLimiter() {
+			log.debug("初始化MemoryRateLimiter!");
+			return new MemoryRateLimiter();
+		}
+	}
+}

+ 29 - 0
framework-gateway/src/main/java/com/micro/cloud/ratelimit/RateLimitProperties.java

@@ -0,0 +1,29 @@
+package com.micro.cloud.ratelimit;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Data
+@ConfigurationProperties(RateLimitProperties.PREFIX)
+public class RateLimitProperties {
+
+	/**
+	 * 限流属性配置前缀
+	 */
+	public static final String PREFIX = "zuul.ratelimit";
+	
+	/**
+	 * 每种类型的限流的策略
+	 */
+	private Map<String,Policy> policies = new LinkedHashMap<>();
+	
+	/**
+	 * 是否开启限流
+	 */
+	private boolean enabled;
+	
+	private boolean behindProxy;
+}

+ 12 - 0
framework-gateway/src/main/java/com/micro/cloud/ratelimit/RateLimiter.java

@@ -0,0 +1,12 @@
+package com.micro.cloud.ratelimit;
+
+public interface RateLimiter {
+
+	/**
+	 * 获取限制结果
+	 * @param policy 限流策略
+	 * @param key 缓存key
+	 * @return
+	 */
+	public Rate consume(Policy policy, String key);
+}

+ 72 - 0
framework-gateway/src/main/java/com/micro/cloud/ratelimit/repository/MemoryRateLimiter.java

@@ -0,0 +1,72 @@
+package com.micro.cloud.ratelimit.repository;
+
+import com.micro.cloud.ratelimit.Policy;
+import com.micro.cloud.ratelimit.Rate;
+import com.micro.cloud.ratelimit.RateLimiter;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 单机内存缓存限流信息
+ * @author zzt
+ *
+ */
+public class MemoryRateLimiter implements RateLimiter {
+	
+	private Map<String, Rate> respository = new ConcurrentHashMap<>();
+	
+	
+	@Override
+	public synchronized Rate consume(final Policy policy, final String key) {
+		Rate rate = create(policy,key);
+		replenish(rate);
+		respository.put(key, rate);
+		return rate;
+	}
+	
+	/**
+	 * 更新rate
+	 * @param rate
+	 */
+	private void replenish(Rate rate) {
+		if(rate.getReset()>0) {
+			Long reset = rate.getExpiration().getTime()-System.currentTimeMillis();
+			rate.setReset(reset);
+		}
+		rate.setRemaining(rate.getRemaining()-1);
+	}
+
+	/**
+	 * 获取一个rate不存在创建
+	 * 存在获取
+	 * @param policy
+	 * @param key
+	 * @return
+	 */
+	private Rate create(Policy policy, String key) {
+		Rate rate = respository.get(key);
+		if(isExpired(rate)) {
+			rate = new Rate();
+			final Long limit = policy.getLimit();
+			final Long refreshInterval = policy.getRefreshInterval();
+			final Date expiration = new Date(System.currentTimeMillis()+(refreshInterval*1000L));
+			
+			rate.setLimit(limit);
+			rate.setRemaining(limit);
+			rate.setExpiration(expiration);
+			rate.setReset(refreshInterval);
+		}
+		return rate;
+	}
+	
+	/**
+	 * 判断rate是否过期
+	 * @param rate
+	 * @return
+	 */
+	private boolean isExpired(Rate rate) {
+		return rate==null||(rate.getExpiration().getTime()<System.currentTimeMillis());
+	}
+}

+ 41 - 0
framework-gateway/src/main/java/com/micro/cloud/ratelimit/repository/RedisRateLimiter.java

@@ -0,0 +1,41 @@
+package com.micro.cloud.ratelimit.repository;
+
+import com.micro.cloud.ratelimit.Policy;
+import com.micro.cloud.ratelimit.Rate;
+import com.micro.cloud.ratelimit.RateLimiter;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.util.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redis中缓存限流信息
+ * @author zzt
+ *
+ */
+public class RedisRateLimiter implements RateLimiter {
+
+	private final RedisTemplate<String,?> template;
+	
+	public RedisRateLimiter(final RedisTemplate<String,?> template) {
+		Assert.notNull(template,"RedisTemplate cannot be null");
+		this.template = template;
+	}
+	
+	/**
+	 * 获取速率
+	 */
+	@Override
+	public Rate consume(final Policy policy, final String key) {
+		final Long limit = policy.getLimit();
+		final Long refreshInterval = policy.getRefreshInterval();
+		final Long current = template.boundValueOps(key).increment(1L);
+		Long expire = template.getExpire(key);
+		if(expire==null||expire==-1) {
+			template.expire(key, refreshInterval, TimeUnit.SECONDS);
+			expire = refreshInterval;
+		}
+		return new Rate(limit,Math.max(-1,limit - current),TimeUnit.SECONDS.toMillis(expire),null);
+	}
+
+}

+ 85 - 0
framework-gateway/src/main/java/com/micro/cloud/utils/HttpServletReqestUtil.java

@@ -0,0 +1,85 @@
+package com.micro.cloud.utils;
+
+import org.apache.commons.lang.StringUtils;
+
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 获取参数工具类
+ * @author zzt
+ *
+ */
+public class HttpServletReqestUtil {
+	
+	/**
+	 * 存放token的key
+	 */
+	public static final String AUTHOR_TOKEN = "Authority";
+
+	/**
+	 * 获取参数
+	 * @param request
+	 * @param parameter
+	 * @return
+	 */
+	public static String getParameter(HttpServletRequest request, String parameter) {
+		String value = getRequestValueIngoreFirstCharCase(request, parameter);
+		
+		if(StringUtils.isBlank(value)&&request instanceof ServletRequestWrapper) {
+			ServletRequestWrapper wrapper = (ServletRequestWrapper) request;
+			request = (HttpServletRequest) wrapper.getRequest();
+			value = getRequestValueIngoreFirstCharCase(request, parameter);
+		}
+		return value;
+	}
+	
+	/**
+	 * 获取参数,忽略首字母大小写
+	 * @param request
+	 * @param parameterKey
+	 * @return
+	 */
+	public static String getRequestValueIngoreFirstCharCase(HttpServletRequest request, String parameterKey) {
+		char[] parameterKeyChar = parameterKey.toCharArray();
+		parameterKeyChar[0] = Character.toUpperCase(parameterKeyChar[0]);
+		String upperParameterKey = String.valueOf(parameterKeyChar);
+		
+		parameterKeyChar[0] = Character.toLowerCase(parameterKeyChar[0]);
+		String lowerParameterKey = String.valueOf(parameterKeyChar);
+		
+		String value = request.getParameter(upperParameterKey);
+		
+		if(StringUtils.isBlank(value)) {
+			value = request.getParameter(lowerParameterKey);
+		}
+		return value;
+	}
+	
+	/**
+	 * 从request中获取信息
+	 * @param request
+	 * @param parameterKey
+	 * @return
+	 */
+	public static String getRequestInfo(HttpServletRequest request, String parameterKey) {
+		String value = request.getHeader(parameterKey);
+		if(StringUtils.isNotBlank(value)) {
+			return value;
+		}
+		value = getParameter(request,parameterKey);
+		if(StringUtils.isNotBlank(value)) {
+			return value;
+		}
+		Cookie[] cookies = request.getCookies();
+		if(cookies!=null) {
+			for(Cookie cookie : cookies) {
+				if(parameterKey.equalsIgnoreCase(cookie.getName())) {
+					return cookie.getValue();
+				}
+			}
+		}
+		return null;
+	}
+}

+ 112 - 0
framework-gateway/src/main/resources/application-dev.yml

@@ -0,0 +1,112 @@
+server:
+  port: 8080
+  tomcat:
+    accept-count: 500 #最大接受队列数
+    uri-encoding: UTF-8
+    max-connections: 1500 #最大连接数
+    connection-timeout: 5000 #等待超时的时间数(以毫秒为单位),设置为0表示永不超时
+    threads:
+      max: 1000 #最大线程数
+      min-spare: 100 #tomcat初始化时创建的线程数,此处我们设置为100
+
+spring:
+  application:
+    name: gateway
+  #redis
+  redis:
+    host: www.wendaocloud.com
+    port: 6379
+    password: 123456
+    timeout: 5000
+    jedis:
+      pool:
+        max-active: 200 #负值没有限制
+        max-idle: 10 #最大空闲
+        min-idle: 10 #最小空闲
+
+logging:
+  config: classpath:config/logback.xml
+
+#feign设置    
+feign:
+  httpclient:
+    enabled: true
+  hystrix:
+    enabled: true
+  compression:
+    request:
+      enabled: true
+      mime-types:
+      - text/xml
+      - application/xml
+      - application/json
+      - application/x-www-form-urlencoded
+      - application/form-data
+      min-request-size: 2048
+    response:
+      enabled: true    
+
+      
+ribbon:
+  ReadTimeout: 5000
+  eureka:
+    enabled: true
+  restclient:
+    enabled: true
+    
+#路由设置
+zuul:
+  RibbonRoutingFilter:
+    route:
+      disable: true
+  SendErrorFilter:
+    error:
+      disable: true
+  host:
+    max-total-connections: 30000
+    max-per-route-connections: 3000
+  sensitive-headers: Cookie,Set-Cookie,Authorization
+  ignored-headers: Access-Control-Allow-Credentials,Access-Control-Allow-Origin
+  retryable: false
+  routes:
+    ignoredServices: '*'
+    command-service:
+      path: /api/**
+    restful-service:
+      path: /rest/**
+  ratelimit:
+    enabled: true
+    behind-proxy: true
+    policies:
+      command-service:
+        limit: 1000 #请求次数限制
+        refresh-interval: 60 #1分钟,一分钟之内的请求限制
+        type:
+        - user
+        - origin
+        - url
+      restful-service:
+        limit: 1000 #请求次数限制
+        refresh-interval: 60 #1分钟,一分钟之内的请求限制
+        type:
+        - user
+        - origin
+        - url
+        
+#注册中心
+eureka:
+  instance:
+    lease-renewal-interval-in-seconds: 5
+    lease-expiration-duration-in-seconds: 10
+    prefer-ip-address: true
+    metadata-map:
+      version: gateway1.1.0
+      instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
+  client:
+    healthcheck:
+      enabled: true
+    registry-fetch-interval-seconds: 2
+    register-with-eureka: true
+    fetch-registry: true
+    service-url:
+      defaultZone: http://101.37.169.198:8035/eureka

+ 112 - 0
framework-gateway/src/main/resources/application-local.yml

@@ -0,0 +1,112 @@
+server:
+  port: 8080
+  tomcat:
+    accept-count: 500 #最大接受队列数
+    uri-encoding: UTF-8
+    max-connections: 1500 #最大连接数
+    connection-timeout: 5000 #等待超时的时间数(以毫秒为单位),设置为0表示永不超时
+    threads:
+      max: 1000 #最大线程数
+      min-spare: 100 #tomcat初始化时创建的线程数,此处我们设置为100
+
+spring:
+  application:
+    name: gateway
+  #redis
+  redis:
+    host: www.wendaocloud.com
+    port: 6379
+    password: 123456
+    timeout: 5000
+    jedis:
+      pool:
+        max-active: 200 #负值没有限制
+        max-idle: 10 #最大空闲
+        min-idle: 10 #最小空闲
+
+logging:
+  config: classpath:config/logback.xml
+
+#feign设置
+feign:
+  httpclient:
+    enabled: true
+  hystrix:
+    enabled: true
+  compression:
+    request:
+      enabled: true
+      mime-types:
+        - text/xml
+        - application/xml
+        - application/json
+        - application/x-www-form-urlencoded
+        - application/form-data
+      min-request-size: 2048
+    response:
+      enabled: true
+
+
+ribbon:
+  ReadTimeout: 5000
+  eureka:
+    enabled: true
+  restclient:
+    enabled: true
+
+#路由设置
+zuul:
+  RibbonRoutingFilter:
+    route:
+      disable: true
+  SendErrorFilter:
+    error:
+      disable: true
+  host:
+    max-total-connections: 30000
+    max-per-route-connections: 3000
+  sensitive-headers: Cookie,Set-Cookie,Authorization
+  ignored-headers: Access-Control-Allow-Credentials,Access-Control-Allow-Origin
+  retryable: false
+  routes:
+    ignoredServices: '*'
+    command-service:
+      path: /api/**
+    restful-service:
+      path: /rest/**
+  ratelimit:
+    enabled: true
+    behind-proxy: true
+    policies:
+      command-service:
+        limit: 1000 #请求次数限制
+        refresh-interval: 60 #1分钟,一分钟之内的请求限制
+        type:
+          - user
+          - origin
+          - url
+      restful-service:
+        limit: 1000 #请求次数限制
+        refresh-interval: 60 #1分钟,一分钟之内的请求限制
+        type:
+          - user
+          - origin
+          - url
+
+#注册中心
+eureka:
+  instance:
+    lease-renewal-interval-in-seconds: 5
+    lease-expiration-duration-in-seconds: 10
+    prefer-ip-address: true
+    metadata-map:
+      version: gateway1.1.0
+      instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
+  client:
+    healthcheck:
+      enabled: true
+    registry-fetch-interval-seconds: 2
+    register-with-eureka: true
+    fetch-registry: true
+    service-url:
+      defaultZone: http://101.37.169.198:8035/eureka

+ 3 - 0
framework-gateway/src/main/resources/application.yml

@@ -0,0 +1,3 @@
+spring:
+  profiles:
+    active: local

+ 48 - 0
framework-gateway/src/main/resources/config/logback.xml

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="false">
+    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
+    <property name="contextName" value="gateway" />
+    <property name="LOG_HOME" value="/var/log/${contextName}" />
+
+    <!-- 彩色日志 -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
+    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
+    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
+    <!-- 彩色日志格式 -->
+    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%20.20t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+    <!-- 控制台输出 -->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+            <!--<pattern>%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%thread] %-5level %logger{50} - %msg%n</pattern>-->
+            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+        </encoder>
+    </appender>
+
+    <!-- 按照每天生成日志文件 -->
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!--日志文件输出的文件名-->
+            <FileNamePattern>${LOG_HOME}/${contextName}-%d{yyyy-MM-dd}.log</FileNamePattern>
+            <!--日志文件保留天数-->
+            <MaxHistory>30</MaxHistory>
+        </rollingPolicy>
+
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+        </encoder>
+
+    </appender>
+
+<!--    <logger name="com.micro.cloud.management.dao" level="WARN"/>-->
+
+    <!-- 日志输出级别 -->
+    <root level="INFO">
+        <appender-ref ref="CONSOLE" />
+        <appender-ref ref="FILE" />
+    </root>
+
+</configuration>

+ 13 - 0
framework-gateway/src/test/java/com/micro/cloud/GatewayServiceApplicationTests.java

@@ -0,0 +1,13 @@
+package com.micro.cloud;
+
+import org.junit.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class GatewayServiceApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}

+ 19 - 0
framework-starter/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.mrxu</groupId>
+        <artifactId>mrxu-upgrader</artifactId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+    <groupId>com.mrxu</groupId>
+    <artifactId>framework-starter</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>framework-starter</name>
+    <description>mrxu starter</description>
+</project>

+ 24 - 0
pom.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.mrxu</groupId>
+        <artifactId>mrxu-upgrader</artifactId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+    <groupId>com.mrxu</groupId>
+    <artifactId>mrxu-framework</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>mrxu-framework</name>
+    <description>mrxu framework</description>
+    <modules>
+        <module>framework-common</module>
+        <module>framework-boot</module>
+        <module>framework-starter</module>
+    </modules>
+</project>