ApplicationConfig application = new ApplicationConfig();
//下面设置ApplicationConfig的配置,省略
ReferenceConfig ref = new ReferenceConfig();
ref.setApplication(application);
RegistryConfig reg = new RegistryConfig;//单节点模式
ref.setRegistries(reg);
if(async){
ref.setAsync(async); // 设置异步调用
async = false;
}
ref.setInterface(interfaceName); // 设置接口名
ref.setGeneric(true); // 泛化
MethodConfig methodConfig = new MethodConfig(); // 方法配置
methodConfig.setName(method);
List methodList = new ArrayList();
methodList.add(methodConfig);
ref.setMethods(methodList);
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
GenericService genericService = cache.get(ref);
上面代码不是可编译的,是为了表达基本的处理步骤而拼在一起的。
调用ReferenceConfigCache.getCache()->getCache("_DEFAULT_")->getCache("_DEFAULT_",DEFAULT_KEY_GENERATOR)
DEFAULT_KEY_GENERATOR是一个内部静态类,正如它的类名所暗示的,它的主要作用就是根据ReferenceConfig生成一个key值串,由方法generateKey来实现。
最后返回的串结构为:group/interface:version。
public static final KeyGenerator DEFAULT_KEY_GENERATOR = new KeyGenerator() {
public String generateKey(ReferenceConfig> referenceConfig) {
String iName = referenceConfig.getInterface();//interface name
if(StringUtils.isBlank(iName)) {
Class> clazz = referenceConfig.getInterfaceClass();
iName = clazz.getName();
}
if(StringUtils.isBlank(iName)) {
throw new IllegalArgumentException("No interface info in ReferenceConfig" + referenceConfig);
}
StringBuilder ret = new StringBuilder();
if(! StringUtils.isBlank(referenceConfig.getGroup())) {
ret.append(referenceConfig.getGroup()).append("/");
}
ret.append(iName);
if(! StringUtils.isBlank(referenceConfig.getVersion())) {
ret.append(":").append(referenceConfig.getVersion());
}
return ret.toString();//"group/iName:version"
}
};
再转向getCache(name,keygenerator)
public static ReferenceConfigCache getCache(String name, KeyGenerator keyGenerator) {
ReferenceConfigCache cache = cacheHolder.get(name);
if(cache != null) {
return cache;
}
cacheHolder.putIfAbsent(name, new ReferenceConfigCache(name, keyGenerator));
return cacheHolder.get(name);
其中cacheHolder是一个concurrentHashMap,所以get的操作在多线程下是高效安全的。ReferenceConfigCache是一个用于缓存ReferenceConfig的工具类,只所以需要缓存是因为ReferenceConfig封装了与注册中心的连接和服务提供方的连接,如果不缓存是存在效率和内存使用问题。
@SuppressWarnings("unchecked")
public T get(ReferenceConfig referenceConfig) {
String key = generator.generateKey(referenceConfig);
ReferenceConfig> config = cache.get(key);
if(config != null) {
return (T) config.get();
}
cache.putIfAbsent(key, referenceConfig);
config = cache.get(key);
return (T) config.get();
}
首先根据生成的key(默认是group/interface:version)找到缓存的ReferenceConfig,然后会调用ReferenceConfig.get方法。
public synchronized T get() {
if (destroyed){
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
这里的ref是接口代理类的引用,对于一个ReferenceConfig来说,第一次调用时ref是null的,所以会调用init方法,另外这个方法用synchronized修饰,所以是可以并发访问的,但也会成为并发调用时的瓶颈。
private void init() {
//避免重复初始化
if (initialized) {
return;
}
//检查接口名称是否为空
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException(" interface not allow null!");
}
// 获取消费者全局配置
checkDefault();
appendProperties(this);
//
if (getGeneric() == null && getConsumer() != null) {
setGeneric(getConsumer().getGeneric());//利用ConsumerConfig配置泛型
}
//如果是泛型接口,那么interfaceClass的类型是GenericService
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);
}
String resolve = System.getProperty(interfaceName);
String resolveFile = null;
//如果服务比较多,可以指定dubbo-resolve.properties文件配置service(service集中配置文件)
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 hsf resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
} else {
logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
}
}
}
//如果application module registries monitor未配置则使用consumer的
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已关联,则关联module的regisries和monitor
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
//如果application已关联,则关联application的registries和monitor
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
checkApplication();
//检查远端和本地服务接口真实存在(是否可load)
checkStubAndMock(interfaceClass);
Map map = new HashMap();
//配置dubbo的端属性(是consumer还是provider)、版本、创建时间、进程号
Map
(1)通过volatile boolean initialized参数来避免同一个ReferenceConfig被重复初始化。
(2)检查接口名称是否为空,如果为空,抛出异常。
(3)获取ConsumerConfig的默认全局配置。
(4)如果ReferenceConfig没有配置泛型标志(这里我们是设置了),设置了ConsumerConfig的泛型标志
(5)如果是泛型调用,接口类设置为GenericService.class,否则正常设置 。
(6)在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,
在JVM启动参数中加入-D参数映射服务地址,如key为服务名,value为服务提供者url。
java
-Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890
如果服务比较多,也可以用文件映射,如用-Ddubbo.resolve.file指定映射文件路径,如果不指定会自动加载${user.home}/dubbo-resolve.properties文件。(7)如果没有通过-D参数映射服务地址,也没有通过-D参数进行文件映射,尝试读取${user.home}/dubbo-resovle.properties文件。
(8)如果没有设置ApplicationConfig,尝试从ConsumerConfig中获取。如果没有设置ModuleConfig,尝试从ConsumerConfig中获取 。
(9)如果没有设置RegistryConfig,尝试从ConsumerConfig中获取,失败再尝试从ModuleConfig中获取,失败再尝试从ApplicationConfig中获取。
(10)如果没有设置MonitorConfig,尝试从ConsumerConfig中获取,失败再尝试从ModuleConfig中获取,失败再尝试从ApplicationConfig中获取。
(11)在装载了application module consumer reference的所有属性后创建代理。
最后欢迎大家访问我的个人网站:1024s