Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

集成 dubbo 时,直连服务是可以的,但是通过注册中心就不成功了 #15

Closed
JimmyDaddy opened this issue Oct 17, 2018 · 12 comments
Assignees
Labels
bug Something isn't working good first issue Good for newcomers

Comments

@JimmyDaddy
Copy link

JimmyDaddy commented Oct 17, 2018

  • 集成 dubbo 时,AddressGroup 的 addressList 为空
// sofa-rpc-node/lib/client/consumer.js 中

  async _init() {
    this._addressGroup = this.createAddressGroup(this.id + '@' + this.group);
    if (this.options.serverHost) {
      const addressList = this.options.serverHost.split(',').map(url => this.parseUrl(url));
      setImmediate(() => { this._addressGroup.addressList = addressList; });
    } else {
      await this.registry.ready();
      this._addressGroup = this.createAddressGroup(this.id + '@' + this.group);
      this._addressListener = addressList => {
// addressList 为 []
        this._addressGroup.addressList = addressList.map(url => this.parseUrl(url));
      };
      this.registry.subscribe(this.registryConfig, this._addressListener);
    }
    await this._addressGroup.ready();
  }

  • 上述原因导致 invoke 时 执行
async getConnection(req) {
    return await this._addressGroup.getConnection(req);
  }

时返回 null

想知道为什么 addressList 是空的?

集成 dubbo 时,直连服务是可以的,但是通过注册中心就不成功了

还有 group 的设置貌似不成功,直连的时候我需要将 group 名字写在 interfaceName 里面才行,单独配置 group 或者在初始化 client 时设置 group 都不成功

@gxcsoccer
Copy link
Member

#17

@gxcsoccer
Copy link
Member

服务端代码用的是官方的 demo,然后将 registry 配置成 zookeeper

https://github.com/apache/incubator-dubbo/tree/master/dubbo-demo/dubbo-demo-provider

'use strict';

const { RpcClient } = require('sofa-node-rpc').client;
const { ZookeeperRegistry } = require('sofa-node-rpc').registry;
const protocol = require('dubbo-remoting');
const logger = console;

const registry = new ZookeeperRegistry({
  logger,
  address: '127.0.0.1:2181/dubbo/',  // 注意:这里需要以 /dubbo/ 结尾
});

async function invoke() {
  const client = new RpcClient({
    logger,
    registry,
    protocol,
    group: 'dubbo', // 这个根据实际情况填
    version: null, // 这个根据实际情况填
  });
  const consumer = client.createConsumer({
    interfaceName: 'org.apache.dubbo.demo.DemoService',
  });
  await consumer.ready();

  const result = await consumer.invoke('sayHello', [{
    $class: 'java.lang.String',
    $: 'zongyu',
  }], { responseTimeout: 3000 });
  console.log(result);
}

invoke().catch(console.error);

@JimmyDaddy
Copy link
Author

JimmyDaddy commented Oct 30, 2018

我按照 demo 试了一下,注册中心连接成功了,但是 group 的设置貌似依然不对
这里是我的代码:

'use strict'

const { RpcClient } = require('sofa-rpc-node').client
const { ZookeeperRegistry } = require('sofa-rpc-node').registry
const { RpcServer } = require('sofa-rpc-node').server
const protocol = require('dubbo-remoting')
const logger = console

const registry = new ZookeeperRegistry({
  logger,
  address: '127.0.0.1:2181/dubbo/'
})
async function call() {
  const client = new RpcClient({
    logger: app.logger,
    protocol,
    registry,
    group: 'HSF'
  })

  const consumer = client.createConsumer({
    interfaceName: 'com.demo.provider.UserBaseQueryProvider',
    version: '1.0.0.dev' //指定版本
  })

  await consumer.ready()

  const result = await consumer.invoke('getUserInfo',
    [{
      $class: ' com.demo.provider.param.UserInfoQueryDTO',
      $: {
        mobilePhoneLike: {
          $class: 'java.lang.String',
          $: '157'
        }
      }
    }], {
      responseTimeout: 3000
    })
  console.log('==============result======================')
  console.log(result)
  console.log('====================================')
  return result
}

结果运行报错:

