SpringCloud

SpringCloud

面向服务(SOA)

面向服务就是将应用程序的不同功能单元(称之为服务)进行拆分,并将这些服务通过接口等联系起来(各服务之间松耦合),想要使用哪个功能,直接进行调用,不会将功能与整个项目紧紧绑定。

微服务

分布式:将业务逻辑进行横向扩展,通过负载均衡将请求分发到不同的服务端上

微服务:微服务一定是分布式,将项目拆分成多个子项目,项目之间使用负载均衡,这些微服务之间是松耦合的,每个微服务都可被独立部署,且都仅关注完成一件任务

SpringCloud Alibaba

组件

  • 注册中心:Nacos;

  • 负载均衡:Netflix Ribbon;

  • 熔断器:Sentinel;

  • 声明式服务调用组件:Feign(最初属 Netflix 公司,后来移交给 OpenFeign 组织);

  • 网关:Spring Cloud Gateway。

Nacos注册中心

注册中心是微服务架构中的纽带,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址并进行调用。注册中心本质上是为了解耦服务提供者和服务消费者。

主要功能:

  • 服务注册:服务实例将自身服务信息注册到注册中心

  • 服务发现:服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务

  • 服务剔除:服务注册中心将出问题的服务自动剔除到可用列表之外,使其不会被调用到

Nacos基于AP原则构建的

CAP原则:在一个分布式系统中,Consistency(一致性),Availability(可用性),Partition tolerance(分区容错性)三者不可兼得

Nacos启动

1.在nacos包下的bin目录下启动cmd

2.输入startup.cmd -m standalone启动nacos

3.浏览器输入localhost:8848/nacos,用户名密码默认为nacos

服务器注册到注册中心

1.在pom文件中修改版本,导入依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-cloud-alibaba.version>2.2.7.RELEASE</spring-cloud-alibaba.version>
    <spring.cloud.version>Hoxton.SR12</spring.cloud.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.83</version>
    </dependency>
    <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中
    心,并实现对其的自动配置 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies>
​
<dependencyManagement>
    <dependencies>
        <!-- Spring Cloud 版本管理器 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud.version}</version>
            <type>pom</type>
            <!-- 在以前的springboot单体应用中, 使用<parent>标签来引入sprinboot依
            赖,但是一个pom.xml只能有一个<parent>标签,如果有多个<parent>标签需要引入怎么办呢?那么这时
            候就可以用<scope>import</scope> 来解决; import 可以引入多个<parent>依赖;-->
            <scope>import</scope>
        </dependency>
        <!-- 需要注意版本 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2.配置文件

#服务的端口号
server.port=8081
server.tomcat.uri-encoding=utf-8
#springcloud相关配置
#服务名称,应用名会注册到nacos中
spring.application.name=productor
#注册中心地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
#注册到注册中心的服务名称
spring.cloud.nacos.discovery.service=${spring.application.name}

3.启动类上添加@EnableDiscoveryClient注解

4.启动服务程序

5.启动后可在nacos页面的服务列表看到注册的productor

生产者消费者案例

生产者(productor)

端口号为8081

@RestController
@RequestMapping("/user")
public class UserController {
​
    // 单独创建一个类用于获取配置文件中的信息
    @Resource
    private ConstantProperties constantProperties;
​
    /**
     * 返回用户信息:端口号,用户名
     * @param username
     * @return
     */
    @GetMapping("/info")
    public Result<?> getUserInfo(String username) {
        JSONObject obj = new JSONObject();
        obj.put("username",username);
        obj.put("port",constantProperties.getPort());
        obj.put("expireTime",constantProperties.getExpireTime());
        return new Result<>().success("获取用户信息成功").put(obj);
    }
}

其中的ConstantProperties类

@Component
@Data
public class ConstantProperties {
    
    // 获取到配置文件中的属性值
    @Value("${server.port}")
    private Integer port;
​
}
消费者(consumer)

端口号为7071

