SpringCloud小白入门+项目搭建

本文章主要是将SpringCloud开发使用的工具进行介绍,以及SpringCloud项目的展出。欢迎来访的批评指正🌹🌹
.
项目的源代码:https://github.com/Gaonavak/SpringCloud
.
如果可以的话给一下关注和点赞,谢谢支持🫡🫡

一、SpringCloud 简介

核心思想

  • Spring Cloud 的核心思想是将大型应用拆分为多个微服务,每个模块可以由不同的团队独立开发,最终通过路由进行服务调用和转发。

Maven配置

  • 父 POM

    • 使用 Maven 构建父 POM 文件,利用 dependencyManagement 来管理子模块的依赖版本,确保所有模块使用一致的依赖版本,从而提高兼容性和维护性。
    • 在父 POM 中的 <modules> 标签下列出所有子模块。
  • 公共模块

    • 创建公共模块时,不需要启动类,只需定义工具类、接口等,其他子模块可以通过 Maven 引用该模块的依赖

模块间通信

  • 不同模组之间是通过 openfeign 工具进行互通,调用模块其需要在其项目中创建一个接口,并且需要在该接口上面添加@FeignClient(value = “xxx”),x为被调用模块的名称。
    • 在其控制层中使用时通过DI其接口,进而在接口中调用调用对应的接口方法。
    • 接口中的内容就是xxx模块中控制层中的方法签名;

常用的中间体

  • Redis:用于缓存和数据存储,可提高系统性能。
  • Nacos:作为服务发现和配置管理工具,支持动态配置和服务注册。
  • Sentinel/Dubbo:用于服务熔断和流量控制,确保系统的稳定性。
  • RabbitMQ/Kafka:用于异步消息处理和事件驱动架构,提升系统的解耦性和响应速度。
  • Spring Cloud Gateway:用于API网关,集中管理路由和过滤器,提供统一的入口。
  • Spring Security 进行认证和授权,保护微服务的访问安全。
  • 任务调度Quartz、Elastic Job:用于定时执行任务,管理定时任务和批量处理,确保任务的可靠执行。

二、Nacos 注册中心

定义

Nacos 是一个开源的服务注册与配置管理中心,旨在帮助开发者简化微服务架构中的服务发现和配置管理。它提供可视化界面,支持动态配置和热重载,从而提高开发效率。为了启用动态更新功能,需要在启动类上添加 @RefreshScope 注解。

作用

  • 服务发现:Nacos 允许服务实例注册,并使其他服务能够发现这些实例,简化了服务间的调用。
  • 动态配置管理:通过 Nacos,开发者可以在运行时修改配置,而无需重启服务,极大提高了系统的灵活性。
  • 负载均衡:结合 Nacos 的实例管理,支持轮询等多种负载均衡策略,确保请求均匀分配到各个实例。
    • 心跳机制:Nacos 通过心跳任务监控服务实例的健康状态。

配置

  • 子模块application.yml配置:以下是子模块的通用配置内容,包含 Nacos 的地址和配置文件的名称。(其中的import中的需要在nacos的可视化平台进行创建)
spring:  
  application:  
    name: gateway-service  
  config:  
    import: nacos:gateway-service-dev.yml  
  cloud:  
    nacos:  
      discovery:  
        server-addr: localhost:8848  
      config:  
        server-addr: localhost:8848  
        file-extension: yml
  • 子模块 pom.xml 配置依赖
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
  • nacos平台配置(跟子模块的配置一样,只是将其内容换到nacos的平台进行设置)
server:
  port: 9083

三、Gateway路由搭建

定义

Gateway 路由的作用是作为统一的网关,为前端提供公共 IP,避免在访问不同模块时需要使用不同的 IP 地址。通过 Gateway,客户端只需访问一个入口点,简化了服务调用。

作用

  • 统一入口:为微服务提供一个统一的访问入口,简化客户端的调用。
  • 负载均衡:通过集成负载均衡器,确保请求均匀分配到多个服务实例。
  • 路由管理:可以根据请求路径、方法等条件进行路由转发,灵活配置路由规则。
  • 流量控制:结合 Sentinel,可以实现流量控制和熔断,保障系统的稳定性。

