LWM

Spring Cloud Alibaba中文参考文档:Spring Cloud Alibaba 参考文档 (spring-cloud-alibaba-group.github.io)

 一、navos

二、Sentinel

 一、navos

由于现在用的是springcloudalibaba的相关组件,不需要之前的一些组件了,所以吧项目之前的组件全部清除掉,只留下book-service、borrow-service、user-service,并且清除掉之前的依赖和配置

1、服务端部署

访问https://github.com/alibaba/nacos

 点击Releases

下滑

点击下载

吧下载好的压缩包解压,后将里边的nacos放到spring_cloud_study项目中

 

 

 要启动nacos可以进入nacos=>bin中在cmd中用startup.cmd -m standalone来启动,直接startup.cmd会报错

(27条消息) 【SpringCloudAlibaba】解决Nacos启动报错:ERROR Nacos failed to start,please see XXX 问题_吴Sir.的博客-CSDN博客

 

 

 

 

 

 解决方法2:编辑startup.cmd,将startup.cmd中的MODE改为standalone,意思就是打开方式为单机版方式启动,这样直接双击startup.cmd也可以启动

 

 

 启动后在浏览器中输入http://localhost:8848/nacos用8848端口访问nacos并且使用用户名nacos和密码nacos登录

 

 

 

 

 

 

 在idea中配置nacos服务的启动

(27条消息) Idea中配置Nacos服务的启动_idea nacos_lucky赵的博客-CSDN博客

有的idea在Edit Configurations中没有shell script,在设置中的Plugins中下载BashSupport,重启idea

 

 

 再次进入Edit Configurations,加号有Bash,点击后配置

 

 

 

 

 Apply后OK,点击启动键后启动nacos

 

 访问http://localhost:8848/nacos/用来测试nacos配置完成情况

 

2、nacos的服务注册于发现

在父类项目中添加依赖

<!--这里引入最新的SpringCloud依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--这里引入最新的SpringCloudAlibaba依赖,2021.0.1.0版本支持SpringBoot2.6.X-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.0.1.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

目前完整pom.xml

<?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>
    <packaging>pom</packaging>
    <modules>
        <module>user-service</module>
        <module>book-service</module>
        <module>borrow-service</module>
        <!--<module>eureka-server</module>-->
        <!--<module>hystrix-dashboard</module>-->
        <!--<module>gateway-server</module>-->
        <!--<module>config-server</module>-->
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring_cloud_study</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring_cloud_study</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>

    </dependencies>

    <!--由于不是每个子项目都要用mybatis,因此只再父项目做版本的管理,子项目自行做引入-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>3.3.0</version>
            </dependency>

            <!--这里引入最新的SpringCloud依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--这里引入最新的SpringCloudAlibaba依赖,2021.0.1.0版本支持SpringBoot2.6.X-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.0.1.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 在三个子项目中添加依赖:

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

目前完整的pom.xml(user-service子项目的)

<?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">
    <parent>
        <artifactId>spring_cloud_study</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

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


</project>

配置文件中配置:

application.yml

  cloud:
    nacos:
      discovery:
        # 配置Nacos注册中心地址
        server-addr: localhost://8848

 

 启动查看nacos

将剩下两个子项目也注册到nacos中

3、使用openfeign,实现服务发现远程调用以及负载均衡

去borrow-service子项目中导入依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--这里需要单独导入LoadBalancer依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

完整pom.xml

<?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">
    <parent>
        <artifactId>spring_cloud_study</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>borrow-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--这里需要单独导入LoadBalancer依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>

</project>

创建client包,在里边创建UserClient.java,和之前的一样

UserClient.java

package com.test.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Date 2023/3/29 - 13:53
 */
@FeignClient(value="userservice",fallback = UserFallbackCilent.class)//声明为userservice服务的HTTP请求客户端
public interface UserClient {

    @RequestMapping("/UserController/hello/{user}")
    String hello(@PathVariable("user") String user);

}

创建controller包,里边创建BorrowController.java

BorrowController.java

package com.test.controller;

//import com.netflix.discovery.converters.Auto;
//import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.test.client.UserClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * @Date 2023/3/27 - 15:22
 */
@RestController
@RequestMapping("BorrowController")
public class BorrowController {

