<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>virtual-threads on </title>
    <link>/tags/virtual-threads/</link>
    <description>Recent content in virtual-threads on </description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Tue, 28 Apr 2026 10:30:00 +0800</lastBuildDate><atom:link href="/tags/virtual-threads/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Spring Boot 3.5 &#43; Java 25 微服务里，Resilience4j 用在 HTTP、Redis、Kafka、DB 上的边界与最佳实践</title>
      <link>/posts/spring-boot-resilience4j-external-calls-best-practices/</link>
      <pubDate>Tue, 28 Apr 2026 10:30:00 +0800</pubDate>
      
      <guid>/posts/spring-boot-resilience4j-external-calls-best-practices/</guid>
      <description>背景 Spring Boot 3.5 配合 Java 25，已经把“同步写法 + virtual threads”这条路线变成了很现实的工程选择。JDK 在 JEP 444 里把 virtual threads 的目标讲得很清楚：让 thread-per-request 风格更容易扩展；Spring Boot 官方文档也提供了 spring.threads.virtual.enabled=true 这条开关入口。
于是一个常见问题会重新冒出来：当一个微服务会同时访问 HTTP 下游、Redis、Kafka、数据库时，Resilience4j 应该怎么用？
这篇文章不是“所有外部调用统一套一层 Resilience4j 模板”的教程，而是一篇事实约束下的探索综述。文中的强结论优先建立在仍在维护的官方资料和工程文档上，例如 Spring / Resilience4j、AWS Builders Library、Azure Architecture Center、Google Cloud retry strategy、Redis 官方文档、Spring for Apache Kafka 文档 和 PostgreSQL 文档。少量技术文章和 case study 只用来补充上下文，不会单独支撑“行业定论”。
如果你使用的是 resilience4j-spring-boot3，或者使用 Spring 生态提供的 Spring Cloud CircuitBreaker + Resilience4j，本文的判断原则都成立；区别更多在接入方式，而不在策略本身。
为什么这篇文章只讨论“按 dependency + operation semantics 设计策略” 我对这个问题最核心的判断是：
Resilience4j 不应该按“所有外部调用统一套模板”来使用，而应该按具体 dependency 与操作语义来设计。</description>
    </item>
    
    <item>
      <title>Spring Boot 3.5 &#43; Java 25 &#43; React：在 K8s 里跑通一套跨链路 OpenFeature flag</title>
      <link>/posts/spring-boot-openfeature-flagd-cross-stack/</link>
      <pubDate>Sun, 26 Apr 2026 15:00:00 +0800</pubDate>
      
      <guid>/posts/spring-boot-openfeature-flagd-cross-stack/</guid>
      <description>本文对应的 demo 项目：sb3-k8s-hot-reload（私有）。代码组织在 gateway/ order-service/ pricing-service/ ui/ k8s/ scripts/ 下，一条 ./scripts/e2e-demo.sh 从 kind 集群创建到端到端验证全跑完。
起点：一个朴素的约束 接到的题目是这样的：
在 Spring Boot 3.5 + Java 25 + Kubernetes（kind 本地）里，不用 Spring Cloud Config Server，不用 Netflix 套件（Eureka / Zuul / Ribbon / Hystrix）的前提下，验证一下&amp;quot;运行时配置变更不重启服务&amp;quot;这件事到底有哪些解、各自取舍是什么。后续要能支持 feature flag。
约束被排除的两块是有理由的：Config Server 在 K8s 原生场景里往往不再是首选（ConfigMap/Secret 已经在那里），Netflix 套件在 Spring Cloud 2023 起官方也已停止维护。但 Spring Cloud 不等于 Spring Cloud Config——spring-cloud-context、spring-cloud-kubernetes 这些仍然在维护，并且在 K8s 场景里依然是常见选择。
把约束精确化之后，问题就清晰了：在允许 spring-cloud-context 的前提下，K8s 上把&amp;quot;配置热加载&amp;quot;和&amp;quot;feature flag&amp;quot;分别做对，应该选什么。
热加载方案的选型矩阵 我把 2026 年还能找到、也比较贴近这个题目的可选项先压成一张表：</description>
    </item>
    
    <item>
      <title>Spring Boot 3.5 BFF 出站 HTTP 客户端：连接池、超时与 HTTP/2 实战</title>
      <link>/posts/bff-http-client-pool-timeout-and-h2c-2026/</link>
      <pubDate>Sat, 25 Apr 2026 10:00:00 +0800</pubDate>
      
      <guid>/posts/bff-http-client-pool-timeout-and-h2c-2026/</guid>
      <description>配套阅读：shop-starter-http-client 设计 介绍完整的 starter 设计；本文专注于其中&amp;quot;连接配置&amp;quot;这一块的细节决定。
1. 问题场景 典型的 BFF 模式：
graph LR BFF[buyer-bff] --&gt; M[marketplace-service] BFF --&gt; O[order-service] BFF --&gt; L[loyalty-service] BFF --&gt; P[promotion-service] BFF --&gt; S[search-service] BFF -. 偶尔 .-&gt; EXT[第三方支付 API] 每个下游的特征可能完全不同：marketplace 响应 50ms，搜索可能要 5s；payment 是外部 HTTPS API，order 是内部 cleartext；某个服务上了 HTTP/2，其他还是 HTTP/1.1。
三个绕不开的问题：
不同下游应该共用一个连接池，还是每个独立？ connectTimeout / readTimeout 怎么按服务定制？ 内部服务用 HTTP/2（h2c）值不值得开？怎么避坑？ 下面逐个讲清楚。
2. 连接池：共享还是隔离？ 我通常会先共享一个 java.net.http.HttpClient 实例。
2.1 为什么很多时候先不需要隔离 HttpClient 的连接复用天然按 origin 收敛 Oracle 的 HttpClient API 文档明确写了“一个 HttpClient 实例通常管理自己的连接池”；连接对同一 origin 复用、不同 origin 分开这一点，可以再配合 OpenJDK 源码 一起看。对 marketplace-service:80 和 order-service:80 的连接不会混在一个全局 bucket 里，所以“共享一个 HttpClient 实例”并不等于“所有下游抢同一组 socket”：</description>
    </item>
    
    <item>
      <title>shop-starter-http-client：Spring Boot 3.5 微服务 HTTP 客户端基础设施设计</title>
      <link>/posts/shop-starter-http-client/</link>
      <pubDate>Fri, 24 Apr 2026 20:00:00 +0800</pubDate>
      
      <guid>/posts/shop-starter-http-client/</guid>
      <description>代码背景来自开源项目 meirongdev/shop，Java 25 + Spring Boot 3.5 + Spring Cloud 微服务电商平台。
本文讲设计——starter 内部为什么这么写。如果你的目标是集成 starter，跟着步骤跑通第一个客户端，请直接看 shop-starter-http-client 集成指南。
配套阅读：Spring Boot 3.5 微服务 tracing 为什么会断链。
1. 背景 项目是典型的 BFF（Backend For Frontend）模式：
graph LR C[Client] --&gt; G[api-gateway] G --&gt; BB[buyer-bff] G --&gt; SB[seller-bff] BB --&gt; M[marketplace] BB --&gt; O[order] BB --&gt; L[loyalty] BB --&gt; P[promotion] SB --&gt; M SB --&gt; O 每个 BFF 都要向多个 domain service 发出站请求。shop-starter-http-client 把以下四件事提取到共享 starter，避免每个 BFF 重复实现：
关注点 由谁负责 身份传播 TracingHeaderInterceptor：Micrometer Baggage → 直接 HTTP header 错误语义保留 SharedDownstreamErrorHandler：4xx/5xx → DownstreamException（带原始状态码） 可观测性 DownstreamServiceObservationConvention：metric / span 加 downstream.</description>
    </item>
    
    <item>
      <title>Spring Boot 3.5 &#43; Java 25 &#43; Cloud Native 系列（三）：BFF 聚合层</title>
      <link>/posts/shop-platform-bff-aggregation/</link>
      <pubDate>Thu, 02 Apr 2026 10:00:00 +0800</pubDate>
      
      <guid>/posts/shop-platform-bff-aggregation/</guid>
      <description>在上一篇中，我们看了 API Gateway 如何作为统一入口处理 JWT 校验和路由分发。请求经过 Gateway 后，到达的就是本文的主角——BFF（Backend for Frontend）聚合层。
📦 本文基于的完整项目源码：https://github.com/meirongdev/shop
上一篇：（二）API Gateway 架构
2026-04 实践更新 当前主线代码里，buyer-bff / seller-bff 都已经完成了 typed @HttpExchange 客户端改造；BuyerAggregationService / SellerAggregationService 不再直接持有一堆 raw RestClient 调用链，而是通过 HttpServiceProxyFactory 注入声明式客户端。本文下面与客户端装配相关的表述已按当前实现校正。
BFF 模式：为什么需要聚合层 在微服务架构中，前端页面往往需要从多个领域服务获取数据。如果没有 BFF，前端直接调用每个领域服务会面临：
前端 ←→ profile-service (获取用户档案) ←→ wallet-service (获取钱包余额) ←→ promotion-service (获取可用优惠) ←→ marketplace-service (获取推荐商品) ←→ loyalty-service (获取积分账户) 问题显而易见：前端需要知道每个服务的地址、认证方式、数据格式；任何服务变动都会影响前端代码；更重要的是，前端暴露了内部服务拓扑，安全和治理成本都会明显上升。
graph LR FE1[&#34;前端&#34;] P[&#34;profile-service&#34;] W[&#34;wallet-service&#34;] PM[&#34;promotion-service&#34;] M[&#34;marketplace-service&#34;] L[&#34;loyalty-service&#34;] FE1 -.-&gt; P FE1 -.-&gt; W FE1 -.-&gt; PM FE1 -.</description>
    </item>
    
    <item>
      <title>Spring Boot 3.5 &#43; OpenTelemetry 实践笔记（2026）</title>
      <link>/posts/spring-boot-35-otel-best-practices/</link>
      <pubDate>Thu, 02 Apr 2026 10:00:00 +0800</pubDate>
      
      <guid>/posts/spring-boot-35-otel-best-practices/</guid>
      <description>背景 在 2026 年，虽然 Spring Boot 4 已经发布，但大多数企业项目（包括我当前的工作项目）仍基于 Spring Boot 3.5，且没有立即升级到 Spring Boot 4 的计划。就我这次接入来说，Spring Boot 3.5 + Java 25 提供的 OpenTelemetry 自动配置已经覆盖了很多常见场景，剩下的主要是按项目需要做取舍。
本文以一个三服务微服务项目（hello-service → user-service + greeting-service）为例，记录我在 Spring Boot 3.5 下接入 OTel 的过程。项目代码完全开源，更适合作为一个可对照的 demo，而不是放之四海而皆准的模板。
实践思路：这个示例先不使用 Javaagent。 主要考虑是想把接入路径尽量放在 Spring Boot 自身的自动配置和 Micrometer Observation 上，这样更容易观察 AOT / Native Image 场景里的行为，也少一层运行时黑盒。对这个 demo 来说，这样的取舍已经够用。
先从 Agentless 方案试起，利用 Micrometer 实现原生可观测性（参见 Micrometer Observation Documentation） 尽量少写接入代码，优先让 Spring Boot 自动配置接管 启用 Java 25 Virtual Threads，观察它在 I/O 场景下的表现（参见 Project Loom） 使用 JFR（Java Flight Recorder）持续性能分析，补全可观测性的第四个信号 用 ArchUnit harness 固化关键约束 项目代码：github.</description>
    </item>
    
    <item>
      <title>Spring Boot 3.5 &#43; Java 25 &#43; Cloud Native 系列（二）：API Gateway 架构</title>
      <link>/posts/shop-platform-api-gateway/</link>
      <pubDate>Wed, 01 Apr 2026 10:00:00 +0800</pubDate>
      
      <guid>/posts/shop-platform-api-gateway/</guid>
      <description>在上一篇总览文章中，我们看到了 Shop Platform 的整体架构分层。API Gateway 作为整个系统当前统一的外部入口，承载了身份验证、流量控制、路由分发、文档聚合等多项职责。
📦 本文基于的完整项目源码：https://github.com/meirongdev/shop
上一篇：（一）Shop Platform 总览
2026-04 实践更新 当前主线代码已经从 HS256 共享密钥切到 RS256 + JWKS，并把网关限流的 Lua 执行器统一到 RedissonClient + RScript。下面相关代码片段已按仓库当前实现同步。
为什么选 Spring Cloud Gateway Server MVC Spring Cloud Gateway 最初基于 WebFlux（Reactor）构建，是一个响应式网关。但在 2024–2025 年间，Spring Cloud 团队推出了 Spring Cloud Gateway Server MVC——一个基于 Servlet/MVC 的网关实现。
我们选择 MVC 而非 WebFlux，主要考虑了三点：
1. 统一编程模型
项目里 15+ 个后端服务全部是 Spring Boot MVC + Virtual Threads。如果网关用 WebFlux，就意味着团队需要同时维护两套编程模型：一套响应式（网关），一套同步阻塞（所有下游服务）。调试、日志追踪、异常处理的风格都不一样，增加了认知负担。
2. Virtual Threads 目前能满足需求
Virtual Threads 让每个 I/O 阻塞操作都运行在轻量级虚拟线程上，网关的并发能力不再完全依赖非阻塞 I/O 模型。对于 API Gateway 这种典型的 I/O 密集型应用（读 JWT、查 Redis、转发请求），它在当前项目的流量模型里已经提供了可接受的吞吐量，同时保留了同步代码的可读性。</description>
    </item>
    
  </channel>
</rss>
