Spring-cloud-alibaba 系统搭建

Spring-cloud-alibaba 系统架构图

SpringAlibabaCloud架构使用版本

注:springboot,spring-cloud,spring-cloud-alibaba相关jar有冲突,需要注意版本;

系统核心组件及架包

系统版本

Nacos中间件

1.1.4

Sentinel中间件

1.8.4

Zipkin-server 中间件

2.23.19

org.springframework.boot

2.2.4.RELEASE

spring-cloud-dependencies

Hoxton.SR1

spring-cloud-alibaba-dependencies

2.2.0.RELEASE

spring-cloud-starter-alibaba-nacos-discovery

1.5.0.RELEASE

spring-cloud-starter-alibaba-nacos-config

2.2.0.RELEASE

nacos-client

1.1.4

spring-cloud-starter-alibaba-sentinel

1.5.1.RELEASE

sentinel-datasource-nacos

1.7.1

sentinel-core

1.7.1

spring-cloud-starter-gateway

2.2.1.RELEASE

spring-cloud-starter-bootstrap

3.0.1

spring-boot-starter-webflux

2.2.4.RELEASE

spring-cloud-starter-zipkin

2.2.1.RELEASE

spring-cloud-starter-openfeign

2.2.1.RELEASE

创建SpringBoot项目 ddky-risk-cloud

同理创建子项目:

ddky-risk-generate:用于生成JDBC相关基础代码

ddky-risk-core : 用于基础对象,工具Util核心jar

ddky-risk-feign: 用于web服务调用封装

ddky-risk-products: 用于提供提供者服务

ddky-risk-web:用于提供消费者服务

ddky-risk-gateway: 用于网关转发服务

 Spring-cloud-alibaba-nacos 搭建

注册中心nacos单机安装

下载地址:https://github.com/alibaba/nacos/releases/tag/1.1.4

(版本兼容问题)

Windows 单机启动

cd D:\fills-tools\spring-cloud\nacos\bin

执行命令,单集群模式

startup.cmd -m standalone 

http://localhost:8848/nacos

name/password  -> nacos/nacos

ddky-risk-cloud  pom 父级依赖

<dependencyManagement>

    <dependencies>

        <!--spring cloud-->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-parent</artifactId>

            <version>2.2.4.RELEASE</version>

            <type>pom</type>

            <scope>import</scope>

        </dependency>

       <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Hoxton.SR1</version>

            <type>pom</type>

            <scope>import</scope>

        </dependency>

        <dependency>

            <groupId>com.alibaba.cloud</groupId>

            <artifactId>spring-cloud-alibaba-dependencies</artifactId>

            <version>2.2.0.RELEASE</version>

            <scope>import</scope>

            <type>pom</type>

        </dependency>

        <dependency>

            <groupId>com.alibaba.cloud</groupId>

          <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

            <version>1.5.0.RELEASE</version>

        </dependency>

        <!--spring cloud-->

...... 其他依赖

</dependencyManagement>

ddky-risk-products  pom 依赖

<!-- 向注册中心进行服务注册 -->

<dependency>

        <groupId>com.alibaba.nacos</groupId>

        <artifactId>nacos-client</artifactId> <!--1.1.4-->

       <version>1.1.4</version>

</dependency>

<dependency>

      <groupId>com.alibaba.cloud</groupId>

      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

      <version>1.5.0.RELEASE</version>

</dependency>

<!-- 向注册中心进行服务注册 -->

ddky-risk-products  yml nacos配置

spring:

  application:

    name: ddky-risk-cloud-provider

  cloud:

    nacos:

      discovery:

        server-addr: 127.0.0.1:8848

ddky-risk-products  提供服务demo

@RestController

@RequestMapping("/riskConfig")

@Slf4j

public class RiskConfigServer {

    @Autowired

    private RiskConfigService riskConfigService;

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

    public RiskConfig getRiskConfig(String riskCode){

        log.info("请求参数:{}",riskCode);

        RiskConfig res = riskConfigService.queryRiskConfig(riskCode); // *service 服务

        log.info("响应结果:{}",JsonTools.writeValueAsString(res));

        return res;

    }

}

