Gm-aaa's blog.

springcloud

2025/03/08
loading

image

springcloud

1. 创建springcloud微服务项目


  • 最外层(项目名)->控制整体的springboot和springcloud、springcloudalibaba版本

    • e.g:
    • <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      <!--    元数据-->
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>3.3.4</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>
          <groupId>cloud.gm-aaa</groupId>
          <artifactId>cloud-demo</artifactId>
          <version>0.0.1</version>
          <packaging>pom</packaging>
          <name>cloud-demo</name>
          <description>cloud-demo</description>
          <modules>
              <module>services</module>
              <module>model</module>
          </modules>
      <!--    版本管理-->
          <properties>
              <java.version>17</java.version>
              <maven.compiler.source>17</maven.compiler.source>
              <maven.compiler.target>17</maven.compiler.target>
              <spring.boot.version>3.3.4</spring.boot.version>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
              <spring.cloud.version>2023.0.3</spring.cloud.version>
              <spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version>
          </properties>
      <!--    依赖管理-->
          <dependencyManagement>
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.cloud</groupId>
                      <artifactId>spring-cloud-dependencies</artifactId>
                      <version>${spring.cloud.version}</version>
                      <type>pom</type>
                      <scope>import</scope>
                  </dependency>
                  <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>
      <!--    插件-->
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                  </plugin>
              </plugins>
          </build>
      
      </project>
      
      





      * 第二层->控制各个微服务组件的数据库实体类bean和公用依赖

      * e.g
      * ```properties
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
      <groupId>cloud.gm-aaa</groupId>
      <artifactId>cloud-demo</artifactId>
      <version>0.0.1</version>
      </parent>

      <artifactId>services</artifactId>
      <packaging>pom</packaging>
      <modules>
      <module>service-product</module>
      <module>service-order</module>
      </modules>

      <properties>
      <maven.compiler.source>17</maven.compiler.source>
      <maven.compiler.target>17</maven.compiler.target>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>

      <dependencies>
      <!-- 服务发现-->
      <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
      </dependency>
      <!-- RPC-->
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>
      <!-- 测试-->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
      <!-- lombok-->
      <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.34</version>
      </dependency>
      <!-- model-->
      <dependency>
      <groupId>cloud.gm-aaa</groupId>
      <artifactId>model</artifactId>
      <version>0.0.1</version>
      </dependency>
      <!-- 负载均衡-->
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-loadbalancer</artifactId>
      </dependency>
      <!-- 配置中心-->
      <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
      </dependency>
      </dependencies>
      </project>
  • 第三层->控制各自微服务组件独有的依赖,也是写代码的地方

  • 整体tree

    • ├─.idea
      │  └─.cache
      │      └─.Apifox_Helper
      ├─model
      │  ├─src
      │  │  ├─main
      │  │  │  ├─java
      │  │  │  │  └─cloud
      │  │  │  │      └─gmaaa
      │  │  │  │          └─main
      │  │  │  │              └─bean
      │  │  │  │                  ├─order
      │  │  │  │                  └─product
      │  │  │  └─resources
      │  │  └─test
      │  │      └─java
      │  └─target
      │      ├─classes
      │      │  └─cloud
      │      │      └─gmaaa
      │      │          └─main
      │      │              └─bean
      │      │                  ├─order
      │      │                  └─product
      │      └─generated-sources
      │          └─annotations
      └─services
          ├─service-order
          │  ├─src
          │  │  ├─main
          │  │  │  ├─java
          │  │  │  │  └─cloud
          │  │  │  │      └─gmaaa
          │  │  │  │          └─main
          │  │  │  │              ├─config
          │  │  │  │              ├─controller
          │  │  │  │              ├─properties
          │  │  │  │              └─service
          │  │  │  │                  └─impl
          │  │  │  └─resources
          │  │  └─test
          │  │      └─java
          │  └─target
          │      ├─classes
          │      │  └─cloud
          │      │      └─gmaaa
          │      │          └─main
          │      │              ├─config
          │      │              ├─controller
          │      │              ├─properties
          │      │              └─service
          │      │                  └─impl
          │      └─generated-sources
          │          └─annotations
          └─service-product
              ├─src
              │  ├─main
              │  │  ├─java
              │  │  │  └─cloud
              │  │  │      └─gmaaa
              │  │  │          └─main
              │  │  │              ├─config
              │  │  │              ├─controller
              │  │  │              └─service
              │  │  │                  └─impl
              │  │  └─resources
              │  └─test
              │      └─java
              │          └─cloud
              │              └─gmaaa
              │                  └─main
              └─target
                  ├─classes
                  │  └─cloud
                  │      └─gmaaa
                  │          └─main
                  │              ├─config
                  │              ├─controller
                  │              └─service
                  │                  └─impl
                  ├─generated-sources
                  │  └─annotations
                  ├─generated-test-sources
                  │  └─test-annotations
                  └─test-classes
                      └─cloud
                          └─gmaaa
                              └─main
      
      

      ## 2. nacos组件

      ---

      ### nacos服务发现(注册中心)

      #### 实现:

      1. maven坐标

      ```xml
      <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
      </dependency>
  1. 服务发现配置

    spring.cloud.nacos.server-addr=<nacos服务ip>:<nacos服务端口>
  2. 启动服务后,在nacos中就能看到服务。spring.application.name​就是服务名,默认分组DEFAULT_GROUP

