Spring Cloud(三):Gateway整合Sentinel

在上篇SpringCloud(二)Gateway工程内操作,不用新建工程

一.Gateway整合Sentinel

1.添加Sentinel的MVN依赖

builde.gradle:

compile group: 'com.alibaba.csp', name: 'sentinel-spring-cloud-gateway-adapter', version: '1.6.3'

compile group: 'com.alibaba.csp', name: 'sentinel-core', version: '1.6.3'

compile group: 'com.alibaba.csp', name: 'sentinel-transport-simple-http', version: '1.6.3'

compile group: 'com.alibaba.csp', name: 'sentinel-datasource-extension', version: '1.6.3'

settings.gradle:

rootProject.name = 'claim-gateway'

includeFlat 'clai-common'

2.创建GatewayConfiguration类

/**

 * Spring Cloud Gateway网关整合Sentinel限流

 * @author Joker

 */

@Configuration

public class GatewayConfiguration {

    private final List viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;



    public GatewayConfiguration(ObjectProvider> viewResolversProvider,

ServerCodecConfigurer serverCodecConfigurer) {

        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);

        this.serverCodecConfigurer = serverCodecConfigurer;

    }



    /*自定义异常*/

    @Bean

    @Order(Ordered.HIGHEST_PRECEDENCE)
    
    public JsonSentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {

    return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);

    }



    @Bean

    @Order(-5)

    public GlobalFilter sentinelGatewayFilter() {

        return new SentinelGatewayFilter();

    }



    @PostConstruct

    public void doInit() {

        initCustomizedApis();

        initGatewayRules();

        initDegradeRule();

        initSystemRule();

    }



    /*自定义分组代码(也可在配置文件配置)*/

    private void initCustomizedApis() {

        Set definitions = new HashSet<>();

        ApiDefinition api1 = new ApiDefinition("Service_API")

            .setPredicateItems(new HashSet() {{

            add(new ApiPathPredicateItem().setPattern("/serviceapi/**")

            .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX));

        }});



        ApiDefinition api2 = new ApiDefinition("Offline_API")

            .setPredicateItems(new HashSet() {{

            add(new ApiPathPredicateItem().setPattern("/offline/**")

            .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX));

        }});



        definitions.add(api1);

        definitions.add(api2);

        GatewayApiDefinitionManager.loadApiDefinitions(definitions);

    }



    /*配置限流规则*/

    private void initGatewayRules() {

        Set rules = new HashSet<>();

        rules.add(new GatewayFlowRule("Service_API")

            .setResourceMode(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX)

            .setCount(1) // 限流阈值

            .setIntervalSec(1)  // 统计时间窗口,单位是秒,默认是 1 秒

        );



        rules.add(new GatewayFlowRule("Offline_API")

            .setResourceMode(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX)

            .setCount(5)

            .setIntervalSec(1)

            .setParamItem(new GatewayParamFlowItem()

            .setParseStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX)

            .setFieldName("ack"))  //指定参数线流

        );

        GatewayRuleManager.loadRules(rules);

    }



    /*设置降级规则*/

    private static void initDegradeRule() {

        List rules = new ArrayList();

        DegradeRule rule = new DegradeRule();

        rule.setResource("Offline_API");   //资源名

        rule.setCount(1);   //平均响应时间阈值

        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); //降级模式,根据评价响应时间降级

        rule.setTimeWindow(5);   //设置降级时间

        rules.add(rule);

        DegradeRuleManager.loadRules(rules);

    }



    /*设置系统规则规则*/

    private static void initSystemRule() {

        List rules = new ArrayList();

        SystemRule rule = new SystemRule();

        rule.setHighestSystemLoad(3.0);

        rule.setHighestCpuUsage(0.6);

        rule.setAvgRt(10);

        rule.setQps(20);

        rule.setMaxThread(500);

        rules.add(rule);

        SystemRuleManager.loadRules(Collections.singletonList(rule));

      }

 }

 