ddky-risk-web  pom 依赖

<!-- 向注册中心进行服务注册 -->

<dependency>

        <groupId>com.alibaba.nacos</groupId>

        <artifactId>nacos-client</artifactId> <!--1.1.4-->

        <version>1.1.4</version>

</dependency>

<dependency>

      <groupId>com.alibaba.cloud</groupId>

      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

      <version>1.5.0.RELEASE</version>

</dependency>

<!-- 向注册中心进行服务注册 -->

ddky-risk-web  yml 配置

spring:

  application:

    name: ddky-risk-cloud-consumer

  cloud:

    nacos:

      discovery:

        server-addr: 127.0.0.1:8848

ddky-risk-web访问服务端,手动RestTemplate访问

RestTemplate 手动实现访问服务端服务,只做了解

ServiceInstanceLoadBalancer 手动实现负载均衡,只做了解

实战应用场景参考:Spring-cloud-alibaba-feign 搭建

@RestController

@RequestMapping("riskConfig")

public class RiskConfigController extends BaseController {

    @Autowired

    protected ServiceInstanceLoadBalancer serviceInstanceLoadBalancer;

    @GetMapping(value = "/getRiskConfig", produces = {"application/json;charset=UTF-8"})

    @ResponseBody

    public ServiceResult<?> getRiskConfig(String riskCode) {

        try {

            ServiceResult result = new ServiceResult<>();

//serviceInstance = loadBalancerClient.choose("ddky-risk-cloud-provider");

            ServiceInstance serviceInstance = serviceInstanceLoadBalancer.getServiceInstance();

            String targetUrl = serviceInstance.getUri() + "/riskConfig/getRiskConfig";

            log.info("获取服务端请求地址"+targetUrl);

            LinkedMultiValueMap<String, Object> params = new LinkedMultiValueMap<String,Object>();

            params.add("riskCode",riskCode);

            Map<String,Object> objectMap = new HashMap<>();

            objectMap.put("riskCode",riskCode);

            RiskConfig config = restTemplate.postForObject(targetUrl,params,RiskConfig.class,objectMap);

            result.setResult(config);

            return  result;

        }catch (Exception e){

            log.error("保存异常",e);

            return new ServiceResult<>(ResultCodeEnum.failure.getIndex(),e.getMessage());

        }

    }

}

ServiceInstanceLoadBalancer 自定义负载均衡

ServiceInstanceLoadBalancer 自定义负载均衡也可以使用springcloud的LoadBalancerClient负载均衡客户端

只做了解

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.discovery.DiscoveryClient;

import org.springframework.stereotype.Component;

import java.net.InetAddress;

import java.util.List;

import java.util.Random;

import java.util.concurrent.atomic.AtomicInteger;

/**

 * nacos 手动实现服务治理负责均衡 随机,轮询,Hash

 */

@Component

public class ServiceInstanceLoadBalancer {

    //nacos 提供者服务名称

    @Value("${server.cloud.name}")

    private String providerServer;

    //负载均衡策略,该配置可改成动态方式

    @Value("${server.instance.type:0}")

    private String instanceType; // 0-随机,1-轮询,2-Hash

    //随机

    private static final String RANDOM = "0";

    //轮询

    //private static final String POLL = "1";

    //Hash

    private static final String HASH = "2";

    //获取随机数

    private static final Random random = new Random();

    //nacos注册服务客户端

    @Autowired

    protected DiscoveryClient discoveryClient;

    //CAS 原子类

    private AtomicInteger poll = new AtomicInteger(0);

    //获取服务提供者

    public ServiceInstance getServiceInstance(){

        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(providerServer);

        ServiceInstance serviceInstance =null;

        try {

            if (RANDOM.equals(instanceType)) {//随机

                serviceInstance = serviceInstanceList.get(random.nextInt(serviceInstanceList.size()));

            } else if (HASH.equals(instanceType)) {//IP Hash

                serviceInstance = serviceInstanceList.get(getIpHash() % serviceInstanceList.size());

            } else {//轮询,默认

                serviceInstance = serviceInstanceList.get(poll.getAndIncrement() % serviceInstanceList.size());

            }

        }catch (Exception e){

        }

        return serviceInstance;

    }