@RestController
@RequestMapping("/user")
public class UserController {
​
    // 远程访问
    @Resource
    private RestTemplate restTemplate;
​
    @Resource
    private DiscoveryClient discoveryClient;
​
    /**
     * 远程调用productor服务的“/user/info”接口,获取返回值
     * @param username
     * @return
     */
    @GetMapping("/info")
    public Result<?> getUserInfo(String username) {
​
        // 从nacos注册中心获取指定服务名称的服务列表
        List<ServiceInstance> list = discoveryClient.getInstances("productor");
        // 从服务列表中获取一个服务
        ServiceInstance instance = list.get(0);
​
        URI uri = instance.getUri();
​
        String url = uri + "/user/info?username=" + username;
​
        // 向服务提供者发起请求(即生产者)
        Result result = restTemplate.getForObject(url,Result.class);
​
        return result;
    }
}

其中的RestTemplate类

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
测试

当访问http://localhost:7071/user/info?username=zhangsan时,会优先进入消费者服务器,在消费者服务器中进行地址拼接后,发起远程访问,访问到生产者服务器,返回值

配置中心

在单体架构的时候我们可以将配置写在配置文件中,但有⼀个缺点就是每次修改配置都需要重启服务才能生效

Nacos除了可以做注册中心,也可以做统一配置管理(配置中心),Nacos提供了一种统一配置管理方案,可以集中管理所有实例的配置

注意:如果配置中心的配置需要在项目启动时进行初始化,则RefreshScope虽然能实时更新配置项,但不能使其生效,需要项目重启后生效

在配置中心进行配置

1.在配置管理的配置列表下,创建配置

  • Data ID

如:productor-dev.properties

${prefix}-${spring.profiles.active}.${file-extension}

prefix 默认为 spring.application.name 的值

spring.profiles.active 即为当前环境对应的 profile,例如开发环境为dev,测试环境为uat,生产环境为prod等

file-exetension 为配置内容的数据格式,支持 properties 、 yaml 和 yml 类型

  • Group:分组,默认为DEFAULT_GROUP

  • 配置格式:目前,Springboot中能识别的配置格式只支持YAML和Properties两个格式

  • 配置内容:根据自己需要配置的内容进行配置

2.将项目启动时需要进行初始化的配置项放在application.properties中,将改动相对频繁,能够实时生效的配置项可以配置在配置中心

application.properties

server.port=8081
server.tomcat.uri-encoding=utf-8
#springcloud相关配置
#应用名会注册到nacos中
spring.application.name=productor
#注册中心地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
#注册到注册中心的服务名称
spring.cloud.nacos.discovery.service=${spring.application.name}

配置中心

#自定义配置
#过期时间
system.expireTime=3000

3.添加依赖

<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

4.再新建bootstrap.properties配置文件,用于拉取配置中心相应的配置

#bootstrap.properties加载优先级比application.properties高

spring.application.name=productor
spring.cloud.nacos.server-addr=localhost:8848
#配置文件的后缀
spring.cloud.nacos.config.file-extension=properties
#指定开发环境信息
spring.profiles.active=dev
#拼接后组成productor-dev.properties与配置中心Data ID对应

#自定义可扩展的配置文件,可以添加多个文件,以数组下标区分(即在配置中心需要拉取多个配置文件)
spring.cloud.nacos.config.extension-configs[0].data-id=productor-test.properties
spring.cloud.nacos.config.extension-configs[0].refresh=true

5.在需要获取配置文件中配置项值的类上添加@RefreshScope注解

@RefreshScope// 实时刷新配置中心修改的配置项
@Component
@Data
public class ConstantProperties {

    @Value("${server.port}")
    private Integer port;

    // 获取到配置文件中的属性值
    @Value("${system.expireTime}")
    private Long expireTime;
}
配置方式

1.在nacos配置中心配置Data ID,格式:前缀(默认${spring.application.name})-环境名(默认${spring.profiles.active}).后缀名(默认${spring.cloud.nacos.config.file-extension})

在bootstrap.properties中需要配置:${spring.application.name}、${spring.profiles.active}、${spring.cloud.nacos.config.file-extension}

