一、服务目录(Directory)核心设计
1. 服务目录的核心作用
图表
代码
复制
下载
全屏
graph TB
A[服务消费者] --> B[服务目录Directory]
B --> C[RegistryDirectory]
B --> D[StaticDirectory]
C --> E[服务发现]
C --> F[配置变更监听]
C --> G[Invoker列表维护]
2. RegistryDirectory核心实现
java
复制
下载
public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
// 核心数据结构
private final Map<String, Invoker<T>> urlInvokerMap = new ConcurrentHashMap<>();
private final Map<String, List<Invoker<T>>> methodInvokerMap = new ConcurrentHashMap<>();
// 服务发现与注册中心
private Registry registry;
private URL consumerUrl;
/**
* 订阅服务变更通知
*/
@Override
public synchronized void notify(List<URL> urls) {
// 1. 服务提供者URL分类
Map<String, List<URL>> categoryUrls = urls.stream()
.filter(UrlUtils::isMatch)
.collect(Collectors.groupingBy(url -> {
if (UrlUtils.isConfigurator(url)) {
return CONFIGURATORS_CATEGORY;
} else if (UrlUtils.isRoute(url)) {
return ROUTERS_CATEGORY;
} else if (UrlUtils.isProvider(url)) {
return PROVIDERS_CATEGORY;
}
return "";
}));
// 2. 处理配置URL
List<URL> configuratorUrls = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
this.configurators = Configurator.toConfigurators(configuratorUrls);
// 3. 处理路由URL
List<URL> routerUrls = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
toRouters(routerUrls).ifPresent(this::addRouters);
// 4. 处理服务提供者URL
List<URL> providerUrls = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
refreshOverrideAndInvoker(providerUrls);
}
/**
* 刷新Invoker列表
*/
private void refreshOverrideAndInvoker(List<URL> urls) {
// 1. 覆盖配置处理
overrideDirectoryUrl();
// 2. 转换为Invoker
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(urls);
// 3. 状态转换
if (newUrlInvokerMap == null || newUrlInvokerMap.isEmpty()) {
// 服务不可用
destroyAllInvokers();
} else {
// 更新Invoker映射
this.urlInvokerMap = newUrlInvokerMap;
// 4. 构建方法级Invoker映射
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap);
// 5. 合并多组Invoker
this.methodInvokerMap = multiGroup ? toMergeInvokerList(newMethodInvokerMap) : newMethodInvokerMap;
// 6. 销毁无用Invoker
destroyUnusedInvokers(newUrlInvokerMap);
}
}
/**
* URL转Invoker
*/
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
if (urls == null || urls.isEmpty()) {
return newUrlInvokerMap;
}
Set<String> keys = new HashSet<>();
for (URL url : urls) {
// 应用配置规则
URL configuredUrl = getConfiguredInvokerUrl(url);
// 生成key: protocol://host:port/path?version
String key = configuredUrl.toFullString();
if (keys.contains(key)) {
continue; // 去重
}
keys.add(key);
// 缓存中获取或创建Invoker
Invoker<T> invoker = invokersCache.get(key);
if (invoker == null) {
// 创建新的Invoker
invoker = protocol.refer(serviceType, configuredUrl);
invokersCache.put(key, invoker);
}
newUrlInvokerMap.put(key, invoker);
}
keys.clear();
return newUrlInvokerMap;
}
}
3. 服务目录的核心功能
3.1 服务发现与注册
java
复制
下载
public class RegistryDirectory<T> extends AbstractDirectory<T> {
// 订阅服务
public void subscribe(URL url) {
this.consumerUrl = url;
// 注册消费者
registry.register(consumerUrl);
// 订阅服务提供者
registry.subscribe(url, this);
}
// 取消订阅
public void unsubscribe(URL url) {
registry.unsubscribe(url, this);
destroyAllInvokers();
}
}
3.2 动态配置更新
java
复制
下载
public class RegistryDirectory<T> extends AbstractDirectory<T> {
// 配置变更监听
private void refreshInvoker(List<URL> invokerUrls) {
if (invokerUrls != null && invokerUrls.size() == 1
&& invokerUrls.get(0) != null
&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
// 服务提供者下线
this.forbidden = true;
this.methodInvokerMap = null;
destroyAllInvokers();
} else {
this.forbidden = false;
Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap;
// 对比新旧URL,增量更新
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
// 找出需要销毁的Invoker
List<Invoker<T>> destroyedInvokers = new ArrayList<>();
for (Map.Entry<String, Invoker<T>> entry : localUrlInvokerMap.entrySet()) {
if (!newUrlInvokerMap.containsKey(entry.getKey())) {
destroyedInvokers.add(entry.getValue());
}
}
// 销毁无用Invoker
for (Invoker<T> invoker : destroyedInvokers) {
invoker.destroy();
invokersCache.remove(invoker.getUrl().toFullString());
}
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
二、路由链(RouterChain)实现原理
1. 路由链的构建过程
java
复制
下载
public class RouterChain<T> {
private List<Router> routers = new ArrayList<>();
// 构建路由链
public static <T> RouterChain<T> buildChain(URL url) {
RouterChain<T> chain = new RouterChain<>();
// 1. 内置路由
chain.addRouter(new TagRouter());
chain.addRouter(new AppRouter());
// 2. 配置的路由(从URL参数获取)
String routerConfig = url.getParameter(Constants.ROUTER_KEY);
if (StringUtils.isNotEmpty(routerConfig)) {
String[] routerTypes = routerConfig.split(",");
for (String routerType : routerTypes) {
Router router = ExtensionLoader.getExtensionLoader(RouterFactory.class)
.getExtension(routerType)
.getRouter(url);
chain.addRouter(router);
}
}
// 3. 脚本路由
chain.addRouter(new ScriptRouter(url));
// 4. 条件路由
chain.addRouter(new ConditionRouter(url));
return chain;
}
// 路由调用
public List<Invoker<T>> route(URL url, Invocation invocation) {
List<Invoker<T>> finalInvokers = invokers;
// 依次通过各个路由筛选
for (Router router : routers) {
finalInvokers = router.route(finalInvokers, url, invocation);
// 如果已经没有可用Invoker,提前终止
if (finalInvokers == null || finalInvokers.isEmpty()) {
return finalInvokers;
}
}
return finalInvokers;
}
}
2. 核心路由实现详解
2.1 条件路由(ConditionRouter)
java
复制
下载
public class ConditionRouter implements Router {
// 路由规则解析
private final Map<String, MatchPair> whenCondition;
private final Map<String, MatchPair> thenCondition;
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers,
URL url,
Invocation invocation) {
if (invokers == null || invokers.isEmpty()) {
return invokers;
}
try {
// 1. 判断是否匹配when条件
if (!matchWhen(url, invocation)) {
return invokers; // 不匹配,返回所有
}
List<Invoker<T>> result = new ArrayList<>();
// 2. 应用then条件筛选
for (Invoker<T> invoker : invokers) {
if (matchThen(invoker.getUrl(), url)) {
result.add(invoker);
}
}
// 3. 如果有匹配的结果,返回;否则返回空
if (!result.isEmpty()) {
return result;
} else if (thenCondition.isEmpty()) {
// then条件为空表示黑名单,返回空
return result;
}
} catch (Throwable t) {
// 路由出错,返回所有
return invokers;
}
return invokers;
}
// 条件匹配
private boolean matchWhen(URL url, Invocation invocation) {
return whenCondition == null || whenCondition.isEmpty()
|| matchCondition(whenCondition, url, null, invocation);
}
// 模式匹配引擎
private boolean matchCondition(Map<String, MatchPair> condition,
URL url,
URL param,
Invocation invocation) {
Map<String, String> sample = url.toMap();
for (Map.Entry<String, MatchPair> entry : condition.entrySet()) {
String key = entry.getKey();
MatchPair pair = entry.getValue();
// 获取参数值
String sampleValue = getSampleValue(key, sample, url, param, invocation);
if (sampleValue == null) {
// 参数不存在但规则要求必须匹配,返回false
if (!pair.matches.isEmpty()) {
return false;
}
continue;
}
// 检查是否匹配
if (!pair.isMatch(sampleValue)) {
return false;
}
}
return true;
}
}
2.2 标签路由(TagRouter)
java
复制
下载
public class TagRouter implements Router {
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers,
URL url,
Invocation invocation) {
if (invokers == null || invokers.isEmpty()) {
return invokers;
}
// 1. 获取请求标签
String tag = StringUtils.isNotEmpty(invocation.getAttachment(TAG_KEY))
? invocation.getAttachment(TAG_KEY)
: url.getParameter(TAG_KEY);
// 2. 如果没有标签,过滤掉有标签的提供者
if (StringUtils.isEmpty(tag)) {
return invokers.stream()
.filter(invoker -> {
String providerTag = invoker.getUrl().getParameter(TAG_KEY);
return StringUtils.isEmpty(providerTag);
})
.collect(Collectors.toList());
}
// 3. 匹配标签
List<Invoker<T>> tagInvokers = invokers.stream()
.filter(invoker -> tag.equals(invoker.getUrl().getParameter(TAG_KEY)))
.collect(Collectors.toList());
// 4. 如果没有匹配的标签服务,降级到无标签服务
if (!tagInvokers.isEmpty()) {
return tagInvokers;
} else {
return invokers.stream()
.filter(invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(TAG_KEY)))
.collect(Collectors.toList());
}
}
}
3. 路由链的扩展机制
java
复制
下载
// 自定义路由实现
@SPI
public interface Router extends Comparable<Router> {
int DEFAULT_PRIORITY = 0;
/**
* 路由方法
*/
<T> List<Invoker<T>> route(List<Invoker<T>> invokers,
URL url,
Invocation invocation);
/**
* 获取路由URL
*/
URL getUrl();
/**
* 路由优先级,数值越小优先级越高
*/
default int getPriority() {
return DEFAULT_PRIORITY;
}
}
// 路由工厂
@SPI
public interface RouterFactory {
/**
* 创建路由器
*/
@Adaptive("protocol")
Router getRouter(URL url);
}
// 扩展点配置
// META-INF/dubbo/internal/com.alibaba.dubbo.rpc.RouterFactory
condition=com.alibaba.dubbo.rpc.cluster.router.condition.ConditionRouterFactory
script=com.alibaba.dubbo.rpc.cluster.router.script.ScriptRouterFactory
file=com.alibaba.dubbo.rpc.cluster.router.file.FileRouterFactory
三、服务目录与路由链的协同工作
1. 完整的调用流程
java
复制
下载
public abstract class AbstractDirectory<T> implements Directory<T> {
@Override
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
// 1. 获取所有Invoker
List<Invoker<T>> invokers = doList(invocation);
// 2. 获取路由链
List<Router> routers = getRouters();
// 3. 应用路由链
if (routers != null && !routers.isEmpty()) {
for (Router router : routers) {
invokers = router.route(invokers, getConsumerUrl(), invocation);
}
}
// 4. 返回过滤后的Invoker
return invokers == null ? Collections.emptyList() : invokers;
}
// 具体实现
protected List<Invoker<T>> doList(Invocation invocation) {
if (forbidden) {
throw new RpcException("No provider available for service " + getInterface().getName());
}
List<Invoker<T>> invokers = null;
// 从缓存中获取
Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap;
if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
String methodName = invocation.getMethodName();
invokers = localMethodInvokerMap.get(methodName);
// 方法级路由
if (invokers == null) {
invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
}
}
return invokers;
}
}
2. 动态配置更新机制
java
复制
下载
public class RegistryDirectory<T> extends AbstractDirectory<T> {
// 监听配置变更
private void notify(List<URL> urls) {
// 路由规则更新
List<URL> routerUrls = filterRouter(urls);
if (!routerUrls.isEmpty()) {
// 更新路由链
List<Router> routers = toRouters(routerUrls);
if (routers != null) {
setRouters(routers);
}
}
// 配置规则更新
List<URL> configuratorUrls = filterConfigurator(urls);
if (!configuratorUrls.isEmpty()) {
// 刷新配置
refreshInvoker(invokerUrls);
}
}
// 路由规则转换
private Optional<List<Router>> toRouters(List<URL> routerUrls) {
List<Router> routers = new ArrayList<>();
for (URL routerUrl : routerUrls) {
try {
// 通过SPI加载路由工厂
RouterFactory factory = ExtensionLoader.getExtensionLoader(RouterFactory.class)
.getAdaptiveExtension();
Router router = factory.getRouter(routerUrl);
routers.add(router);
} catch (Throwable t) {
logger.error("Failed to create router from url: " + routerUrl, t);
}
}
return Optional.of(routers);
}
}
四、高级特性与优化
1. 路由缓存与性能优化
java
复制
下载
public class RouterChain<T> {
// 路由结果缓存
private final Map<String, List<Invoker<T>>> routeCache = new LRUCache<>(1000);
public List<Invoker<T>> route(URL url, Invocation invocation) {
// 生成缓存key
String cacheKey = generateCacheKey(url, invocation);
// 尝试从缓存获取
List<Invoker<T>> cachedResult = routeCache.get(cacheKey);
if (cachedResult != null) {
return cachedResult;
}
// 计算路由结果
List<Invoker<T>> result = doRoute(url, invocation);
// 放入缓存(设置合适的TTL)
if (shouldCache(invocation)) {
routeCache.put(cacheKey, result);
}
return result;
}
private String generateCacheKey(URL url, Invocation invocation) {
StringBuilder sb = new StringBuilder();
sb.append(url.getServiceKey())
.append("#")
.append(invocation.getMethodName())
.append("#")
.append(getParameterHash(url, invocation));
return sb.toString();
}
}
2. 灰度发布与路由策略
java
复制
下载
// 基于权重的灰度路由
public class WeightRouter implements Router {
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers,
URL url,
Invocation invocation) {
// 计算总权重
int totalWeight = 0;
List<WeightedInvoker<T>> weightedInvokers = new ArrayList<>();
for (Invoker<T> invoker : invokers) {
int weight = invoker.getUrl().getParameter(WEIGHT_KEY, DEFAULT_WEIGHT);
if (weight > 0) {
totalWeight += weight;
weightedInvokers.add(new WeightedInvoker<>(invoker, weight));
}
}
// 随机选择
if (totalWeight > 0) {
int random = ThreadLocalRandom.current().nextInt(totalWeight);
int current = 0;
for (WeightedInvoker<T> weightedInvoker : weightedInvokers) {
current += weightedInvoker.weight;
if (random < current) {
return Collections.singletonList(weightedInvoker.invoker);
}
}
}
// 降级到随机选择
return invokers;
}
private static class WeightedInvoker<T> {
final Invoker<T> invoker;
final int weight;
WeightedInvoker(Invoker<T> invoker, int weight) {
this.invoker = invoker;
this.weight = weight;
}
}
}
3. 故障转移与熔断路由
java
复制
下载
public class FailoverRouter implements Router {
private final CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.getInstance();
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers,
URL url,
Invocation invocation) {
return invokers.stream()
.filter(invoker -> {
// 检查熔断器状态
String serviceKey = invoker.getUrl().getServiceKey();
CircuitBreaker circuitBreaker = circuitBreakerRegistry.getCircuitBreaker(serviceKey);
if (circuitBreaker != null && circuitBreaker.isOpen()) {
// 熔断器打开,跳过该服务
return false;
}
// 检查健康状态
return isHealthy(invoker);
})
.collect(Collectors.toList());
}
private <T> boolean isHealthy(Invoker<T> invoker) {
// 检查最近错误率
// 检查响应时间
// 检查连接状态
return true;
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
五、最佳实践与配置
1. 配置示例
xml
复制
下载
运行
<!-- 服务消费者配置 -->
<dubbo:reference id="userService" interface="com.example.UserService">
<!-- 路由规则 -->
<dubbo:parameter key="router" value="tag,condition" />
<!-- 条件路由规则 -->
<dubbo:parameter key="rule" value="
=> host != 192.168.1.100
& method = find* => host = 192.168.1.*
& version = 2.0 => cluster = cluster1
" />
<!-- 标签路由 -->
<dubbo:parameter key="tag" value="gray" />
</dubbo:reference>
<!-- 动态配置 -->
<dubbo:config-center address="zookeeper://127.0.0.1:2181">
<dubbo:parameter key="router" value="
scope: service
key: com.example.UserService
enabled: true
rules:
- priority: 1
conditions:
- method=find* => host=192.168.1.*
- priority: 2
conditions:
- => tag=gray
" />
</dubbo:config-center>
2. 监控与调试
java
复制
下载
// 路由链监控
public class RouterChainMonitor {
public void monitorRouteChain(RouterChain<?> chain, Invocation invocation) {
List<Invoker<?>> initialInvokers = chain.getInitialInvokers();
Map<String, List<Invoker<?>>> routeHistory = new LinkedHashMap<>();
// 记录每个路由器的输出
for (Router router : chain.getRouters()) {
List<Invoker<?>> before = routeHistory.isEmpty()
? initialInvokers
: routeHistory.values().iterator().next();
List<Invoker<?>> after = router.route(before, getUrl(), invocation);
routeHistory.put(router.getClass().getSimpleName(), after);
// 记录到监控系统
recordRouteMetrics(router, before.size(), after.size());
}
// 输出路由链详情
logRouteChainDetail(routeHistory);
}
}
六、总结
Dubbo服务目录与路由链的核心要点:
-
服务目录:
-
负责维护可用的服务提供者列表
-
监听注册中心变化,动态更新Invoker
-
提供方法级的服务分组
-
-
路由链:
-
通过责任链模式实现多重路由规则
-
支持条件路由、标签路由、脚本路由等
-
路由优先级控制执行顺序
-
-
关键特性:
-
动态更新:配置热更新,无需重启
-
扩展性强:SPI机制支持自定义路由
-
性能优化:缓存、懒加载等优化手段
-
容错机制:路由失败降级策略
-
-
最佳实践:
-
合理设计路由规则,避免过度复杂
-
监控路由链性能,及时优化
-
结合业务场景选择合适的路由策略
-
这种设计使得Dubbo能够实现复杂的流量治理需求,如灰度发布、蓝绿部署、区域路由等高级功能。