com.alibaba.dubbo.remoting.RemotingException: 
Not found exported service: 
com.demo.provider.UserBaseQueryProvider: 1.0 .0.dev: 21880 
in 
[
  HSF / com.demo.provider.UserBaseProvider: 1.0 .0.dev: 21880,  
  HSF / com.demo.provider.customer.CustomerIdentityProvider: 1.0 .0.dev: 21880, 
  HSF / com.demo.testclient.provider.city.BaseUserCityRelationshipProvider: 1.0 .0.dev: 21880, 
  HSF / com.demo.testclient.provider.permission.menu.PermissionMenuProvider: 1.0 .0.dev: 21880, 
  HSF / com.demo.provider.UserBaseTagProvider: 1.0 .0.dev: 21880,
  HSF / com.demo.provider.UserBaseQueryProvider: 1.0 .0.dev: 21880
], 
may be version or group mismatch, channel: consumer: /121.*.*.1:55359 --> provider: /
121.*.* .202: 21880, message: RpcInvocation[
  methodName = getUserInfo, 
  parameterTypes = [class com.demo.provider.param.UserInfoQueryDTO], 
  arguments = [com.demo.provider.param.UserInfoQueryDTO @86f0588], 
  attachments = {
    path = com.demo.provider.UserBaseQueryProvider,
    input = 433,
    service = com.demo.provider.UserBaseQueryProvider: 1.0 .0.dev,
    dubbo = 5.3 .0,
    version = 1.0 .0.dev
  }
]

并且程序还尝试了去连接这个接口的另外两个版本(1.0.0.daily 和 1.0.0.test)(我并没有去指定连接它们),抛出警告:

2018-10-30 10:52:27,188 WARN 15449 [ConnectionManager] create connection: dubbo://10.*.*.68:12210/com.demo.provider.UserBaseQueryProvider?ROUTE=-1&_CONTAINERID=ecc:e63288c0-17ca-4d62-bb79-a765c447d2fb&_ENV=DEFAULT&_SERIALIZETYPE=hessian&_TID=dcc6f6d8-6697-4d65-b520-5c4ae10d507f&_TIMEOUT=3000&_ih2=y&_p=hessian2&application=d58921e6-953c-4f37-9228-efd1b6413eb1&dubbo=2.6.1&group=HSF&interface=com.demo.provider.UserBaseQueryProvider&side=provider&timeout=3000&v=2.0&version=1.0.0.daily failed, caused by socket#dubbo://10.*.*.68:12210/com.demo.provider.UserBaseQueryProvider?ROUTE=-1&_CONTAINERID=ecc:e63288c0-17ca-4d62-bb79-a765c447d2fb&_ENV=DEFAULT&_SERIALIZETYPE=hessian&_TID=dcc6f6d8-6697-4d65-b520-5c4ae10d507f&_TIMEOUT=3000&_ih2=y&_p=hessian2&application=d58921e6-953c-4f37-9228-efd1b6413eb1&dubbo=2.6.1&group=HSF&interface=com.demo.provider.UserBaseQueryProvider&side=provider&timeout=3000&v=2.0&version=1.0.0.daily connect timeout(3000ms)
2018-10-30 10:52:27,189 WARN 15449 [ConnectionManager] create connection: dubbo://172.*.*.6:12202/com.demo.provider.UserBaseQueryProvider?APP=f42e78eb-d28c-4921-b058-248e99841976&ROUTE=-1&_CONTAINERID=ecc:8b6b6208-8dce-4b68-a80d-b2f3240363ee&_ENV=DEFAULT&_SERIALIZETYPE=hessian&_TID=5c098aaa-2bf1-4782-a641-fd63ec280452&_TIMEOUT=3000&_p=hessian2&application=f42e78eb-d28c-4921-b058-248e99841976&dubbo=2.6.1&group=HSF&interface=com.demo.provider.UserBaseQueryProvider&side=provider&timeout=3000&v=2.0&version=1.0.0.test failed, caused by socket#dubbo://172.*.*.6:12202/com.demo.provider.UserBaseQueryProvider?APP=f42e78eb-d28c-4921-b058-248e99841976&ROUTE=-1&_CONTAINERID=ecc:8b6b6208-8dce-4b68-a80d-b2f3240363ee&_ENV=DEFAULT&_SERIALIZETYPE=hessian&_TID=5c098aaa-2bf1-4782-a641-fd63ec280452&_TIMEOUT=3000&_p=hessian2&application=f42e78eb-d28c-4921-b058-248e99841976&dubbo=2.6.1&group=HSF&interface=com.demo.provider.UserBaseQueryProvider&side=provider&timeout=3000&v=2.0&version=1.0.0.test connect timeout(3000ms)