    @Resource
    private UserClient userClient;

//    @HystrixCommand(fallbackMethod = "onError")
    @GetMapping("/hello/{borrow}")
    public String hello(@PathVariable("borrow") String borrow, HttpServletRequest request){
//        测试gateway过滤器
        System.out.println(request.getHeader("Test"));

        String user=userClient.hello(borrow);

        return borrow+user;
    }

////    备选方案,这里直接返回一个字符串
////    注意参数和返回值要和上面一致
//    String onError(String borrow){
//        System.out.println("2");
//        return "error:"+borrow;
//    }

}

在启动类中添加注解开启openfeign

@EnableFeignClients

 

 UserController.java

package com.test.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Date 2023/3/27 - 15:24
 */
@RestController
@RequestMapping("UserController")
public class UserController {

    @GetMapping("/hello/{user}")
    public String hello(@PathVariable("user") String user){
        System.out.println("hello Userservice");
        return user;
    }

}

测试:

在创建一个user-service实例

 

 

 

 点加号=>Spring Boot,然后配置

 

把之前有的userservice改个名字

 

Apply=>OK

重启两个userservice

去nacos中看眼

测试:

 

 

 刷新五次,可以看见两个服务已经被负载均衡

 

 

4、通过配置文件进行修改临时实例:

临时和非临时区别:

 

 给borrow-service子项目添加配置

        # ephemeral改为false,表示非临时实例
        ephemeral: false

 

 重启服务,去nacos中查看,点击详情

 

 

 

 看到临时实例为false,不加上边的配置为true

关闭borrow-service服务再次查看nacos,可以看到没有在nacos中消失,而是标记健康实例数为0,详情中的健康状态为false

 

 

 

5、nacos的集群分区

消费者调用生产者应该优先调用同一个区域的,这样响应速度更快。

默认的分区是default

 

 修改分区:给userservice-01和userservice-02分别添加环境变量

spring.cloud.nacos.discovery.cluster-name=Chongqing

也可以在配置文件中添加配置

 

 重启两个服务,在nacos中查看

 

 

 

 再把borrow-service的集群改成成都的,重启查看nacos

 

 

 

 这时候请求borrow-service测试四次,发现userservice-01和userservice-02都有打印语句,说明现在的服务并没有按照集群划分。

 

 

 

 因此在borrow-service下添加配置

    # 将loadbalancer的nacos支持开启,集成nacos负载均衡
    loadbalancer:
      nacos:
        enabled: true

 

 注意这个loadbalancer是cloud下的

 再次重启测试,请求五次,发现请求全是和borrow-service同一集群的userservice-02响应的

 

 

 

 在nacos中把userservice-02下线,再次请求五次,发现这次请求全是userservice-01响应的。

 

 

6、权重机制

根据区域优先调用之外,同一区域内的实例也可以单独设置权重,nacos会优先选择权重更大的实例进行调用,我们可以直接在nacos管理页面配置,或者是在配置文件中配置

 

 配置文件:

7、nacos配置中心

在nacos管理界面的配置管理

 

 

 添加配置信息,把userservice的application.yml中的配置粘贴到配置内容中,发布。

 在userservice中添加依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

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

 

 添加配置:bootstrap.yml

spring:
  application:
    name: userservice
  profiles:
    # 环境也是和配置文件保持一致
    active: dev
  cloud:
    nacos:
      config:
        # 配置文件后缀名
        file-extension: yml
        # 配置中心服务器地址,也是nacos地址
        server-addr: localhost:8848

 

 重启测试

 

 nacos还支持文件的热更新,比如我们在配置文件中添加了一个属性,而这时候可能需要实时修改,并在后端实时更新。

测试:

在userservice中的controller中做点改动

UserController.java

package com.test.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Date 2023/3/27 - 15:24
 */
@RestController
@RequestMapping("UserController")
public class UserController {

    @Value("${test.aa}")//从配置文件中读取test.aa的字符串值,作为test接口的返回值
    String test;

    @GetMapping("/hello/{user}")
    public String hello(@PathVariable("user") String user){
        System.out.println("hello Userservice");
        System.out.println(test);
        return user;
    }

}

 

 然后再nacos中在userservice的配置添加test.aa以及值

 

 

 重启userservice测试

 

 

 

 现在在nacos中修改配置,把aa改成1111111

 如果不重启服务响应的还是之前的nihao

添加注解@RefreshScope就可以实现自动刷新了

在UserController.java上加注解

 

 重启userservice服务,先请求看test.aa的值,在去nacos中修改配置值后再次查看。

请求一次

 

 修改test.aa的值

 

 userservice的日志已经更新,发现test.aa被修改

 

 再次请求,响应的是22222

 

8、nacos命名空间

