今天启动dubbo,提供者没问题,消费者报错,提示 没有可用的提供者
然后具体跟了以下dubbo代码:
根据报错的位置是在
org.apache.dubbo.config.ReferenceConfig#checkInvokerAvailable
private void checkInvokerAvailable() throws IllegalStateException {
if (shouldCheck() && !invoker.isAvailable()) {//shouldCheck() 默认是开启检查的,invoker.isAvailable()判断是否可用
invoker.destroy();
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());
}
}
问题应该是就出在了 invoker.isAvailable()中,其中invoker是由
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
产生的,所有应该是DubboInvoker,我们看下DubboInvoker
@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;
}
根据断点提示 client.isConnected() 等于 false,说明此处client是不可用的,继续进去client.isConnected()的方法
org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeClient#isConnected
@Override
public boolean isConnected() {
return channel.isConnected();
}
org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeChannel#isConnected
@Override
public boolean isConnected() {
return channel.isConnected();
}
此处我们看下channel的对接端信息
这就奇怪了,原本我是单机起的两个服务进行调用,为啥ip不一致呢,目前通过 ifconfig 查询 我本机ip应该是 192.168.124.2,即消费端ip是正确的,提供端ip错误了。
然后我们定位下 服务端的 ip 获取方式,因为RegistryProtocol是开始zk注册的入口,我们直接看下 org.apache.dubbo.registry.integration.RegistryProtocol#export
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
//此方法用于组织提供端的服务信息,所以我们断点看下providerUrl信息
URL providerUrl = getProviderUrl(originInvoker);
。。。
}
由图可以看到 此处已经获取到了一个错误的ip
我们继续看下如何获取的这个错误ip
private URL getProviderUrl(final Invoker<?> originInvoker) {
//String EXPORT_KEY = "export"
Object providerURL = originInvoker.getUrl().getAttribute(EXPORT_KEY);
if (!(providerURL instanceof URL)) {
throw new IllegalArgumentException("The registry export url is null! registry: " + originInvoker.getUrl().getAddress());
}
return (URL)providerURL;
}
由此处我们得知 ip是url中存储的,对于存储 我们知道在 ServiceConfig的doExportUrlsFor1Protocol方法中会存储map信息到url中
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
、、、
//获取host
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
、、、
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
//EXPORT_KEY = "export",将url保存进url中
registryURL.putAttribute(EXPORT_KEY, url));
}
我们马上就接近真像了,继续看下findConfigedHosts方法
private String findConfigedHosts(ProtocolConfig protocolConfig,
List<URL> registryURLs,
Map<String, String> map) {
、、、
logger.info("No valid ip found from environment, try to find valid host from DNS.");
hostToBind = InetAddress.getLocalHost().getHostAddress();
、、、
}
然后我们执行下 InetAddress.getLocalHost().getHostAddress() 发现 确实是 错误的ip:192.168.1.3
为什么呢?
此处我们找到了问题的根源,当前我们的mac环境的hostname是篡改为了虚拟的,
wangwenyong@wangwenyong ~ % hostname
bogon
具体查看文章中解释及修复方式: 点击此链接
既然我们看到了这里,索性 看下 消费端的获取方式是什么:
我们可以从org.apache.dubbo.registry.integration.RegistryProtocol#refer入手,既然订阅 肯定要有自己的ip
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
、、、
//REFER_KEY = "refer"
Map<String, String> qs = (Map<String, String>)url.getAttribute(REFER_KEY);
、、、
return doRefer(cluster, registry, type, url, qs);
}
protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url, Map<String, String> parameters) {
//REGISTER_IP_KEY = "register.ip"
URL consumerUrl = new URL(parameters.get(PROTOCOL_KEY) == null ? DUBBO : parameters.get(PROTOCOL_KEY), parameters.get(REGISTER_IP_KEY), 0, getPath(parameters, type), parameters);
url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl);
ClusterInvoker<T> migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl);
return interceptInvoker(migrationInvoker, url, consumerUrl, url);
}
我们看下 url中的refer中的register.ip的存储时机的代码,毫无疑问 在org.apache.dubbo.config.ReferenceConfig中
我们直接搜索 REGISTER_IP_KEY便可定位到 在org.apache.dubbo.config.ReferenceConfig#init中
public synchronized void init() {
、、、
String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);
if (StringUtils.isEmpty(hostToRegistry)) {
//此处为获取ip
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
map.put(REGISTER_IP_KEY, hostToRegistry);
、、、
}
所以我们得出结论 在消费端 使用的 NetUtils.getLocalHost() 来获取,提供端用的 InetAddress.getLocalHost().getHostAddress()导致的ip不一致,又因为 计算机名错误 获取ip错误
问题的发现多谢了 http://blog.chinaunix.net/uid-540802-id-138782.html 文章的指点
版权归原作者 m0_54850825 所有, 如有侵权,请联系我们删除。