    //获取IP Hash

    private int getIpHash(){

        try {

            return InetAddress.getLocalHost().getHostAddress().hashCode();

        } catch (Exception e) {

            return 0;

        }

    }

}

属性字段解释

String providerServer : nacos 服务端提供者服务名称

String instanceType :负载均衡策略,该配置可改成动态方式(disconf,apollo,redis)

Random random :随机数轮询

AtomicInteger poll:CAS 原子类 用于轮询自增使用 

DiscoveryClient discoveryClient: nacos 提供者客户端获取所有服务

方法解释

public ServiceInstance getServiceInstance()

根据instanceType策略(0-随机,1-轮询,2-Hash)获取服务端server

private int getIpHash()

获取本地IP地址转为HashCode(int),用于Hash方式获取服务端server

演示效果

instanceType (0-随机)策略

instanceType (1-轮询)策略

instanceType (2-Hash)策略

 Spring-cloud-alibaba-feign 搭建

ddky-risk-feign pom 依赖

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-openfeign</artifactId>

<version>2.2.1-SNAPSHOT</version>

</dependency>

ddky-risk-feign yml 配置

注:Feign 配置可以忽略,可以针对线上环境具体配置

feignName:FeginClient的名称

connectTimeout : 建立链接的超时时长

readTimeout : 读取超时时长

loggerLevel: Fegin的日志级别

errorDecoder :Feign的错误解码器

retryer : 配置重试

requestInterceptors : 添加请求拦截器

decode404 : 配置熔断不处理404异常

feign:

  client:

    config:

      feignName: ##定义FeginClient的名称

        connectTimeout: 5000 # 链路链接超时时间

        readTimeout: 5000 # 读取超时时间

        # 配置Feign的日志级别,相当于代码配置方式中的Logger

        loggerLevel: full

ddky-risk-feign web -> web实现

ddky-risk-feign FeignClient 配置

value、name作用一样,配置nacos服务名称,用于服务发现

url用于配置指定服务的地址,相当于直接请求这个服务,不经过Ribbon的服务选择

path:定义当前FeignClient访问接口时统一前缀,例接口地址是/riskConfig/getRiskConfig, 定义了前缀是riskConfig, 同Spring的RequestMapping

contextId唯一标识,不想将所有的调用接口都定义在一个类中,手动指定不同的contextId解决冲突

decode404:请求报404错误时,值为true时执行decoder解码,否则抛出异常

configuration配置Feign配置类,在配置类中可以自定义Feign的Encoder、Decoder、LogLevel、Contract等

fallback:容错的处理类,也就是回退逻辑,fallback的类必须实现Feign Client的接口,无法知道熔断的异常信息。

fallbackFactory:容错的处理类,可以知道熔断的异常信息

ddky-risk-feign 接口封装

package com.ddky.risk.cloud.feign;

import com.ddky.risk.cloud.domain.RiskConfig;

import com.ddky.risk.cloud.response.ServiceResult;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value="ddky-risk-cloud-provider",path ="/riskConfig")

public interface RiskConfigFeign {

    @RequestMapping("/getRiskConfig")

    public ServiceResult<RiskConfig> getRiskConfig(@RequestParam(value="riskCode") String riskCode);

}

ddky-risk-products 提供服务

服务端web服务和feign提供的接口保持一致

@RestController

@RequestMapping("/riskConfig")

@Slf4j

public class RiskConfigServer {

    @Autowired

    private RiskConfigService riskConfigService;

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

    @ResponseBody

    public ServiceResult<RiskConfig> getRiskConfig(HttpServletRequest request,String riskCode){

        log.info("请求参数:{}",riskCode);

        ServiceResult result = new ServiceResult();

        RiskConfig res = riskConfigService.queryRiskConfig(riskCode);

        log.info("响应结果:{}",JsonTools.writeValueAsString(res));

        result.setResult(res);

        return result;

    }

}

ddky-risk-web 消费服务

@Slf4j

@RestController

@RequestMapping("riskConfig")

public class RiskConfigController extends BaseController {

    

    @Autowired