#bootstrap.properties加载优先级比application.properties高
​
spring.application.name=productor
spring.cloud.nacos.server-addr=localhost:8848
#配置文件的后缀
spring.cloud.nacos.config.file-extension=properties
#指定开发环境信息
spring.profiles.active=dev
#拼接后组成productor-dev.properties与配置中心Data ID对应
​
#自定义可扩展的配置文件,可以添加多个文件,以数组下标区分(即在配置中心需要拉取多个配置文件)
spring.cloud.nacos.config.extension-configs[0].data-id=productor-test.properties
spring.cloud.nacos.config.extension-configs[0].refresh=true

2.在nacos配置中心使用Data ID和group一起定义配置文件,需要在配置中心的配置文件中手动指定group(任意定义group名称)

在bootstrap.properties中不用配置${spring.profiles.active},需要配置${spring.cloud.nacos.config-group}

#bootstrap.properties加载优先级比application.properties高
​
spring.application.name=productor
spring.cloud.nacos.server-addr=localhost:8848
#配置文件的后缀
spring.cloud.nacos.config.file-extension=properties
#指定开发环境信息
spring.cloud.nacos.config.group=dev
#拼接后组成productor-dev.properties与配置中心Data ID对应
​
#自定义可扩展的配置文件,可以添加多个文件,以数组下标区分(即在配置中心需要拉取多个配置文件)
#下标值越大,优先级越高,会发生覆盖;默认配置的优先级高于可覆盖配置的优先级
spring.cloud.nacos.config.extension-configs[0].data-id=${spring.application.name}.${spring.cloud.nacos.config.file-extension}
spring.cloud.nacos.config.extension-configs[0].refresh=true
spring.cloud.nacos.config.extension-configs[0].group=test

Ribbon

负载均衡器

负载均衡是我们处理高并发、缓解网络压力和进行服务器扩容的重要手段之一,但是一般情况下我们所说的负载均衡通常都是指服务器端负载均衡

案例

1.在消费者服务端RestTemplateConfig类上添加@LoadBalanced注解

@Configuration
public class RestTemplateConfig {

    @Bean
    // 负载均衡注解,默认负载均衡策略是轮询策略
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

2.消费者服务端controller方法

@RestController
@RequestMapping("/user")
public class UserController {
    // 远程访问
    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/add")
    public Result<?> addUser(String username) {
        JSONObject param = new JSONObject();
        param.put("username",username);
        // url中使用服务名来指定要访问的服务
        // 服务名称会被LoadBalanced注解拦截,根据服务名从nacos获取服务列表,再根据负载均衡策略选择服务器进行处理
        Result result = restTemplate.postForObject("http://productor/user/add", param, Result.class);
        return result;
    }
}

3.在生产者服务端使用maven,clean后进行package

4.将jar包复制到文件夹中进行部署,使用cmd,第一次部署命令为java -jar springcloud_primary-0.0.1-SNAPSHOT.jar(包名),第二次部署命令为java -jar springcloud_primary-0.0.1-SNAPSHOT.jar(包名) --server.port=8082(指定端口号,防止重复)

5.此时再去访问时,第一次端口号为8081,第二次为8082,第三次为8081,......(轮询策略)

常用负载均衡策略

策略配置方式

文件配置
#等号后为策略对应类的全类名
productor.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.WeightedResponseTimeRule
代码配置
// 创建如下类
@Configuration
public class MyRule {
	@Bean
	public IRule iRule() {
		return new WeightedResponseTimeRule();
	}
}
// 启动类上添加,其中name为服务提供者的名称,configuration为定义的策略类
@RibbonClient(name = "productor", configuration = MyRule.class)

Ribbon饥饿加载

Ribbon负载均衡策略默认在第一次请求发送时进行初始化

饥饿加载:在项目启动时进行初始化

#开启 Ribbon 的饥饿加载模式
ribbon.eager-load.enabled=true
#指定需要饥饿加载的服务名,也就是你需要调用的服务,若有多个则用逗号隔开
ribbon.eager-load.clients=productor

Feign

Feign是一个声明式的REST客户端,简化了微服务之间的调用,类似controller调用service

Feign集成了Ribbon和Hystrix,其中Ribbon(默认为轮询策略):利用负载均衡策略选定目标机器;Hystrix:根据熔断器的开启状态,决定是否发起此次调用

案例

1.在消费者服务器中添加依赖

<!-- feign组件所需依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.在消费者启动类上添加注解@EnableFeignClients注解,开启远程调用

3.在消费者service接口上添加@FeignClient注解,value属性:远程调用的服务名称,到注册中心找到服务器

4.定义远程访问接口方法(即在service层发送远程调用请求到生产者服务器),返回数据类型要和服务提供者接口的数据类型一致;方法上添加Mapping注解,请求方式要和服务提供者的请求方式一致

如果是get请求,则需要在接口方法的参数上添加@RequestParam注解;如果是post请求,参数上的@RequestParam注解可以省略

@FeignClient(value = "productor")
public interface UserService {
    @GetMapping("/user/info")
    Result<?> getUserInfo(@RequestParam("username") String username);
}

日志配置

1.自定义日志配置类

日志级别:

  • NONE:不输出日志

  • BASIC:只输出请求方法的 URL 和响应的状态码以及接口执行的时间

  • HEADERS:将 BASIC 信息和请求头信息输出

  • FULL:输出完整的请求信息

@Configuration
public class FeignConfig {
    @Bean
    public Logger.Level feignLevel() {
        return Logger.Level.FULL;
    }
}

2.在service层的@FeignClient注解中添加属性configuration = FeignConfig.class

@FeignClient(value = "productor", configuration = FeignConfig.class)

3.配置文件中配置日志级别

logging.level.com.springcloud=debug

策略配置方式

与Ribbon配置方式相同,区别是不是在启动类上添加注解,而是在service层添加注解

1.创建配置类

@Configuration
public class MyRule {
    @Bean
    public IRule iRule() {
        return new WeightedResponseTimeRule();
    }
}

2.在service接口上的@FeignClient注解中添加configuration属性

@FeignClient(value = "productor", configuration = MyRule.class)

当需要同时使用日志配置和策略配置时,可将两个配置写在一个配置类中国,此时configuration就只有一个属性

降级处理

Feign先在UserService接口方法中远程调用服务提供者,如果服务提供者响应出现异常,则调用该接口方法的实现类方法

1.创建service接口的实现类

@Service
public class UserServiceImpl implements UserService {
    @Override
    public Result<?> getUserInfo(String username) {
        return new Result<>().error("获取用户信息失败");
    }
}

2.在service层@FeignClient注解中添加fallback属性,指定哪个类来做降级处理

@FeignClient(value = "productor", configuration = MyRule.class, fallback = UserServiceImpl.class)

3.在配置文件中开启熔断

feign.hystrix.enabled=true

Sentinel熔断器

雪崩效应: 一个微服务中可能会调用多个微服务接口才能完成, 形成复杂的链式调用. 当其中一个服务接口出现异常访问, 会导致整个线程堵塞. 越来越多的用户请求就会有越来越多的线程堵塞,资源无法释放,导致当前微服务的服务器资源耗尽, 当前服务挂掉, 从而引起雪崩效应

熔断的意义是为了起到保护作用,如果某个目标服务调用比较慢或者大量的超时,这个时候如果触发熔断机制,则可以保证后续的请求不会继续发送到目标服务上,而是直接返回降级的逻辑并且快速释放资源。如果目标服务的情况恢复了,那么熔断机制又会动态进行关闭

启动

1.在sentinel-dashboard-1.8.6.jar的目录下启动cmd

2.输入命令java -jar sentinel-dashboard-1.8.6.jar(默认启动是8080,保证8080端口不能被占用)

3.浏览器输入localhost:8080即可访问,默认用户名/密码:sentinel/sentinel

项目初始化

1.在消费者服务器中配置,引入依赖

<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2.配置文件中配置

# sentinel dashboard 地址
spring.cloud.sentinel.transport.dashboard=localhost:8080
# sentinel dashboard 内部通信端口,默认为 8719,如果被占用会自动 +1,直到找到为止
spring.cloud.sentinel.transport.port=8719

流控规则

1.在sentinel页面找到簇点链路后,找到对应的接口,点击流控操作

2.设置自定义的流控规则

注意:重启项目后,设置的流控规则会消失

熔断规则

1.在sentinel页面找到簇点链路后,找到对应的接口,点击熔断操作

2.设置自定义的流控规则

RT响应时间:执行一个请求从开始到最后收到响应数据所花费的总体时间,即从客户端发起请求到收到服务器响应结果的时间。

慢调用当调用的时间(响应的实际时间)>设置的RT的时,这个调用叫做慢调用。

比例阈值慢调用次数 / 调用次数=慢调用比例,当慢调用比例大于比例阈值时,会触发熔断。

熔断时长满足该熔断规则的条件后,系统停止调用该接口的时长。

统计时长规则统计的时间范围。

自定义降级处理

整合Ribbon案例

1.在controller需要处理的接口方法上添加@SentinelResource注解,其中有value(必需),blockHandler,fallback属性