@gxcsoccer
Copy link
Member

我大概知道了,稍等

@gxcsoccer
Copy link
Member

#18

@JimmyDaddy 重新安装一下,再试试

@gxcsoccer gxcsoccer self-assigned this Oct 31, 2018
@gxcsoccer gxcsoccer added bug Something isn't working good first issue Good for newcomers labels Oct 31, 2018
@JimmyDaddy
Copy link
Author

问题还是存在的
我在 1.5.1 版本里面
通过 registry 报错:

no provider of *** found!

直连是可以成功的,
我稍微看了一下,貌似还是 address 获取失败的原因

  async getConnection(req) {
    const meta = req.meta;
    meta.connectionGroup = this.key;

    const address = this._loadbalancer.select(req); // address 为 null
    if (!address) return null;

    const { connectionOpts, connectionClass } = this.options;
    return await this.connectionManager.createAndGet(address, connectionOpts, connectionClass);
  }

测试代码和上面的 demo 一样

@gxcsoccer
Copy link
Member

@JimmyDaddy 把完整的日志给我

@gxcsoccer
Copy link
Member

要具体看下订阅到的 url,之前都可以访问到机器,只是 group 没有匹配上,现在找不到,说明是新加的逻辑把它过滤掉了

@JimmyDaddy
Copy link
Author

JimmyDaddy commented Nov 1, 2018

日志并不多,仅仅抛出了一个我上面提到的错误;
我看了看源码打了些日志,执行请求时:

  • client/consumer.js_init() 里:this.registryConfig 为:
{ 
  protocol: 'bolt',
  interfaceName: 'com.demo.provider.UserBaseQueryProvider',
  version: '1.0.0.dev',
  group: 'HSF',
  appName: undefined, 
  timeout: 3000 
}
  • registry/zk/data_client.js 里面 subscribe 方法:
subscribe(config, listener) {
    assert(config && config.interfaceName, '[ZookeeperRegistry] subscribe(config, listener) config.interfaceName is required');
    const interfaceName = config.interfaceName;

    if (!this._subscribeMap.has(interfaceName)) {
      this._subscribeMap.set(interfaceName, null);
      const providerPath = this._buildProviderPath(config); // /dubbo/com.sxc.jothuheim.userbaseclient.provider.UserBaseQueryProvider/providers
      this._zkClient.mkdirp(providerPath)
        .then(() => {
          this._zkClient.watchChildren(providerPath, (err, children) => {
            if (err) {
              this.emit('error', err);
              return;
            }
            console.log('=============children=======================');
            console.log( children); // 这里的 children 返回了四个版本的接口,是我们服务器上已有的
            console.log('====================================');
            const originAddressList = children.map(url => urlencode.decode(url));
            const addressList = originAddressList.filter(url => this._isMatch(config, url));
            this.logger.info('[ZookeeperRegistry] receive interface:%s:%s@%s address list (%d):\n%s\nvalid providers (%d):\n%s',
              config.interfaceName, config.version || '', config.group || '',
              originAddressList.length, formatAddrs(originAddressList), addressList.length, formatAddrs(addressList));
            this._subscribeMap.set(interfaceName, addressList);
            this.emit(interfaceName, addressList);
          });
        })
        .catch(err => { this.emit('error', err); });

      const consumerPath = this._buildConsumerPath(config);
      const consumerUrl = fmt('%s://%s?uniqueId=%s&version=%s&pid=%s&timeout=%s&appName=%s&serialization=%s&startTime=',
        config.protocol, localIp, config.uniqueId || '', '1.0', process.pid, config.timeout, config.appName || '', Date.now());

      const path = consumerPath + '/' + urlencode.encode(consumerUrl);
      this._zkClient.mkdirp(consumerPath)
        .then(() => {
          return this._zkClient.create(path, EMPTY, CreateMode.EPHEMERAL);
        })
        .catch(err => {
          this.logger.warn('[ZookeeperRegistry] create consumerPath: %s failed, caused by %s', path, err.message);
        });
    } else {
      const addressList = this._subscribeMap.get(interfaceName);
      if (addressList) {
        setImmediate(() => { listener(addressList); });
      }
    }
    this.on(interfaceName, listener);
  }