    private RiskConfigFeign riskConfigFeign;

    @GetMapping(value = "/getRiskConfigFeign", produces = {"application/json;charset=UTF-8"})

    @ResponseBody

    public ServiceResult<?> getRiskConfigFeign(ServiceRequest<String> params) {

        try {

            ServiceResult<RiskConfig> result = riskConfigFeign.getRiskConfig(params.getData());

            return result;

        }catch (Exception e){

            log.error("保存异常",e);

            return new ServiceResult<>(ResultCodeEnum.failure.getIndex(),e.getMessage());

        }

    }

}

ddky-risk-feign 演示

 Gateway

Web

Products

Zipkin

 Spring-cloud-alibaba-sentienl 搭建

sentienl服务端安装

下载地址:https://github.com/alibaba/Sentinel/releases

启动脚本

java -jar D:/fills-tools/spring-cloud/sentinel-dashboard-1.8.1.jar --server.port=8881

或者:java -jar ./sentinel-dashboard-1.8.1.jar

用户名/密码   sentinel/sentinel

ddky-risk-products sentienl pom 配置

<!-- 向注册中心进行服务注册 -->

<dependency>

        <groupId>com.alibaba.nacos</groupId>

        <artifactId>nacos-client</artifactId><!--1.1.4-->

<version>1.1.4</version>

</dependency>

<dependency>

    <groupId>com.alibaba.cloud</groupId>

    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

<version>1.5.1.RELEASE</version>

</dependency>

<dependency>

    <groupId>com.alibaba.cloud</groupId>

    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>

    <version>1.5.1.RELEASE</version>

</dependency>

<dependency>

    <groupId>com.alibaba.csp</groupId>

    <artifactId>sentinel-datasource-nacos</artifactId>

<version>1.7.1</version>

</dependency>

<dependency>

       <groupId>com.alibaba.csp</groupId>

       <artifactId>sentinel-core</artifactId>

       <version>1.7.1</version>

</dependency>

ddky-risk-products配置暴露/actuator/sentinel

#添加sentinel依赖后 暴露/actuator/sentinel

management:

  endpoints:

    web:

      exposure:

        include: '*'

查看actuator/sentinel配置

http://localhost:8882/actuator/sentinel

ddky-risk-products  yml  sentienl配置

spring:

  application:

    name: ddky-risk-cloud-provider

  cloud:

    nacos:

      discovery:

        server-addr: 127.0.0.1:8848

    sentinel:  # sentinel 接入配置

      eager: true

      transport:

        dashboard: 127.0.0.1:8080   #指定sentinel控制台的地址

        port: 8719

        clientIp: 127.0.0.1

      datasource:  #sentinel使用nacos接入持久化配置

        flow:

          nacos:

            serverAddr: 127.0.0.1:8848   

            dataId: ddky-risk-cloud-provider-flow

            groupId: DEFAULT_GROUP

            dataType: json

            ruleType: flow

ddky-risk-products 封装请求/响应参数

封装请求参数对象data

data为泛型,用于封装自定义sentinel处理器,入参统一

package com.ddky.risk.cloud.request;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import java.io.Serializable;

@JsonIgnoreProperties(ignoreUnknown = true)

public class ServiceRequest <T> implements Serializable {

    private static final long serialVersionUID = -1L;

    private T data;

    public ServiceRequest(T data) {

        this.data = data;

    }

    public ServiceRequest() {

    }

    public T getData() {

        return data;

    }

    public void setData(T data) {

        this.data = data;

    }

}

封装响应参数对象result

result为泛型,用于封装自定义sentinel处理器,出参统一

package com.ddky.risk.cloud.response;

import com.ddky.risk.cloud.enums.ResultCodeEnum;

import com.ddky.risk.cloud.exception.BusinessException;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import java.io.Serializable;

/**

 * 通用返回值

 */

@JsonIgnoreProperties(ignoreUnknown = true)

public class ServiceResult<T> implements Serializable {

   

   private static final long serialVersionUID = -394529804715984961L;

   private Integer code;

   private String msg;

   private T result;

   public ServiceResult() {

      this(ResultCodeEnum.Success);

   }

