服务引用原理图一
- referencebean 负责服务暴露入口,相关初始化工作,injvm引用和远程引用协议调用
- 注册协议主要负责注册中心获取以及注册消费者,订阅路由,配置和提供者,通过集群装饰RegistryDirectory统一暴露Invoker ,通过代理构建接口
- RegistryDirectory既负责提供负载路由功能,也负责监听zk,调用dubboProtocol构建dubboInvoker
- dubboProtocol也负责通信层的构建
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c86845ce5f0b364b9d333306f3a3afa4.png)
服务引用原理图二
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/113f5ab9c98c9bbe2d1a3bcbf835ae70.png)
源码分析
ReferenceBean获取消费者代理对象
- spring的factoryBean,在被依赖注入时,通过getObject获取目标对象
- 目标对象是一个消费者动态代理对象
- 构建url参数map
- 创建代理对象
调用get方法
public class ReferenceBean<T> implements FactoryBean{
@Override
public Object getObject() {
return get();
}
}
调用init方法
public synchronized T get() {
checkAndUpdateSubConfigs();
if (destroyed) {
throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
public synchronized void init() {
step-1: 构建消费者url的key-val对应map
Map<String, String> map = new HashMap<String, String>();
map.put(INTERFACE_KEY, interfaceName);
step-2: 构建完毕后map 创建代理
ref = createProxy(map);
发布相关bean实例化事件
dispatch(new ReferenceConfigInitializedEvent(this, invoker));
}
createProxy 创建消费者接口动态代理
- 本地引用匹配 则构建本地Invoker
- 如果url属性不为空,则根据直连地址构建url
- 否则查找注册中心构建url
- 根据url调用相应协议构建Invoker
- 多url则需要在通过StaticDirectory和RegistryAwareCluster统一Invoker暴露
- 通过代理工厂创建目标消费者接口代理对象
private T createProxy(Map<String, String> map) {
step-1: 构建injvmInvoker 判断规则是去InjvmProtocol协议中判断已经暴露的服务servicekey是否匹配消费者 匹配则找到
if (shouldJvmRefer(map)) {
URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
step-2: 直接调用InjvmProtocol返回 Invoker对象
invoker = REF_PROTOCOL.refer(interfaceClass, url);
} else {
step-3.1 直连url或者注册url构建urls
直连地址,可以是服务提供者的地址
if (url != null && url.length() > 0) {
按照,分割
String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (StringUtils.isEmpty(url.getPath())) {
url = url.setPath(interfaceName);
}
注册中心的地址,带上服务引用的配置参数
if (UrlUtils.isRegistry(url)) {
注册中心的地址,带上服务引用的配置参数
urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
} else {
服务提供者的地址
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else {
step-3.2注册url构建urls 获取配置的注册中心地址 一般注册中心走这个
if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
checkRegistry();
这里是获取应用配置的注册信息 RegisterConfig url 也就是注册中心地址
List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
if (CollectionUtils.isNotEmpty(us)) {
for (URL u : us) {
来自于多注册中心ConfigValidationUtils.loadRegistries
urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
}
}
}
}
if (urls.size() == 1) {
step-4: 远程协议调用RegistryProtocol的refer构建Invoker实例
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else {
...... 删除多注册中心 或者多直连地址
}
}
step-5:创建代理 同服务暴露 创建wrapper对象加速调用 避免反射
return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
RegistryProtocol.doRefer第一部分
public class RegistryProtocol implements Protocol {
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = URLBuilder.from(url)
.setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
.removeParameter(REGISTRY_KEY)
.build();
支持分组功能
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
String group = qs.get(GROUP_KEY);
if (group != null && group.length() > 0) {
if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
return doRefer(getMergeableCluster(), registry, type, url);
}
}
调用doRefer实现服务引用
return doRefer(cluster, registry, type, url);
}
}
RegistryProtocol.doRefer核心
- 创建RegistryDirectory 负责监听zk,[监听时触发dubboProtocol协议调用]
- 向zk服务器创建消费者url
- 向zk服务器订阅providers configurators routers目录
- RegistryDirectory会通过监听机制触发对dubboProtocol协议调用
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
...... 流程中删除部分代码
step-1:创建 RegistryDirectory 对象,并设置注册中心 RegistryDirectory自身也实现了NotifyListener监听zk
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
step-2:向注册中心注册自己(服务消费者)
if (directory.isShouldRegister()) {
directory.setRegisteredConsumerUrl(subscribeUrl);
registry.register(directory.getRegisteredConsumerUrl());
}
directory.buildRouterChain(subscribeUrl);
step-3: 订阅zk的providers configurators router目录变更;从而触发dubbo协议构建Invoker到directory
directory.subscribe(toSubscribeUrl(subscribeUrl));
step-3:统一暴露集群Invoker 默认failover集群
Invoker<T> invoker = cluster.join(directory);
List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
if (CollectionUtils.isEmpty(listeners)) {
return invoker;
}
RegistryInvokerWrapper<T> registryInvokerWrapper = new RegistryInvokerWrapper<>(directory, cluster, invoker);
return registryInvokerWrapper;
}
RegistryDirectory.subscribe
- 将RegistryDirectory加入注册中心集合,用于将来监听时查询指定监听器负责监听事件
- doSubscribe实现监听订阅逻辑
public void subscribe(URL url) {
setConsumerUrl(url);
CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this);
serviceConfigurationListener = new ReferenceConfigurationListener(this, url);
将自己作用NotifyListener监听注册中心
registry.subscribe(url, this);
}
将RegistryDirectory添加到注册中心subscribed集合
public void subscribe(URL url, NotifyListener listener) {
...... 删除其他代码
Set<NotifyListener> listeners = subscribed.computeIfAbsent(url, n -> new ConcurrentHashSet<>());
listeners.add(listener);
}
public void subscribe(URL url, NotifyListener listener) {
将监听器加入注册中心元数据
super.subscribe(url, listener);
removeFailedSubscribed(url, listener);
try {
实现订阅
doSubscribe(url, listener);
} catch (Exception e) {
...... 删除其他代码
}
}
ZookeeperRegistry.doSubscribe
- 根据分类Categories监听订阅相关目录
- 通过notify实现订阅事件处理
public void doSubscribe(final URL url, final NotifyListener listener) {
List<URL> urls = new ArrayList<>();
根据toCategoriesPath订阅配置 路由 提供者目录
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> {
return new ConcurrentHashMap<>();
});
step-1: 向zk注册监听订阅
zkClient.create(path, false);
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
step-2: 首次全量数据获取完成时,调用 `#notify(...)` 方法,回调 NotifyListener
notify(url, listener, urls);
}
ZookeeperRegistry.notify
- 通过zk变更调用RegistryDirectory.notify
protected void notify(URL url, NotifyListener listener, List<URL> urls) {
for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
这里会调用RegisterDirectory进行refer跟新消费端的Invoker信息 调用dubboProtocol.refer [当然还有其他监听listener,这里我们只关注消费端]
listener.notify(categoryList);
}
}
RegistryDirectory.notify通知核心
- 刷新配置信息
- 刷新路由信息
- 刷新overrideDirectoryUrl和 提供者信息
public synchronized void notify(List<URL> urls) {
Map<String, List<URL>> categoryUrls = urls.stream()
.filter(Objects::nonNull)
.filter(this::isValidCategory)
.filter(this::isNotCompatibleFor26x)
.collect(Collectors.groupingBy(this::judgeCategory));
step-1: 刷新配置信息
List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);
step-2: 刷新路由信息
List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
toRouters(routerURLs).ifPresent(this::addRouters);
List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
...... 删除其他代码
step-3: 刷新overrideDirectoryUrl和 提供者信息
refreshOverrideAndInvoker(providerURLs);
}
RegistryDirectory.refreshInvoker构建DubboInvoker
- 根据zk的监听变化调用dubboProtocol.refer 构建 新的Invokers
- 变更路由链
- 修改RegistryDirectory的urlInvokerMap
private void refreshInvoker(List<URL> invokerUrls) {
...... 删除其他代码
step-1: 根据zk的监听变化调用dubboProtocol.refer 构建 新的Invokers
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
step-2: 变更路由链
routerChain.setInvokers(newInvokers);
this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
step-3: 修改RegistryDirectory的urlInvokerMap
this.urlInvokerMap = newUrlInvokerMap;
...... 删除其他代码
}
toInvokers 调用dubboProtocol构建Invoker
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
根据disable enable判断允许变更否
boolean enabled = true;
if (url.hasParameter(DISABLED_KEY)) {
enabled = !url.getParameter(DISABLED_KEY, false);
} else {
enabled = url.getParameter(ENABLED_KEY, true);
}
if (enabled) {
使用dubbo协议创建具有远程通信功能的Invoker
构建dubbo invoker dubboprotocol 其中dubboInvoker含有 数据转换ExchangeClient 以及通信层对象
invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
}
if (invoker != null) {
newUrlInvokerMap.put(key, invoker);
}
keys.clear();
return newUrlInvokerMap;
}
dubbo协议refer实现
public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
optimizeSerialization(url);
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
总结
- 讲述服务引用核心流程构建Invoker ,Invoker加目标接口构建动态代理对象
- Invoker的核心是RegistryDirectory
- RegistryDirectory负责监听zk,处理提供者,配置路由的变更
- RegistryDirectory负责在服务调用时处理路由负载均衡
- dubboInvoker 负责处理通信层的构建
- RegistryProtocol主要负责注册消费者URL和监听服务提供者,路由以及配置目录URL
- 监听的事件通过RegistryDirectory调用DubboProtocol构建dubboInvoker
扩展点一服务引用Invoker结构
ClusterInvoker | RegistryDirectory | InvokerDelegate | ListenerInvokerWrapper | CallbackRegistrationInvoker | AsyncToSyncInvoker | DubboInvoker | ExchangeClient |
---|
统一暴露,容错熔断 | 负载路由 | 委派 | 监听 | 过滤 | 异步转同步 | 协议Invoker | 通信层 |