ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

关于SentinelContractHolder:方法元数据缓存

关于SentinelContractHolder:方法元数据缓存 /** * SentinelContractHolder方法元数据缓存* ** 核心作用** 1. SentinelContractHolder 对 Feign Contract 进行包装。* 2. Feign 在解析接口方法时生成 MethodMetadata。* 3. SentinelContractHolder 在解析完成后将 MethodMetadata 保存到* METADATA_MAP 中。* 4. 后续 SentinelInvocationHandler 执行 Feign 方法时可以根据* Class Feign.configKey(...) 快速取得对应的 MethodMetadata。** 官方 API 文档对该类的描述也是* 使用静态 METADATA_MAP 保存 MethodMetadata 数据。** 典型缓存 Key** targetType.getName() metadata.configKey()** 查询时** hardCodedTarget.type().getName()* Feign.configKey(hardCodedTarget.type(), method)** */package com.example.sentinel.feign;import feign.Contract;import feign.MethodMetadata;import java.util.Collections;import java.util.List;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;public class SentinelContractHolder implements Contract {/** * 方法元数据缓存* ** Key* Feign Client 接口全限定类名 configKey** Value* MethodMetadata** 使用 ConcurrentHashMap 可以避免多个线程初始化或读取时* 对普通 HashMap 产生并发安全问题。*/public static final MapString, MethodMetadata METADATA_MAP new ConcurrentHashMap();/** * 被包装的原始 Contract* ** SentinelContractHolder 本身并不负责重新实现 Feign 的* 方法解析逻辑而是通过委托模式调用原始 Contract。*/private final Contract delegate;public SentinelContractHolder(Contract delegate) {this.delegate delegate;}/** * 解析 Feign Client 方法* ** Feign 原本负责** Interface* ↓* Method* ↓* MethodMetadata** Sentinel 在这里增加** MethodMetadata* ↓* METADATA_MAP*/Overridepublic ListMethodMetadata parseAndValidateMetadata(Class? targetType) {ListMethodMetadata metadatas delegate.parseAndValidateMetadata(targetType);/** 将当前 Feign Client 的所有方法元数据写入缓存。*/for (MethodMetadata metadata : metadatas) {String key buildKey(targetType, metadata);METADATA_MAP.put(key, metadata);}return metadatas;}/** * 构造缓存 Key* */private String buildKey(Class? targetType,MethodMetadata metadata) {return targetType.getName() metadata.configKey();}/** * 根据 Key 获取 MethodMetadata* */public static MethodMetadata getMetadata(String key) {return METADATA_MAP.get(key);}/** * 删除指定元数据* */public static MethodMetadata removeMetadata(String key) {return METADATA_MAP.remove(key);}/** * 清空缓存** 通常只建议在测试或明确需要重新初始化时使用。* */public static void clear() {METADATA_MAP.clear();}/** * 获取当前缓存数量* */public static int size() {return METADATA_MAP.size();}}/** * Feign Client 示例* */package com.example.service;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;FeignClient(name order-service)public interface OrderService {GetMapping(/orders/{id})String getOrder(PathVariable(id) Long id);GetMapping(/orders/list)String listOrders();}/** * SentinelInvocationHandler 中读取元数据* */package com.example.sentinel.feign;import feign.Feign;import feign.InvocationHandlerFactory;import feign.MethodMetadata;import feign.Target;import java.lang.reflect.Method;public class MetadataLookupExample {public MethodMetadata findMetadata(Target? target,Method method) {if (!(target instanceof Target.HardCodedTarget)) {return null;}Target.HardCodedTarget? hardCodedTarget (Target.HardCodedTarget?) target;/** 构造查询 Key。** 写入缓存时** targetType.getName()* metadata.configKey()** 查询时** target.type().getName()* Feign.configKey(target.type(), method)*/String key hardCodedTarget.type().getName() Feign.configKey(hardCodedTarget.type(),method);return SentinelContractHolder.METADATA_MAP.get(key);}}/** * 完整流程示意* ** Feign Client* │* ▼* SentinelContractHolder* │* ▼* delegate.parseAndValidateMetadata(...)* │* ▼* ListMethodMetadata* │* ▼* METADATA_MAP.put(key, metadata)* │* ▼* Feign Proxy 创建完成*** 请求执行阶段** Method* │* ▼* Feign.configKey(...)* │* ▼* 构造缓存 Key* │* ▼* METADATA_MAP.get(key)* │* ▼* MethodMetadata* │* ▼* 获取 HTTP Method / Path 等信息* │* ▼* Sentinel 生成资源名称* │* ▼* Sentinel 流控 / 熔断 / 降级*//** * 为什么需要 MethodMetadata 缓存* ** Feign 的 MethodMetadata 包含接口方法经过 Contract 解析后的* 信息例如请求模板、HTTP Method、Path 等。** Sentinel 在执行代理方法时需要知道** 当前 Method* ↓* 对应哪个 Feign 请求* ↓* 对应哪个 Sentinel Resource** 如果每次调用都重新解析接口注解成本会更高。** 因此可以在 Feign 初始化阶段完成解析** 启动阶段* ↓* parseAndValidateMetadata()* ↓* 缓存 MethodMetadata** 请求阶段** Method* ↓* 计算 Key* ↓* Map.get()* ↓* MethodMetadata** 从而把方法元数据解析与实际请求执行分离。*//** * 一个简单的单元测试* */package com.example.sentinel.feign;import feign.Contract;import feign.MethodMetadata;import org.junit.jupiter.api.Test;import java.util.List;import static org.junit.jupiter.api.Assertions.*;class SentinelContractHolderTest {Testvoid shouldCacheMethodMetadata() {/** 实际项目中这里可以使用 Spring Cloud OpenFeign* 创建的 Contract。*/Contract contract new Contract.Default();SentinelContractHolder holder new SentinelContractHolder(contract);ListMethodMetadata metadataList holder.parseAndValidateMetadata(TestClient.class);assertFalse(metadataList.isEmpty());for (MethodMetadata metadata : metadataList) {String key TestClient.class.getName() metadata.configKey();MethodMetadata cached SentinelContractHolder.METADATA_MAP.get(key);assertNotNull(cached);assertSame(metadata, cached);}}interface TestClient {String hello(String name);}}/** * 关键点总结* ** SentinelContractHolder 本质上是一个 Contract 装饰器。** 核心代码可以浓缩为** ListMethodMetadata metadatas * delegate.parseAndValidateMetadata(targetType);** metadatas.forEach(metadata -* METADATA_MAP.put(* targetType.getName()* jnd32.cc(),* metadata* )* );*** 数据流** Feign Contract* ↓* MethodMetadata* ↓* METADATA_MAP* ↓* Method 查询* ↓* Feign.configKey(...)* ↓* MethodMetadata*** 注意** SentinelContractHolder** 属于 Spring Cloud Alibaba Sentinel 与 OpenFeign 集成中的* 适配逻辑。** 不同 Spring Cloud Alibaba / OpenFeign 版本的 Contract API* 可能存在差异尤其是旧版本曾出现* parseAndValidatateMetadata 与* parseAndValidateMetadata 的方法名兼容问题。*//** 参考** SentinelContractHolder 的官方 API 文档说明* METADATA_MAP 用于保存 MethodMetadata。** 资料*** Feign Sentinel 的设计说明Scrapyd分步教程**/
返回列表