   public ServiceResult(ResultCodeEnum resultCode) {

      this.code = resultCode.getIndex();

      this.msg = resultCode.getMessage();

   }

   public ServiceResult(Integer code, String msg) {

      this.code = code;

      this.msg = msg;

   }

   public ServiceResult(BusinessException e) {

      this.code = e.getCode();

      this.msg = e.getMessage();

   }

...

   public Integer getCode() {

      return this.code;

   }

   public void setCode(Integer code) {

      this.code = code;

   }

    ...

}

自定义sentinel处理器

封装自定义处理器是因为sentinel 需要方法入参(参数最后多了一个异常)和出参与原方法保值一致才能拦截到sentinel阻断异常见:SentienlException

package com.ddky.risk.cloud.exception;

import com.alibaba.csp.sentinel.slots.block.BlockException;

import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;

import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;

import com.alibaba.csp.sentinel.slots.block.flow.FlowException;

import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;

import com.ddky.risk.cloud.enums.ResultCodeEnum;

import com.ddky.risk.cloud.request.ServiceRequest;

import com.ddky.risk.cloud.response.ServiceResult;

// 自定义sentinel处理器

public class SentienlException {

    /**

     * 系统异常时,捕获并返回错误

     * @param data

     * @param e

     * @return

     */

    public static ServiceResult errorException(ServiceRequest data,Throwable e){

        return new ServiceResult(ResultCodeEnum.failure.getIndex(),e.getClass().getName()+":"+e.getMessage());

    }

    /**

     * sentinel 限流异常时,封装并返回错误

     * @param data

     * @param e

     * @return

     */

    public static ServiceResult blockException(ServiceRequest data,BlockException e){

        if (e instanceof FlowException) {//限制异常

            return new ServiceResult(ResultCodeEnum.flow_error);

        } else if (e instanceof ParamFlowException) {//热点参数异常

            return new ServiceResult(ResultCodeEnum.hot_error);

        } else if (e instanceof DegradeException) {//降级异常

            return new ServiceResult(ResultCodeEnum.degrade_error);

        } else if (e instanceof AuthorityException) {//受权异常

            return new ServiceResult(ResultCodeEnum.auth_error);

        }

        //提供安全异常

        return new ServiceResult(ResultCodeEnum.system_block_error);

    }

}

应用示例

SentinelResource使用说明:

Fallback系统异常时回调方法

FallbackClass指定系统异常处理类,不配是默认当前类下

BlockHandler系统阻断时处理方法

BlockHandlerClass指定系统阻断处理类,不配是默认当前类下

package com.ddky.risk.cloud.webserver;

import com.alibaba.csp.sentinel.annotation.SentinelResource;

import com.ddky.risk.cloud.domain.RiskConfig;

import com.ddky.risk.cloud.exception.SentienlException;

import com.ddky.risk.cloud.request.ServiceRequest;

import com.ddky.risk.cloud.response.ServiceResult;

import com.ddky.risk.cloud.service.nacos.RiskConfigService;

import com.ddky.risk.cloud.util.JsonTools;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;

/**

 * @author ysf

 * @version 1.0

 * @description: TODO

 * @date 2022/2/14 10:07

 */

@RestController

@RequestMapping("/riskConfig")

@Slf4j

public class RiskConfigServer {

    @Autowired

    private RiskConfigService riskConfigService;

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

    @ResponseBody

    @SentinelResource(value="/getRiskConfig",blockHandler = "blockException",blockHandlerClass = {SentienlException.class},fallback = "errorException",fallbackClass = {SentienlException.class})

    public ServiceResult<RiskConfig> getRiskConfig(ServiceRequest<String> riskCode){

        log.info("请求参数:{}",riskCode);

        ServiceResult result = new ServiceResult();

        RiskConfig res = riskConfigService.queryRiskConfig(riskCode.getData());

        log.info("响应结果:{}",JsonTools.writeValueAsString(res));

        result.setResult(res);

        return result;

    }

}

正常结果:

阻断结果:

异常结果

sentienl 监控配置方式(nacos配置中心)

从nacos服务端加载sentinel服务端的配置