  • value:资源名,可以是接口的url,也可以自定义资源名

  • blockHandler:对应处理服务 BlockException 的函数名称

    BlockException其包含五个子类,分别对应sentinel流控降级规则的授权规则、降级规则、限流规则、热点规则、系统规则,因此blockHandler是专门用来处理sentinel异常的

    1.blockHandler 函数访问范围需要是 public

    2.返回类型需要与原方法相匹配

    3.参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException

    4.blockHandler 函数默认需要和原方法在同一个类中

  • fallback:用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常进行处理

1.函数访问范围需要是 public

2.返回值类型必须与原函数返回值类型一致

3.方法参数列表需要和原函数一致,可以额外多一个 Throwable 类型的参数用于接收对应的异常

4.fallback 函数默认需要和原方法在同一个类中

@GetMapping("/info")
@SentinelResource(value = "getUserInfo", blockHandler = "userInfoBlockHandler",fallback ="userInfoFallback")
public Result<?> getUserInfo(String username) {
    String url = "http://productor/user/info?username=" + username;
    
    Result result = restTemplate.getForObject(url,Result.class);
​
    return result;
}

2.编写blockHandler方法

public Result<?> userInfoBlockHandler(String username, BlockException e) {
    Result<?> result = new Result<>().error();
    if (e instanceof FlowException) {
        result.setMsg("接口被限流了");
    } else if (e instanceof DegradeException) {
        result.setMsg("服务被降级了");
    }
    return result;
}

3.编写fallback方法

public Result<?> userInfoFallback(String username) {
    return new Result<>().error("业务服务被降级了");
}

注意fallback和blockHandler区别:

  1. blockHandler是专门(只)处理sentinel流控降级规则抛出的BlockException。

  2. 而fallback默认处理所有的异常,其中包括BlockEcxeption(因为BlockException是Exception的子类),也就是说如果我们不配置blockHandler,其抛出BlockEcxeption也将会进入fallback方法中。