配置和依赖

  • 搭建 Gateway 路由需要在 pom.xml 中添加以下依赖:
<!-- Gateway -->
<dependency>  
    <groupId>org.springframework.cloud</groupId>  
    <artifactId>spring-cloud-starter-gateway</artifactId>  
</dependency>  
<dependency>  
    <groupId>org.springframework.cloud</groupId>  
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>  
</dependency>

  • 其 Gateway 在 nacos 中的 yml 配置
server:
  port: 9998
spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=origin,gateway # 添加名为origin的请求头,值为gateway
      globalcors:
        cors-configurations:
          '[/**]': # 匹配所有请求
            allowedOrigins: "*" #跨域处理 允许所有的域
            allowedMethods: # 支持的方法
              - GET
              - POST
              - PUT
              - DELETE
      discovery:
        locator:
          enabled: true #开启注册中心路由功能
      routes:  # 路由
        - id: order-service #路由ID,没有固定要求,但是要保证唯一,建议配合服务名
          uri: lb://order-service # 匹配提供服务的路由地址
          predicates: # 断言
            - Path=/order/** # 断言,路径相匹配进行路由
        - id: point-service #路由ID,没有固定要求,但是要保证唯一,建议配合服务名
          uri: lb://point-service # 匹配提供服务的路由地址
          predicates: # 断言
            - Path=/point/** # 断言,路径相匹配进行路由
        - id: user-service #路由ID,没有固定要求,但是要保证唯一,建议配合服务名
          uri: lb://user-service # 匹配提供服务的路由地址
          predicates: # 断言
            - Path=/user/** # 断言,路径相匹配进行路由

关于 Gateway 和 Nginx 的区别

  • Gateway:通常是微服务架构中用于API管理的组件,负责路由、负载均衡、认证、限流等功能。它可以处理内部服务间的通信和外部请求。
  • Nginx:是一个高性能的反向代理服务器和负载均衡器。除了处理静态文件外,Nginx 还可以用于流量控制、SSL 终端、缓存等。

四、Sentinel 检测中心

定义

Sentinel 是阿里巴巴开源的一个轻量级流量控制组件,旨在帮助开发者在分布式系统中实现流量控制、熔断、降级等功能,以保障系统的稳定性和高可用性。它通过监控服务的 QPS(每秒请求数)、响应时间等指标,动态调整流量策略。

流控规则

QPS(Queries Per Second)是衡量系统处理请求能力的重要指标。Sentinel 允许开发者设定 QPS 限流规则,当请求数超过设定的阈值时,自动进行流量控制,防止系统过载。通过监控 QPS,开发者可以及时发现并处理流量异常问题。

  • 阀值类型:限制流量的标准,可以是固定值或动态生成的值。
    • QPS
    • 并发线程数
  • 单机阀值:针对单个服务实例的流量限制,如 1000 QPS、1000个线程
  • 集群:针对单个服务实例的流量限制,如 1000 QPS、1000个线程
  • 流控模式:
    • 直接:直接对请求进行限流。
    • 关联:多个资源间的关联流控。
    • 链路:根据服务调用链的情况进行流控。
  • 流控效果:
    • 快速失败:一旦超过阈值,立即返回错误。(即在查询访问期间,最多可以只有单机阀值个查询,其他的就返回错误)
    • Warm Up:在流量增长时逐渐增加流量限制。
      • 预热时长:指在流量增加的过程中,系统所需的时间段。在这个时间段内,流量限制会逐渐上升,而不是立即达到最大值。
    • 排队等待:将请求排队,直到流量恢复正常。
      • 超时时长:超过多少时间后,对应的查询就是失败。

熔断

熔断是指在系统出现故障时,主动中断对某个服务的调用,以避免错误请求进一步影响系统的稳定性。Sentinel 提供了熔断机制,当请求失败率超过一定比例时,将自动断开对该服务的请求,直到系统恢复正常。熔断的主要目的是保护系统,避免因个别服务的故障导致整个系统崩溃。

  • 熔断策略
    • 慢调用比例:定义在一定时间窗口内,慢请求占总请求的比例。若该比例超过配置值,则触发熔断。例如,设置为 20%,意味着如果在统计窗口内,20% 以上的请求响应时间超过设定的最大 RT,则触发熔断。
    • 异常比例:定义在一定时间窗口内,失败请求占总请求的比例。当请求失败率超过此比例时,触发熔断。例如,设置为 10%,表示如果 10% 以上的请求失败,则进行熔断。
    • 异常数:在统计窗口内,请求失败的绝对数量。当失败次数超过此值时触发熔断。例如,设置为 50 表示如果在统计窗口内失败的请求数超过 50 次,则触发熔断。
  • 其他配置
    • 最大 RT:最大响应时间阈值。如果请求的响应时间超过此值,将被视为慢请求。可设置为 500 ms。
    • 比例阀值:熔断触发的比例阈值。可以设置为 30%,表示当请求失败率达到 30% 时触发熔断。
    • 熔断时长:熔断持续的时间。设置为 60 秒,在此期间系统将拒绝所有对该服务的请求。
    • 最小请求数:触发熔断所需的最小请求数。只有当请求数超过该值时,熔断策略才会生效。例如,设置为 100,意味着在统计窗口内至少需要 100 次请求。
    • 统计时长:统计熔断条件的时间窗口。设置为 1 分钟,表示在过去 1 分钟内统计请求的成功率和失败率。

热点

热点问题是指某个特定的资源或服务在短时间内接收到大量请求,可能导致系统过载。Sentinel 的热点规则可以识别并控制这种情况,允许开发者设定特定资源的流量控制策略,以确保系统的稳定性。例如,可以对特定的 URL 或 API 接口设置流量限制,避免瞬时流量带来的压力。

  • 限流模式:通常使用 QPS 模式,即以每秒请求数(QPS)为单位对热点资源进行流量控制。
  • 参数索引:用于识别请求中的具体参数,例如,可以根据某个查询参数(如用户 ID 或商品 ID)进行流控。开发者可以设定请求的特定参数作为热点索引,以便更精确地控制流量。
  • 单机阀值:针对单个服务实例的流量限制。例如,设置单机阀值为 100 QPS,意味着该服务实例在单位时间内最多处理 100 个请求。
  • 统计窗口时长:用于统计流量的时间窗口,通常设置为 1 秒、5 秒或 1 分钟等。例如,设置为 10 秒表示在过去的 10 秒内统计请求数,以判断是否触发流量控制。

授权规则

授权规则用于控制哪些请求可以访问特定的服务或资源。Sentinel 允许开发者定义细粒度的授权规则,根据请求的来源、请求参数等进行流量控制。通过配置授权规则,可以实现对某些用户或请求的优先级管理,从而提高系统的安全性和稳定性。

  • 流控应用:指定哪些服务或资源需要应用授权规则。例如,可以针对某个特定的微服务或 API 接口配置流控规则,以确保只有符合条件的请求可以访问。
  • 授权类型
    • 白名单:允许访问的请求来源。可以根据 IP 地址、用户 ID 或其他标识符来设置白名单。只有在白名单中的请求才能成功访问被保护的资源。例如,设置特定的用户 ID(如 12345)为白名单,只有该用户的请求会被允许通过。
    • 黑名单:禁止访问的请求来源。类似于白名单,可以根据 IP 地址、用户 ID 或其他标识符来设置黑名单。黑名单中的请求将被拒绝访问。例如,设置某些已知的恶意 IP 为黑名单,这些 IP 的请求将直接被拒绝。:

五、JWT鉴权使用

定义

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境中安全地传递信息。JWT 可以用于用户身份验证和信息交换,具有良好的安全性和传输效率。

作用

  • 用户身份验证:通过 JWT,用户登录后会得到一个 token,后续请求中携带该 token 以证明用户身份。
  • 信息安全性:JWT 可以通过数字签名(如 HMAC 或 RSA)确保信息未被篡改。
  • 无状态:JWT 是自包含的,服务器无需保存会话状态,减轻服务器负担

配置和使用

  • 一般将其书写在一个公共模块里面,并且用静态方法进行设置对应的配置
public class JWTutil {

    // 定义用户名常量
    private static final String UserName = "admin";

    // 定义JWT签名的密钥
    private static final String key = "token_key";

    // 定义token的过期时间,单位为毫秒(这里设置为1分钟)
    public static final int token_time_out = 1000 * 60;

    // 生成JWT token的方法
    public static String getToken(String userId) {
        // 创建JWT生成器
        JWTCreator.Builder jwtBuilder = JWT.create();

        // 创建头部信息的Map
        Map<String, Object> headers = new HashMap<>();

        // 设置头部的类型为JWT
        headers.put("type", "jwt");
        // 设置算法为HS256
        headers.put("alg", "hs256");

        // 生成token,包含头部、载荷和签名
        String token = jwtBuilder
            // 设置JWT的头部
            .withHeader(headers)
            // 添加自定义声明,包含用户ID
            .withClaim("userId", userId)
            // 设置token的过期时间
            .withExpiresAt(new Date(System.currentTimeMillis() + token_time_out))
            // 设置token的签发时间
            .withIssuedAt(new Date(System.currentTimeMillis()))
            // 设置token的发行者
            .withIssuer(UserName)
            // 使用指定的密钥和算法进行签名
            .sign(Algorithm.HMAC256(key));

        // 返回生成的token
        return token;
    }
}
  • 使用的依赖:
  <!-- jwt -->
  <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
  </dependency>
  • 拦截器的设置:
@Component
public class GlobalFilterConfig implements GlobalFilter, Ordered {
    //application.yml配置文件中,设置token在redis中的过期时间
    @Value("${config.redisTimeout}")
    private Long redisTimeout;

    @Autowired
    private RedisTemplate redisTemplate;

    private static final String HEADER_NAME = "Acess-Token";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("============过滤器============");

        // 获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        // 获取响应对象
        ServerHttpResponse response = exchange.getResponse();
        // 获取请求地址
        String url = request.getURI().getPath();
        // 获取token信息
        String token = request.getHeaders().getFirst(HEADER_NAME);

        // 判断是否为白名单请求,以及一些内置不需要验证的请求。(登录请求也包含其中)。
        // 如果当前请求中包含token令牌不为空的时候,也会继续验证Token的合法性,这样就能保证
        // Token中的用户信息被业务接口正常访问到了。而如果当token为空的时候,白名单的接口可以
        // 被网关直接转发,无需登录验证。当然被转发的接口,也无法获取到用户的token身份数据了。
        if (this.shouldNotFilter(url)) {
            return chain.filter(exchange);
        }
        if (StringUtils.isEmpty(token)) {
            return unAuthorize(exchange);
        }
        //验证redis中是否存在token
        if (!redisTemplate.hasKey(token)) {
            return unAuthorize(exchange);
        }

        //验证通过,刷新token过期时间
        redisTemplate.expire(token, redisTimeout, TimeUnit.SECONDS);
        String userId = String.valueOf(redisTemplate.opsForValue().get(token));
        System.out.println("============登录用户id:" + userId + "============");
        //把新的 exchange放回到过滤链
        ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, token).build();
        ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
        return chain.filter(newExchange);

    }
    // 返回未登录的自定义错误
    private Mono<Void> unAuthorize(ServerWebExchange exchange) {
        // 设置错误状态码为401
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 设置返回的信息为JSON类型
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        // 自定义错误信息
        String errorMsg = "{\"error\": \"" + "用户未登录或登录超时,请重新登录" + "\"}";
        // 将自定义错误响应写入响应体
        return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(errorMsg.getBytes())));
    }
    /**
     * 判断当前请求URL是否为白名单地址,以及一些内置的不用登录的接口,
     * @param url 请求的url。
     * @return 是返回true,否返回false。
     */
    private boolean shouldNotFilter(String url) {
       if (url.startsWith("/user/login")) {
           return true;
       }
       return false;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值