在nacos界面的命名空间中点击新建命名空间

 

创建一个开发环境

 

 

 

点击dev可以查看刚创建的空间情况

 

 

 把bookservice服务放到dev中

在bookservice服务的配置文件中添加配置信息

 

 这里namespace的值为nacos中生成dev空间的id值

 

 重启bookservice查看nacos

 

 把userservice也放进dev空间中

现在是borrowservice没在dev中,userservice在dev中,两个服务没有在同一个命名空间中,请求borrowservice,会发现userservice不能被发现

 

 

 

 把borrowservice也放进dev中就可以发现了。

 

 

 

 又能访问了。

这里的分组也是在配置中能配置。默认就是DEFAULT_GROUP

 

 

 

9、nacos在linux环境中搭建集群

Nacos:集群搭建(一)_哔哩哔哩_bilibili

在navicat中创建数据库,运行SQL文件,运行的文件为nacos-server=>conf=>nacos-mysql.sql

 

 

 

 

 

 把nacos-server传到linux环境中,并且解压

 

解压命令:unzip 包名

 

 

 

 

 进入nacos/conf/中编辑application.properties

 

 

 

### Default web server port:
server.port=8801

#*************** Network Related Configurations ***************#
### If prefer hostname over ip for Nacos server addresses in cluster.conf:
# nacos.inetutils.prefer-hostname-over-ip=false

### Specify local server's IP:
# nacos.inetutils.ip-address=


#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://xxx/nacos_demo?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=nacos
db.password.0=nacos
db.url.0、db.user.0、db.password.0都修改成自己的,就是连接这个图中的nacos_demo数据库

然后配置集群

把cluster.conf.example的.example去掉

 

 

 编辑cluster.conf之前用ifconfig看内网ip,是127.0.0.1

 

 

  编辑cluster.conf

编辑前

 

 

 编辑后

 

 

 保存退出

修改nacos的内存分配以及前台启动,直接修改startup.sh文件

改动前

改动后

 

 

 在拷贝一个nacos出来,名字为nacos2

 

 用两个会话分别打开两个nacos

 

 

 

 下载nginx

wget http://nginx.org/download/nginx-1.13.7.tar.gz

解压

tar -xvf nginx-1.13.7.tar.gz

进入nginx-1.13.7=>conf

编辑nginx.conf,内容都改成自己的

 

 

 ...

 

二、Sentinel

1.配置sentinel

Releases · alibaba/Sentinel (github.com)下载jar

 

 

 

在spring_cloud_study项目中创建目录sentinel,把下好的jar包放进去

idea中Edit

 

 

加号,JAR Application

 

 

 配置

 

 应用=>OK

启动,测试,用户和密码都是sentinel

 

 

 

 把服务连接到控制台中,给三个服务添加依赖

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

修改配置文件,在cloud下边,spring.cloud.sentinel.transport.dashboard

    sentinel:
      transport:
        # 添加监控页面地址
        dashboard: localhost:8858

 

 启动测试,这时候还是没内容

 

 进行一次请求

 

 

 

QPS是每秒查询率

 

 2.sentinel流量控制

在sentinel中点想要限流的接口后边的流控

 

单机阈值定为1,阈值类型为qps,意思就是一秒之内只能有一条请求

流控模式Sentinel 三种流控模式_sentinel流控模式_杨 戬的博客-CSDN博客

 

流控效果是sentinel的三种限流措施Sentinel 三种流控效果_sentinel流控效果_杨 戬的博客-CSDN博客

 方案一就是快速失败、方案二就是warm up、方案三就是排队等待(排队等待的超时时间单位为毫秒)

 设置好后点击新增。

 流控规则中就有一条刚新增的规则

测试:正常一秒请求一次和一秒多点几次

 

 

 测试流控模式:关联

 

修改流控模式为关联,并且关联资源为/error

 去postman中设置一个对/error的请求,请求100次,间隔500毫秒

postman右下角的runner,在把左边保存的请求拖到run order中,在右边设置迭代和间隔,点击run

 开始请求

 现在在BorrowController/hello/aaaass接口的相关接口/error请求时,在浏览器中对BorrowController/hello/aaaass进行请求

 怎么请求都会被限流

在/error请求完后这个接口不被限流

 

测试流控模式:链路

我们可以用@SentinelResource注解对某个方法进行标注,对某一个方法进行限流控制,无论是谁在何处调用了他,那么就会进行监控。

