一概述
我们知道在spring整合dubbo以后,在spring容器中取客户端对象时通过关键字ID来获取,跟平常的取法一模一样。那么这章来分析下spring怎么把dubbo框架的客户端对象加载到spring容器中的,怎么创建接口的代理对象,然后如何调用到远程服务器的真实的接口实现。
二初始化客户端配置
Spring在初始化IOC容器,通过DubboNamespaceHandler类来解析dubbo相关标签,在解析客户端标签dubbo:reference时,标签解析出来的相关属性都是存储到ReferenceBean类中,因为ReferenceBean类实现了InitializingBean接口,所以在设置标签所有属性后会调用afterPropertiesSet方法,关于标签bean载体类实现spring框架的InitializingBean接口相关知识可以自己百度了解下,有很多写的比较好的文章,这里就不在细说。具体看afterPropertiesSet方法如下:@SuppressWarnings({ "unchecked"})
public void afterPropertiesSet() throws Exception {
//判断当前ConsumerConfig是否存在,如果不存在从spring容器中取相关的ConsumerConfig对象,并设置到当前属性中
if (getConsumer() == null) {
Map<String, ConsumerConfig> consumerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class, false, false);
if (consumerConfigMap != null && consumerConfigMap.size() > 0) {
ConsumerConfig consumerConfig = null;
for (ConsumerConfig config : consumerConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (consumerConfig != null) {
throw new IllegalStateException("Duplicate consumer configs: " + consumerConfig + " and " + config);
}
consumerConfig = config;
}
}
if (consumerConfig != null) {
//设置当前ConsumerConfig对象
setConsumer(consumerConfig);
}
}
}
//判断当前ApplicationConfig是否存在,不存在从spring容器中获取,并关联到当前类的属性中
if (getApplication() == null
&& (getConsumer() == null || getConsumer().getApplication() == null)) {
Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
ApplicationConfig applicationConfig = null;
for (ApplicationConfig config : applicationConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (applicationConfig != null) {
throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
}
applicationConfig = config;
}
}
if (applicationConfig != null) {
//设置当前ApplicationConfig属性
setApplication(applicationConfig);
}
}
}
//判断当前ModuleConfig是否存在,不存在从spring容器中获取,并关联到当前类属性中
if (getModule() == null
&& (getConsumer() == null || getConsumer().getModule() == null)) {
Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
ModuleConfig moduleConfig = null;
for (ModuleConfig config : moduleConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (moduleConfig != null) {
throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
}
moduleConfig = config;
}
}
if (moduleConfig != null) {
//设置当前ModuleConfig对象
setModule(moduleConfig);
}
}
}
//判断当前List<RegistryConfig>注册中心是否存在,如果不存在从spring容器中获取,并关联到当前类属性中
if ((getRegistries() == null || getRegistries().size() == 0)
&& (getConsumer() == null || getConsumer().getRegistries() == null || getConsumer().getRegistries().size() == 0)
&& (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
if (registryConfigMap != null && registryConfigMap.size() > 0) {
List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
for (RegistryConfig config : registryConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
registryConfigs.add(config);
}
}
if (registryConfigs != null && registryConfigs.size() > 0) {
//设置当前的注册中心
super.setRegistries(registryConfigs);
}
}
}
//判断当前MonitorConfig监控中心是否存在,如果不存在从spring容器中获取
if (getMonitor() == null
&& (getConsumer() == null || getConsumer().getMonitor() == null)
&& (getApplication() == null || getApplication().getMonitor() == null)) {
Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
MonitorConfig monitorConfig = null;
for (MonitorConfig config : monitorConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (monitorConfig != null) {
throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
}
monitorConfig = config;
}
}
if (monitorConfig != null) {
//设置当前的监控中心
setMonitor(monitorConfig);
}
}
}
Boolean b = isInit();
if (b == null && getConsumer() != null) {
b = getConsumer().isInit();
}
//是否容器启动加载时,立即初始化,默认是不立刻初始化处理,可以通过在dubbo:reference标签里面配置init=true来设置
if (b != null && b.booleanValue()) {
getObject();
}
}
这里整个方法都是在做一些的Consumer,Application,Module,Registries等标签配置信息的检测,在方法的最后有个if判断,默认Boolean
b属性的值为false,表示不做初始化。如果需要立刻做初始化需要在dubbo:reference标签里面配置init=true来设置,执行getObject方法。系统默认配置是不立刻初始化处理,那么getObject方法什么时候执行呢?这里有个跟spring容器相关的知识点。因为在解析dubbo:reference标签加载ReferenceBean时,ReferenceBean类是实现了FactoryBean接口的,并重写了getObject方法,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,当根据该Bean的Id从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象。也就是说我们在使用dubbo框架获取客户端对象的时候
,其实是执行了getObject方法返回的对象。好了我们继续看dubbo框架源中的getObject方法,这是创建接口代理对象的关键入口处//返回在spring容器中根据id找对象
public Object getObject() throws Exception {
return get();
}
public synchronized T get() {
if (destroyed){
throw new IllegalStateException("Already destroyed!");
}
//ref为空时就开始初始化返回的对象,也就是说根据id在spring容器中拿到的对象是ref属性。
if (ref == null) {
init();
}
return ref;
}
方法加入了synchronized锁机制防止并发问题,里面判断了ref属性是否为空,为空就调用init方法初始化。所以ref应该就是最终的接口代理对象属性吧!private void init() {
//防止多次初始化
if (initialized) {
return;
}
//设置已经初始化
initialized = true;
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");
}
// 获取消费者全局配置
//检查当前ConsumerConfig 是否存在,否则新建一个
checkDefault();
appendProperties(this);
//是否使用泛型接口
if (getGeneric() == null && getConsumer() != null) {
//设置泛型接口
setGeneric(getConsumer().getGeneric());
}
if (ProtocolUtils.isGeneric(getGeneric())) {
//如果使用了泛型接口,设置泛型接口
interfaceClass = GenericService.class;
} else {
//否则设置设置指定的interfaceName为泛型接口
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
//检测标签配置中的方法在接口中是否存在
checkInterfaceAndMethods(interfaceClass, methods);
}
//这块没看懂是做什么,好像是在加载什么属性配置文件
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 " + resolveFile + ", cause: " + e.getMessage(), e);
} 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) {
url = resolve;
if (logger.isWarnEnabled()) {
if (resolveFile != null && resolveFile.length() > 0) {
logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
} else {
logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
}
}
}
//再次检测ConsumerConfig配置
if (consumer != null) {
if (application == null) {
application = consumer.getApplication();
}
if (module == null) {
module = consumer.getModule();
}
if (registries == null) {
registries = consumer.getRegistries();
}
if (monitor == null) {
monitor = consumer.getMonitor();
}
}
//再次检测module配置
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
//再次检测application配置
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
//检测application
checkApplication();
checkStubAndMock(interfaceClass);
Map<String, String> map = new HashMap<String, String>();
Map<Object, Object> attributes = new HashMap<Object, Object>();
//设置map版本,时间,进程号 等属性值
map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
//设置pid值
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);
}
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if(methods.length == 0) {
logger.warn("NO method found in service interface " + interfaceClass.getName());
map.put("methods", Constants.ANY_VALUE);
}
else {
map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
map.put(Constants.INTERFACE_KEY, interfaceName);
//设置application中属性值到map中
appendParameters(map, application);
//设置module中属性值到map中
appendParameters(map, module);
//设置consumer中属性值到map中
appendParameters(map, consumer, Constants.DEFAULT_KEY);
//设置当前标签bean属性值到map中
appendParameters(map, this);
String prifix = StringUtils.getServiceKey(map);
if (methods != null && methods.size() > 0) {
for (MethodConfig method : methods) {
appendParameters(map, method, method.getName());
String retryKey = method.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
map.put(method.getName() + ".retries", "0");
}
}
appendAttributes(attributes, method, prifix + "." + method.getName());
checkAndConvertImplicitConfig(method, map, attributes);
}
}
//attributes通过系统context进行存储.
StaticContext.getSystemContext().putAll(attributes);
//重点地方根据map中的属性创建接口的代理对象,map中保存了application,module,consumer,reference配置信息
ref = createProxy(map);
}
init总体思路就是再次检测了Consumer,Application,Module,Registries等配置信息,并将配置信息全部存入到Map中。注意最后一段代码 ref = createProxy(map); 看代码应该就能理解它的作用创建代理对象,并传入了前面存进所有参数的map属性。重点的地方来了代理是怎么创建出来的。三创建客户端invoker对象。createProxy方法详情如下
@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
private T createProxy(Map<String, String> map) {
//根据map中的属性值生成URL对象
URL tmpUrl = new URL("temp", "localhost", 0, map);
final boolean isJvmRefer;
if (isInjvm() == null) {
if (url != null && url.length() > 0) { //指定URL的情况下,不做本地引用
isJvmRefer = false;
} else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
//默认情况下如果本地有服务暴露,则引用本地服务.
isJvmRefer = true;
} else {
isJvmRefer = false;
}
} else {
isJvmRefer = isInjvm().booleanValue();
}
if (isJvmRefer) {
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
invoker = refprotocol.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
//判断当前客户端是否是点对点直连,直连会跳过注册中心
//直连或者注册连接的url都会存储在urls中
if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心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.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 {
// 通过注册中心配置拼装URL
List<URL> us = loadRegistries(false);
if (us != null && us.size() > 0) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
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.size() == 0) {
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) {
//只有一个注册服务器时,生成客户端的代理invoker
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
//当有多个注册服务器时,生成多个客户端代理
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
//多个服务端,生成多个对应的invoker对象
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最后一个注册服务器作为注册中心 registry url
}
}
if (registryURL != null) { // 有 注册中心协议的URL
// 对有注册中心的集群 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 注册中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
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());
}
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
}
上面方法中主要做了一件事情就是创建客户端接口的invoker对象,我们重点解析这段代码 invoker = refprotocol.refer(interfaceClass, url);refprotocol对象是什么怎么创建的,创建代码如下 Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();具体如何创建出来的后面专门通过章节来讲解, 这里我就默认refprotocol属性是DubboProtocol类了,查看该类的refer方法如下:
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
// modified by lishen
optimizeSerialization(url);
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
invoker对象原来就是DubboInvoker类。因为DubboInvoker继承了AbstractInvoker类,它重写了父类的doInvoke方法。在看看AbstractInvoker代码,它的invoke方法中最终还是执行了doInvoke方法,因为当前类的doInvoke是抽象的,所以最终还是执行了DubboInvoker中的doInvoke方法。按个人猜想这里的invoke应该就是处理客户端请求的入口吧!public Result invoke(Invocation inv) throws RpcException {
if(destroyed) {
throw new RpcException("Rpc invoker for service " + this + " on consumer " + NetUtils.getLocalHost()
+ " use dubbo version " + Version.getVersion()
+ " is DESTROYED, can not be invoked any more!");
}
RpcInvocation invocation = (RpcInvocation) inv;
invocation.setInvoker(this);
if (attachment != null && attachment.size() > 0) {
invocation.addAttachmentsIfAbsent(attachment);
}
Map<String, String> context = RpcContext.getContext().getAttachments();
if (context != null) {
invocation.addAttachmentsIfAbsent(context);
}
if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
}
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
try {
//执行了子类的doInvoke方法
return doInvoke(invocation);
} catch (InvocationTargetException e) { // biz exception
Throwable te = e.getTargetException();
if (te == null) {
return new RpcResult(e);
} else {
if (te instanceof RpcException) {
((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
}
return new RpcResult(te);
}
} catch (RpcException e) {
if (e.isBiz()) {
return new RpcResult(e);
} else {
throw e;
}
} catch (Throwable e) {
return new RpcResult(e);
}
}
好吧我们继续查看DubboInvoker该类的代码如下:public class DubboInvoker<T> extends AbstractInvoker<T> {
private final ExchangeClient[] clients;
private final AtomicPositiveInteger index = new AtomicPositiveInteger();
private final String version;
private final ReentrantLock destroyLock = new ReentrantLock();
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){
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;
}
@Override
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
if (isOneway) {
//只负责发送消息,不需要等待反馈接口,所以结果始终未NULL
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
} else if (isAsync) {
//异步请求
ResponseFuture future = currentClient.request(inv, timeout) ;
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
//默认阻塞请求
RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();
}
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
@Override
public boolean isAvailable() {
if (!super.isAvailable())
return false;
for (ExchangeClient client : clients){
if (client.isConnected() && !client.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY)){
//cannot write == not Available ?
return true ;
}
}
return false;
}
public void destroy() {
//防止client被关闭多次.在connect per jvm的情况下,client.close方法会调用计数器-1,当计数器小于等于0的情况下,才真正关闭
if (super.isDestroyed()){
return ;
} else {
//dubbo check ,避免多次关闭
destroyLock.lock();
try{
if (super.isDestroyed()){
return ;
}
super.destroy();
if (invokers != null){
invokers.remove(this);
}
for (ExchangeClient client : clients) {
try {
client.close();
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
}finally {
destroyLock.unlock();
}
}
}
}
注意看doInvoke方法中的实现,从ExchangeClient
clients[]数组中取出一个对象currentClient将客户端接口请求信息发送给服务端处理。注意了这里的ExchangeClient保存的是客户端与服务器建立的socket链接对象,也就是netty的客户端对象。 在发送请求处理有3个逻辑判断1.只负责发送请求,不需要等待反馈消息
2.异步请求
3.默认是阻塞的请求
四 创建netty客户端链接对象
上面已经讲到了通过netty的客户端发消息给服务器请求,那netty客户端是怎么创建的?如果看了前面服务端的发布分析应该会很快明白!这里我们回到创建invoker对象的代码,如下:DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);看看里面调用了getClients方法。
private ExchangeClient[] getClients(URL url){
//是否共享连接
boolean service_share_connect = false;
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 {
//不共享链接,那么开启netty客户端连接对象
clients[i] = initClient(url);
}
}
return clients;
}
里面有是否共享链接对象的一个逻辑判断处理,创建链接对象需跟进initClient方法的处理。/**
* 创建新连接.
*/
private ExchangeClient initClient(URL url) {
// client type setting.
String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
String version = url.getParameter(Constants.DUBBO_VERSION_KEY);
boolean compatible = (version != null && version.startsWith("1.0."));
url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() && compatible ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
//默认开启heartbeat
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
// BIO存在严重性能问题,暂时不允许使用
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(), " "));
}
ExchangeClient client ;
try {
//设置连接应该是lazy的
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)){
client = new LazyConnectExchangeClient(url ,requestHandler);
} else {
//新建一个netty客户端链接
client = Exchangers.connect(url ,requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service(" + url
+ "): " + e.getMessage(), e);
}
return client;
}
看到该方法最下面的catch块代码:Exchangers.connect(url ,requestHandler);调用connect时传入了url和requestHandler两个属性,你会发现netty的客户端和服务器响应处理器都使用了相同的处理器。前面已经分析过,这里就不废话了。继续往connect方法中看。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");
return getExchanger(url).connect(url, handler);
}
/**
* 开启netty客户端链接
* @param url
* @param handler
* @return
* @throws RemotingException
*/
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
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 = new ChannelHandlerDispatcher(handlers);
}
return getTransporter().connect(url, handler);
}
//创建netty客户端链接对象
public Client connect(URL url, ChannelHandler listener) throws RemotingException {
return new NettyClient(url, listener);
}
看看这一连串的connect方法的处理最终生成了NettyClient对象,并设置netty客户端响应处理。到这里已经一层一层剥离到netty框架层次了。熟悉netty的朋友可以继续往下看非常容易看懂dubbo的实现了。
四创建接口代理对象
终于到看到代理的处理,一路坎坷!我们回到代理的创建方法ReferenceConfig.createProxy中最后一行的代码return (T) proxyFactory.getProxy(invoker); 这里的proxyFactory属性请允许我默认当做AbstractProxyFactory类实现
public abstract class AbstractProxyFactory implements ProxyFactory {
public <T> T getProxy(Invoker<T> invoker) 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];
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};
}
return getProxy(invoker, interfaces);
}
public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);
}
最后还是执行了抽象的getProxy方法。因为JavassistProxyFactory类继承了AbstractProxyFactory,并重写了getProxy方法所以最终还是执行了JavassistProxyFactory类中的getProxy方法。@SuppressWarnings("unchecked")
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
getProxy方法中的实现完全依靠了jdk自带的动态代理生成功能,不太清楚的可以查看下相关资料,估计一看就明白啦。到这里就已经生成了客户端接口的代理实现对象。其实可以将生成的代理对象类结构代码贴出来,能更好的帮助你理解里面的执行机制!
五 总结
如果你看玩dubbo的服务发布代码分析,估计看客户端的发布应该比较轻松。主要还是涉及到的知识点任然还是那些!不过刚开始看的时候没太理解ReferenceBean类实现FactoryBean,InitializingBean接口的机制绕了一些弯路。其它没什么想说的!下章来仔细分析下dubbo里面的SPI技术对接口的扩展,感觉这SPI应该放在最前面讲解比较好,因为源码中接口的实现对象获取跟它有很大的关系!提前讲解会比较容易理解。后面有时间把整个章节顺序调整下!