从天气项目看Spring Cloud微服务治理(完结无密)

2 阅读5分钟

从天气项目看Spring Cloud微服务治理---xingkeit.top/7724/

微服务治理是架构师面试的深水区,也是项目从“能跑”到“健壮”的分水岭。很多开发者对“熔断降级”、“服务雪崩”、“流量染色”等概念耳熟能详,但一上手写代码就发懵。

理论总是抽象的,但场景是具体的。本文将通过构建一个 “智能天气查询系统” ,带你深入实战 Spring Cloud Alibaba 的三大治理利器:Nacos(配置中心与注册中心)Sentinel(熔断与限流) 、以及 Feign(服务调用与容错)

一、 项目背景与架构设计

假设我们需要开发一个面向全国用户的天气 App,包含两个核心微服务:

  1. 城市服务:提供城市 ID、名称查询,数据更新不频繁。
  2. 天气服务:调用第三方 API 获取实时天气,高并发,依赖网络 IO。

治理痛点预演

  • 配置管理:第三方天气 API Key 经常变,如何做到不重启服务就热更新?
  • 服务雪崩:第三方天气接口挂了或者响应超时,是否会拖垮整个系统?
  • 流量突增:突发热点城市(如举办奥运会)查询量激增,如何保护服务不被打死?

二、 动态配置治理:Nacos 配置中心实战

传统方式是将 Key 写在 application.yml 里,改一次 Key 需要重启所有天气服务实例。利用 Nacos Config,我们可以实现配置的热更新

1. 引入依赖

2. Bootstrap 配置

yaml

复制

# bootstrap.yml (优先级高于 application.yml)
spring:
  application:
    name: weather-service
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      config:
        file-extension: yaml
        shared-configs:
          - data-id: common-config.yaml # 公共配置,如数据库连接
            refresh: true

3. 动态刷新代码

在 Nacos 控制台新建 weather-service.yaml,内容如下:

yaml

复制

weather:
  api:
    key: "old_api_key_123"  # 模拟第三方 Key
    url: "http://api.weather.com/v1"

代码中如何实现热更新?使用 @RefreshScope

java

复制

@RestController
@RefreshScope // <--- 核心注解:标记该 Bean 的配置可以在运行时刷新
@RequestMapping("/weather")
public class WeatherController {

    @Value("${weather.api.key}")
    private String apiKey; // 这个字段会被 Nacos 动态修改

    @Value("${weather.api.url}")
    private String apiUrl;

    @GetMapping("/now")
    public String getWeather(String city) {
        // 模拟调用第三方 API
        System.out.println("当前使用的 Key: " + apiKey);
        return String.format("城市: %s, 天气: 晴, 使用Key: %s", city, apiKey);
    }
}

实战效果:当你在 Nacos 控制台修改 weather.api.key 并发布后,控制台会收到刷新事件,apiKey 变量立即更新,无需重启服务!

三、 稳定性治理:Sentinel 熔断与限流

天气服务依赖外部网络,极其不稳定。如果外部接口响应超过 1 秒,我们希望快速失败,告诉用户“稍后再试”,而不是让线程一直卡死。

1. 引入依赖

2. 配置 Sentinel 控制台

yaml

复制

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080 # Sentinel 控制台地址
        port: 8719 # 微服务与控制台通讯端口

3. 定义资源与降级逻辑

Sentinel 提供了 @SentinelResource 注解来精细化控制资源。

java

复制

@Service
public class WeatherService {

    // 模拟第三方天气 API 调用
    public String callThirdPartyApi(String city) {
        // 模拟偶尔的慢响应
        try {
            Thread.sleep(ThreadLocalRandom.current().nextInt(200, 1500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return city + "多云";
    }

    /**
     * 查询天气的主入口
     * @SentinelResource:
     * value: 资源名称,用于在控制台展示
     * blockHandler: 触发限流或规则失败时的本地降级方法
     * fallback: 调用本身抛出异常(如 RpcException)时的降级方法
     */
    @SentinelResource(value = "getWeatherDetail",
            blockHandler = "handleBlock", 
            fallback = "handleFallback")
    public String getWeatherDetail(String city) {
        // 这里可能抛出异常,或者响应很慢触发 Sentinel 的熔断规则
        if (city.equals("ErrorCity")) {
            throw new RuntimeException("模拟第三方 API 报错");
        }
        return callThirdPartyApi(city);
    }

    // 降级方法:签名必须和原方法一致,最后多加一个 BlockException 参数
    public String handleBlock(String city, BlockException ex) {
        return "系统繁忙(触发限流/降级),请稍后再试:" + city;
    }

    // 异常Fallback
    public String handleFallback(String city, Throwable ex) {
        return "服务异常(内部报错),正在疯狂修复中:" + city;
    }
}

实战规则配置(在 Sentinel 控制台操作)

  1. 流控规则:QPS 阈值设为 2。一秒内超过 2 次请求,直接返回 handleBlock 的内容。
  2. 熔断降级:选择慢调用比例策略。最大 RT 设为 500ms,比例阈值 0.5,最小请求数 5。意味着:如果 5 个请求里有 50% 响应超过 0.5 秒,熔断器打开,后续请求直接拒绝,进入“半开/开启”状态保护服务。

四、 调用链治理:OpenFeign 整合 Sentinel

微服务之间会有调用。假设  “推荐服务”  需要调用  “天气服务” 。如果“天气服务”挂了,“推荐服务”不能跟着挂。

1. Feign Client 开启 Sentinel 支持

yaml

复制

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

2. 定义 Feign 接口与降级类

java

复制

// Feign 接口
@FeignClient(value = "weather-service", fallback = WeatherFeignFallback.class)
public interface WeatherFeignClient {

    @GetMapping("/weather/now")
    String getWeather(@RequestParam("city") String city);
}

// 降级实现类:当 weather-service 不可用时,会调用此类中的方法
@Component
public class WeatherFeignFallback implements WeatherFeignClient {

    @Override
    public String getWeather(String city) {
        // 返回兜底数据,保证主流程不断
        return "【默认天气】数据加载中,暂无法获取" + city + "的实时信息";
    }
}

3. 业务调用

java

复制

@Service
public class RecommendService {

    @Autowired
    private WeatherFeignClient weatherFeignClient;

    public String getTravelRecommend(String city) {
        // 即使天气服务挂了,这里也能拿到兜底值,不影响推荐逻辑的后续执行
        String weather = weatherFeignClient.getWeather(city);
        return "今日推荐游览" + city + "," + weather;
    }
}

五、 总结:微服务治理的思维跃迁

通过这个天气项目,我们将微服务治理从抽象概念落实到了代码层面:

  1. 配置治理:利用 Nacos @RefreshScope 实现了配置的热更新,解决了“改配置重启服务”的痛点。
  2. 稳定性治理:利用 Sentinel @SentinelResource 实现了资源的精细化控制,针对慢调用和异常进行自动熔断,防止服务雪崩。
  3. 容错治理:利用 Feign 的 fallback 机制,实现了服务调用的优雅降级,保证了系统的最终可用性。

微服务治理的核心不在于引入多少组件,而在于如何通过规则和代码,让系统在不可靠的网络环境中,提供尽可能可靠的服务。这就是架构师的价值所在。