这里给borrow-service的controller层的hello方法加@SentinelResource注解,并且命名为test

 然后在application.yml中添加配置

      # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制
      web-context-unify: false

完整

server:
  port: 8301
#  配置数据源信息
spring:
  application:
    name: borrowservice
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        # 配置Nacos注册中心地址
        server-addr: localhost:8848
        # ephemeral改为false,表示非临时实例
        ephemeral: false
        cluster-name: Chengdu
        # 权重大小,越大越优先调用,默认为1
        weight: 0.5
        namespace: f34784a5-ec4d-4659-a907-f464a4ff7aaf
    # 将loadbalancer的nacos支持开启,集成nacos负载均衡
    loadbalancer:
      nacos:
        enabled: true
    sentinel:
      transport:
        # 添加监控页面地址
        dashboard: localhost:8858
      # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制
      web-context-unify: false

然后重启服务

重新对borrowservice进行请求后

 刷新sentinel控制台

 然后可以针对这个test入口进行添加限流策略,点击test后边的流控

这里的流控模式也可以用直接、关联或者是链路,直接使用链路,设置入口资源(想限制那个入口就写那个)

 

 新增

 再次请求,点一次正常,连点两次报错限流

 控制台报错

 

新增系统规则

除了直接对接口进行限流规则控制之外,我们也可以根据当前系统的资源使用情况,决定是否进行限流,在系统规则的右上角可以新增系统规则

 

3、限流和异常处理

如果我们不想让限流后返回默认的界面,而是自己自定义的结果。

 在borrow-service的BorrowController中创建要返回的结果

    @RequestMapping("/blocked")
    JSONObject blocked(){
        JSONObject object=new JSONObject();
        object.put("code",403);
        object.put("success",false);
        object.put("message","您的请求过块,请稍后在试!");
        return object;
    }

 在application.yml中做配置

      #  将刚刚编写的请求映射设定为限流页面
      block-page: /BorrowController/blocked

完整

server:
  port: 8301
#  配置数据源信息
spring:
  application:
    name: borrowservice
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        # 配置Nacos注册中心地址
        server-addr: localhost:8848
        # ephemeral改为false,表示非临时实例
        ephemeral: false
        cluster-name: Chengdu
        # 权重大小,越大越优先调用,默认为1
        weight: 0.5
        namespace: f34784a5-ec4d-4659-a907-f464a4ff7aaf
    # 将loadbalancer的nacos支持开启,集成nacos负载均衡
    loadbalancer:
      nacos:
        enabled: true
    sentinel:
      transport:
        # 添加监控页面地址
        dashboard: localhost:8858
      # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制
      web-context-unify: false
      #  将刚刚编写的请求映射设定为限流页面
      block-page:/BorrowController/blocked

重启测试,被限流的情况

 

对于方法级别的限流:

 在之前针对方法级别的限流上边的@SentinelResource注解上边添加一些设置

 注意这个

  • 必须是public修饰
  • 返回类型与原方法一致
  • 参数类型需要和原方法相匹配,并在最后加BlockException类型的参数。

 在访问指定的方法会返回替代方案,不是默认的界面

测试,在给test加了限流后重新请求

 注意blockHandler只能处理限流情况下抛出的异常,包括下面要介绍的热点参数限流也是同理,如果是方法本身抛出的其他类型异常,不在管控范围内,但是可以通过其他参数进行处理

修改borrow-service中的BorrowController

当他里边抛出异常时会走替代方案except

 

package com.test.controller;

//import com.netflix.discovery.converters.Auto;
//import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSONObject;
import com.sun.deploy.security.BlockedException;
import com.test.client.UserClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collections;

/**
 * @Date 2023/3/27 - 15:22
 */
@RestController
@RequestMapping("BorrowController")
public class BorrowController {

    @Resource
    private UserClient userClient;

    //    @HystrixCommand(fallbackMethod = "onError")
    @SentinelResource(value = "test", blockHandler = "oooo"//监控此方法,无论被谁执行都在监控范围内,这里给的value是自定义名称,
            // 这个注解可以加在任何方法上边,包括Controller中的请求映射方法,跟HystrixCommand很像。指定blockHandler,
            // 也就是被限流之后的替代解决方案,这样就不会使用默认的抛出异常的形式了
            , fallback = "except",    //fallback指定出现异常时的替代方案(指的是所有异常,blockHandler是BlockException的子异常)
            exceptionsToIgnore = IOException.class  //忽略那些异常,也就是说这些异常出现时不适用替代方案
    )
    @GetMapping("/hello/{borrow}")
    public String hello(@PathVariable("borrow") String borrow, HttpServletRequest request) {
        throw new RuntimeException("helloworld");

//        测试gateway过滤器
//        System.out.println(request.getHeader("Test"));

//        String user = userClient.hello(borrow);

//        return borrow + user;
    }

