文章目录
服务消费端应该做哪些事情
- 生成一个代理对象(帮我们实现网络通信的细节)
- 建立通信连接(netty)
- 从zk去获取目标地址(订阅节点的变化)
- 负载均衡
- 容错
- mock降级策略
- 序列化
引用远程服务的两种方式:
- dubbo直连(开发,测试环境)
- 基于注册中心
重点分析通过注册中心引用服务的过程,主要包括从注册中心获取配置,Invoker创建,代理类创建
服务引用原理
服务引用时机:
- Spring容器调用ReferenceBean的afterPropertiesSet方法时引用服务(饿汉式)
- 在ReferenceBean对应的服务被注入到其他类中时引用。(懒汉式,默认的)
按默认配置来分析懒汉式引用:
入口是ReferenceBean的getObject()开始,当我们的服务被注入到其他类中时候,spring会第一时间调用getObject()方法,该方法来会执行引用逻辑。执行逻辑之前需要先进行配置检查与收集工作,然后根据收集到的配置信息来决定服务引用的方式;
- 引用本地JVM服务(在同一个jvm上,不需要走远端调用)
- 通过直连方式引用远程服务
- 通过注册中心来引用远程服务。
以上三种引用方式都会得到一个Invoker实例,如有多个注册中心,多个服务提供者会得到一组Invoker实例,此时需要Cluster将多个Invoker合并成一个实例。合并后的Invoker实例具备调用本地和远程服务的能力,但不能将此时离暴露给用户使用,如此会对用户业务代码造成侵入,所以需要通过代理工程类(ProxyFactory)为服务接口生成代理类,并让代理类去调用Invoker逻辑,避免Dubbo框架代码对业务代码造成侵入。
源码分析
1. ReferenceBean 的 getObject()
// 该方法定义在 Spring 的 FactoryBean 接口
public Object getObject() throws Exception {
return get();
}
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("Already destroyed!");
}
// 检测 ref 是否为空,为空则通过 init 方法创建
if (ref == null) {
// init 方法主要用于处理配置,以及调用 createProxy 生成代理类
init();
}
return ref;
}
1.1 处理配置
与服务端暴露服务一样,init里面首先会对dubbo的配置进行检查和处理,以保证配置的正确性。配置解析的逻辑都在ReferenceConfig 的init方法中
private void init() {
// ------------------------------------------------------------
// 这一部分代码用于检测ConsumerConfig实例是否存在,如果不存在则创建新的实例
// 通过系统变量或dubbo.properties配置文件填充ConsumerConfig的字段
// 检查泛化配置,并根据配置设置interfaceClass的值。
// 避免重复初始化
if (initialized) {
return;
}
initialized = true;
// 检测接口名合法性
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("interface not allow null!");
}
// 检测 consumer 变量是否为空,为空则创建
checkDefault();
appendProperties(this);
if (getGeneric() == null && getConsumer() != null) {
// 设置 generic
setGeneric(getConsumer().getGeneric());
}
// 检测是否为泛化接口
if (ProtocolUtils.isGeneric(getGeneric())) {
interfaceClass = GenericService.class;
} else {
try {
// 加载类
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
checkInterfaceAndMethods(interfaceClass, methods);
}
// -------------------------------✨ 分割线1 ✨------------------------------
// 这份的主要逻辑用于从该系统属性或配置文件中加载与接口名相对应的配置,并将解析结果赋值给url字段
// url字段的租用一般是用于点对点调试
// 从系统变量中获取与接口名对应的属性值
String resolve = System.getProperty(interfaceName);
String resolveFile = null;
if (resolve == null || resolve.length() == 0) {
// 从系统属性中获取解析文件路径
resolveFile = System.getProperty("dubbo.resolve.file");
if (resolveFile == null || resolveFile.length() == 0) {
// 从指定位置加载配置文件
File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
if (userResolveFile.exists()) {
// 获取文件绝对路径
resolveFile = userResolveFile.getAbsolutePath();
}
}
if (resolveFile != null && resolveFile.length() > 0) {
Properties properties = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(new File(resolveFile));
// 从文件中加载配置
properties.load(fis);
} catch (IOException e) {
throw new IllegalStateException("Unload ..., cause:...");
} finally {
try {
if (null != fis) fis.close();
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
}
// 获取与接口名对应的配置
resolve = properties.getProperty(interfaceName);
}
}
if (resolve != null && resolve.length() > 0) {
// 将 resolve 赋值给 url
url = resolve;
}
// -------------------------------✨ 分割线2 ✨------------------------------
// 这部分代码用于检测几个核心配置类是否为空,为空则尝试从其他配置类中获取
if (consumer != null) {
if (application == null) {
// 从 consumer 中获取 Application 实例,下同
application = consumer.getApplication();
}
if (module == null) {
module = consumer.getModule();
}
if (registries == null) {
registries = consumer.getRegistries();
}
if (monitor == null) {
monitor = consumer.getMonitor();
}
}
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
// 检测 Application 合法性
checkApplication();
// 检测本地存根配置合法性
checkStubAndMock(interfaceClass);
// -------------------------------✨ 分割线3 ✨------------------------------
// 这部分代码用于手机各种配置,并将配置存储到map中
Map<String, String> map = new HashMap<String, String>();
Map<Object, Object> attributes = new HashMap<Object, Object>();
// 添加 side、协议版本信息、时间戳和进程号等信息到 map 中
map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
// 非泛化服务
if (!isGeneric()) {
// 获取版本
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put("revision", revision);
}
// 获取接口方法列表,并添加到 map 中
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
map.put("methods", Constants.ANY_VALUE);
} else {
map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
map.put(Constants.INTERFACE_KEY, interfaceName);
// 将 ApplicationConfig、ConsumerConfig、ReferenceConfig 等对象的字段信息添加到 map 中
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, consumer, Constants.DEFAULT_KEY);
appendParameters(map, this);
// -------------------------------✨ 分割线4 ✨------------------------------
// 用于处理MethodConfig实例
// 该实例包含了事件通知配置,onreturn ,onthrow, oninvoke等。
String prefix = StringUtils.getServiceKey(map);
if (methods != null && !methods.isEmpty()) {
// 遍历 MethodConfig 列表
for (MethodConfig method : methods) {
appendParameters(map, method, method.getName());
String retryKey = method.getName() + ".retry";
// 检测 map 是否包含 methodName.retry
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
// 添加重试次数配置 methodName.retries
map.put(method.getName() + ".retries", "0");
}
}
// 添加 MethodConfig 中的“属性”字段到 attributes
// 比如 onreturn、onthrow、oninvoke 等
appendAttributes(attributes, method, prefix + "." + method.getName());
checkAndConvertImplicitConfig(method, map, attributes);
}
}
// -------------------------------✨ 分割线5 ✨------------------------------
// 这部分代码主要用于解析服务消费者ip,以及调用createProxy创建代理对象
// 获取服务消费者 ip 地址
String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
if (hostToRegistry == null || hostToRegistry.length() == 0) {
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property..." );
}
map.put(Constants.REGISTER_IP_KEY, hostToRegistry);
// 存储 attributes 到系统上下文中
StaticContext.getSystemContext().putAll(attributes);
// 创建代理类
ref = createProxy(map);
// 根据服务名,ReferenceConfig,代理类构建 ConsumerModel,
// 并将 ConsumerModel 存入到 ApplicationModel 中
ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
}
这部分的主要逻辑,用分割线分割了这几大块:
- 第一部分代码用于检测ConsumerConfig实例是否存在,如果不存在则创建新的实例,通过系统变量或dubbo.properties配置文件填充ConsumerConfig的字段检查泛化配置,并根据配置设置interfaceClass的值。
- 第二部分主要逻辑用于从该系统属性或配置文件中加载与接口名相对应的配置,并将解析结果赋值给url字段
url字段的租用一般是用于点对点调试
- 第三部分代码用于检测几个核心配置类是否为空,为空则尝试从其他配置类中获取
- 第四部分代码用于手机各种配置,并将配置存储到map中
- 第五部分用于处理MethodConfig实例,该实例包含了事件通知配置,onreturn ,onthrow, oninvoke等。
- 最后一部分代码主要用于解析服务消费者ip,以及调用createProxy创建代理对象
1.2 引用服务
ReferenceConfig.createProxy()开始,这个方法不仅会创建一个代理对象,而且还会调用其他方法构建以及合并invoker实例。
private T createProxy(Map<String, String> map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
final boolean isJvmRefer;
if (isInjvm() == null) {
// url 配置被指定,则不做本地引用
if (url != null && url.length() > 0) {
isJvmRefer = false;
// 根据 url 的协议、scope 以及 injvm 等参数检测是否需要本地引用
// 比如如果用户显式配置了 scope=local,此时 isInjvmRefer 返回 true
} else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
isJvmRefer = true;
} else {
isJvmRefer = false;
}
} else {
// 获取 injvm 配置值
isJvmRefer = isInjvm().booleanValue();
}
// 本地引用
if (isJvmRefer) {
// 生成本地引用 URL,协议为 injvm
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
// 调用 refer 方法构建 InjvmInvoker 实例
invoker = refprotocol.refer(interfaceClass, url);
// 远程引用
} else {
// url 不为空,表明用户可能想进行点对点调用
if (url != null && url.length() > 0) {
// 当需要配置多个 url 时,可用分号进行分割,这里会进行切分
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (url.getPath() == null || url.getPath().length() == 0) {
// 设置接口全限定名为 url 路径
url = url.setPath(interfaceName);
}
// 检测 url 协议是否为 registry,若是,表明用户想使用指定的注册中心
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
// 将 map 转换为查询字符串,并作为 refer 参数的值添加到 url 中
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
// 合并 url,移除服务提供者的一些配置(这些配置来源于用户配置的 url 属性),
// 比如线程池相关配置。并保留服务提供者的部分配置,比如版本,group,时间戳等
// 最后将合并后的配置设置为 url 查询字符串中。
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else {
// 加载注册中心 url
List<URL> us = loadRegistries(false);
if (us != null && !us.isEmpty()) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
// 添加 refer 参数到 url 中,并将 url 添加到 urls 中
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
// 未配置注册中心,抛出异常
if (urls.isEmpty()) {
throw new IllegalStateException("No such any registry to reference...");
}
}
// 单个注册中心或服务提供者(服务直连,下同)
if (urls.size() == 1) {
// 调用 RegistryProtocol 的 refer 构建 Invoker 实例
invoker = refprotocol.refer(interfaceClass, urls.get(0));
// 多个注册中心或多个服务提供者,或者两者混合
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
// 获取所有的 Invoker
for (URL url : urls) {
// 通过 refprotocol 调用 refer 构建 Invoker,refprotocol 会在运行时
// 根据 url 协议头加载指定的 Protocol 实例,并调用实例的 refer 方法
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url;
}
}
if (registryURL != null) {
// 如果注册中心链接不为空,则将使用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
// 创建 StaticDirectory 实例,并由 Cluster 对多个 Invoker 进行合并
invoker = cluster.join(new StaticDirectory(u, invokers));
} else {
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true;
}
// invoker 可用性检查
if (c && !invoker.isAvailable()) {
throw new IllegalStateException("No provider available for the service...");
}
// 生成代理类
return (T) proxyFactory.getProxy(invoker);
}
上述代码的逻辑:
- 首先根据配置检查是否为本地调用,如果是本地调用则调用InjvmProtocol生成injvmInvoker实例
- 若非本地调用,则读取直连配置项,或注册中心url,将读取到的url存储到urls里面,
- 判断urls的数量,若=1,直接通过Protocol自适应扩展类构建Invoker实例。
- 若urls数量>1,表示存在多个注册中心或服务直连url,此时根据url构建invoker,然后通过cluster合并多个invoker,最后调用ProxyFactory生成代理类
1.2.1 创建Invoker
Invoker是Dubbo的核心模型,代表一个可执行体。
在服务提供方:Invoker用于调用服务提供类。
在服务消费方:Invoker用于执行远程调用。
Invoker是由Protocol实现类构建而来,Protocol实现类有很多,RegistryProtocol和DubboProtocol,根据不同协议对应不同实现类。现在默认分析dubbo协议的实现类DubboProtocol;
DubboProtocol.refer()
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
optimizeSerialization(url);
// 创建 DubboInvoker
// getClients用于获取客户端实例,实例类型为ExchangeClient
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
getClients用于获取客户端实例,实例类型为ExchangeClient,但ExchangeClient实际上并不具备通信能力,dubbo默认使用NettyClient实例进行通信;
getClients
private ExchangeClient[] getClients(URL url) {
// 是否共享连接
boolean service_share_connect = false;
// 获取连接数,默认为0,表示未配置
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
// 如果未配置 connections,则共享连接
if (connections == 0) {
service_share_connect = true;
connections = 1;
}
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;
}
connections数量决定是获取共享客户端还是创建爱你新的客户端实例,默认情况下,使用共享客户端实例。下面来分析获取共享客户端实例的过程:
DubboProtocol.getSharedClient()
先访问缓存,若缓存未命中,则通过initClient方法创建新的ExchangeClient实例
private ExchangeClient getSharedClient(URL url) {
String key = url.getAddress();
// 获取带有“引用计数”功能的 ExchangeClient
// 先访问缓存,若缓存未命中,则通过initClient方法创建新的ExchangeClient实例
ReferenceCountExchangeClient client = referenceClientMap.get(key);
if (client != null) {
if (!client.isClosed()) {
// 增加引用计数
client.incrementAndGetCount();
return client;
} else {
referenceClientMap.remove(key);
}
}
locks.putIfAbsent(key, new Object());
synchronized (locks.get(key)) {
if (referenceClientMap.containsKey(key)) {
return referenceClientMap.get(key);
}
// 创建 ExchangeClient 客户端
ExchangeClient exchangeClient = initClient(url);
// 将 ExchangeClient 实例传给 ReferenceCountExchangeClient,这里使用了装饰模式
client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
referenceClientMap.put(key, client);
ghostClientMap.remove(key);
locks.remove(key);
return client;
}
}
** DubboProtocol.initClient(url)**
创建 ExchangeClient 客户端
initClient 方法首先获取用户配置的客户端类型,默认为netty,检测用户配置的客户端类型是否存在,不存在则抛异常。
private ExchangeClient initClient(URL url) {
// 获取客户端类型,默认为 netty
String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
// 添加编解码和心跳包参数到 url 中
url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
// 检测客户端类型是否存在,不存在则抛出异常
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported client type: ...");
}
ExchangeClient client;
try {
// 获取 lazy 配置,并根据配置值决定创建的客户端类型
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
// 创建懒加载 ExchangeClient 实例
client = new LazyConnectExchangeClient(url, requestHandler);
} else {
// 创建普通 ExchangeClient 实例
client = Exchangers.connect(url, requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service...");
}
return client;
}
** Exchangers.connect(url, requestHandler)**
创建 ExchangeClient 客户端,
public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
// 获取 Exchanger 实例,默认为 HeaderExchangeClient
// getExchanger 会通过 SPI 加载 HeaderExchangeClient 实例
return getExchanger(url).connect(url, handler);
}
HeaderExchangeClient.connect
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
// 这里包含了多个调用,分别如下:
// 1. 创建 HeaderExchangeHandler 对象
// 2. 创建 DecodeHandler 对象
// 3. 通过 Transporters 构建 Client 实例
// 4. 创建 HeaderExchangeClient 对象
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
}
Transporters.connect()
public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
ChannelHandler handler;
if (handlers == null || handlers.length == 0) {
handler = new ChannelHandlerAdapter();
} else if (handlers.length == 1) {
handler = handlers[0];
} else {
// 如果 handler 数量大于1,则创建一个 ChannelHandler 分发器
handler = new ChannelHandlerDispatcher(handlers);
}
// 获取 Transporter 自适应拓展类,并调用 connect 方法生成 Client 实例
// 这里默认加载NettyTransporter并调用该类的connect方法
return getTransporter().connect(url, handler);
}
NettyTransporter.connect()
public Client connect(URL url, ChannelHandler listener) throws RemotingException {
// 创建 NettyClient 对象
return new NettyClient(url, listener);
}
DubboProtocol的refer()到这里执行完了,再次回到RegistryProtocol的refer逻辑
RegistryProtocol.refer()
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 取 registry 参数值,并将其设置为协议头
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
// 获取注册中心实例
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// 将 url 查询字符串转为 Map
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
// 获取 group 配置
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
|| "*".equals(group)) {
// 通过 SPI 加载 MergeableCluster 实例,并调用 doRefer 继续执行服务引用逻辑
return doRefer(getMergeableCluster(), registry, type, url);
}
}
// 调用 doRefer 继续执行服务引用逻辑
return doRefer(cluster, registry, type, url);
}
首先为url设置协议头,然后根据url参数加载注册中心实例,然后获取group配置,根据group配置决定doRefer第一个参数的类型
核心方法 RegistryProtocol.doRefer()
创建 RegistryDirectory 实例
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
// 创建 RegistryDirectory 实例
//1. 构建一个协议地址保存到zk, consumer://ip:port
//2. 从注册中戏获取到地址(providerUrl)
//3. 建立连接
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
// 设置注册中心和协议
directory.setRegistry(registry);
directory.setProtocol(protocol);
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);
// 注册服务消费者,在 consumers 目录下新节点
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)));
}
// 订阅 providers、configurators、routers 等节点数据
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
/**********************************/
// 到这已经建立好了通信连接,开始考虑负载和目标调用
// 一个注册中心可能有多个服务提供者,因此这里需要将多个服务提供者合并为一个
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
主要逻辑:
- doRefer ()创建 RegistryDirectory 实例,然后生成服务者消费者链接,并向注册中心进行注册。
- 注册完毕,订阅provider,configurators,routers等节点下的数据。
- 完成订阅后,RegistryDirectory会收到这几个节点下子节点的信息,由于一个服务可能部署在多台服务器上,这样provider下就会产生多个节点,cluster会将多个服务节点合并成一个,生成一个Invoker
1.2.2 创建代理
Invoker创建完毕后,需要为服务接口生成代理对象,有了代理对象即可进行远程通信。代理对象生成的入口方法为ProxyFactory的getProxy。
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
// 调用重载方法
return getProxy(invoker, false);
}
public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {
Class<?>[] interfaces = null;
// 获取接口列表
String config = invoker.getUrl().getParameter("interfaces");
if (config != null && config.length() > 0) {
// 切分接口列表
String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
if (types != null && types.length > 0) {
interfaces = new Class<?>[types.length + 2];
// 设置服务接口类和 EchoService.class 到 interfaces 中
interfaces[0] = invoker.getInterface();
interfaces[1] = EchoService.class;
for (int i = 0; i < types.length; i++) {
// 加载接口类
interfaces[i + 1] = ReflectUtils.forName(types[i]);
}
}
}
if (interfaces == null) {
interfaces = new Class<?>[]{invoker.getInterface(), EchoService.class};
}
// 为 http 和 hessian 协议提供泛化调用支持,参考 pull request #1827
if (!invoker.getInterface().equals(GenericService.class) && generic) {
int len = interfaces.length;
Class<?>[] temp = interfaces;
// 创建新的 interfaces 数组
interfaces = new Class<?>[len + 1];
System.arraycopy(temp, 0, interfaces, 0, len);
// 设置 GenericService.class 到数组中
interfaces[len] = GenericService.class;
}
// 调用重载方法 创建代理类
return getProxy(invoker, interfaces);
}
public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);
getProxy中大部分的逻辑是通过反射机制获取interface数组,最后调用了getProxy()
Dubbo框架默认JavassistProxyFactory 来生成代理类,下面看一下JavassistProxyFactory对getProxy的实现;
// 创建代理类
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
// 生成 Proxy 子类(Proxy 是抽象类)。并调用 Proxy 子类的 newInstance 方法创建 Proxy 实例
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
- 通过Proxy的getProxy方法获取Proxy子类,然后创建InvokerInvocationHandler对象,并将该对象传给newInstance生成的Proxy实例。
- InvokerInvocationHandler实现了自jdk的InvocationHandler接口,具体的用途是拦截接口类调用。