哈哈哈和服务暴露一样,也有两种:
// 推荐
<dubbo:service scope="local" />
// 不推荐使用,准备废弃
<dubbo:service injvm="true" />
<dubbo:service scope="remote" />
这篇看看本地引用。
在ReferenceConfig中的init() 方法里,会在配置初始化完成后,调用顺序图的起点 createProxy(map) 方法,开始引用服务:
/**
* 自适应 Protocol 实现对象
*/
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
/**
* 自适应 ProxyFactory 实现对象
*/
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
/**
* 直连服务提供者地址
*/
// url for peer-to-peer invocation
private String url;
private T createProxy(Map<String, String> map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
// 是否本地引用
final boolean isJvmRefer;
// injvm 属性为空,不通过该属性判断
if (isInjvm() == null) {
// 直连服务提供者,参见文档《直连提供者》http://dubbo.apache.org/zh-cn/docs/user/demos/explicit-target.html
if (url != null && url.length() > 0) { // if a url is specified, don't do local reference
isJvmRefer = false;
// 通过 `tmpUrl` 判断,是否需要本地引用
} else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
// by default, reference local service if there is
isJvmRefer = true;
// 默认不是
} else {
isJvmRefer = false;
}
// 通过 injvm 属性。
} else {
isJvmRefer = isInjvm();
}
// 本地引用
if (isJvmRefer) {
// 创建服务引用 URL 对象
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
// 引用服务,返回 Invoker 对象
invoker = refprotocol.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
// 正常流程,一般为远程引用
} else {
// ... 省略本文暂时不分享的服务远程引用
}
}
// 启动时检查
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true; // default true
}
if (c && !invoker.isAvailable()) {
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
// 创建 Service 代理对象
// create service proxy
return (T) proxyFactory.getProxy(invoker);
}
上面调用InjvmProtocol的isInjvmRefer(url) 方法,通过 tmpUrl 判断,是否需要本地引用。使用 tmpUrl ,相当于使用服务引用配置对象的配置项:
/**
* 是否本地引用
*
* @param url URL
* @return 是否
*/
public boolean isInjvmRefer(URL url) {
final boolean isJvmRefer;
String scope = url.getParameter(Constants.SCOPE_KEY);
// Since injvm protocol is configured explicitly, we don't need to set any extra flag, use normal refer process.
// 当 `protocol = injvm` 时,本身已经是 jvm 协议了,走正常流程就是了。
if (Constants.LOCAL_PROTOCOL.toString().equals(url.getProtocol())) {
isJvmRefer = false;
// 当 `scope = local` 或者 `injvm = true` 时,本地引用
} else if (Constants.SCOPE_LOCAL.equals(scope) || (url.getParameter("injvm", false))) {
// if it's declared as local reference
// 'scope=local' is equivalent to 'injvm=true', injvm will be deprecated in the future release
isJvmRefer = true;
// 当 `scope = remote` 时,远程引用
} else if (Constants.SCOPE_REMOTE.equals(scope)) {
// it's declared as remote reference
isJvmRefer = false;
// 当 `generic = true` 时,即使用泛化调用,远程引用。
} else if (url.getParameter(Constants.GENERIC_KEY, false)) {
// generic invocation is not local reference
isJvmRefer = false;
// 当本地已经有该 Exporter 时,直接本地引用
} else if (getExporter(exporterMap, url) != null) {
// by default, go through local reference if there's the service exposed locally
isJvmRefer = true;
// 默认,远程引用
} else {
isJvmRefer = false;
}
return isJvmRefer;
}
refer(type, url) 方法:
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 注册中心
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
// 引用服务,返回 Invoker 对象
// 给改 Invoker 对象,包装成带有 Filter 过滤链的 Invoker 对象
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
ProtocolListenerWrapper:refer(type, url) 方法:
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 注册中心协议
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
// 引用服务
Invoker<T> invoker = protocol.refer(type, url);
// 获得 InvokerListener 数组
List<InvokerListener> listeners = Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(InvokerListener.class).getActivateExtension(url, Constants.INVOKER_LISTENER_KEY));
// 创建 ListenerInvokerWrapper 对象
return new ListenerInvokerWrapper<T>(invoker, listeners);
}
InjvmProtocol : refer(type, url) 方法 : 创建 InjvmInvoker 对象。注意,传入的 exporterMap 参数,包含所有的 InjvmExporter 对象。
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
}
AbstractInvoker ,实现 Invoker 接口,抽象 Invoker 类,主要提供了 Invoker 的通用属性和 invoke(Invocation) 方法的通用实现。属性:
/**
* 接口类型
*/
private final Class<T> type;
/**
* 服务 URL
*/
private final URL url;
/**
* 公用的隐式传参。在 {@link #invoke(Invocation)} 方法中使用。
*/
private final Map<String, String> attachment;
/**
* 是否可用
*/
private volatile boolean available = true;
/**
* 是否销毁
*/
private AtomicBoolean destroyed = new AtomicBoolean(false);
InjvmInvoker ,实现 AbstractInvoker 抽象类,Injvm Invoker 实现类, 属性:
/**
* 服务键
*/
private final String key;
/**
* Exporter 集合
*
* key: 服务键
*
* 该值实际就是 {@link com.alibaba.dubbo.rpc.protocol.AbstractProtocol#exporterMap}
*/
private final Map<String, Exporter<?>> exporterMap;
InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap) {
super(type, url);
this.key = key;
this.exporterMap = exporterMap;
}
isAvailable() 方法,是否可用:
开启启动时检查时,调用该方法,判断该 Invoker 对象,是否有对应的 Exporter 。若不存在,说明依赖服务不存在,检查不通过。
@Override
public boolean isAvailable() {
// 判断是否有 Exporter 对象
InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key);
if (exporter == null) {
return false;
} else {
return super.isAvailable();
}
}
ListenerInvokerWrapper ,实现 Invoker 接口,具有监听器功能的 Invoker 包装器:
public class ListenerInvokerWrapper<T> implements Invoker<T> {
private static final Logger logger = LoggerFactory.getLogger(ListenerInvokerWrapper.class);
/**
* 真实的 Invoker 对象
*/
private final Invoker<T> invoker;
/**
* Invoker 监听器数组
*/
private final List<InvokerListener> listeners;
public ListenerInvokerWrapper(Invoker<T> invoker, List<InvokerListener> listeners) {
if (invoker == null) {
throw new IllegalArgumentException("invoker == null");
}
this.invoker = invoker;
this.listeners = listeners;
// 执行监听器
if (listeners != null && !listeners.isEmpty()) {
for (InvokerListener listener : listeners) {
if (listener != null) {
try {
listener.referred(invoker);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
}
}
}
public Class<T> getInterface() {
return invoker.getInterface();
}
public URL getUrl() {
return invoker.getUrl();
}
public boolean isAvailable() {
return invoker.isAvailable();
}
public Result invoke(Invocation invocation) throws RpcException {
return invoker.invoke(invocation);
}
@Override
public String toString() {
return getInterface() + " -> " + (getUrl() == null ? " " : getUrl().toString());
}
public void destroy() {
try {
invoker.destroy();
} finally {
// 执行监听器
if (listeners != null && !listeners.isEmpty()) {
for (InvokerListener listener : listeners) {
if (listener != null) {
try {
listener.destroyed(invoker);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
}
}
}
}
}
InvokerListener ,Invoker 监听器:
@SPI
public interface InvokerListener {
/**
* The invoker referred
*
* 当服务引用完成
*
* @param invoker
* @throws RpcException
* @see com.alibaba.dubbo.rpc.Protocol#refer(Class, URL)
*/
void referred(Invoker<?> invoker) throws RpcException;
/**
* The invoker destroyed.
*
* 当服务销毁引用完成
*
* @param invoker
* @see com.alibaba.dubbo.rpc.Invoker#destroy()
*/
void destroyed(Invoker<?> invoker);
}
InvokerListenerAdapter ,实现 InvokerListener 接口,InvokerListener 适配器抽象类:
public abstract class InvokerListenerAdapter implements InvokerListener {
public void referred(Invoker<?> invoker) throws RpcException { }
public void destroyed(Invoker<?> invoker) { }
}
DeprecatedInvokerListener ,实现 InvokerListenerAdapter 抽象类 ,引用废弃的服务时,打印错误日志提醒:
@Activate(Constants.DEPRECATED_KEY)
public class DeprecatedInvokerListener extends InvokerListenerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(DeprecatedInvokerListener.class);
public void referred(Invoker<?> invoker) throws RpcException {
if (invoker.getUrl().getParameter(Constants.DEPRECATED_KEY, false)) {
LOGGER.error("The service " + invoker.getInterface().getName() + " is DEPRECATED! Declare from " + invoker.getUrl());
}
}
}
@Activate(Constants.DEPRECATED_KEY) 注解,基于 Dubbo SPI Activate 机制加载。配置方式如下:
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" deprecated="true" />
本地引用服务的配置方式如下 (本地引用服务时,不是使用服务提供者的 URL ,而是服务消费者的 URL ):
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" protocol="injvm">
<dubbo:parameter key="deprecated" value="true" />
</dubbo:reference>