常用API:

  1. 远程调用

    在主启动类上需要添加@EnableDiscoveryClient​注解,代表开启服务发现功能

    当请求打到order时,order需要向product发起http请求以查到商品数据生成订单。此时借助spring的RestTemplate​类的T getForObject(url, T.class)​方法发起请求,得到T​对象

    e.g:

    @Bean
    public RestTemplate restTemplate() {
    return new RestTemplate();
    }
        private Product getProductById(Long productId) {
    //方式1:获取商品服务所在服务器IP:端口
    // List<ServiceInstance> productServices = discoveryClient.getInstances("service-product");
    // ServiceInstance instance = productServices.get(0);
    //方式2:负载均衡调用
    // ServiceInstance serviceInstance = loadBalancerClient.choose("service-product");
    // String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/"+productId;
    //方式3:基于注解的负载均衡
    String url="http://service-product/product/"+productId;
    // log.info("url:{}", url);
    //给远程发送请求
    return restTemplate.getForObject(url, Product.class);
    }
  2. 负载均衡

    当一个服务具有多个实例时,需要分摊请求到各个实例上,实现服务的高可用时,需要启用loadbalancer​以达到负载均衡

    • 负载均衡的默认策略是轮询

    • 当启用负载均衡后,第一次请求会向nacos​发起请求以获取可用实例IP+端口,这些数据会被缓存在本地,即使之后nacos​服务下线,只要其他需要调用的微服务实例未下线也依然可用

      e.g:

      @Bean
      @LoadBalanced //加上loadbalanced注解后,url的‘ip:port’可以替换为服务名,使用restTemplate发起的请求默认使用轮询
      public RestTemplate restTemplate() {
      return new RestTemplate();
      }
          private Product getProductById(Long productId) {
      //方式1:获取商品服务所在服务器IP:端口
      // List<ServiceInstance> productServices = discoveryClient.getInstances("service-product");
      // ServiceInstance instance = productServices.get(0);
      //方式2:负载均衡调用
      // ServiceInstance serviceInstance = loadBalancerClient.choose("service-product");
      // String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/"+productId;
      //方式3:基于注解的负载均衡
      String url="http://service-product/product/"+productId;
      // log.info("url:{}", url);
      //给远程发送请求
      return restTemplate.getForObject(url, Product.class);
      }

nacos配置中心(配置中心)