其中 this._zkClient.watchChildren 返回的 四个版本的 children 是我们已有的四个版本,分别为:

  • dubbo://192.168.0.59:21880/com.demo.provider.UserBaseQueryProvider?anyhost=true&application=user-service&default.group=HSF&default.version=1.0.0.yrc&dubbo=2.0.1&generic=false&interface=com.provider.UserBaseQueryProvider&logger=slf4j&methods=getListUserProperty,getMDSUserInfoByEdata,getByIds,getById,getByPhoneAndPassword,getByEdata,getUserInfoPage,getMockUserInfoByCatGroupId,getUserInfo,getByMobilePhone&pid=1379&revision=1.0.0-SNAPSHOT&side=provider&timestamp=1541040082598
    这一个的 provider 是局域网 ip,
    它的 providerInfo 为 :
URLSearchParams {
  'anyhost' => 'true',
  'application' => 'user-service',
  'default.group' => 'HSF',
  'default.version' => '1.0.0.yrc',
  'dubbo' => '2.0.1',
  'generic' => 'false',
  'interface' => 'com.demo.provider.UserBaseQueryProvider',
  'logger' => 'slf4j',
  'methods' => 'getListUserProperty,getMDSUserInfoByEdata,getByIds,getById,getByPhoneAndPassword,getByEdata,getUserInfoPage,getMockUserInfoByCatGroupId,getUserInfo,getByMobilePhone',
  'pid' => '30009',
  'revision' => '1.0.0-SNAPSHOT',
  'side' => 'provider',
  'timestamp' => '1540797787690' }
  • dubbo://121.*.*.*:21880/com.demo.provider.UserBaseQueryProvider?anyhost=true&application=user-service&default.group=HSF&default.version=1.0.0.dev&dubbo=2.0.1&generic=false&interface=com.provider.UserBaseQueryProvider&logger=slf4j&methods=getListUserProperty,getMDSUserInfoByEdata,getByIds,getById,getByPhoneAndPassword,getByEdata,getUserInfoPage,getMockUserInfoByCatGroupId,getUserInfo,getByMobilePhone&pid=30009&revision=1.0.0-SNAPSHOT&side=provider&timestamp=1540797787690
    这一个就是我要访问的那一个版本(1.0.0.dev)的 provider ,宿主是外网 ip 可访问的,直连能返回数据,但是通过 registry 就无法定位到它了,而且这个和 下面两个可定位到的 provider 有很大的不同 ,
    它的 providerInfo 为:
URLSearchParams {
  'anyhost' => 'true',
  'application' => 'user-service',
  'default.group' => 'HSF', // group 和 version 变为了 default.group 和 default.version
  'default.version' => '1.0.0.dev',
  'dubbo' => '2.0.1',
  'generic' => 'false',
  'interface' => 'com.demo.provider.UserBaseQueryProvider',
  'logger' => 'slf4j',
  'methods' => 'getListUserProperty,getMDSUserInfoByEdata,getByIds,getById,getByPhoneAndPassword,getByEdata,getUserInfoPage,getMockUserInfoByCatGroupId,getUserInfo,getByMobilePhone',
  'pid' => '30009',
  'revision' => '1.0.0-SNAPSHOT',
  'side' => 'provider',
  'timestamp' => '1540797787690' }
  • dubbo://172.*.*.6:12200/com.demo.provider.UserBaseQueryProvider?APP=f42e78eb-d28c-4921-b058-248e99841976&ROUTE=-1&_CONTAINERID=ecc:8b6b6208-8dce-4b68-a80d-b2f3240363ee&_ENV=DEFAULT&_SERIALIZETYPE=hessian&_TID=5c098aaa-2bf1-4782-a641-fd63ec280452&_TIMEOUT=3000&_p=hessian2&application=f42e78eb-d28c-4921-b058-248e99841976&dubbo=2.6.1&group=HSF&interface=com.provider.UserBaseQueryProvider&side=provider&timeout=3000&v=2.0&version=1.0.0.test
    这一个是属于内网 ip,我尝试过访问该版本的服务,能够定位到这个服务,因为内网 Ip 的缘故连接时 timeout ,