  3. 如果同时配置了blockHandler和fallback,出现BlockException时将进入blockHandler方法中处理。例如当接口发生异常时会调用fallback降级,如果异常数超出了sentinel配置的熔断规则,则会触发BlockException,此时会进入blockHandler方法进行处理。

整合Feign案例

1.创建userService接口的实现类,用来做降级处理

@Service
public class UserServiceImpl implements UserService {
    @Override
    public Result<?> getUserInfo(String username) {
        return new Result<>().error("获取用户信息失败");
    }
}

2.在自定义的UserService的接口上的@FeignClient注解里添加处理这个接口的熔断类:fallback=UserServiceImpl.class

@FeignClient(value = "productor", configuration = MyRule.class, fallback = UserServiceImpl.class)

3.配置application文件,开启熔断组件

feign.sentinel.enabled=true

注意Ribbon中fallback方法需要自定义,而Feign中fallback方法就是接口的实现类

Sentinel规则持久化

当重启应用,Sentinel规则将消失,所以需要将配置规则进行持久化

导入依赖

<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
流量控制

1.在注册中心添加配置

[
    {
        "resource": "getUserInfo",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]
  • resource:资源名,可以随意定义,资源名是限流规则的作用对象,比如请求资源getUser。

  • limitApp:流控针对的调用来源。默认就是default,代表不区分调用来源。

  • grade:限流阈值类型,QPS或线程数模式。0表示线程数,1表示QPS。默认为1,即QPS模式。

  • count:限流阈值。比如值为2表示1秒超过2个请求就限流。

  • strategy:流控模式:直接、链路、关联,默认直接。0表示直接,1表示关联,2表示链路。

  • controlBehavior:流控效果(直接拒绝/排队等待/慢启动模式),0表示快速失败,1表示WarmUp,2表示排队等待。

  • clusterMode:是否为集群。值为boolean类型。

2.在消费者服务器的配置类中配置

#nacos配置中心的dataID
spring.cloud.sentinel.datasource.flow.nacos.data-id=user-flow-sentinel.json
#nacos的注册地址
spring.cloud.sentinel.datasource.flow.nacos.server-addr=localhost:8848
#nacos配置中心的数据配置格式
spring.cloud.sentinel.datasource.flow.nacos.data-type=json
#nacos分组
spring.cloud.sentinel.datasource.flow.nacos.group-id=DEFAULT_GROUP
#sentinel规则类型
# flow:控流、degrade:熔断、param_flow:热点规则、system:系统规则、authority:授权规则
spring.cloud.sentinel.datasource.flow.nacos.rule-type=flow

3.再次发送请求到配置了持久化的服务器,就会自动开启规则

熔断控制

1.在注册中心添加配置

[
    {
        "resource": "addUser",
        "grade": 0,
        "count": 1,
        "timeWindow": 10,
        "minRequestAmount": 5,
        "statIntervalMs": 100000,
        "slowRatioThreshold":0.1
    }
]
  • resource:资源名,资源名是限流规则的作用对象,比如请求资源getUser。

  • grade:熔断策略,支持慢调用比例/异常比例/异常数策略。0:慢调用比例,1:异常比例,2:异常数。默认为0,慢调用比例。

  • count:慢调用比例模式下为慢调用临界RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值。

  • timeWindow:熔断时长,单位为秒。

  • minRequestAmount:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断。默认为5。

  • statIntervalMs:统计时长(单位为ms),如60*1000代表分钟级。默认为1000ms。

  • slowRatioThreshold:慢调用比例阈值,仅慢调用比例模式有效

2.在消费者服务器的配置类中配置

#nacos配置中心的dataID
spring.cloud.sentinel.datasource.degrade.nacos.data-id=user-degrade-sentinel.json
#nacos的注册地址
spring.cloud.sentinel.datasource.degrade.nacos.server-addr=localhost:8848
#nacos配置中心的数据配置格式
spring.cloud.sentinel.datasource.degrade.nacos.data-type=json
#nacos分组
spring.cloud.sentinel.datasource.degrade.nacos.group-id=DEFAULT_GROUP
#sentinel规则类型
spring.cloud.sentinel.datasource.degrade.nacos.rule-type=degrade

3.再次发送请求到配置了持久化的服务器,就会自动开启规则

注意持久化与@SentinelResource注解无关,但如果需要在方法上添加@SentinelResource注解时,value的值必须与资源名相同

Gateway(API网关)

是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。

  • 路由:是 Spring Cloud Gateway 中基础的组件,通常由一个 id 标识,目标 URI,以及一系列断言(Predicate)和过滤器组成。

  • 断言(Predicate):是 Java 8 函数库的 Predicate 对象,用于匹配 HTTP 请求上数据信息,如请求头信息,请求体信息。如果对于某个请求的断言为 true,那么它所关联的路由就算匹配成功,然后将请求给这个路由处理。

  • 过滤器:用于某一个路由的请求或者响应进行修改的组件,在 Spring Cloud Gateway 都要实现GatewayFilter 接口,并且需要由基于 GatewayFilterFactory 具体实现类构造。

  • 客户端请求首先被 GatewayHandlerMapping 获取,然后根据断言匹配找到对应的路由。

  • 找到路由后,走完所关联的一组请求过滤器的处理方法,请求到目标 URI 所对应的服务程序,获得服务响应。

  • 网关收到响应后,通过关联的响应过滤器的处理方法后,同样由 GatewayHandlerMapping 返回响应给客户端。

配置路由网关

1.新建一个SpringBoot项目,注意不需要添加Spring Web

2.修改版本为2.3.12.RELEASE,删除无关文件,并添加如下依赖

<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-
        8
    </project.reporting.outputEncoding>
    <spring-cloud-alibaba.version>2.2.7.RELEASE</spring-cloud-alibaba.version>
    <spring.cloud.version>Hoxton.SR12</spring.cloud.version>
</properties>
<dependencies>
    <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中
    心,并实现对其的自动配置 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--gateway路由网关依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <!-- Spring Cloud 版本管理器 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud.version}</version>
            <type>pom</type>
            <!--
            在以前的springboot单体应用中, 使用<parent>标签来引入sprinboot依
            赖,但是一个pom.xml只能有一个<parent>标签,如果我有多个<parent>标签需要引入怎么办呢?
            那么这时候就可以用 <scope>import</scope> 来解决; import 可以引入
            多个<parent>依赖;
            -->
            <scope>import</scope>
        </dependency>
        <!-- 需要注意版本 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3.启动类添加@EnableDiscoveryClient注解

4.配置文件

server.port=6001
server.tomcat.uri-encoding=UTF-8
spring.application.name=gateway
#注册中心
spring.cloud.nacos.server-addr=localhost:8848
spring.cloud.nacos.discovery.service=${spring.application.name}

#设置路由id,没有固定规则,要求唯一,建设配合服务名
spring.cloud.gateway.routes[0].id=gateway-consumer
#设置路由的uri,lb表示负载均衡,consumer是应用的服务名称(即在注册中心找到相应服务器的服务名称)
spring.cloud.gateway.routes[0].uri=lb://consumer
#断言
spring.cloud.gateway.routes[0].predicates[0]=Path=/gw/**
#过滤器,StripPrefix=1表示将断言中url路径切分,获取最后一个路径转发
#简单理解:将断言中添加的路径前缀过滤掉,转发真实的url
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
#开启网关路由
spring.cloud.gateway.discovery.locator.enabled=true

5.访问路径变成 http://localhost:6001/gw/user/info?username=zhangsan

网关过滤器

引入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>
全局过滤器

只要是通过网关访问访问服务器的都会被拦截

1.新建IpFilter类

/**
 * 全局过滤器:
 * 所有的请求都会被拦截
 */
@Component
public class IpFilter implements GlobalFilter, Ordered {
​
    private static List<String> ipBlackList;
​
    static {
        ipBlackList = new ArrayList<>();
        ipBlackList.add("127.0.0.1");
        ipBlackList.add("0:0:0:0:0:0:0:1");
    }
​
    // IP黑名单过滤
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
​
        // 获取请求ip
        String ip = request.getRemoteAddress().getHostString();
        if (ipBlackList.contains(ip)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            JSONObject obj = new JSONObject();
            obj.put("code",401);
            obj.put("msg","拒绝访问");
            DataBuffer dataBuffer = response.bufferFactory().wrap(obj.toString().getBytes());
            response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
            return response.writeWith(Mono.just(dataBuffer));
        }
        return chain.filter(exchange);
    }
​
    // 设置过滤器的优先级,数值越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}
局部过滤器

1.自定义gatewayFilter,重写filter方法

/**
 * 敏感词汇过滤
 */
public class TermFilter implements GatewayFilter, Ordered {
​
    /**
     * 敏感词汇列表初始化
     */
    private static List<String> termList;
​
    static {
        termList = new ArrayList<>();
        termList.add("zhangsan");
        termList.add("lisi");   
    }
​
​
    /**
     * 从请求中获取参数值,判断参数值是否在敏感词汇名单中
     * 存在则拒绝访问
     * 不存在则放行
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
​
        // 获取请求中的参数列表
        MultiValueMap<String,String> queryParams = request.getQueryParams();
        for (String key : queryParams.keySet()) {
            // 根据参数名获取参数值列表
            List<String> list = queryParams.get(key);
            for (String p:list) {
                if (termList.contains(p)) {
                    response.setStatusCode(HttpStatus.UNAUTHORIZED);
                    JSONObject obj = new JSONObject();
                    obj.put("code",401);
                    obj.put("msg","包含敏感词汇,拒绝访问");
​
                    DataBuffer buffer = response.bufferFactory().wrap(obj.toString().getBytes());
                    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                    return response.writeWith(Mono.just(buffer));
                }
            }
        }
        return chain.filter(exchange);
    }
​
    @Override
    public int getOrder() {
        return 0;
    }
}

2.创建gatewayFilterFactory(命名格式:前缀+GatewayFilterFactory),重写apply方法,在该方法中初始化自定义的gatewayFilter

@Component
public class TermGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    @Override
    public GatewayFilter apply(Object config) {
        return new TermFilter();
    }
}

3.在application.properties中使用gatewayFilterFactory的名称前缀作为过滤器

spring.cloud.gateway.routes[0].filters[1]=Term

注意当同时存在全局过滤器和局部过滤器时,需要在getOrder方法中设置优先级,如将全局过滤器的优先级设置为10

网关跨域配置

配置文件方式
#网关跨域
spring.cloud.gateway.globalcors.cors-configurations[/**].allow-credentials=true
spring.cloud.gateway.globalcors.cors-configurations[/**].allowed-headers=*
spring.cloud.gateway.globalcors.cors-configurations[/**].allowed-methods=*
spring.cloud.gateway.globalcors.cors-configurations[/**].allowed-origins=*
代码方式
/**
 * 注意:
 * gateway使用的filter是响应式的CorsWebFilter,SpringBoot使用的是CorsFilter
 * gateway使用响应式的UrlBasedCorsConfigurationSource,SpringBoot使用的是普通的UrlBasedCorsConfigurationSource
 */
@Configuration
public class CorsWebConfig {
    @Bean
    public CorsWebFilter corsWebFilter() {
​
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.addAllowedOrigin("*");
​
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",config);
​
        return new CorsWebFilter(source);
    }
}
Spring Cloud是一个用于构建分布式系统的开发工具集合。它提供了一些常用的组件和框架,包括服务注册和发现、负载均衡、断路器、分布式配置等等。在使用Spring Cloud时,有一些常见的错误和注意事项需要注意。 首先,关于Spring Boot和Spring Cloud版本对应错误。在使用Spring Cloud时,需要确保Spring Boot和Spring Cloud的版本兼容。不同版本之间可能存在依赖冲突或不兼容的情况,因此需要根据官方文档或者相关文档来选择合适的版本。 另外,Spring Cloud Config是一个用于集中管理和动态获取配置的工具。它支持从Git、SVN或本地文件系统中获取配置文件,并提供了服务器和客户端支持。你可以通过官方使用说明文档了解更多关于Spring Cloud Config的详细信息。 此外,关于选择使用Nacos还是Eureka作为服务注册和发现组件的问题。Nacos是一个功能更强大的服务注册和发现组件,它整合了Spring Cloud Eureka、Spring Cloud Config和Spring Cloud Bus的功能。使用Nacos可以实现配置的中心动态刷新,而不需要为配置中心新增集群或使用消息队列。另一方面,Eureka是Spring Cloud原生全家桶的一部分,相对来说更加稳定一些。选择使用哪个组件需要根据具体的需求和项目特点来决定。 综上所述,Spring Cloud是一个用于构建分布式系统的开发工具集合,它提供了一些常用的组件和框架。在使用Spring Cloud时,需要注意Spring Boot和Spring Cloud版本的兼容性,并可以使用Spring Cloud Config来动态获取配置。同时,可以选择使用Nacos或Eureka作为服务注册和发现组件,具体选择需要根据项目需求来决定。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值