3.创建自定义异常类

public class JsonSentinelGatewayBlockExceptionHandler implements WebExceptionHandler{



private final List viewResolvers;

private final List> messageWriters;



public JsonSentinelGatewayBlockExceptionHandler(List viewResolvers,

ServerCodecConfigurer serverCodecConfigurer) {

this.viewResolvers = viewResolvers;

this.messageWriters = serverCodecConfigurer.getWriters();

}



@Override

public Mono handle(ServerWebExchange exchange, Throwable ex) {

if (exchange.getResponse().isCommitted()) {

return Mono.error(ex);

}

if (!BlockException.isBlockException(ex)) {

return Mono.error(ex);

}

return handleBlockedRequest(exchange,ex).flatMap(response -> writeResponse(response,exchange));

}



/*重写writeResponse(),自定义返回的异常数据*/

private Mono writeResponse(ServerResponse response, ServerWebExchange exchange) {

ServerHttpResponse serverHttpResponse = exchange.getResponse();

serverHttpResponse.getHeaders().add("Context-Type", "application/json;charset=UTF-8");

//利用RestResult将异常数据封装,并转换呈Json格式

RestResult restResult = new RestResult();

restResult.setStatus(ReturnCode.REC_21.getCode());

restResult.setMsg(ReturnCode.REC_21.getName());

Gson gson = new Gson();

String json = gson.toJson(restResult);

//响应客户端异常数据

DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(json.getBytes());

return serverHttpResponse.writeWith(Mono.just(buffer));

}



private Mono handleBlockedRequest(ServerWebExchange exchange, Throwable ex) {

return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, ex);

}



}

4.新建gradle工程名为clai-common

不需要创建主启动类和配置文件,build.gradle:

buildscript {

repositories {

 maven{ url 'http://nexus.d17w.cn:9092/nexus/content/groups/public'}

}

}

apply plugin: 'java'

group 'com.claimplat'

version = '1.0.0'

sourceCompatibility = 1.8

targetCompatibility = 1.8



tasks.withType(JavaCompile){

options.encoding = "UTF-8"

}



jar {

baseName = 'claimplat-common'

}



repositories {

maven{ url 'http://nexus.d17w.cn:9092/nexus/content/groups/public'}

}



dependencies {   

    compile group: 'org.apache.commons', name: 'commons-lang3', version: "3.9"  

    compile group: 'commons-codec', name: 'commons-codec', version: "1.13"

    compile group: 'com.google.code.gson', name: 'gson', version: "2.8.5"

    compile group: 'com.alibaba', name: 'fastjson', version: "1.2.60"

    compile group: 'dom4j', name: 'dom4j', version: "1.6.1"      

    

testCompile group: 'ch.qos.logback', name: 'logback-classic'

}

创建RestResult类(异常数据自定义)

public class RestResult implements Serializable{

private static final long serialVersionUID = -5007715487173036938L;

/*返回的编码,标识接口调用成功或失败*/

protected String status = ReturnCode.REC_1.getCode();

/*返回的提示消息*/

protected String msg = ReturnCode.REC_1.getName();

/*返回的数据*/

protected Object data;



public static RestResult success(Object data) {

RestResult restResult = new RestResult();

restResult.setData(data);

return restResult;

}



public String getStatus() {

return status;

}



public void setStatus(String status) {

this.status = status;

}



public String getMsg() {

return msg;

}



public void setMsg(String msg) {

this.msg = msg;

}



public Object getData() {

return data;

}



public void setData(Object data) {

this.data = data;

}



@Override

public String toString() {

return "RestResult [status=" + status + ", msg=" + msg + ", data=" + data + "]";

}

}

创建ReturnCode枚举类:

public enum ReturnCode {

    REC_0("0","系统繁忙,请稍后再试",""),

    REC_1("1","操作成功",""),

    REC_2("2","参数验证出错",""),

    REC_3("3","参数映射错误",""),

    REC_4("4","认证出错",""),

    REC_5("5","找不到请求的接口地址",""),

    REC_6("6","不支持的请求方法",""),

    REC_7("7","不正确的Content-Type请求头标识",""),

    REC_10("10","请求出错了",""),

    REC_11("11","签名错误",""),

    REC_12("12","数据配置错误",""),

    REC_21("21","请求速度太快,请稍后再试",""),

    REC_22("22","非法的时间戳字段",""),

    REC_23("23","非法的重复请求",""),
    
    REC_100("100","请求错误",""),

    REC_2001("2001","商户不存在",""),

    REC_2002("2002","商户被禁用",""),

    REC_2003("2003","用户未登录",""),

    REC_2004("2004","账号密码错误","");



    private String code;

    private String name;

    private String desc;



    private ReturnCode(String code,String name,String desc) {

        this.code = code;

        this.name = name;

        this.desc = desc;

    }



    public String getCode() {

        return code;

    }

    public void setCode(String code) {

        this.code = code;

     }



    public String getName() {

        return name;

    }



    public String getDesc() {

        return desc;

    }

}

二.测试工程

1.创建新Gradle工程,名为claim-offline,build.gradle文件如下:

buildscript {

    ext{

        springBootVersion = '2.1.7.RELEASE'

    }

    repositories {

         maven{ url 'http://nexus.d17w.cn:9092/nexus/content/groups/public'}

         maven{ url 'http://repo.spring.io/libs-milestone'}

    }

    dependencies {

        classpath ( "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    
        classpath ( "io.spring.gradle:dependency-management-plugin:1.0.8.RELEASE")

        classpath ( "com.bmuschko:gradle-cargo-plugin:2.6.1")

    }

}



apply plugin: 'java'

apply plugin: 'war'

apply plugin: 'org.springframework.boot'

apply plugin: 'io.spring.dependency-management'



group 'com.claimplat'

version = '1.0.0'

sourceCompatibility = 1.8

targetCompatibility = 1.8



tasks.withType(JavaCompile){

    options.encoding = "UTF-8"

}



def env = System.getProperty("package.environment") ?: "dev"



jar{

    baseName = 'claimplat-offline'

}



war {

enabled = true

baseName = 'claimplat-offline'

archiveName 'offline.war'

}

repositories {

    maven{ url 'http://nexus.d17w.cn:9092/nexus/content/groups/public'}

    maven{ url 'http://repo.spring.io/libs-milestone'}

}



dependencyManagement{

    imports {

        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR2'

    }

}



dependencies {   

    compile project(':claimplat-common')   

    compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config'

    compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-eureka-client'

    compile group: 'org.springframework.boot', name: 'spring-boot-autoconfigure'

    compile group: 'org.springframework.boot', name: 'spring-boot-starter-logging'

    compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop'

    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'

    compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'

    compile group: 'org.springframework.boot', name: 'spring-boot-devtools'  



    testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'

    testCompile group: 'junit', name: 'junit'

}

2.创建主启动类和properties配置文件

@EnableDiscoveryClient

@ServletComponentScan

@SpringBootApplication

public class OfflineApplication implements ApplicationListener{



    private static final Logger LOGGER = LoggerFactory.getLogger(OfflineApplication.class);



    public static void main(String[] args) {

        SpringApplication.run(OfflineApplication.class, args);

}



    @Override

    public void onApplicationEvent(ApplicationEvent event) {

        if(event instanceof ApplicationEnvironmentPreparedEvent) {

            LOGGER.info("初始化环境变量");

        }else if(event instanceof ApplicationPreparedEvent) {

            LOGGER.info("初始化完成");

        }else if(event instanceof ContextRefreshedEvent) {

            LOGGER.info("应用刷新");

        }else if(event instanceof ApplicationReadyEvent) {

            LOGGER.info("应用已启动完成");

        }else if(event instanceof ContextStartedEvent) {

            LOGGER.info("应用启动");

        }else if(event instanceof ContextStoppedEvent) {

            LOGGER.info("应用停止");

        }else if(event instanceof ContextClosedEvent) {

            LOGGER.info("应用关闭");

        }else {
    
            //LOGGER.info("其他事件"+event.toString());

        }

    }

}

Propertise配置文件:

server.port=3083

server.servlet.context-path=/offline



spring.application.name=clai-offline

eureka.client.serviceUrl.defaultZone=http://localhost:8761/claimplat-eureka/eureka/

eureka.instance.preferIpAddress=true

3.创建测试类

@RestController

@RequestMapping(value = "/out")

public class OutInterfaceRest {



protected Logger logger = LoggerFactory.getLogger(getClass());



    @RequestMapping(value = "/ack", method = {RequestMethod.GET,RequestMethod.POST})

    public RestResult ack() {

        RestResult result = new RestResult();

        try {

            result.setData("ack ok");

            logger.info("ack ok");

        } catch (Exception e) {

            logger.error("心跳检测出错",e);

        }

            return result;

    }

}

 

三.Sentinel Dashboard(控制台)接入工程

--本地运行:

本地下载windows版sentinel-dashboard-1.6.3.jar:

在此文件目录进入cmd,执行java -jar sentinel-dashboard-1.6.3.jar指令

Spring Cloud(三):Gateway整合Sentinel_第1张图片

工程接入sentinel控制台,启动时需配置JVM启动参数:

-Dcsp.sentinel.dashboard.server=localhost:8080             --sentinel 控制台ip:port

-Dcsp.sentinel.api.port=8666                                            --客户端用于接收控制台流控规则端口号

-Dproject.name=Sentinel-Windows                                       --名称

-Dcsp.sentinel.app.type=1                                                 --Gateway接入Sentinel需要配置(1.6.3版本才有)

可自定义用户与密码参数:

-Dsentinel.dashboard.auth.username=sentinel

-Dsentinel.dashboard.auth.password=123456

(注:触发客户端连接控制台,客户端配置好了与控制台的连接参数之后,并不会主动连接上控制台,需要触发一次客户端的规则才会开始进行初始化,并向控制台发送心跳和客户端规则等信息。)

 

访问控制台地址:http://localhost:8080 默认ID与PWD:sentinel

Step01:运行clai-eureka工程(必须先运行注册中心)

Step02:clai-gateway工程需设置JVM启动参数再运行

Step03:运行clai-offline测试工程

Step04:通过Gateway网关端口访问offline测试工程(localhost:3562/offline/out/ack)

 

--服务器运行

服务器下载Linux版sentinel-dashboard.jar,运行时JVM参数如下:

java -server -XX:+UseG1GC -Xmx2048m

-Dserver.port=8152                                                            --控制台port

-Dcsp.sentinel.dashboard.server=localhost:8152              --指定控制台地址:服务器ip:port,启动后可看到自身信息

-Dproject.name=Sentinel-Linux

 -jar sentinel-dashboard-1.6.3.jar                                        --控制它jar包文件

 

将gateway工程打成jar文件放入服务器中,运行时JVM参数如下:

java -server -XX:+UseG1GC -Xmx2048m

-Dcsp.sentinel.api.port=8666                                            --API机器端口

-Dcsp.sentinel.dashboard.server=10.0.98.75:8152          --服务器ip:port

-Dproject.name=Claimplat-gateway                                  --名称

-Dcsp.sentinel.app.type=1

-jar claimplat-gateway-1.0.0.jar                                         --项目jar包文件

 

访问控制台地址:http://服务器IP:控制台port 默认ID与PWD:sentinel

 

PS:希望各位点赞支持,感谢

你可能感兴趣的:(Spring Cloud(三):Gateway整合Sentinel)