    //    替代方案,注意参数和返回值需要保持一致,最后可以添加一个Throwable作为参数接受异常
    public String except(String borrow, HttpServletRequest request, Throwable t) {
        return "error throwable";
    }

    //    替代方案,注意参数和返回值需要保持一致,并且参数最后还需要额外添加一个BlockException
    public String oooo(String borrow, HttpServletRequest request, BlockException e) {
        return "error";
    }

    @RequestMapping("/blocked")
    JSONObject blocked() {
        JSONObject object = new JSONObject();
        object.put("code", 403);
        object.put("success", false);
        object.put("message", "您的请求过块,请稍后在试!");
        return object;
    }

////    备选方案,这里直接返回一个字符串
////    注意参数和返回值要和上面一致
//    String onError(String borrow){
//        System.out.println("2");
//        return "error:"+borrow;
//    }

}

重启测试

 注意:如果blockHandler和fallback同时生效,优先级高的是blockHandler

 

4、热点参数限流

 在borrow-service中的BorrowController中添加一个接口映射方法

    @RequestMapping("/abc")
    @SentinelResource("abc")
    String abc(@RequestParam(value = "a",required = false) String a,
                    @RequestParam(value = "b",required = false) String b,
                    @RequestParam(value = "c",required = false) String c){
        return "success a="+a+",b="+b+",c="+c;
    }
package com.test.controller;

//import com.netflix.discovery.converters.Auto;
//import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSONObject;
import com.sun.deploy.security.BlockedException;
import com.test.client.UserClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collections;

/**
 * @Date 2023/3/27 - 15:22
 */
@RestController
@RequestMapping("BorrowController")
public class BorrowController {

    @Resource
    private UserClient userClient;

    //    @HystrixCommand(fallbackMethod = "onError")
    @SentinelResource(value = "test", blockHandler = "oooo"//监控此方法,无论被谁执行都在监控范围内,这里给的value是自定义名称,
            // 这个注解可以加在任何方法上边,包括Controller中的请求映射方法,跟HystrixCommand很像。指定blockHandler,
            // 也就是被限流之后的替代解决方案,这样就不会使用默认的抛出异常的形式了
            , fallback = "except",    //fallback指定出现异常时的替代方案(指的是所有异常,blockHandler是BlockException的子异常)
            exceptionsToIgnore = IOException.class  //忽略那些异常,也就是说这些异常出现时不适用替代方案
    )
    @GetMapping("/hello/{borrow}")
    public String hello(@PathVariable("borrow") String borrow, HttpServletRequest request) {
//        throw new RuntimeException("helloworld");

//        测试gateway过滤器
        System.out.println(request.getHeader("Test"));

        String user = userClient.hello(borrow);

        return borrow + user;
    }

    //    替代方案,注意参数和返回值需要保持一致,最后可以添加一个Throwable作为参数接受异常
    public String except(String borrow, HttpServletRequest request, Throwable t) {
        return "error throwable";
    }

    //    替代方案,注意参数和返回值需要保持一致,并且参数最后还需要额外添加一个BlockException
    public String oooo(String borrow, HttpServletRequest request, BlockException e) {
        return "error";
    }

    @RequestMapping("/blocked")
    JSONObject blocked() {
        JSONObject object = new JSONObject();
        object.put("code", 403);
        object.put("success", false);
        object.put("message", "您的请求过块,请稍后在试!");
        return object;
    }

////    备选方案,这里直接返回一个字符串
////    注意参数和返回值要和上面一致
//    String onError(String borrow){
//        System.out.println("2");
//        return "error:"+borrow;
//    }


    @RequestMapping("/abc")
    @SentinelResource("abc")
    String abc(@RequestParam(value = "a",required = false) String a,
                    @RequestParam(value = "b",required = false) String b,
                    @RequestParam(value = "c",required = false) String c){
        return "success a="+a+",b="+b+",c="+c;
    }




}

重启请求测试

 然后对携带参数a的请求进行限流

进入sentinel控制台的热点规则,点击新建热点限流规则

 资源名为@SentinelResource注解后边设置的名字,参数索引是第几个请求参数,这里0就是a

 测试,一秒点一次正常响应,多点几次会被拦截

 如果携带参数b,怎么都不会被拦截

 如果携带参数a和参数b,也会被当作携带a来处理

 