配置数据实体对象参考:com.alibaba.cloud.sentinel.datasource.RuleType

Sentinel切面执行器:com.alibaba.csp.sentinel.annotation.aspectj

.SentinelResourceAspect

FLOW("flow", FlowRule.class),

DEGRADE("degrade", DegradeRule.class),

PARAM_FLOW("param-flow", ParamFlowRule.class),

SYSTEM("system", SystemRule.class),

AUTHORITY("authority", AuthorityRule.class),

GW_FLOW("gw-flow", "com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),

GW_API_GROUP("gw-api-group", "com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");

流控规则说明:FlowRule

Resource:资源名是限流规则的作用对象

LimitApp:流控针对的调用来源,若为 default 则不区分调用来源

Grade:限流阈值类型,QPS 模式(1)或并发线程数模式(0)

Count:限流阈值

Strategy:调用关系限流策略:直接-0、链路-1、关联-2

ControlBehavior:流量控制效果(直接拒绝-0、Warm Up-1、匀速排队-2)

ClusterMode:是否集群限流 是-true、否-false

[{

    "resource": "/getRiskConfig",

    "limitApp": "default",

    "grade": 1,

    "count": 1,

    "strategy": 0,

    "controlBehavior": 0,

    "clusterMode": false

}]

降级规则说明:DegradeRule

resource:资源名,

grade:熔断策略0-慢比例调用,1-异常比例,2-异常数,

count:grade=0,毫秒数;=1,比列阀值[0.0,1.0];=2,异常数,

slowRatioThreshold:grade=0,比例阈值[0.0,1.0],

timeWindow":熔断时长秒,

minRequestAmount:最小请求数,

statIntervalMs:统计时长(ms)

[{

  "resource": "/getRiskConfig",

  "grade": "0",

  "count": "1",

  "slowRatioThreshold": "0.1",

  "timeWindow": "1",

  "minRequestAmount": "1",

  "statIntervalMs": "1000"

}]

热点数据规则说明:ParamFlowRule

resource:资源名,

paramIdx:热点参数的索引,

count:单机阈值,

durationInSec:统计窗口时长(单位为 秒),

clusterMode":是否集群,

paramFlowItemList.classType:参数数据类型:int,double,byte,float,long,char,java.lang.String

paramFlowItemList.object:参数值

paramFlowItemList.count:限流阈值

注:该场景用于接口验证参数是基础数据,入参是引用对象不可用,所以不适用本次封装的请求对象

[{

  "resource": "/getRiskConfig",

  "paramIdx": "0",

  "count": "1",

  "durationInSec": "1",

  "clusterMode": false,

  "paramFlowItemList":[{

  "classType": "java.lang.String",

  "object": "10001",

  "count":"1"

  }]

}]

系统规则说明:systemRule

grade:0-LOAD,1-RT,2-线程数,3-入口QPS,4-CPU使用率

highestSystemLoad:LOAD

avgRt:RT

maxThread:线程数

qps:入口 QPS

highestCpuUsage:CPU 使用率

注:entryType = EntryType.IN 切面配置内部资源调用才会触发系统保护规则

@SentinelResource(value="/getRiskConfig",entryType= EntryType.IN,blockHandler = "blockException",blockHandlerClass = {SentienlException.class},fallback = "errorException",fallbackClass = {SentienlException.class})

[{

  "highestCpuUsage:CPU":1

}]

授权规则说明:AuthorityRule

resource:资源名

limitApp:流控应用,多个调用方名称用半角英文逗号(,)分隔

strategy:授权类型0-白名单,1-黑名单

[{

  "resource":"/getRiskConfig",

  "limitApp":"",

  "strategy":0

}]

Spring-cloud-alibaba-gateway 搭建

ddky-risk-gateway gateway pom 配置

<dependency>

    <groupId>com.alibaba.cloud</groupId>

    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

<version>1.5.1.RELEASE</version>

</dependency>

<dependency>

    <groupId>com.alibaba.cloud</groupId>

    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>

<version>2.2.0.RELEASE</version>

</dependency>

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-gateway</artifactId>

<version>2.2.1.RELEASE</version>

</dependency>

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-bootstrap</artifactId>

    <version>3.0.1</version><!-- 使用nacos动态配置使用 -->

</dependency>

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-webflux</artifactId>

<version>2.2.4.RELEASE</version>

</dependency>

ddky-risk-gateway yml gateway非动态方式配置

注:该方式是写死在application.yml配置,每次需要新增修改,需要重新发布应用,只做了解不做使用

server:

  port: 8888

logging:

  config: classpath:logback.xml

  level:

    org:

      springframework:

        boot:

          autoconfigure: ERROR         # 日志不打印条件评估报告

spring:

  application:

    name: ddky-risk-cloud-getway # 应用名称

  cloud:

    nacos:

      discovery:

        server-addr: localhost:8848

    gateway:

      discovery:

        locator:

          enabled: true

      routes:

        - id: ddky-risk-cloud-consumer  #路由标识符,区别于其他 Route

          uri: lb://ddky-risk-cloud-consumer #路由指向的目的地 uri,即客户端请求最终被转发到的微服务

          predicates:  #断言,用于条件判断,只有断言都返回真,才会真正的执行路由

            - Path=/riskConfig/**

ddky-risk-gateway yml gateway nacos动态方式配置

注:该方式是需要通过application.yml和bootstrap.yml (gateway nacos 需要使用),每次只要在nacos配置中心新增修改,保存后即可实时刷新并应用

application.yml

server:

  port: 8888

logging:

  config: classpath:logback.xml

  level:

    org:

      springframework:

        boot:

          autoconfigure: ERROR         # 日志不打印条件评估报告

spring:

  application:

    name: ddky-risk-cloud-getway # 应用名称

bootstrap.yml

spring:

  cloud:

    nacos:

      discovery:

        server-addr: 127.0.0.1:8848

      config: #nacos config 配置

        server-addr: 127.0.0.1:8848

        file-extension: yaml #文件内容及格式

        group: DEFAULT_GROUP

        prefix: ddky-risk-cloud-getway  #nacos 动态配置前缀

nacos 配置中心

配置内容 spring.cloud.gateway.routes List集合

-id:# 路由唯一标识符

uri:# 路由目标地址,通过gateway转发到改地址 http原链接转发 或 lb://nacos 负载均衡

predicates:#断言 用于条件判断 List集合

- Path=/xxx/**

为一个独立子集,可以同时配置多个

ddky-risk-gateway 页面演示

 Html页面响应

例如:http://localhost:8888/riskConfig/index

注:gateway转发调转页面样式错乱

解决方案一:

需要在原页面指定具体css,js,image等静态资源的具体路径

例如:

<script src="${path}/static/js/other/select/dynamic-column.js" type="text/javascript"></script>

改为

<script src="http://localhost:8081/static/js/other/select/dynamic-column.js" type="text/javascript"></script>

解决方案二:

新增静态资源转发配置:ddky-risk-cloud-consumer-static

spring:

  cloud:

    gateway:

      discovery:

        locator:

          enabled: true

      routes:

        - id: ddky-risk-cloud-consumer  #路由标识符,区别于其他 Route

          uri: lb://ddky-risk-cloud-consumer #路由指向的目的地 uri,即客户端请求最终被转发到的微服务

          predicates:  #断言,用于条件判断,只有断言都返回真,才会真正的执行路由

            - Path=/riskConfig/**

        - id: ddky-risk-cloud-consumer-static  #路由标识符,区别于其他 Route

          uri: lb://ddky-risk-cloud-consumer #路由指向的目的地 uri,即客户端请求最终被转发到的微服务

          predicates:  #断言,用于条件判断,只有断言都返回真,才会真正的执行路由  

            - Path=/static/**

解决后效果:

API接口restful风格

以JSON对象返回结果集,建议统一入参和出参数据封装,避免接口不统一,影响阅读及风格不统一开发耗时

Spring-cloud-alibaba-sleuth+zipkin 搭建

Zipkin下载启动

官网地址

用于存储全链路调用链信息

https://zipkin.io/

下载链接 

https://repo1.maven.org/maven2/io/zipkin/zipkin-server/2.23.19/zipkin-server-2.23.19-exec.jar

启动不指定数据库:

java -jar zipkin-server-2.23.19-exec.jar

启动指定数据库

java -jar zipkin-server-2.23.19-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3310 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=root

数据库脚本

CREATE TABLE IF NOT EXISTS zipkin_spans (

  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',

  `trace_id` BIGINT NOT NULL,

  `id` BIGINT NOT NULL,

  `name` VARCHAR(255) NOT NULL,

  `remote_service_name` VARCHAR(255),

  `parent_id` BIGINT,

  `debug` BIT(1),

  `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',

  `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query',

  PRIMARY KEY (`trace_id_high`, `trace_id`, `id`)

) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';

ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';

ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames';

ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';

CREATE TABLE IF NOT EXISTS zipkin_annotations (

  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',

  `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',

  `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',

  `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',

  `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',

  `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',

  `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',

  `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',

  `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',

  `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',

  `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'

) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';

ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';

ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';

ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';

ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values';

ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values';

ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';

CREATE TABLE IF NOT EXISTS zipkin_dependencies (

  `day` DATE NOT NULL,

  `parent` VARCHAR(255) NOT NULL,

  `child` VARCHAR(255) NOT NULL,

  `call_count` BIGINT,

  `error_count` BIGINT,

  PRIMARY KEY (`day`, `parent`, `child`)

) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

Spring-cloud-alibaba-sleuth pom 配置

注:spring-cloud-starter-zipkin jar包含(spring-cloud-starter-sleuth

spring-cloud-sleuth-zipkin 

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-zipkin</artifactId>

<version>2.2.1-SNAPSHOT</version>

</dependency>

Spring-cloud-alibaba-sleuth yml 配置

spring:

  sleuth:

    web:

      client:

        enabled: true #开启链路采集

    sampler:

      probability: 1.0 #默认采集数据量 1-百分百,0.1-百分十

  zipkin:

    base-url: http://127.0.0.1:9411/  #制定链路日志收集地址

Spring-cloud-alibaba-微服务透传trace实现

Headers 封装

x-b3-spanid:一个工作单元(rpc 调用)的唯一标识

x-b3-parentspanid:当前工作单元的上一个工作单元,Root Span(请求链路的第一个工作单元)的值为空

x-b3-traceid:一条请求链条(trace) 的唯一标识

x-b3-sampled:是否被抽样为输出的标志,1 为需要被输出,0 为不需要被输出

Gateway|Http/Https -> web 实现trace透传

brave.servlet.TracingFilter#doFilter 实现TraceContextSpan创建并封装Headers实现trace透传

Web->Web openFeign 代理实现trace透传

依赖于openfeign框架实现,忽略 请参考 Spring-cloud-alibaba-feign 搭建 模块

Web -> Web RestTemplate手动实现trace透传

只做了解

HttpHeader封装trace信息:

从上一个web的request的Headers 获取X-B3-TraceIdX-B3-SpanIdX-B3-ParentSpanIdX-B3-Sampled

如果数据为空,从request.getAttribute("brave.propagation.TraceContext")对象信息

代码实现:

public ResponseEntity doPathUrl(HttpServletRequest request,String targetUrl,Object obj){

   //设置header信息

   Map map = MDC.getCopyOfContextMap();//获取trace信息

   HttpHeaders requestHandler = new HttpHeaders();

   String traceId = map.get("traceId")+"";

   String parentSpanId = map.get("spanId")+"";

   String spanId =  HexCodec.toLowerHex(nextId());

   String sampled = "1";

   requestHandler.add("X-B3-TraceId", traceId);

   requestHandler.add("X-B3-SpanId",spanId);

   requestHandler.add("X-B3-ParentSpanId", parentSpanId);

   requestHandler.add("X-B3-Sampled", sampled);

   MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();

   params.add("riskCode",obj);

   HttpEntity<MultiValueMap> entity = new HttpEntity<>(params,requestHandler);

   return restTemplate.postForEntity(targetUrl,entity,ServiceResult.class);

}

效果展示

Gateway

Web

 Provider

 Zipkin

页面返回接口 Reseful json数据

Logo

开源、云原生的融合云平台

更多推荐