欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈
1. 入口
跟服务发现机制的入口是一样的,也是DubboBootstrap.start()方法:
public DubboBootstrap start() {
if (started.compareAndSet(false, true)) {
startup.set(false);
shutdown.set(false);
awaited.set(false);
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
}
// 1. export Dubbo Services
// 服务暴露
exportServices();
// 服务引用
referServices();
..
return this;
}
private void referServices() {
if (cache == null) {
cache = ReferenceConfigCache.getCache();
}
// 遍历所有<dubbo:reference/>标签
configManager.getReferences().forEach(rc -> {
// TODO, compatible with ReferenceConfig.refer()
ReferenceConfig<?> referenceConfig = (ReferenceConfig<?>) rc;
referenceConfig.setBootstrap(this);
if (!referenceConfig.isRefreshed()) {
referenceConfig.refresh();
}
// 判断当前<dubbo:reference/>标签引用的实例是否需要立即初始化
if (rc.shouldInit()) {
if (rc.shouldReferAsync()) { // 判断是否是异步引用
ExecutorService executor = executorRepository.getServiceReferExecutor();
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
cache.get(rc);
} catch (Throwable t) {
logger.error("refer async catch error : " + t.getMessage(), t);
}
}, executor);
asyncReferringFutures.add(future);
} else { // 处理同步引用
cache.get(rc);
}
}
});
}
@SuppressWarnings("unchecked")
public <T> T get(ReferenceConfigBase<T> referenceConfig) {
// 生成服务标识key,其格式为 group/interface:version
String key = generator.generateKey(referenceConfig);
// 获取业务接口class
Class<?> type = referenceConfig.getInterfaceClass();
// proxies为一个缓存map,其为一个双层map。该缓存map中存放的是业务接口对应的所有服务
// 外层map的key为业务接口class,value为一个内层map
// 内层map的key为服务标识key,value为该服务对应的代理对象
// 从缓存map中获取当前业务接口的内层map,即获取到当前业务接口的所有服务
ConcurrentMap<String, Object> proxiesOfType = proxies.computeIfAbsent(type, _t -> new ConcurrentHashMap<>());
// 返回内层map的value,即当前服务标识对应的代理对象
return (T) proxiesOfType.computeIfAbsent(key, _k -> {
// todo 创建代理对象
Object proxy = referenceConfig.get();
referredReferences.put(key, referenceConfig);
return proxy;
});
}
接下里ReferenceConfig.get()方法:
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
}
if (ref == null) {
init(); // 创建代理对象
}
return ref;
}
protected synchronized void init() {
...
// 创建并初始化一个URL使用的map
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, CONSUMER_SIDE);
...
// 注册元数据
serviceMetadata.getAttachments().putAll(map);
// 创建代理对象
ref = createProxy(map);
serviceMetadata.setTarget(ref);
serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
ConsumerModel consumerModel = repository.lookupReferredService(serviceMetadata.getServiceKey());
consumerModel.setProxyObject(ref);
consumerModel.init(attributes);
initialized = true; // 修改初始化状态
checkInvokerAvailable();
}
createProxy()方法:
private T createProxy(Map<String, String> map) {
if (shouldJvmRefer(map)) { // 判断是否是本地引用
URL url = new ServiceConfigURL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
invoker = REF_PROTOCOL.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else { // 处理远程引用
urls.clear();
// 若<dubbo:reference/>的url属性不空,则当前对provider的调用为“直连”调用,无需注册中心
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
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.putAttribute(REFER_KEY, map));
} else {
URL peerURL = ClusterUtils.mergeUrl(url, map);
peerURL = peerURL.putAttribute(PEER_KEY, true);
urls.add(peerURL);
}
}
}
} else { // assemble URL from register center's configuration
// 处理有注册中心的调用情况
// if protocols not injvm checkRegistry
if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
// 检测注册中心可用性。只要有一个注册中心不可用,就会抛出异常
checkRegistry();
// 获取注册中心的标准URL,其仅仅包含registry://...格式的URL,
// 没有service-discovery-registry://...的URL
List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
if (CollectionUtils.isNotEmpty(us)) {
// 遍历所有注册中心URL
for (URL u : us) {
// 获取所有监控中心URL
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
if (monitorUrl != null) {
// 以monitor属性的形式将监控中心URL添加到注册中心URL中
u = u.putAttribute(MONITOR_KEY, monitorUrl);
}
// 以refer属性的形式将消费者URL添加到注册中心URL中
urls.add(u.putAttribute(REFER_KEY, map));
}
}
if (urls.isEmpty()) {
throw new IllegalStateException(
"No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() +
" use dubbo version " + Version.getVersion() +
", please config <dubbo:registry address="..." /> to your spring config.");
}
}
}
if (urls.size() == 1) {
// 获取provider的委托对象
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else { // 处理注册中心有多个的情况
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
// For multi-registry scenarios, it is not checked whether each referInvoker is available.
// Because this invoker may become available later.
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (UrlUtils.isRegistry(url)) {
registryURL = url; // use last registry url
}
}
// 若有可用的注册中心
if (registryURL != null) { // registry url is available
// for multi-subscription scenario, use 'zone-aware' policy by default
String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
// The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker
// 由于注册中心的数量是不可变的,所以这里的Directory是静态的
invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers));
} else { // not a registry url, must be direct invoke.
String cluster = CollectionUtils.isNotEmpty(invokers)
?
(invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) :
Cluster.DEFAULT)
: Cluster.DEFAULT;
invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
}
}
}
if (logger.isInfoEnabled()) {
logger.info("Referred dubbo service " + interfaceClass.getName());
}
URL consumerURL = new ServiceConfigURL(CONSUMER_PROTOCOL, map.get(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
MetadataUtils.publishServiceDefinition(consumerURL);
// create service proxy todo 创建代理对象
return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
2. 本地引用
if (shouldJvmRefer(map)) { // 判断是否是本地引用
URL url = new ServiceConfigURL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
invoker = REF_PROTOCOL.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
}
判断是否是本地引用:
protected boolean shouldJvmRefer(Map<String, String> map) {
URL tmpUrl = new ServiceConfigURL("temp", "localhost", 0, map);
boolean isJvmRefer;
// 若<dubbo:reference/>的injvm属性为null
if (isInjvm() == null) {
// if a url is specified, don't do local reference
// 若<dubbo:reference/>的url属性不空,则为直连引用,不属于本地引用
if (url != null && url.length() > 0) {
isJvmRefer = false;
} else {
// by default, reference local service if there is
// 若<dubbo:reference/>的injvm与url属性均为空,则继续判断
isJvmRefer = InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl);
}
} else {
isJvmRefer = isInjvm();
}
return isJvmRefer;
}
第一行new 了一个tempUrl,后面的就是判断要不要injvm,也就是本地引用,如果我们这里配置了scope=injvm或者injvm=true就会执行到isJvmRefer = isInjvm().booleanValue(); 也就isJvmRefer 这个变量设置成true。
接着就是执行进isJvmRefer =true这个代码段中,首先是封装一个url,其中protocol是injvm,host是127.0.0.1。接着调用invoker = REF_PROTOCOL.refer(interfaceClass, url);这句,我们先看下这个REF_PROTOCOL对象:
private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
很显然是Protocol接口对象,根据dubbo spi 扩展点的自适应特性,会在url找protocol属性的值,这里url的protocol属性值是injvm,也就是
com.apache.dubbo.rpc.protocol.injvm.InjvmProtocol:接下里看一下它的refer方法:
2.1 InjvmProtocol.refer方法
// AbstractProtocol.refer
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
return protocolBindingRefer(type, url);
}
// InjvmProtocol
@Override
public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
}
这里new了一个InjvmInvoker 类并且返回 ,参数分别是 1-咱们那个接口class 类,2-url ,3-接口的全类名,4-exporterMap 缓存了本地暴露的服务,这个exporterMap 对象,我们在本地服务暴露的时候,最后将serviceKey 与exporter缓存了exporterMap 这个map中。
这个InjvmInvoker 先放这,我们回到com.apache.dubbo.config.ReferenceConfig#createProxy 这个方法继续看。
2.2 com.apache.dubbo.config.ReferenceConfig#createProxy
远程引用的代码先忽略,接着就是(T) proxyFactory.getProxy(invoker);
这句话,创建service代理对象。其实这个proxyFactory 我们在服务暴露的时候见过了,我们再来看下:
private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
其实根据dubbo spi 扩展点自适应特性,当你指定proxy属性值的时候 用你指定的实现,如果没有话使用默认的实现,这里也就是javassist,JavassistProxyFactory,我们看下JavassistProxyFactory 类
2.3 com.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory#getProxy
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
// getProxy() 创建代理类proxy的class
// newInstance() 创建这个class的实例,即代理对象
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
这里是我那 Proxy.getProxy 生成的一个代理类。 比如说我这个接口是这个样子的:
public interface IHelloProviderService {
String getName(Integer id);
}
生成的代理类
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.rpc.service.EchoService;
import com.xuzhaocai.dubbo.provider.IHelloProviderService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class proxy0
implements ClassGenerator.DC,
EchoService,
IHelloProviderService {
public static Method[] methods;
private InvocationHandler handler;
@Override
public String getName(Integer n) {
Object[] arrobject = new Object[]{n};
Object object = this.handler.invoke(this, methods[0], arrobject);
return (String)object;
}
public Object $echo(Object object) {
Object[] arrobject = new Object[]{object};
Object object2 = this.handler.invoke(this, methods[1], arrobject);
return object2;
}
public proxy0() {
}
public proxy0(InvocationHandler invocationHandler) {
this.handler = invocationHandler;
}
}
我们可以看到Proxy生成的代理类实现那个接口,然后在实现接口方法里面将参数封装到数据里面,然后调用了invocationHandler的invoke方法。我们看下这个invocationHandler 。
2.4 com.apache.dubbo.rpc.proxy.InvokerInvocationHandler
public class InvokerInvocationHandler implements InvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
private final Invoker<?> invoker;
private ConsumerModel consumerModel;
private URL url;
private String protocolServiceKey;
public static Field stackTraceField;
public InvokerInvocationHandler(Invoker<?> handler) {
this.invoker = handler;
this.url = invoker.getUrl();
String serviceKey = this.url.getServiceKey();
this.protocolServiceKey = this.url.getProtocolServiceKey();
if (serviceKey != null) {
this.consumerModel = ApplicationModel.getConsumerModel(serviceKey);
}
}
// 远程调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 若当前调用方法为Object的方法,则直接调用该本地方法
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
// 若当前调用方法为如下重写的方法,则直接调用该本地方法
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) {
if ("toString".equals(methodName)) {
return invoker.toString();
} else if ("$destroy".equals(methodName)) {
invoker.destroy();
return null;
} else if ("hashCode".equals(methodName)) {
return invoker.hashCode();
}
} else if (parameterTypes.length == 1 && "equals".equals(methodName)) {
return invoker.equals(args[0]);
}
// 生成RpcInvocation
RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), protocolServiceKey, args);
String serviceKey = url.getServiceKey();
rpcInvocation.setTargetServiceUniqueName(serviceKey);
// invoker.getUrl() returns consumer url.
RpcServiceContext.setRpcContext(url);
if (consumerModel != null) {
rpcInvocation.put(Constants.CONSUMER_MODEL, consumerModel);
rpcInvocation.put(Constants.METHOD_MODEL, consumerModel.getMethodModel(method));
}
return invoker.invoke(rpcInvocation).recreate();
}
}
在这里invocationHandler对象里面invoker就是咱们前面InjvmInvoker 对象。我们可以看到这个InvokerInvocationHandler#invoke 方法里面帮我们获取了执行的方法名与方法参数类型,如果是toString,hashCode,equals 方法直接调用InjvmInvoker 的,并将方法与参数封装成一个RpcInvocation ,然后调用了InjvmInvoker 的invoke方法, 我们先来看下new RpcInvocation(method, args);
2.5 com.apache.dubbo.rpc.protocol.injvm.InjvmInvoker
这个InjvmInvoker 继承 AbstractInvoker ,然后AbstractInvoker 实现Invoker 接口,实现了Result invoke(Invocation invocation) throws RpcException;方法,与 Class<T> getInterface()
方法。
class InjvmInvoker<T> extends AbstractInvoker<T> {
private final String key;
private final Map<String, Exporter<?>> exporterMap;
private final ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap) {
super(type, url);
this.key = key;
this.exporterMap = exporterMap;
}
@Override
public boolean isAvailable() {
InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key);
if (exporter == null) {
return false;
} else {
return super.isAvailable();
}
}
@Override
public Result doInvoke(Invocation invocation) throws Throwable {
Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl());
if (exporter == null) {
throw new RpcException("Service [" + key + "] not found.");
}
RpcContext.getServiceContext().setRemoteAddress(LOCALHOST_VALUE, 0);
// Solve local exposure, the server opens the token, and the client call fails.
URL serverURL = exporter.getInvoker().getUrl();
boolean serverHasToken = serverURL.hasParameter(Constants.TOKEN_KEY);
if (serverHasToken) {
invocation.setAttachment(Constants.TOKEN_KEY, serverURL.getParameter(Constants.TOKEN_KEY));
}
if (isAsync(exporter.getInvoker().getUrl(), getUrl())) {
((RpcInvocation) invocation).setInvokeMode(InvokeMode.ASYNC);
// use consumer executor
ExecutorService executor = executorRepository.createExecutorIfAbsent(getUrl());
CompletableFuture<AppResponse> appResponseFuture = CompletableFuture.supplyAsync(() -> {
Result result = exporter.getInvoker().invoke(invocation);
if (result.hasException()) {
return new AppResponse(result.getException());
} else {
return new AppResponse(result.getValue());
}
}, executor);
// save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
FutureContext.getContext().setCompatibleFuture(appResponseFuture);
AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, invocation);
result.setExecutor(executor);
return result;
} else {
return exporter.getInvoker().invoke(invocation);
}
}
我们看到invoke方法没有在InjvmInvoker类中,我们看下它的父类中invoke方法:
@Override
public Result invoke(Invocation inv) throws RpcException {
// if invoker is destroyed due to address refresh from registry, let's allow the current invoke to proceed
if (isDestroyed()) {
logger.warn("Invoker for service " + this + " on consumer " + NetUtils.getLocalHost() + " is destroyed, "
+ ", dubbo version is " + Version.getVersion() + ", this invoker should not be used any longer");
}
RpcInvocation invocation = (RpcInvocation) inv;
// prepare rpc invocation
prepareInvocation(invocation);
// do invoke rpc invocation and return async result
AsyncRpcResult asyncResult = doInvokeAndReturn(invocation); // 远程调用
// wait rpc result if sync 同步调用,这里将阻塞
waitForResultIfSync(asyncResult, invocation);
return asyncResult;
}
private AsyncRpcResult doInvokeAndReturn(RpcInvocation invocation) {
AsyncRpcResult asyncResult;
try {
// todo
asyncResult = (AsyncRpcResult) doInvoke(invocation);
}
..
// set server context
RpcContext.getServiceContext().setFuture(new FutureAdapter<>(asyncResult.getResponseFuture()));
return asyncResult;
}
可以看到前面判断了一下销毁没有,然后从Rpc上下文中get到一些kv然后设置进去。我们看后面一句doInvoke(invocation);
,这个才是真正的调用,子类实现了这个方法,这里也就是咱们的InjvmInvoker类:
@Override
public Result doInvoke(Invocation invocation) throws Throwable {
Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl());
if (exporter == null) {
throw new RpcException("Service [" + key + "] not found.");
}
RpcContext.getServiceContext().setRemoteAddress(LOCALHOST_VALUE, 0);
// Solve local exposure, the server opens the token, and the client call fails.
URL serverURL = exporter.getInvoker().getUrl();
boolean serverHasToken = serverURL.hasParameter(Constants.TOKEN_KEY);
if (serverHasToken) {
invocation.setAttachment(Constants.TOKEN_KEY, serverURL.getParameter(Constants.TOKEN_KEY));
}
if (isAsync(exporter.getInvoker().getUrl(), getUrl())) {
((RpcInvocation) invocation).setInvokeMode(InvokeMode.ASYNC);
// use consumer executor
ExecutorService executor = executorRepository.createExecutorIfAbsent(getUrl());
CompletableFuture<AppResponse> appResponseFuture = CompletableFuture.supplyAsync(() -> {
Result result = exporter.getInvoker().invoke(invocation);
if (result.hasException()) {
return new AppResponse(result.getException());
} else {
return new AppResponse(result.getValue());
}
}, executor);
// save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
FutureContext.getContext().setCompatibleFuture(appResponseFuture);
AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, invocation);
result.setExecutor(executor);
return result;
} else {
return exporter.getInvoker().invoke(invocation);
}
}
首先InjvmProtocol.getExporter(exporterMap, getUrl())
;这行代码其实是从exporterMap 这个缓存中获取我们这个url中的服务名对应的实现类,我们可以回想下,我们在本地服务暴露的时候,然后以serviceKey为key ,exporter为value缓存到了这个map中,如果获取的这个exporter是null的话抛出异常,如果不是null的话,往RpcContext中设置RemoteAddress 的ip是127.0.0.1 ,port是0。最后调用了exporter.getInvoker().invoke(invocation);,我们可以回顾下当初服务暴露的时候invoker是这样子创建的:
然后最后调用的是wrapper.invokeMethod的方法。我们再来看下这个wrapper的结构是啥样子的:
public class Wrapper$1 {
public static String[] pns;// 字段名
public static Map pts;//<字段名,字段类型>
public static String[] mns;//方法名
public static String[] dmns;//自己方法的名字
public static Class[] mts;//方法参数类型
public String[] getPropertyNames(){ return pns; }
public boolean hasProperty(String n){ return pts.containsKey(n); }
public Class getPropertyType(String n){ return (Class)pts.get(n); }
public String[] getMethodNames(){ return mns; }
public String[] getDeclaredMethodNames(){ return dmns; }
public void setPropertyValue(Object o, String n, Object v){
com.xuzhaocai.dubbo.provider.IHelloProviderService w;
try{
w = (( com.dubbo.provider.IHelloProviderService)$1);
}catch(Throwable e) {
throw new IllegalArgumentException(e);
}
if( $2.equals("字段名")){
w."字段名"= $3;
return ;
}
}
public Object getPropertyValue(Object o, String n){
com.xuzhaocai.dubbo.provider.IHelloProviderService w;
try{
w = (( com.dubbo.provider.IHelloProviderService)$1);
}catch(Throwable e){
throw new IllegalArgumentException(e);
}
if( $2.equals("字段名")){
return ($w) w."字段名";
}
return null;
}
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws InvocationTargetException{
com.dubbo.provider.IHelloProviderService w;
try{
w = (( com.dubbo.provider.IHelloProviderService)$1);
}catch(Throwable e){
throw new IllegalArgumentException(e);
}
try{
if("方法名".equals($2) && 方法参数个数 == $3.length && $3[1].getName().equals("方法第几个参数的name")){
w.方法名(参数);
}
if("方法名".equals($2) && 方法参数个数 == $3.length && $3[1].getName().equals("方法第几个参数的name")){
w.方法名(参数);
}
} catch(Throwable e) {
throw new java.lang.reflect.InvocationTargetException(e);
}
throw new NoSuchMethodException("Not found method "+$2+" in class 你传进来那个实现类");
}
}
其实到最后就是调用的服务提供者的方法。
3. 远程引用(创建Proxy流程)
3.1 com.apache.dubbo.config.ReferenceConfig#createProxy
接着ReferenceConfig#createProxy 方法下下半部分接着分析。
private T createProxy(Map<String, String> map) {
if (shouldJvmRefer(map)) { // 判断是否是本地引用
...
} else { // 处理远程引用
urls.clear();
// 若<dubbo:reference/>的url属性不空,则当前对provider的调用为“直连”调用,无需注册中心
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
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.putAttribute(REFER_KEY, map));
} else {
URL peerURL = ClusterUtils.mergeUrl(url, map);
peerURL = peerURL.putAttribute(PEER_KEY, true);
urls.add(peerURL);
}
}
}
} else { // assemble URL from register center's configuration
// 处理有注册中心的调用情况
// if protocols not injvm checkRegistry
if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
// 检测注册中心可用性。只要有一个注册中心不可用,就会抛出异常
checkRegistry();
// 获取注册中心的标准URL,其仅仅包含registry://...格式的URL,
// 没有service-discovery-registry://...的URL
List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
if (CollectionUtils.isNotEmpty(us)) {
// 遍历所有注册中心URL
for (URL u : us) {
// 获取所有监控中心URL
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
if (monitorUrl != null) {
// 以monitor属性的形式将监控中心URL添加到注册中心URL中
u = u.putAttribute(MONITOR_KEY, monitorUrl);
}
// 以refer属性的形式将消费者URL添加到注册中心URL中
urls.add(u.putAttribute(REFER_KEY, map));
}
}
if (urls.isEmpty()) {
throw new IllegalStateException(
"No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() +
" use dubbo version " + Version.getVersion() +
", please config <dubbo:registry address="..." /> to your spring config.");
}
}
}
if (urls.size() == 1) {
// 获取provider的委托对象
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else { // 处理注册中心有多个的情况
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
// For multi-registry scenarios, it is not checked whether each referInvoker is available.
// Because this invoker may become available later.
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (UrlUtils.isRegistry(url)) {
registryURL = url; // use last registry url
}
}
// 若有可用的注册中心
if (registryURL != null) { // registry url is available
// for multi-subscription scenario, use 'zone-aware' policy by default
String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
// The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker
// 由于注册中心的数量是不可变的,所以这里的Directory是静态的
invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers));
} else { // not a registry url, must be direct invoke.
String cluster = CollectionUtils.isNotEmpty(invokers)
?
(invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) :
Cluster.DEFAULT)
: Cluster.DEFAULT;
invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
}
}
}
...
// create service proxy todo 创建代理对象
return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
这里我们就找着重要的说,分为两个大部分,首先是获取url,将获取的url添加到urls,接着就是判断urls的size:
- 如果是一个的话直接调用 refprotocol.refer(interfaceClass, urls) 获得invoker ,
- 如果是多个的话就会循环调用然后塞到invokers,之后将多个invoker 进行join合并成一个。
下面我们就看下 refprotocol.refer(interfaceClass, urls)
这个是怎样生成invoker的。 这里这个refprotocol是 private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
是Protocol接口自适应类,这里我们protocol是registry,所以这个refprotocol.refer其实最后就调到了RegistryProtocol。
下面我们看下com.apache.dubbo.registry.integration.RegistryProtocol#refer方法。
3.2 com.apache.dubbo.registry.integration.RegistryProtocol#refer
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = getRegistryUrl(url);
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
Map<String, String> qs = (Map<String, String>) url.getAttribute(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(Cluster.getCluster(MergeableCluster.NAME), registry, type, url, qs);
}
}
// 获取cluster属性,即集群容错策略,默认failover策略
Cluster cluster = Cluster.getCluster(qs.get(CLUSTER_KEY));
return doRefer(cluster, registry, type, url, qs); // 继续订阅
}
protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url, Map<String, String> parameters) {
Map<String, Object> consumerAttribute = new HashMap<>(url.getAttributes());
consumerAttribute.remove(REFER_KEY);
URL consumerUrl = new ServiceConfigURL(parameters.get(PROTOCOL_KEY) == null ? DUBBO : parameters.get(PROTOCOL_KEY),
null,
null,
parameters.get(REGISTER_IP_KEY),
0, getPath(parameters, type),
parameters,
consumerAttribute);
url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl);
// 创建一个具有“注册中心迁移”功能的invoker
ClusterInvoker<T> migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl);
// 拦截invoker,为invoker添加迁移监听器
return interceptInvoker(migrationInvoker, url, consumerUrl, url);
}
-
第一行是将url 中parameters 里面的registry的值获取出来,如果你是用zk作为注册中心,获取出来的就是zookeeper,然后设置到url的protocol属性上,之后就是将parameters 的 registry remove掉了,这时候url的protocol 就是zookeeper了
-
第二行 就是根据 url的protocol来获取RegistryFactory 的实现类,也就是ZookeeperRegistryFactory ,然后就是调用它的getRegistry()方法,获取Registry ,总而言之就是获取 注册中心的Registry 对象。
-
接着就是如果你这个接口类型是RegistryService 的话,就找proxyFactory 来生成invoker。
-
再往下走就是解析将url中refer 属性值取出来,然后解析成map,其实这个refer就是在ReferenceConfig#createProxy 中塞进去的,就是一些属性,然后获取group 属性,进行分组。
-
Cluster cluster = Cluster.getCluster(qs.get(CLUSTER_KEY));
-
走到最后就是调用了doRefer()方法,我么看下这个doRefer方法。
3.2.1 Cluster接口
@SPI(Cluster.DEFAULT) // 默认是failover 失败重试
public interface Cluster {
String DEFAULT = "failover";
/**
* Merge the directory invokers to a virtual invoker.
*
* @param <T>
* @param directory
* @return cluster invoker
* @throws RpcException
*/
@Adaptive
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
static Cluster getCluster(String name) {
return getCluster(name, true);
}
static Cluster getCluster(String name, boolean wrap) {
if (StringUtils.isEmpty(name)) {
name = Cluster.DEFAULT;
}
return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(name, wrap);
}
}
这里用户没有特殊配置的话,就是使用FailoverCluster 子类的join方法。但是使用dubbo spi的时候,还有包装机制,会将FailoverCluster对象外层包上一个MockClusterWrapper,所以我们需要先看下MockClusterWrapper:
public class MockClusterWrapper implements Cluster {
private Cluster cluster;
public MockClusterWrapper(Cluster cluster) {
this.cluster = cluster;
}
@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
return new MockClusterInvoker<T>(directory,
this.cluster.join(directory));
}
}
接下来,我们重点看一下join方法: 我们看到先是调用了cluster.join()方法,然后将返回的invoker对象用MockClusterInvoker 包了一层,这里这个cluster 才是真正的FailoverCluster对象,我们来看看它干了啥。
public class FailoverCluster extends AbstractCluster {
public final static String NAME = "failover";
@Override
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
return new FailoverClusterInvoker<>(directory);
}
}
我们可以看到new FailoverClusterInvoker 对象返回了,这个FailoverClusterInvoker 其实是一个失败重试invoker。
我们一会在来看FailoverClusterInvoker 内部实现机制,因为我们现在已经得到了invoker对象,现在我们一层一层返回去,这时候我们发现退回到了最初com.apache.dubbo.config.ReferenceConfig#createProxy方法。
上面这个红框框起来的是我们刚才调用的,这是urls就一个是情况,多个的url的时候我们看到是循环来获得invoker,然后取得最后一个invoker赋值给registryURL,最后将多个invoker合成了一个。接着再往下走:
3.2.2 interceptInvoker()
这个是重点的方法,服务订阅,路由配置都是在这里,我们具体来看一下:
protected <T> Invoker<T> interceptInvoker(ClusterInvoker<T> invoker, URL url, URL consumerUrl, URL registryURL) {
List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
if (CollectionUtils.isEmpty(listeners)) {
return invoker;
}
// 遍历所有监听器,为订阅过程添加这些监听功能
for (RegistryProtocolListener listener : listeners) {
listener.onRefer(this, invoker, consumerUrl, registryURL);
}
return invoker;
}
接下来看listener.onRefer()
方法,由于代码比较深,我们直接说调用路径,有兴趣的可以直接从Dubbo3.0源码注释github地址 拉下代码,里面都有注释。
listener.onRefer()
-> MigrationRuleListener#onRefer
->MigrationRuleHandler#doMigrate -> MigrationRuleHandler#refreshInvoker -> MigrationInvoker#migrateToApplicationFirstInvoker ->MigrationInvoker#refreshInterfaceInvoker -> RegistryProtocol#getInvoker -> RegistryProtocol#doCreateInvoker :
protected <T> ClusterInvoker<T> doCreateInvoker(DynamicDirectory<T> directory, Cluster cluster, Registry registry, Class<T> type) {
directory.setRegistry(registry);
directory.setProtocol(protocol);
// all attributes of REFER_KEY
Map<String, String> parameters = new HashMap<String, String>(directory.getConsumerUrl().getParameters());
URL urlToRegistry = new ServiceConfigURL(
parameters.get(PROTOCOL_KEY) == null ? DUBBO : parameters.get(PROTOCOL_KEY),
parameters.remove(REGISTER_IP_KEY), 0, getPath(parameters, type), parameters);
if (directory.isShouldRegister()) {
directory.setRegisteredConsumerUrl(urlToRegistry);
// 将当前consumer注册到注册中心
registry.register(directory.getRegisteredConsumerUrl());
}
// 将所有RouterFactory激活扩展类创建的router添加到directory
directory.buildRouterChain(urlToRegistry);
// 服务订阅
directory.subscribe(toSubscribeUrl(urlToRegistry));
// 将多个invoker伪装为一个具有复合功能的invoker
return (ClusterInvoker<T>) cluster.join(directory);
}
3.2.2.1 registry.register()
ZookeeperRegistry#doSubscribe方法如下:
public void doSubscribe(final URL url, final NotifyListener listener) {
try {
// 处理<dubbo:reference/>的interface属性为"*"的情况
if (ANY_VALUE.equals(url.getServiceInterface())) {
String root = toRootPath();
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
ChildListener zkListener = listeners.computeIfAbsent(listener, k -> (parentPath, currentChilds) -> {
for (String child : currentChilds) {
child = URL.decode(child);
if (!anyServices.contains(child)) {
anyServices.add(child);
subscribe(url.setPath(child).addParameters(INTERFACE_KEY, child,
Constants.CHECK_KEY, String.valueOf(false)), k);
}
}
});
zkClient.create(root, false);
List<String> services = zkClient.addChildListener(root, zkListener);
if (CollectionUtils.isNotEmpty(services)) {
for (String service : services) {
service = URL.decode(service);
anyServices.add(service);
subscribe(url.setPath(service).addParameters(INTERFACE_KEY, service,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
} else { // 处理<dubbo:reference/>的interface属性为普通值的情况
CountDownLatch latch = new CountDownLatch(1);
List<URL> urls = new ArrayList<>();
// 遍历configurators、routers与providers三个路径
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, path, k, latch));
if (zkListener instanceof RegistryChildListenerImpl) {
((RegistryChildListenerImpl) zkListener).setLatch(latch);
}
// 创建持久节点
zkClient.create(path, false);
// 为创建的节点添加子节点列表变更的watcher监听
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
// toUrlsWithEmpty
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
// 主动调用notify更新本地invoker
notify(url, listener, urls);
// tells the listener to run only after the sync notification of main thread finishes.
latch.countDown();
}
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
完成服务的注册 和订阅,并且更新本地的invoker列表:重点看notify(url, listener, urls); 直接看org.apache.dubbo.registry.integration.RegistryDirectory#notify:
public synchronized void notify(List<URL> urls) {
if (isDestroyed()) {
return;
}
Map<String, List<URL>> categoryUrls = urls.stream()
.filter(Objects::nonNull) // 过滤掉为null的元素
.filter(this::isValidCategory) // 过滤掉不可用分类元素
.filter(this::isNotCompatibleFor26x) // 过滤掉不兼容2.6版本的元素
.collect(Collectors.groupingBy(this::judgeCategory));
// 处理configurators分类节点
List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);
// 处理routers分类节点
List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
// 读取routers子节点下的路由规则,并添加到directory中
toRouters(routerURLs).ifPresent(this::addRouters);
// providers
// 处理providers分类节点
List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
/**
* 3.x added for extend URL address
*/
ExtensionLoader<AddressListener> addressListenerExtensionLoader = ExtensionLoader.getExtensionLoader(AddressListener.class);
List<AddressListener> supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
if (supportedListeners != null && !supportedListeners.isEmpty()) {
for (AddressListener addressListener : supportedListeners) {
providerURLs = addressListener.notify(providerURLs, getConsumerUrl(),this);
}
}
// 刷新invoker
refreshOverrideAndInvoker(providerURLs);
}
更新本地的invoker列表:
// 更新本地invoker列表
private void refreshInvoker(List<URL> invokerUrls) {
Assert.notNull(invokerUrls, "invokerUrls should not be null");
if (invokerUrls.size() == 1
&& invokerUrls.get(0) != null
&& EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { // 判断是否有empty://开头
this.forbidden = true; // Forbid to access 禁止远程调用
this.invokers = Collections.emptyList();
routerChain.setInvokers(this.invokers);
destroyAllInvokers(); // Close all invokers 将缓存中的所有invoker删除
} else {
this.forbidden = false; // Allow to access 允许远程访问
// 缓存map,更新本地invoker列表,就是更新这里
// key为URL,value为该URL对应的invoker实例(invoker委托对象)
// 先将缓存map发生变更前的值进行暂存
Map<URL, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls == Collections.<URL>emptyList()) {
invokerUrls = new ArrayList<>();
}
// 这段代码可以增加provider的可用性,
// 如果provider和注册中心网络抖动,导致注册不成功,但是之前注册成功并且更新到本地缓存了,这种情况我们可以直接调用
if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet<>();
this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
}
// 走到这里invokerUrls仍为空,则说明真的是没有任何可用的invoker
if (invokerUrls.isEmpty()) {
return;
}
// 从缓存map中获取相应的invoker,若存在,则返回,并将其从缓存map中删除
// 若不存在,则创建一个invoker
Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
/**
* If the calculation is wrong, it is not processed.
*
* 1. The protocol configured by the client is inconsistent with the protocol of the server.
* eg: consumer protocol = dubbo, provider only has other protocol services(rest).
* 2. The registration center is not robust and pushes illegal specification data.
*
*/
if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
.toString()));
return;
}
// 获取到最新的可用invoker列表
List<Invoker<T>> 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);
this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
// 将缓存map更新为新map
this.urlInvokerMap = newUrlInvokerMap;
try {
// 删除老map中剩余的invoker
destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
} catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
// notify invokers refreshed
this.invokersChanged();
}
}
3.2.2.2 directory.buildRouterChain(urlToRegistry)
路由相关的,我们后面在分析
3.2.2.3 cluster.join(directory)
我们分析一下这个方法org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster#join:
@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
if (directory instanceof StaticDirectory) {
return doJoin(directory);
}
return buildClusterInterceptors(doJoin(directory), directory.getUrl().getParameter(REFERENCE_INTERCEPTOR_KEY));
}
private <T> Invoker<T> buildClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, String key) {
// AbstractClusterInvoker<T> last = clusterInvoker;
AbstractClusterInvoker<T> last = buildInterceptorInvoker(new ClusterFilterInvoker<>(clusterInvoker));
if (Boolean.parseBoolean(ConfigurationUtils.getProperty(CLUSTER_INTERCEPTOR_COMPATIBLE_KEY, "false"))) {
return build27xCompatibleClusterInterceptors(clusterInvoker, last);
}
return last;
}
org.apache.dubbo.rpc.cluster.support.FailoverCluster#doJoin:
public class FailoverCluster extends AbstractCluster {
public final static String NAME = "failover";
@Override
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
return new FailoverClusterInvoker<>(directory);
}
}
接着就是创建 proxy代理类了
3.3 Proxy代理类生成过程
(T) proxyFactory.getProxy(invoker)
我们先来看看这个ProxyFactory这个接口,三个抽象方法,这个getInvoker方法我们在服务暴露那几篇已经探讨过了,根据dubbo spi 的自适应规则,先去找url中PROXY_KEY 的值,如果有值的话使用我们自定义的,如果没有值的话就使用@SPI注解里面实现类JavassistProxyFactory。
@SPI("javassist")
public interface ProxyFactory {
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException;
// 是否范化调用
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
JavassistProxyFactory 继承父类AbstractProxyFactory ,然后这个两个getProxy方法都是在父类实现的
这个一个参数的getProxy调用两个参数的getProxy,然后范化参数是false,表示不范化调用。
我们可以看到先从invoker的url里面获取interfaces,这个一看就就是多个interface的,我们这里是null,直接走了是null的情况,将我们自己业务接口class 与EchoService class 放到了interfaces中,下面那个是范化调用的封装,我们暂时先不管它,接着就是调用子类的getProxy(invoker,Class<?>[] types)
方法。
我们可以直接看下生成的proxy代码。
我们看到在方法调用的时候 其实是调用的invoker中的invoke方法,由这个invoker来帮你处理,这篇主要是讲了Proxy代理创建的大体流程,然后最后得到我们在业务调用方法的时候其实是调用的invoker的invoke方法,我们在下节主要是讲下这个invoker 里面的invoke方法是怎样子执行的。
4. 远程调用前的工作
第3节中讲解了服务引用的时候使用RegistryProtocol 获得invoker,然后使用invoker封装生成代理类,其实我们在使用RegistryProtocol doRefer方法中创建了RegistryDirectory,然后又向注册中心订阅了providers,routers,configurators,最后使用cluster的join方法生成了invoker,如果你不指定使用那个FailoverCluster,dubbo默认的invoker是FailoverClusterInvoker,我们可以看一下FailoverClusterInvoker 的invoke方法(在实际调用的时候就是走的invoke方法),invoke方法实际上是在FailoverClusterInvoker 父类AbstractClusterInvoker实现的,在父类定义了doInvoke抽象方法由子类实现,AbstractClusterInvoker 的invoke 方法中处理了传输参数的从RpcContext取出来封装到invocation中,获得了loadbalance的具体实现(这里默认使用的是random,也就是随机),获得所有服务提供者invoker集合,最后交由子类的doInvoke来做具体的一些集群容错处理,比如说快速失败,失败重试等等。
我们来看下AbstractClusterInvoker 的invoke方法:
@Override
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
// binding attachments into invocation.
// Map<String, Object> contextAttachments = RpcContext.getClientAttachment().getObjectAttachments();
// if (contextAttachments != null && contextAttachments.size() != 0) {
// ((RpcInvocation) invocation).addObjectAttachmentsIfAbsent(contextAttachments);
// }
// 通过路由策略,将不符合路由规则的invoker过滤掉,获取所有提供者的集合
List<Invoker<T>> invokers = list(invocation);
// 获取负载均衡策略,并创建相应的负载均衡实例,默认的random
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
// 调用具体的集群容错策略中的doInvoke()
return doInvoke(invocation, invokers, loadbalance);
}
然后再来看下FailoverClusterInvoker 的doInvoke失败重试是怎样实现的:
/**
* 进行调用
* @param invocation 封装调用信息实体
* @param invokers 服务提供者invoker集合
* @param loadbalance 具体某个负载均衡策略
* @return 执行结果
* @throws RpcException
*/
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyInvokers = invokers;
// 检测invokers列表是否为空
checkInvokers(copyInvokers, invocation);
// 获取RPC调用的方法名
String methodName = RpcUtils.getMethodName(invocation);
// 获取retries属性值
int len = calculateInvokeTimes(methodName);
// retry loop.
RpcException le = null; // last exception.
// 存放所有已经尝试调用过的invoker,这些invoker中,除了最后一个外,其它的都是不可用的
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
for (int i = 0; i < len; i++) {
//Reselect before retry to avoid a change of candidate `invokers`.
//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
if (i > 0) {
// 检测委托对象invoker是否被销毁
checkWhetherDestroyed();
// 更新本地invoker列表
copyInvokers = list(invocation);
// check again 重新检测invokers列表是否为空
checkInvokers(copyInvokers, invocation);
}
// 负载均衡
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
// 将选择出的invoker写入到invoked集合
invoked.add(invoker);
RpcContext.getServiceContext().setInvokers((List) invoked);
try {
// 远程调用
Result result = invokeWithContext(invoker, invocation);
//重试过程中,将最后一次调用的异常信息以 warn 级别日志输出
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + methodName
+ " in the service " + getInterface().getName()
+ " was successful by the provider " + invoker.getUrl().getAddress()
+ ", but there have been failed providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ le.getMessage(), le);
}
return result;
} catch (RpcException e) {
// 如果是业务性质的异常,不再重试,直接抛出
if (e.isBiz()) { // biz exception.
throw e;
}
// 其他性质的异常统一封装成RpcException
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
// 将提供者的地址添加到providers
providers.add(invoker.getUrl().getAddress());
}
} // end-for
// 最后抛出异常
throw new RpcException(le.getCode(), "Failed to invoke the method "
+ methodName + " in the service " + getInterface().getName()
+ ". Tried " + len + " times of the providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ Version.getVersion() + ". Last error is: "
+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
}
我们可以看到就是直接使用for循环完成的失败重试,如果是业务异常就不会再走下次循环结束重试,如果是非业务异常就进行失败重试,然后根据负载均衡算法(这个后面会有文章单独讲解)从服务提供者invoker集合中找一个合适的,然后执行服务提供者invoker的invoke方法进行调用。
那么问题来了,服务提供者invoker集合是怎么来的? 这个问题也是我们本篇要讲解的?
根据我们上面的介绍,invokers是AbstractClusterInvoker的invoke方法获得的,也就是下面这段代码:
/ 通过路由策略,将不符合路由规则的invoker过滤掉,获取所有提供者的集合
List<Invoker<T>> invokers = list(invocation);
我们看下list方法
/**
* 获取所有服务提供者的invoker
* @param invocation
* @return
* @throws RpcException
*/
protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
// 从directory 中获取所有的invoker
return getDirectory().list(invocation);
}
public Directory<T> getDirectory() {
return directory;
}
directory对象就在 3.2.2.3 小结中创建的对象。
我们接着看下directory 的list方法,这是在RegistryDirectory父类AbstractDirectory中实现的,然后子类实现doList方法(可以发现都是一个套路,哈哈哈哈):
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
// 判断是否销毁
if (destroyed) {
throw new RpcException("Directory already destroyed .url: " + getUrl());
}
// 调用子类实现
return doList(invocation);
}
可以看出来是子类实现的doList方法获得的invokers,接着往下看看子类(我们这边指的是RegistryDirectory)是怎样实现的:
@Override
public List<Invoker<T>> doList(Invocation invocation) {
...
List<Invoker<T>> invokers = null;
try {
// Get invokers from cache, only runtime routers will be executed.
// todo
invokers = routerChain.route(getConsumerUrl(), invocation);
} catch (Throwable t) {
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
}
return invokers == null ? Collections.emptyList() : invokers;
}
public List<Invoker<T>> route(URL url, Invocation invocation) {
AddrCache<T> cache = this.cache.get();
if (cache == null) {
throw new RpcException(RpcException.ROUTER_CACHE_NOT_BUILD, "Failed to invoke the method "
+ invocation.getMethodName() + " in the service " + url.getServiceInterface()
+ ". address cache not build "
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion()
+ ".");
}
// 获取属性invokers集合值,这个是本地缓存的invoker集合
BitList<Invoker<T>> finalBitListInvokers = new BitList<>(invokers, false);
// 处理状态路由
for (StateRouter stateRouter : stateRouters) {
if (stateRouter.isEnable()) {
RouterCache<T> routerCache = cache.getCache().get(stateRouter.getName());
finalBitListInvokers = stateRouter.route(finalBitListInvokers, routerCache, url, invocation);
}
}
List<Invoker<T>> finalInvokers = new ArrayList<>(finalBitListInvokers.size());
for(Invoker<T> invoker: finalBitListInvokers) {
finalInvokers.add(invoker);
}
// 处理普通路由
for (Router router : routers) {
finalInvokers = router.route(finalInvokers, url, invocation);
}
return finalInvokers;
}
我们重点看一下RouterChain的属性invokers是怎么设置进去的。
public class RouterChain<T> {
private volatile List<Invoker<T>> invokers = Collections.emptyList();
}
我们可以找到RegistryDirectory#refreshInvoker中:
// 更新本地invoker列表
private void refreshInvoker(List<URL> invokerUrls) {
...
// 获取到最新的可用invoker列表
List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
// 设置routerChain的invokers的值
routerChain.setInvokers(newInvokers);
this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
// 将缓存map更新为新map
this.urlInvokerMap = newUrlInvokerMap;
...
this.invokersChanged();
}
我们在refer的时候,向注册中心订阅了providers,routers,configurators组,当有变动的时候,注册中心就会调用咱们这个NotifyListener 的notify来进行通知,然后调用RegistryDirectory#refreshInvoker方法。
回到 第4小节刚开始的代码中,我们应该走
// 远程调用
Result result = invokeWithContext(invoker, invocation);
但是远程调用的内容较多,我们放到下一篇文章进行解析。敬请期待
5. 服务订阅整体流程图