providerInfo 为 :

URLSearchParams {
  'ROUTE' => '-1',
  '_CONTAINERID' => 'ecc:e63288c0-17ca-4d62-bb79-a765c447d2fb',
  '_ENV' => 'DEFAULT',
  '_SERIALIZETYPE' => 'hessian',
  '_TID' => 'dcc6f6d8-6697-4d65-b520-5c4ae10d507f',
  '_TIMEOUT' => '3000',
  '_ih2' => 'y',
  '_p' => 'hessian2',
  'application' => 'd58921e6-953c-4f37-9228-efd1b6413eb1',
  'dubbo' => '2.6.1',
  'group' => 'HSF',
  'interface' => 'com.demo.provider.UserBaseQueryProvider',
  'side' => 'provider',
  'timeout' => '3000',
  'v' => '2.0',
  'version' => '1.0.0.test' }
  • dubbo://10.*.*.68:12210/com.demo.provider.UserBaseQueryProvider?ROUTE=-1&_CONTAINERID=ecc:e63288c0-17ca-4d62-bb79-a765c447d2fb&_ENV=DEFAULT&_SERIALIZETYPE=hessian&_TID=dcc6f6d8-6697-4d65-b520-5c4ae10d507f&_TIMEOUT=3000&_ih2=y&_p=hessian2&application=d58921e6-953c-4f37-9228-efd1b6413eb1&dubbo=2.6.1&group=HSF&interface=com.provider.UserBaseQueryProvider&side=provider&timeout=3000&v=2.0&version=1.0.0.daily
    这一个是属于内网 ip,我尝试过访问该版本的服务,也能够定位到这个服务,因为内网 Ip 的缘故连接时 timeout ,
    providerInfo 为 :
URLSearchParams {
  'ROUTE' => '-1',
  '_CONTAINERID' => 'ecc:e63288c0-17ca-4d62-bb79-a765c447d2fb',
  '_ENV' => 'DEFAULT',
  '_SERIALIZETYPE' => 'hessian',
  '_TID' => 'dcc6f6d8-6697-4d65-b520-5c4ae10d507f',
  '_TIMEOUT' => '3000',
  '_ih2' => 'y',
  '_p' => 'hessian2',
  'application' => 'd58921e6-953c-4f37-9228-efd1b6413eb1',
  'dubbo' => '2.6.1',
  'group' => 'HSF',
  'interface' => 'com.demo.provider.UserBaseQueryProvider',
  'side' => 'provider',
  'timeout' => '3000',
  'v' => '2.0',
  'version' => '1.0.0.daily' }

前两个是直接注册到 zk 上的,后两个则是通过 edas 注册到 zk 上的,并且前两个的 dubbo 版本 为 2.0.1 ,后两个版本为 2.6.1

我发现问题出在 data_client.js_isMatch 方法上:

 _isMatch(consumer, urlStr) {
    const url = new URL(urlStr);
    const providerInfo = url.searchParams || {};
    const interfaceName = providerInfo.get('interface') || url.pathname.slice(1);
    if (interfaceName && consumer.interfaceName !== interfaceName) {
      return false;
    }
    const category = providerInfo.get('category');
    if (category && category !== 'providers') {
      return false;
    }
    const enabled = providerInfo.get('enabled');
    if (enabled && enabled !== 'true') {
      return false;
    }
    const consumerGroup = consumer.group;
    const consumerVersion = consumer.version;
    const providerGroup = providerInfo.get('group'); // group 有可能为 default.group
    const providerVersion = providerInfo.get('version'); // version 有可能为 default.version
    return (!consumerGroup || consumerGroup === providerGroup) &&
      (!consumerVersion || consumerVersion === providerVersion);
  }

我将 _isMatch 里面的两行代码改了一下:

    const providerGroup = providerInfo.get('group');
    const providerVersion = providerInfo.get('version');

改为

    const providerGroup = providerInfo.get('group') || providerInfo.get('default.group');
    const providerVersion = providerInfo.get('version') || providerInfo.get('default.version');

就能访问成功了

@gxcsoccer
Copy link
Member

@JimmyDaddy ok,我改下

@gxcsoccer
Copy link
Member

@JimmyDaddy 再试试

@JimmyDaddy
Copy link
Author

好了,没问题了@gxcsoccer 谢谢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

2 participants