上篇本地,这篇远程。
createProxy(map) 方法中,涉及远程引用服务:
/**
* 服务引用 URL 数组
*/
private final List<URL> urls = new ArrayList<URL>();
/**
* 直连服务地址
*
* 1. 可以是注册中心,也可以是服务提供者
* 2. 可配置多个,使用 ; 分隔
*/
// url for peer-to-peer invocation
private String url;
/**
* 创建 Service 代理对象
*
* @param map 集合
* @return 代理对象
*/
@SuppressWarnings({
"unchecked", "rawtypes", "deprecation"})
private T createProxy(Map<String, String> map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
// 【省略代码】是否本地引用
final boolean isJvmRefer;
// 【省略代码】本地引用
if (isJvmRefer) {
// 正常流程,一般为远程引用
} else {
// 定义直连地址,可以是服务提供者的地址,也可以是注册中心的地址
if (url != null && url.length() > 0) {
// user specified URL, could be peer-to-peer address, or register center's address.
// 拆分地址成数组,使用 ";" 分隔。
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
// 循环数组,添加到 `url` 中。
if (us != null && us.length > 0) {
for (String u : us) {
// 创建 URL 对象
URL url = URL.valueOf(u);
// 设置默认路径
if (url.getPath() == null || url.getPath().length() == 0) {
url = url.setPath(interfaceName);
}
// 注册中心的地址,带上服务引用的配置参数
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
// 服务提供者的地址
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
// 注册中心
} else {
// assemble URL from register center's configuration
// 加载注册中心 URL 数组
List<URL> us = loadRegistries(false);
// 循环数组,添加到 `url` 中。
if (us != null && !us.isEmpty()) {
for (URL u : us) {
// 加载监控中心 URL
URL monitorUrl = loadMonitor(u);
// 服务引用配置对象 `map`,带上监控中心的 URL
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
// 注册中心的地址,带上服务引用的配置参数
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))); // 注册中心,带上服务引用的配置参数
}
}
if (urls == null || urls.isEmpty()) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config to your spring config.");
}
}
// 单 `urls` 时,引用服务,返回 Invoker 对象
if (urls.size() == 1) {
// 引用服务
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
// 循环 `urls` ,引用服务,返回 Invoker 对象
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
// 引用服务
invokers.add(refprotocol.refer(interfaceClass, url));
// 使用最后一个注册中心的 URL
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // use last registry url
}
}
// 有注册中心
if (registryURL != null) {
// registry url is available
// 对有注册中心的 Cluster 只用 AvailableCluster
// use AvailableCluster only when register's cluster is available
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
// 无注册中心
} else {
// not a registry url
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
// 【省略代码】启动时检查
// 创建 Service 代理对象
// create service proxy
return (T) proxyFactory.getProxy(invoker);
}
mdzz这看来看去跟最早一篇远程暴露极像啊,
ProtocolFilterWrapper:refer(type, url) 方法:
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 注册中心:当 invoker.url.protocl = registry ,注册中心的 URL ,无需创建 Filter 过滤链
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);
}
RegistryProtocol:refer(type, url) 方法:
/**
* Cluster 自适应拓展实现类对象
*/
private Cluster cluster;
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 获得真实的注册中心的 URL
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
// 获得注册中心
Registry registry = registryFactory.getRegistry(url);
// TODO 芋艿
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// 获得服务引用配置参数集合
// group="a,b" or group="*"
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
// 分组聚合,参见文档 http://dubbo.apache.org/zh-cn/docs/user/demos/group-merger.html
if (group != null && group.length() > 0) {
if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
|| "*".equals(group)) {
// 执行服务引用
return doRefer(getMergeableCluster(), registry, type, url);
}
}
// 执行服务引用
return doRefer(cluster, registry, type, url);
}
getMergeableCluster() 方法,获得可合并的 Cluster 对象:
private Cluster getMergeableCluster() {
return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("mergeable");
}
doRefer(cluster, registry, type, url) 方法,执行服务引用的逻辑:
/**
* 执行服务引用,返回 Invoker 对象
*
* @param cluster Cluster 对象
* @param registry 注册中心对象
* @param type 服务接口类型
* @param url 注册中心 URL
* @param 泛型
* @return Invoker 对象
*/
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
// 创建 RegistryDirectory 对象,并设置注册中心
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// 创建订阅 URL
// all attributes of REFER_KEY
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters()); // 服务引用配置集合
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
// 向注册中心注册自己(服务消费者)
if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
&& url.getParameter(Constants.REGISTER_KEY, true)) {
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
// 向注册中心订阅服务提供者
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
// 创建 Invoker 对象
Invoker invoker = cluster.join(directory);
// 向本地注册表,注册消费者
ProviderConsumerRegTable.registerConsuemr(invoker, url, subscribeUrl, directory);
return invoker;
}
DubboProtocol:refer(type, url) 方法:
// AbstractProtocol.java 父类
/**
* Invoker 集合
*/
//TODO SOFEREFENCE
protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
// DubboProtocol.java
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
// 初始化序列化优化器
optimizeSerialization(url);
// 获得远程通信客户端数组
// 创建 DubboInvoker 对象
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
// 添加到 `invokers`
invokers.add(invoker);
return invoker;
}
getClients(url) 方法,获得连接服务提供者的远程通信客户端数组:
/**
* 获得连接服务提供者的远程通信客户端数组
*
* @param url 服务提供者 URL
* @return 远程通信客户端
*/
private ExchangeClient[] getClients(URL url) {
// 是否共享连接
// whether to share connection
boolean service_share_connect = false;
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
// if not configured, connection is shared, otherwise, one connection for one service
if (connections == 0) {
// 未配置时,默认共享
service_share_connect = true;
connections = 1;
}
// 创建连接服务提供者的 ExchangeClient 对象数组
ExchangeClient[] clients = new ExchangeClient[connections];
for (int i = 0; i < clients.length; i++) {
if (service_share_connect) {
// 共享
clients[i] = getSharedClient(url);
} else {
// 不共享
clients[i] = initClient(url);
}
}
return clients;
}
#getClients(url) 方法,获得连接服务提供者的远程通信客户端数组:
/**
* 通信客户端集合
*
* key: 服务器地址。格式为:host:port
*/
private final Map<String, ReferenceCountExchangeClient> referenceClientMap = new ConcurrentHashMap<String, ReferenceCountExchangeClient>(); //
/**
* TODO 8030 ,这个是什么用途啊。
*
* key: 服务器地址。格式为:host:port 。和 {@link #referenceClientMap} Key ,是一致的。
*/
private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap = new ConcurrentHashMap<String, LazyConnectExchangeClient>();
private ExchangeClient getSharedClient(URL url) {
// 从集合中,查找 ReferenceCountExchangeClient 对象
String key = url.getAddress();
ReferenceCountExchangeClient client = referenceClientMap.get(key);
if (client != null) {
// 若未关闭,增加指向该 Client 的数量,并返回它
if (!client.isClosed()) {
client.incrementAndGetCount();
return client;
// 若已关闭,移除
} else {
referenceClientMap.remove(key);
}
}
// 同步,创建 ExchangeClient 对象。
synchronized (key.intern()) {
// 创建 ExchangeClient 对象
ExchangeClient exchangeClient = initClient(url);
// 将 `exchangeClient` 包装,创建 ReferenceCountExchangeClient 对象
client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
// 添加到集合
referenceClientMap.put(key, client);
// 添加到 `ghostClientMap`
ghostClientMap.remove(key);
return client;
}
}
ghostClientMap,幽灵客户端集合:
添加:每次 ReferenceCountExchangeClient 彻底关闭( 指向归零 ) ,其内部的 client 会替换成重新创建的 LazyConnectExchangeClient 对象,此时叫这个对象为幽灵客户端,添加到 ghostClientMap 中。
移除:当幽灵客户端,对应的 URL 的服务器被重新连接上后,会被移除。
注意,在幽灵客户端被移除之前,referenceClientMap 中,依然保留着对应的 URL 的 ReferenceCountExchangeClient 对象。所以,ghostClientMap 相当于标记 referenceClientMap 中,哪些 LazyConnectExchangeClient 对象,是幽灵状态。
initClient(url) 方法,创建 ExchangeClient 对象,”连接”服务器:
private ExchangeClient initClient(URL url) {
// 校验 Client 的 Dubbo SPI 拓展是否存在
// client type setting.
String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
// BIO is not allowed since it has severe performance issue.
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported client type: " + str + "," +
" supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
}
// 设置编解码器为 Dubbo ,即 DubboCountCodec
url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
// 默认开启 heartbeat
// enable heartbeat by default
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
// 连接服务器,创建客户端
ExchangeClient client;
try {
// 懒连接,创建 LazyConnectExchangeClient 对象
// connection should be lazy
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
client = new LazyConnectExchangeClient(url, requestHandler);
// 直接连接,创建 HeaderExchangeClient 对象
} else {
client = Exchangers.connect(url, requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
}
return client;
}
DubboInvoker实现 AbstractExporter 抽象类,Dubbo Invoker 实现类:
/**
* 远程通信客户端数组
*/
private final ExchangeClient[] clients;
/**
* 使用的 {@link #clients} 的位置
*/
private final AtomicPositiveInteger index = new AtomicPositiveInteger();
/**
* 版本
*/
private final String version;
/**
* 销毁锁
*
* 在 {@link #destroy()} 中使用
*/
private final ReentrantLock destroyLock = new ReentrantLock();
/**
* Invoker 集合,从 {@link DubboProtocol#invokers} 获取
*/
private final Set<Invoker<?>> invokers;
public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients) {
this(serviceType, url, clients, null);
}
public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers) {
//调用父类构造方法。该方法中,会将 interface group version token timeout 添加到公用的隐式传参 AbstractInvoker.attachment 属性。
super(serviceType, url, new String[]{
Constants.INTERFACE_KEY, Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY});
this.clients = clients;
// get version.
this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0");
this.invokers = invokers;
}
ReferenceCountExchangeClient实现 ExchangeClient 接口,支持指向计数的信息交换客户端实现类。
构造方法:
/**
* URL
*/
private final URL url;
/**
* 指向数量
*/
private final AtomicInteger refenceCount = new AtomicInteger(0);
/**
* 幽灵客户端集合
*/
private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap;
/**
* 客户端
*/
private ExchangeClient client;
public ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap) {
this.client = client;
// 指向加一
refenceCount.incrementAndGet();
this.url = client.getUrl();
if (ghostClientMap == null) {
throw new IllegalStateException("ghostClientMap can not be null, url: " + url);
}
this.ghostClientMap = ghostClientMap;
}
装饰器模式,每个实现方法都是调用client的对应方法:
@Override
public void send(Object message) throws RemotingException {
client.send(message);
}
计数:
public void incrementAndGetCount() {
refenceCount.incrementAndGet();
}
关闭:
@Override
public void close(int timeout) {
if (refenceCount.decrementAndGet() <= 0) {
// 关闭 `client`
if (timeout == 0) {
client.close();
} else {
client.close(timeout);
}
// 替换 `client` 为 LazyConnectExchangeClient 对象。
client = replaceWithLazyClient();
}
}
上面调用replaceWithLazyClient() 方法,替换 client 为 LazyConnectExchangeClient 对象:
private LazyConnectExchangeClient replaceWithLazyClient() {
// this is a defensive operation to avoid client is closed by accident, the initial state of the client is false
URL lazyUrl = url.addParameter(Constants.LAZY_CONNECT_INITIAL_STATE_KEY, Boolean.FALSE)
.addParameter(Constants.RECONNECT_KEY, Boolean.FALSE) // 不重连
.addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString())
.addParameter("warning", Boolean.TRUE.toString())
.addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true)
.addParameter("_client_memo", "referencecounthandler.replacewithlazyclient"); // 备注
// 创建 LazyConnectExchangeClient 对象,若不存在。
String key = url.getAddress();
// in worst case there's only one ghost connection.
LazyConnectExchangeClient gclient = ghostClientMap.get(key);
if (gclient == null || gclient.isClosed()) {
gclient = new LazyConnectExchangeClient(lazyUrl, client.getExchangeHandler());
ghostClientMap.put(key, gclient);
}
return gclient;
}
LazyConnectExchangeClient实现 ExchangeClient 接口,支持懒连接服务器的信息交换客户端实现类。
构造:
static final String REQUEST_WITH_WARNING_KEY = "lazyclient_request_with_warning";
/**
* URL
*/
private final URL url;
/**
* 通道处理器
*/
private final ExchangeHandler requestHandler;
/**
* 连接锁
*/
private final Lock connectLock = new ReentrantLock();
/**
* lazy connect 如果没有初始化时的连接状态
*/
// lazy connect, initial state for connection
private final boolean initialState;
/**
* 通信客户端
*/
private volatile ExchangeClient client;
/**
* 请求时,是否检查告警
*/
protected final boolean requestWithWarning;
/**
* 警告计数器。每超过一定次数,打印告警日志。参见 {@link #warning(Object)}
*/
private AtomicLong warningcount = new AtomicLong(0);
public LazyConnectExchangeClient(URL url, ExchangeHandler requestHandler) {
// lazy connect, need set send.reconnect = true, to avoid channel bad status.
this.url = url.addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString());
this.requestHandler = requestHandler;
this.initialState = url.getParameter(Constants.LAZY_CONNECT_INITIAL_STATE_KEY, Constants.DEFAULT_LAZY_CONNECT_INITIAL_STATE);
this.requestWithWarning = url.getParameter(REQUEST_WITH_WARNING_KEY, false);
}
isConnected() 方法:
@Override
public boolean isConnected() {
if (client == null) {
// 客户端未初始化
return initialState;
} else {
return client.isConnected();
}
}
warningcount 属性,警告计数器。每超过一定次数,打印告警日志。每次发送请求时,会调用 warning(request) 方法,根据情况,打印告警日志:
private void warning(Object request) {
if (requestWithWarning) {
// 开启
if (warningcount.get() % 5000 == 0) {
// 5000 次
logger.warn(new IllegalStateException("safe guard client , should not be called ,must have a bug."));
}
warningcount.incrementAndGet(); // 增加计数
}
}
初始化客户端:
private void initClient() throws RemotingException {
// 已初始化,跳过
if (client != null) {
return;
}
if (logger.isInfoEnabled()) {
logger.info("Lazy connect to " + url);
}
// 获得锁
connectLock.lock();
try {
// 已初始化,跳过
if (client != null) {
return;
}
// 创建 Client ,连接服务器
this.client = Exchangers.connect(url, requestHandler);
} finally {
// 释放锁
connectLock.unlock();
}
}
发送消息/请求前,都会调用该方法,保证客户端已经初始化:
public void send(Object message, boolean sent) throws RemotingException {
initClient();
client.send(message, sent);
}
@Override
public ResponseFuture request(Object request, int timeout) throws RemotingException {
warning(request);
initClient();
return client.request(request, timeout);
}