如果我们想限流带a的参数,但是又希望a=10时不被限流,qps达到3在进行限流

 在热点规则中编辑刚添加的规则,在高级选项中设置,参数类型为参数的类型,比如a的参数类型是string,参数值就是指定的参数值,限流阈值就是qps值,设置完后点击添加再点击保存。

 测试,再一秒点击三次的时候会被拦截,a不等于10的时候是一秒点击一次的时候被拦截

 

5、服务熔断和降级

 sentinel中的熔断和降级:再sentinel的控制台中点击熔断规则,然后新增熔断规则

慢调用比例策略:

 rt是响应时间超过后会被判定为慢调用。

比例阈值是被判定为慢调用的请求比例超过阈值会触发熔断。

熔断时长是在熔断之前是一个状态,熔断后就会进入另一个状态进行试探,这个状态的持续时间就是熔断时长。

最小请求数和统计时长是在统计时长内达到最小请求数后才能正常的统计(按照上边的规则进行判断),如果统计时长内没有执行到最小请求数时,虽然他阈值超过也不会熔断,如果统计时长内超过到最小请求数时,阈值也超过了就会熔断。

测试:

先修改borrow-service中的BorrowController的接口

给hello这个接口加个睡眠时间为1秒

 在创建个熔断规则

 新增后测试

 请求localhost:8301/BorrowController/hello/aaaass后被熔断,熔断和限流一样会走限流页面。

在等5秒后重新请求localhost:8301/BorrowController/hello/aaaass会正常,再次请求会又被重定向到限流页面

 

异常比例策略

修改borrow-service中的BorrowController的接口,让这个hello接口直接抛异常就行

 新建熔断规则

这个策略就是执行的过程中出现异常的次数,1s(统计时长)请求2次(最小请求数),如果有1次(比例阈值)出现问题就会被熔断,被熔断5秒(熔断时长)。点击新增

测试:请求慢点就是正常的异常情况,请求快点就会走限流页面。

 注意:如果异常有替代方案,则会走异常的替代方案,不会去限流页面

 

异常数策略就是具体的异常数量,到了数量就熔断

 

自定义服务降级也是用@SentinelResource注解的blockHandler参数,和限流一样

 

让Feign也支持Sentinel,前面我们使用Hystrix的时候,就可以直接对Feign的每个接口调用单独进行服务降级,而使用Sentinel也可以

在borrow-service的配置文件application.yml中加配置

#  开启feign对sentinel的支持
feign:
  sentinel:
    enabled: true

完整

server:
  port: 8301
#  配置数据源信息
spring:
  application:
    name: borrowservice
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        # 配置Nacos注册中心地址
        server-addr: localhost:8848
        # ephemeral改为false,表示非临时实例
        ephemeral: false
        cluster-name: Chengdu
        # 权重大小,越大越优先调用,默认为1
        weight: 0.5
        namespace: f34784a5-ec4d-4659-a907-f464a4ff7aaf
    # 将loadbalancer的nacos支持开启,集成nacos负载均衡
    loadbalancer:
      nacos:
        enabled: true
    sentinel:
      transport:
        # 添加监控页面地址
        dashboard: localhost:8858
      # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制
      web-context-unify: false
      #  将刚刚编写的请求映射设定为限流页面
      block-page: /BorrowController/blocked
#  开启feign对sentinel的支持
feign:
  sentinel:
    enabled: true

添加好UserClient.java和 UserFallbackCilent.java用于接口降级

UserClient.java

package com.test.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Date 2023/3/29 - 13:53
 */
@FeignClient(value="userservice",fallback = UserFallbackCilent.class)//声明为userservice服务的HTTP请求客户端
public interface UserClient {

    @RequestMapping("/UserController/hello/{user}")
    String hello(@PathVariable("user") String user);

}

 UserFallbackCilent.java

package com.test.client;

import org.springframework.stereotype.Component;

/**
 * @Date 2023/3/30 - 14:22
 */
@Component
public class UserFallbackCilent implements UserClient {
    @Override
    public String hello(String user) {
        return "error:"+user;
    }
}

 正常重启请求,然后把userservice停止服务,会走替代方案

userservice停止服务后

 

传统的RestTemplate

 

posted on 2023-04-02 15:22  Lwmm  阅读(102)  评论(0编辑  收藏  举报