实现:

  1. maven坐标

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
  2. 配置中心在微服务中的配置

    • 导入配置中心依赖后,默认会开启配置检查​,如果在nacos的配置中不存在对应配置,则将会导致微服务项目启动失败,解决方案:
    • spring.cloud.nacos.config.import-check.enabled=false #关闭nacos配置中心导入配置检查,实现无需在nacos创建dataID
      
      * 在微服务配置中导入nacos的配置

      * ```properties
      spring.config.import=nacos:<dataId数据集名字> #e.g nacos:service-order.yml,common:common.yml

  3. 读取nacos中的配置和自动刷新

    • 读取时,通过get方法获取到对应的值
    • @Component
      @ConfigurationProperties(prefix = "order") //指定配置中心dataId的前缀
      @Data
      public class OrderProperties {
          String timeout;
          String autoConfirm; //nacos中的羊肉串写法映射为驼峰命名
      }
      



      #### 常用API:

      1. 配置监听nacos的配置文件,实现当配置变化时,以邮件或其他方式通知

      * ‍
      * 借助spring提供的`ApplicationRunner`​和`nacosConfigManager`​API实现

      * 它是在spring项目启动时会执行的一次性任务
      * 它是函数式接口,源码如下:
      * ```Java
      @FunctionalInterface
      public interface ApplicationRunner extends Runner {
      void run(ApplicationArguments args) throws Exception;
      }
      * 具体实现: * ```Java @Bean ApplicationRunner runner(NacosConfigManager nacosConfigManager) { return args -> { ConfigService configService = nacosConfigManager.getConfigService(); // 通过configservice的addllistener配置监听的dataID,组 configService.addListener("service-order.yml", "DEFAULT_GROUP", new Listener() { @Override public Executor getExecutor() { //getExecutor方法用于分配线程池 return Executors.newFixedThreadPool(4); } @Override public void receiveConfigInfo(String configInfo) { //receiveConfigInfo方法用于获取到修改后的配置 System.out.println("-----config-----"+configInfo+"-----------------------"); } }); }; }

      配置好bean之后,微服务项目启动时便会监听指定dataID

      2. 当本地和nacos的配置重复时,会应用哪个配置?

      nacos的数据集中的配置会生效,因为nacos的设计初衷就是集中配置以方便管理。

      优先级比较:**nacos配置&gt;本地配置**

      低优先级的配置会被丢弃,高优先级的配置会作为项目的环境变量启动;优先级规则:`先导入优先,外部优先`​

      * **如何确定配置的优先级?**

      * **先导入优先:** 指的是配置加载的顺序。一般来说,配置加载的顺序会影响最终生效的配置。如果本地配置在 Nacos 配置之后加载,那么 Nacos 配置将会覆盖本地配置。 具体加载顺序取决于你使用的配置管理框架(例如 Spring Cloud Alibaba Nacos Config)。通常,框架会提供配置加载顺序的控制方式。
      * **外部优先:** 指的是外部配置源(例如 Nacos)通常比应用程序内部的配置(例如本地配置文件)具有更高的优先级。这是因为外部配置更容易管理和更新,并且可以集中控制多个应用程序的配置。

      更具体地说,以下是通常的优先级顺序(从低到高):

      * **默认配置:** 代码中硬编码的默认值。
      * **本地配置文件:** 例如 `application.properties`​ 或 `application.yml`​。
      * **命令行参数:** 通过命令行传递给应用程序的参数。
      * **环境变量:** 操作系统环境变量。
      * **Nacos 配置:** 从 Nacos 服务器获取的配置。
      * **Spring Cloud Config Server (如果使用):** 如果应用集成了Spring Cloud Config Server, 它的优先级一般高于Nacos,但可以通过配置调整.

      3. 数据隔离

      * 当项目具有多个环境时(如:开发、测试、生产)时,用以区分不同环境的叫做命名空间(namespace),它需要被提前创建
      * 当项目不同的微服务具有不同的配置时,使用组来进行区分

      e.g:

      ```yml
      spring:
      application:
      name: service-order
      profiles:
      active: dev #激活哪个环境
      cloud:
      nacos:
      server-addr: 192.168.1.200:8848
      config:
      import-check:
      enabled: false
      namespace: ${spring.profiles.active:dev} #默认namespace是dev,否则从spring.profiles.active取值
      #三种不同环境的配置
      ---
      spring:
      config:
      activate:
      on-profile: dev
      import:
      - nacos:service-order.yml?group=order

      ---
      spring:
      config:
      activate:
      on-profile: test
      import:
      - nacos:service-order.yml?group=order
      ---
      spring:
      config:
      activate:
      on-profile: prod
      import:
      - nacos:service-order.yml?group=order

openfeign远程调用

  1. maven坐标

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

    #配置openfeign的日志(配置类中也需要创建一个bean)
    logging:
    level:
    cloud.gmaaa.main.feign: debug

    @Bean //配置openfeign的日志(yml配置中也需要指明)
    Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
    }

实现:

主启动类上需要添加@EnableFeignClients​注解,代表开启openfeign远程调用,对于微服务的多个实例,openfeign会默认轮询地调用API实现负载均衡

@FeignClient(value = "service-product",url="<远程调用的域名>",contextId = "order-feignProductClient"<指定该client的名字>) //发送远程调用的客户端
public interface FeignProductClient {
@GetMapping("/product/{productId}")//发送GET请求,值为请求路径
Product getProductById(@PathVariable("productId") Long productId);

}

超时控制:

超时控制分为是①连接超时(connectTimeout)②读取超时(readTimeout)

  • feign客户端在远程调用时,会先向服务端发起连接,然后才会发送请求,服务端接收请求后返回数据

    ①在向服务端发起连接时失败由connectTimeout控制,在失败后返回错误信息,默认10s

    ②在发送请求后,服务端总是未返回数据由readTimeout控制,在失败后返回错误信息,默认60s

重试机制:

  • 控制openfeign在调用失败后,重新发起请求的机制

    openfeign的默认策略为NEVER_RETRY​从不重试。

    openfeign的默认重试器为重试间隔100ms​,最大重试次数5次​,最大间隔时间1s​。当第一次请求失败时,假如重试间隔为100ms​,那么第二次和第一次重试的间隔为100ms​,第三次和第二次的重试间隔为100ms*1.5​,第四次和第三次的重试间隔为100ms*1.5*1.5​,以此类推。

  • 自定义重试器

    spring:
    cloud:
    openfeign:
    client:
    config:
    default: #feign客户端默认配置
    logger-level: full
    connect-timeout: 3000 #单位是ms
    read-timeout: 5000
    order-feignProductClient:
    logger-level: full
    connect-timeout: 3000 #单位是ms
    read-timeout: 5000
    retryer: feign.Retryer.Default #配置重试机制
    request-interceptors:
    - cloud.gmaaa.main.interceptor.OrderRequestInterceptor #配置拦截器
    @Bean //配置openfeign重试器
    Retryer feignRetryer() {
    return new Retryer.Default(50L, 1L, 3); //重试间隔50ms,最大间隔时间1s,最大重试次数3次
    }

拦截器机制:

请求拦截器:

  • 用于在feign客户端发起请求时,拦截器可以拦截请求实现添加请求头、请求参数等信息的功能

    @Component
    public class OrderRequestInterceptor implements RequestInterceptor {
    /**
    * 请求拦截器
    * @param requestTemplate 请求模板
    */
    @Override
    public void apply(RequestTemplate requestTemplate) {
    requestTemplate.header("X-token", UUID.randomUUID().toString());
    }
    }
    spring:
    cloud:
    openfeign:
    client:
    config:
    default: #feign客户端默认配置
    logger-level: full
    connect-timeout: 3000 #单位是ms
    read-timeout: 5000
    order-feignProductClient:
    logger-level: full
    connect-timeout: 3000 #单位是ms
    read-timeout: 5000
    retryer: feign.Retryer.Default #配置重试机制
    request-interceptors:
    - cloud.gmaaa.main.interceptor.OrderRequestInterceptor #配置拦截器

响应拦截器:

fallback机制:

  • 兜底返回,需要搭配sentinel​实现。fallback函数不一定会被调用,只有当远程调用失败时才会调用。其返回的数据可以是默认数据​、缓存数据​、假数据

    @FeignClient(value = "service-product",contextId = "order-feignProductClient",fallback = FeignProductClientFallBack.class) //发送远程调用的客户端
    public interface FeignProductClient {
    @GetMapping("/product/{productId}")//发送GET请求
    Product getProductById(@PathVariable("productId") Long productId);

    }
    @Component
    public class FeignProductClientFallBack implements FeignProductClient {
    @Override
    public Product getProductById(Long productId) {
    Product product = new Product();
    product.setId(0L);
    product.setName("未知商品");
    product.setPrice(new BigDecimal("0"));
    product.setNum(0);
    return product;
    }
    }
    feign:
    sentinel:
    enabled: true # 开启openfeign的fallback

sentinel流量控制与熔断

  • 在sentinel中,所有web接口​均为资源(包括openfeign的远程调用)

    使用@SentinelResource​注解声明为sentinel的资源

流控规则:

  • 流控规则是控制资源请求的流量

    QPS​=每秒最大通过的请求数

CATALOG
  1. 1. springcloud
    1. 1.1. 1. 创建springcloud微服务项目
      1. 1.1.0.1. 常用API:
    2. 1.1.1. nacos配置中心(配置中心)
      1. 1.1.1.1. 实现:
  2. 1.2. openfeign远程调用
    1. 1.2.1. 实现:
    2. 1.2.2. 超时控制:
    3. 1.2.3. 重试机制:
    4. 1.2.4. 拦截器机制:
      1. 1.2.4.1. 请求拦截器:
      2. 1.2.4.2. 响应拦截器:
    5. 1.2.5. fallback机制:
  3. 1.3. sentinel流量控制与熔断
    1. 1.3.1. 流控规则: