《Java 服务端通信工程》10.1 TLS 与 mTLS
把 linkd-v3 的 9093 从明文换成 mTLS:用 keytool 生成 CA、服务端与客户端证书,再用 TlsServerCredentials 和 TlsChannelCredentials 接入 gRPC,讲清 PEM、SAN、主机名校验与握手失败的判读。
tag
把 linkd-v3 的 9093 从明文换成 mTLS:用 keytool 生成 CA、服务端与客户端证书,再用 TlsServerCredentials 和 TlsChannelCredentials 接入 gRPC,讲清 PEM、SAN、主机名校验与握手失败的判读。
错误映射不是一张状态码表,而是服务端如何构造 google.rpc.Status、客户端如何从 trailers 还原、网关如何写出 Retry-After 的完整往返。本节给出 GrpcErrors 工具、ErrorInfo 与 RetryInfo 的打包解析、gRPC 与 HTTP 双向映射,以及服务端 onError 和网关写响应的代码。
BFF 的价值不是把多个接口拼在一起,而是用一次并行扇出、单跳 300ms 和明确的降级策略,把页面需要的字段裁成稳定契约。本节用 CompletableFuture.allOf 聚合并发调用,逐个处理部分失败,给出 BookCard 裁剪、800ms 总预算和预期响应示例。
外部客户端只会说 HTTP,内部服务说的是 gRPC。本节用 com.sun.net.httpserver.HttpServer 搭出 9094 网关,把路径映射到 GetBook 与 SearchBooks,按 Accept 在 JSON 和 protobuf 之间切换,并用 HttpClient 保留一条直连 libraryd 的兼容路径,最后给出完整的状态码映射表。
队列不是越大越好:有界 ArrayBlockingQueue 加明确的拒绝策略,才能把过载变成可观测的失败。本节给出 BoundedDispatcher 的构造、拒绝计数与队列深度指标,并用 disableAutoRequest/request(n) 把 gRPC 入站流控接到有界队列上,算清 128 个等待位在 300 ms 预算下的排队上限。
并发限制不是给线程池设个数字,而是要在拦截器里对在途调用精确计数、在超限时用 RESOURCE_EXHAUSTED 和 ErrorInfo 明确拒绝。本节给出 ServerInterceptor 的计数与释放路径、64/128 的装配方式,以及流式响应中用 isReady 和 onReadyHandler 回压生产者的完整实现。
令牌桶的关键不是公式,而是时钟、burst 和拒绝语义。本节用 System.nanoTime 补桶,给出 tryAcquire 与返回等待时长的实现,讨论冷启动满桶、多实例配额分摊和 retry-after 的边界,并用漏桶对照说明什么时候该排队、什么时候该立即拒绝。
熔断挡住已经坏掉的下游,Bulkhead 挡住「还没坏但很慢」的下游。本节用 Semaphore 加有界等待队列实现并发隔离,给出 acquire/release 配对、CompletableFuture.orTimeout 与取消的完整代码,并对比线程池隔离的代价。
熔断不是布尔开关,而是滑动窗口、最小请求数、失败率边界与半开探针共同组成的并发状态机。本节用 AtomicReference 切状态、用一把锁保护窗口,给出完整 CircuitBreaker 与按下游实例分桶的接入代码,并说明迟到响应为什么必须用 generation 丢弃。
从连接 drain 之后的下游抖动讲起,把重试拆成可重试码判定、指数退避与 ±20% 抖动、总 deadline 扣减和全局重试预算;给出 RetryPolicy、RetryBudget 与保留幂等键的 ClientCall 实现,并算清三层重试叠加时的放大倍数。
发布时最怕新请求还在进、老请求被一刀切断。本节拆开 ManagedChannel 与 Server 的 shutdown 语义,用 DrainCoordinator 统计在途请求,给出 linkd-v3 从停止入口、健康降级到关闭 channel 的完整顺序和时间预算。
一条 channel 通常就够,但隔离、并发额度和故障半径会逼你建第二条。本节给出 linkd-v3 的池键设计、有界 channel 池的完整实现,以及池大小与 MAX_CONCURRENT_STREAMS 的换算,避免把连接池写成每请求一条连接。
一条 HTTP/2 连接能同时跑多少请求,取决于流上限、流控窗口和服务端并发配置。本节用 1.76.0 的实测默认值拆开 MAX_CONCURRENT_STREAMS 与 1 MiB 窗口,给出连接数估算表和 linkd-v3 的 9093 配置。
拿到地址列表只是开始,选哪一条、怎么判断它还能不能用才是负载均衡。本节对比 pick_first 与 round_robin 的子通道行为,用 HealthStatusManager 和 HealthGrpc 做出真实的 SERVING/NOT_SERVING 探活,并给出 linkd-v3 的探活周期与抖动控制。
forTarget 里的 scheme 决定谁来解析地址。本节实现 static-registry scheme 的 NameResolverProvider 与 Listener2,把 StaticRegistry 的端点转成 EquivalentAddressGroup,并用 round_robin 跑出真实的 resolver-reply=ok。
服务发现的第一步不是选组件,而是定义注册中心里到底存什么。本节用 linkd-v3 的 StaticRegistry 给出服务、端点、修订号和快照的数据模型,讨论租约、健康与失效,并把注册、解析、管理端三条消费路径接起来,附可编译实现与启动顺序。
消息从对象到字节要过 Marshaller、压缩和长度前缀三道关。本节拆开 gRPC 的 5 字节分帧、gzip 的协商位置,用本机探针读出 1.76.0 的 4 MiB 默认上限,并给出超限时 RESOURCE_EXHAUSTED 的真实输出。
请求 ID、租户、traceparent 不该散落在每个业务方法里。本节用 linkd-v3 的客户端与服务端拦截器,演示 Metadata 键的定义与校验、Context 的附着和清理、响应头回显,并给出拦截器顺序、异常与重试传播的边界。
Channel 管连接,Stub 管一次调用。本节用 linkd-v3 的 9093 端口拆开 ManagedChannel 的状态机、阻塞与异步 Stub 的不可变语义,给出预热、复用以及 shutdown、shutdownNow、awaitTermination 的完整代码,并用自包含探针验证 unary 调用。
区分 at-most-once、at-least-once 和 effectively-once,说明 x-idempotency-key 由谁生成、作用域怎么定、结果缓存多久,给出 20 ms 起、指数 2、±20% 抖动的退避实现,以及用 CompletableFuture 做并发去重的服务端代码。