dubbo用在微服务,那么肯定是需要使用到集群的 ,面对集群就需要处理机器宕机的问题,如果是普通服务,一个机子宕机,那么我们还可以人工去维护,在集群里面宕机,就很麻烦,所以需要我们软件有容错的功能,dubbo在容错机制,分为四个
1.服务目录 Directory(也就是本章)
2.服务路由Router
3.集群Cluster
4.负责均衡LoadBalance
对于服务目录是什么?
它存储了服务提供者的信息,比如Ip,port等等,它需要有效性,也就是需要动态的更新,我们的服务提供者把自己注册到zk上面,消费者就会到zk上拉取,为每提供者生成一个invoker对象,保存起来。也正是因为有服务目录的存在,所以当zk挂掉的时候,dubbo还能继续根据服务目录的信息,进行通信。
我们的类其实很简单,分为StaticDirectory和RegistryDirectory,他们共同的父类AbstractDirectory
1.AbstracDirectory类
核心方法,列举invoker,比较简单,其实也就是遍历一下
public abstract class AbstractDirectory implements Directory {
private static final Logger logger = LoggerFactory.getLogger(AbstractDirectory.class);
private final URL url;
private volatile boolean destroyed = false;
private volatile URL consumerUrl;
protected RouterChain routerChain; //invoker 封装
...
public List> list(Invocation invocation) throws RpcException {
//委派模式 抽象方法
return doList(invocation);
}
...
}
我们看一下RouterChain,路由链,
public class RouterChain {
// full list of addresses from registry, classified by method name.
private List> invokers = Collections.emptyList();
// containing all routers, reconstruct every time 'route://' urls change.
private volatile List routers = Collections.emptyList();
// Fixed router instances: ConfigConditionRouter, TagRouter, e.g., the rule for each instance may change but the
// instance will never delete or recreate.
private List builtinRouters = Collections.emptyList();
...
}
2.StaticDirectory
静态服务目录,内存存放的invoker是不会发生改变的,它有一个唯一字段
private final List> invokers;
然后就是父类定义的抽象方法实现 doList()
protected List> doList(Invocation invocation) throws RpcException {
List> finalInvokers = invokers; //取出来
if (routerChain != null) { //父容器
try { //路由链非空,过滤一下
finalInvokers = routerChain.route(getConsumerUrl(), invocation); //【入】
} catch (Throwable t) {
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
}
}
return finalInvokers == null ? Collections.emptyList() : finalInvokers;
}
3.RegistryDirectory
是一种动态服务目录,实现了NotifyListener接口,当注册中心服务配置发生变化之后,RegistryDirectory可收到与当前服务相关的变化,然后刷新到invoker list中。重要的逻辑三个:invoker列举,接收服务配置更新,Invoker刷新。
这个类的内容,明显就比StaticDirectory复杂多了,它的字段,方法量很大,而且还有三个内部类。其方法主要还是实现上面说的三个逻辑。
3.1.invoker列举
public List> doList(Invocation invocation) {
if (forbidden) {
// 1.没有服务提供者or 服务提供者死了
}
if (multiGroup) {
return this.invokers == null ? Collections.emptyList() : this.invokers;
}
//直接从父类中获取
List> invokers = null;
try {
invokers = routerChain.route(getConsumerUrl(), invocation);
}
return invokers == null ? Collections.emptyList() : invokers;
}
3.2.接收服务配置更新
public synchronized void notify(List urls) {
//流式 遍历,根据category的内容,把url放到下方3个不同的url中。
Map> categoryUrls = urls.stream()
.filter(Objects::nonNull)
.filter(this::isValidCategory)
.filter(this::isNotCompatibleFor26x)
.collect(Collectors.groupingBy(this::judgeCategory));
//定义三个集合存放 配置器url,路由URL, 服务提供者的 Url。
//配置器url
List configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
//将url转换成为 Configurator
this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);
//路由url
List routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
//将url 转换成为 Router
toRouters(routerURLs).ifPresent(this::addRouters);
//服务提供者 url
List providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
/**
* 3.x added for extend URL address
*/
ExtensionLoader addressListenerExtensionLoader = ExtensionLoader.getExtensionLoader(AddressListener.class);
List supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
if (supportedListeners != null && !supportedListeners.isEmpty()) {
for (AddressListener addressListener : supportedListeners) {
providerURLs = addressListener.notify(providerURLs, getConsumerUrl(),this);
}
}
//刷新invoker 列表
refreshOverrideAndInvoker(providerURLs); //【入】
}
3.3.invoker刷新
private void refreshOverrideAndInvoker(List urls) {
// mock zookeeper://xxx?mock=return null
overrideDirectoryUrl();
refreshInvoker(urls);
}
private void refreshInvoker(List invokerUrls) {
Assert.notNull(invokerUrls, "invokerUrls should not be null");
//invokerUrls只有一个元素,且url协议头为empty,此时表示禁用所有服务。
if (invokerUrls.size() == 1
&& invokerUrls.get(0) != null
&& EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // Forbid to access
this.invokers = Collections.emptyList();
routerChain.setInvokers(this.invokers);
destroyAllInvokers(); // 摧毁所有的 invoker
} else {
this.forbidden = false; // Allow to access
Map> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls == Collections.emptyList()) {
invokerUrls = new ArrayList<>();
}
if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
//添加缓冲url 到invokerUrls 中
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet<>();
// 缓冲invokerUrils
this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
}
if (invokerUrls.isEmpty()) {
return;
}
//1.将url 转换成 invoker 【分析 入】
Map> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
//转换出错,直接打印异常返回
if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
.toString()));
return;
}
//将newUrlInvoker 转成方法名到invoker列表 的映射
List> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
// pre-route and build cache, notice that route cache should build on original Invoker list.
// toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
routerChain.setInvokers(newInvokers);
//2.合并多个组的invoker 【分析 入】
this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
this.urlInvokerMap = newUrlInvokerMap;
try {
//3.摧毁 无用的 invoker 【分析 入】
destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap);
} catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}
3.3.1 url转invoker
//将 urls 转换成为 invoker
private Map> toInvokers(List urls) {
Map> newUrlInvokerMap = new HashMap<>();
if (urls == null || urls.isEmpty()) {
return newUrlInvokerMap;
}
Set keys = new HashSet<>();
//获取 服务消费端配置的 协议
String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
for (URL providerUrl : urls) {
// If protocol is configured at the reference side, only the matching protocol is selected
if (queryProtocols != null && queryProtocols.length() > 0) {
boolean accept = false;
String[] acceptProtocols = queryProtocols.split(",");
//检测 服务提供者 协议是否 被服务消费者所支持
for (String acceptProtocol : acceptProtocols) {
if (providerUrl.getProtocol().equals(acceptProtocol)) {
accept = true;
break;
}
}
if (!accept) {//若服务消费者协议头 不被消费者所支持,则忽略当前providerUrl
continue;
}
}//忽略 empty 协议
if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}//通过 SPI 检测服务端协议是否被消费端支持,不支持则抛出异常
....
//合并url
URL url = mergeUrl(providerUrl);
String key = url.toFullString(); // The parameter urls are sorted
if (keys.contains(key)) { // 忽略重复 url
continue;
}
keys.add(key);
// 将本地缓冲invoker 缓冲赋值给LocalUrlInvokerMap
Map> localUrlInvokerMap = this.urlInvokerMap; // local reference
//获取与url 对应的invoker
Invoker invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // 缓冲 未命中
try {
boolean enabled = true;
if (url.hasParameter(DISABLED_KEY)) {
//获取 disable配置,取反,然后赋值给enable变量
enabled = !url.getParameter(DISABLED_KEY, false);
} else {
//获取 enable配置,并赋值给enable变量
enabled = url.getParameter(ENABLED_KEY, true);
}
if (enabled) {
//调用refer 获取invoker
invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
}
} catch (Throwable t) {
logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
}
if (invoker != null) { // 缓存invoker 实例
newUrlInvokerMap.put(key, invoker);
}
} else { //缓存命中
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
3.3.2 合并invoker
private List> toMergeInvokerList(List> invokers) {
List> mergedInvokers = new ArrayList<>();
//方法名 -> invoker列表
Map>> groupMap = new HashMap<>();
for (Invoker invoker : invokers) { //遍历传入参数,
//获得group(分组配置) 作为map的key 进行初始化,然后把invoker放进去
String group = invoker.getUrl().getParameter(GROUP_KEY, "");
groupMap.computeIfAbsent(group, k -> new ArrayList<>());
groupMap.get(group).add(invoker);
}
if (groupMap.size() == 1) {
mergedInvokers.addAll(groupMap.values().iterator().next());
} else if (groupMap.size() > 1) {
//遍历map的values值,合并map
for (List> groupList : groupMap.values()) {
StaticDirectory staticDirectory = new StaticDirectory<>(groupList);
staticDirectory.buildRouterChain();
mergedInvokers.add(CLUSTER.join(staticDirectory));
}
} else {
mergedInvokers = invokers;
}
return mergedInvokers;
}
3.3.3 摧毁无用invoker
private void destroyUnusedInvokers(Map> oldUrlInvokerMap, Map> newUrlInvokerMap) {
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
destroyAllInvokers();
return;
}
// check deleted invoker
List deleted = null;
if (oldUrlInvokerMap != null) {
//获取新 生成的invoker 列表
Collection> newInvokers = newUrlInvokerMap.values();
//遍历老的 invoker映射表
for (Map.Entry> entry : oldUrlInvokerMap.entrySet()) {
if (!newInvokers.contains(entry.getValue())) {
if (deleted == null) {
deleted = new ArrayList<>();
}//如果不包含,则把老的invoker 放到deleted列表中
deleted.add(entry.getKey());
}
}
}
if (deleted != null) {
//遍历deleted集合,销毁
for (String url : deleted) {
if (url != null) { //从老的mao中 删除
Invoker invoker = oldUrlInvokerMap.remove(url);
if (invoker != null) {
try {
invoker.destroy(); //摧毁
if (logger.isDebugEnabled()) {
logger.debug("destroy invoker[" + invoker.getUrl() + "] success. ");
}
} catch (Exception e) {
logger.warn("destroy invoker[" + invoker.getUrl() + "] failed. " + e.getMessage(), e);
}
}
}
}
}
}